diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go
index a62e55191e689..026d8abf81305 100644
--- a/src/cmd/asm/internal/arch/arch.go
+++ b/src/cmd/asm/internal/arch/arch.go
@@ -109,6 +109,10 @@ func archX86(linkArch *obj.LinkArch) *Arch {
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
+ if linkArch == &x86.Linkamd64 {
+ // Alias g to R14
+ register["g"] = x86.REGG
+ }
// Register prefix not used on this architecture.
instructions := make(map[string]obj.As)
diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go
index 2e83e176b297c..c6def15e20eac 100644
--- a/src/cmd/asm/internal/asm/operand_test.go
+++ b/src/cmd/asm/internal/asm/operand_test.go
@@ -259,6 +259,7 @@ var amd64OperandTests = []operandTest{
{"R15", "R15"},
{"R8", "R8"},
{"R9", "R9"},
+ {"g", "R14"},
{"SI", "SI"},
{"SP", "SP"},
{"X0", "X0"},
diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go
index 154cf9c7a7854..f1d37bc2c8d72 100644
--- a/src/cmd/asm/internal/asm/parse.go
+++ b/src/cmd/asm/internal/asm/parse.go
@@ -305,7 +305,7 @@ func (p *Parser) pseudo(word string, operands [][]lex.Token) bool {
// references and writes symabis information to w.
//
// The symabis format is documented at
-// cmd/compile/internal/gc.readSymABIs.
+// cmd/compile/internal/ssagen.ReadSymABIs.
func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
switch word {
case "TEXT":
diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go
deleted file mode 100644
index 6625ccf5e24a8..0000000000000
--- a/src/cmd/compile/fmt_test.go
+++ /dev/null
@@ -1,599 +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"
-
- // 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 {
- return filepath.ToSlash(typ.String())
-}
-
-// 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 0811df7f7b800..0000000000000
--- a/src/cmd/compile/fmtmap_test.go
+++ /dev/null
@@ -1,211 +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/gc.EscLocation %v": "",
- "*cmd/compile/internal/gc.Mpflt %v": "",
- "*cmd/compile/internal/gc.Mpint %v": "",
- "*cmd/compile/internal/gc.Node %#v": "",
- "*cmd/compile/internal/gc.Node %+S": "",
- "*cmd/compile/internal/gc.Node %+v": "",
- "*cmd/compile/internal/gc.Node %L": "",
- "*cmd/compile/internal/gc.Node %S": "",
- "*cmd/compile/internal/gc.Node %j": "",
- "*cmd/compile/internal/gc.Node %p": "",
- "*cmd/compile/internal/gc.Node %v": "",
- "*cmd/compile/internal/ssa.Block %s": "",
- "*cmd/compile/internal/ssa.Block %v": "",
- "*cmd/compile/internal/ssa.Func %s": "",
- "*cmd/compile/internal/ssa.Func %v": "",
- "*cmd/compile/internal/ssa.Register %s": "",
- "*cmd/compile/internal/ssa.Register %v": "",
- "*cmd/compile/internal/ssa.SparseTreeNode %v": "",
- "*cmd/compile/internal/ssa.Value %s": "",
- "*cmd/compile/internal/ssa.Value %v": "",
- "*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
- "*cmd/compile/internal/types.Field %p": "",
- "*cmd/compile/internal/types.Field %v": "",
- "*cmd/compile/internal/types.Sym %0S": "",
- "*cmd/compile/internal/types.Sym %S": "",
- "*cmd/compile/internal/types.Sym %p": "",
- "*cmd/compile/internal/types.Sym %v": "",
- "*cmd/compile/internal/types.Type %#L": "",
- "*cmd/compile/internal/types.Type %#v": "",
- "*cmd/compile/internal/types.Type %+v": "",
- "*cmd/compile/internal/types.Type %-S": "",
- "*cmd/compile/internal/types.Type %0S": "",
- "*cmd/compile/internal/types.Type %L": "",
- "*cmd/compile/internal/types.Type %S": "",
- "*cmd/compile/internal/types.Type %p": "",
- "*cmd/compile/internal/types.Type %s": "",
- "*cmd/compile/internal/types.Type %v": "",
- "*cmd/internal/obj.Addr %v": "",
- "*cmd/internal/obj.LSym %v": "",
- "*math/big.Float %f": "",
- "*math/big.Int %#x": "",
- "*math/big.Int %s": "",
- "*math/big.Int %v": "",
- "[16]byte %x": "",
- "[]*cmd/compile/internal/ssa.Block %v": "",
- "[]*cmd/compile/internal/ssa.Value %v": "",
- "[][]string %q": "",
- "[]byte %s": "",
- "[]byte %x": "",
- "[]cmd/compile/internal/ssa.Edge %v": "",
- "[]cmd/compile/internal/ssa.ID %v": "",
- "[]cmd/compile/internal/ssa.posetNode %v": "",
- "[]cmd/compile/internal/ssa.posetUndo %v": "",
- "[]cmd/compile/internal/syntax.token %s": "",
- "[]string %v": "",
- "[]uint32 %v": "",
- "bool %v": "",
- "byte %08b": "",
- "byte %c": "",
- "byte %q": "",
- "byte %v": "",
- "cmd/compile/internal/arm.shift %d": "",
- "cmd/compile/internal/gc.Class %d": "",
- "cmd/compile/internal/gc.Class %s": "",
- "cmd/compile/internal/gc.Class %v": "",
- "cmd/compile/internal/gc.Ctype %d": "",
- "cmd/compile/internal/gc.Ctype %v": "",
- "cmd/compile/internal/gc.Nodes %#v": "",
- "cmd/compile/internal/gc.Nodes %+v": "",
- "cmd/compile/internal/gc.Nodes %.v": "",
- "cmd/compile/internal/gc.Nodes %v": "",
- "cmd/compile/internal/gc.Op %#v": "",
- "cmd/compile/internal/gc.Op %v": "",
- "cmd/compile/internal/gc.Val %#v": "",
- "cmd/compile/internal/gc.Val %T": "",
- "cmd/compile/internal/gc.Val %v": "",
- "cmd/compile/internal/gc.fmtMode %d": "",
- "cmd/compile/internal/gc.initKind %d": "",
- "cmd/compile/internal/gc.itag %v": "",
- "cmd/compile/internal/ssa.BranchPrediction %d": "",
- "cmd/compile/internal/ssa.Edge %v": "",
- "cmd/compile/internal/ssa.GCNode %v": "",
- "cmd/compile/internal/ssa.ID %d": "",
- "cmd/compile/internal/ssa.ID %v": "",
- "cmd/compile/internal/ssa.LocalSlot %s": "",
- "cmd/compile/internal/ssa.LocalSlot %v": "",
- "cmd/compile/internal/ssa.Location %s": "",
- "cmd/compile/internal/ssa.Op %s": "",
- "cmd/compile/internal/ssa.Op %v": "",
- "cmd/compile/internal/ssa.Sym %v": "",
- "cmd/compile/internal/ssa.ValAndOff %s": "",
- "cmd/compile/internal/ssa.domain %v": "",
- "cmd/compile/internal/ssa.flagConstant %s": "",
- "cmd/compile/internal/ssa.posetNode %v": "",
- "cmd/compile/internal/ssa.posetTestOp %v": "",
- "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.Error %q": "",
- "cmd/compile/internal/syntax.Expr %#v": "",
- "cmd/compile/internal/syntax.LitKind %d": "",
- "cmd/compile/internal/syntax.Node %T": "",
- "cmd/compile/internal/syntax.Operator %s": "",
- "cmd/compile/internal/syntax.Pos %s": "",
- "cmd/compile/internal/syntax.Pos %v": "",
- "cmd/compile/internal/syntax.position %s": "",
- "cmd/compile/internal/syntax.token %q": "",
- "cmd/compile/internal/syntax.token %s": "",
- "cmd/compile/internal/types.EType %d": "",
- "cmd/compile/internal/types.EType %s": "",
- "cmd/compile/internal/types.EType %v": "",
- "cmd/internal/obj.ABI %v": "",
- "error %v": "",
- "float64 %.2f": "",
- "float64 %.3f": "",
- "float64 %.6g": "",
- "float64 %g": "",
- "int %#x": "",
- "int %-12d": "",
- "int %-6d": "",
- "int %-8o": "",
- "int %02d": "",
- "int %6d": "",
- "int %c": "",
- "int %d": "",
- "int %v": "",
- "int %x": "",
- "int16 %d": "",
- "int16 %x": "",
- "int32 %#x": "",
- "int32 %d": "",
- "int32 %v": "",
- "int32 %x": "",
- "int64 %#x": "",
- "int64 %+d": "",
- "int64 %-10d": "",
- "int64 %.5d": "",
- "int64 %d": "",
- "int64 %v": "",
- "int64 %x": "",
- "int8 %d": "",
- "int8 %v": "",
- "int8 %x": "",
- "interface{} %#v": "",
- "interface{} %T": "",
- "interface{} %p": "",
- "interface{} %q": "",
- "interface{} %s": "",
- "interface{} %v": "",
- "map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "",
- "map[*cmd/compile/internal/gc.Node][]*cmd/compile/internal/gc.Node %v": "",
- "map[cmd/compile/internal/ssa.ID]uint32 %v": "",
- "map[int64]uint32 %v": "",
- "math/big.Accuracy %s": "",
- "reflect.Type %s": "",
- "rune %#U": "",
- "rune %c": "",
- "rune %q": "",
- "string %-*s": "",
- "string %-16s": "",
- "string %-6s": "",
- "string %q": "",
- "string %s": "",
- "string %v": "",
- "time.Duration %d": "",
- "time.Duration %v": "",
- "uint %04x": "",
- "uint %5d": "",
- "uint %d": "",
- "uint %x": "",
- "uint16 %d": "",
- "uint16 %x": "",
- "uint32 %#U": "",
- "uint32 %#x": "",
- "uint32 %d": "",
- "uint32 %v": "",
- "uint32 %x": "",
- "uint64 %08x": "",
- "uint64 %b": "",
- "uint64 %d": "",
- "uint64 %x": "",
- "uint8 %#x": "",
- "uint8 %d": "",
- "uint8 %v": "",
- "uint8 %x": "",
- "uintptr %d": "",
-}
diff --git a/src/cmd/compile/internal-abi.md b/src/cmd/compile/internal-abi.md
new file mode 100644
index 0000000000000..f4ef2cc86949f
--- /dev/null
+++ b/src/cmd/compile/internal-abi.md
@@ -0,0 +1,628 @@
+# Go internal ABI specification
+
+This document describes Go’s internal application binary interface
+(ABI), known as ABIInternal.
+Go's ABI defines the layout of data in memory and the conventions for
+calling between Go functions.
+This ABI is *unstable* and will change between Go versions.
+If you’re writing assembly code, please instead refer to Go’s
+[assembly documentation](/doc/asm.html), which describes Go’s stable
+ABI, known as ABI0.
+
+All functions defined in Go source follow ABIInternal.
+However, ABIInternal and ABI0 functions are able to call each other
+through transparent *ABI wrappers*, described in the [internal calling
+convention proposal](https://golang.org/design/27539-internal-abi).
+
+Go uses a common ABI design across all architectures.
+We first describe the common ABI, and then cover per-architecture
+specifics.
+
+*Rationale*: For the reasoning behind using a common ABI across
+architectures instead of the platform ABI, see the [register-based Go
+calling convention proposal](https://golang.org/design/40724-register-calling).
+
+## Memory layout
+
+Go's built-in types have the following sizes and alignments.
+Many, though not all, of these sizes are guaranteed by the [language
+specification](/doc/go_spec.html#Size_and_alignment_guarantees).
+Those that aren't guaranteed may change in future versions of Go (for
+example, we've considered changing the alignment of int64 on 32-bit).
+
+| Type | 64-bit | | 32-bit | |
+| --- | --- | --- | --- | --- |
+| | Size | Align | Size | Align |
+| bool, uint8, int8 | 1 | 1 | 1 | 1 |
+| uint16, int16 | 2 | 2 | 2 | 2 |
+| uint32, int32 | 4 | 4 | 4 | 4 |
+| uint64, int64 | 8 | 8 | 8 | 4 |
+| int, uint | 8 | 8 | 4 | 4 |
+| float32 | 4 | 4 | 4 | 4 |
+| float64 | 8 | 8 | 8 | 4 |
+| complex64 | 8 | 4 | 8 | 4 |
+| complex128 | 16 | 8 | 16 | 4 |
+| uintptr, *T, unsafe.Pointer | 8 | 8 | 4 | 4 |
+
+The types `byte` and `rune` are aliases for `uint8` and `int32`,
+respectively, and hence have the same size and alignment as these
+types.
+
+The layout of `map`, `chan`, and `func` types is equivalent to *T.
+
+To describe the layout of the remaining composite types, we first
+define the layout of a *sequence* S of N fields with types
+t1, t2, ..., tN.
+We define the byte offset at which each field begins relative to a
+base address of 0, as well as the size and alignment of the sequence
+as follows:
+
+```
+offset(S, i) = 0 if i = 1
+ = align(offset(S, i-1) + sizeof(t_(i-1)), alignof(t_i))
+alignof(S) = 1 if N = 0
+ = max(alignof(t_i) | 1 <= i <= N)
+sizeof(S) = 0 if N = 0
+ = align(offset(S, N) + sizeof(t_N), alignof(S))
+```
+
+Where sizeof(T) and alignof(T) are the size and alignment of type T,
+respectively, and align(x, y) rounds x up to a multiple of y.
+
+The `interface{}` type is a sequence of 1. a pointer to the runtime type
+description for the interface's dynamic type and 2. an `unsafe.Pointer`
+data field.
+Any other interface type (besides the empty interface) is a sequence
+of 1. a pointer to the runtime "itab" that gives the method pointers and
+the type of the data field and 2. an `unsafe.Pointer` data field.
+An interface can be "direct" or "indirect" depending on the dynamic
+type: a direct interface stores the value directly in the data field,
+and an indirect interface stores a pointer to the value in the data
+field.
+An interface can only be direct if the value consists of a single
+pointer word.
+
+An array type `[N]T` is a sequence of N fields of type T.
+
+The slice type `[]T` is a sequence of a `*[cap]T` pointer to the slice
+backing store, an `int` giving the `len` of the slice, and an `int`
+giving the `cap` of the slice.
+
+The `string` type is a sequence of a `*[len]byte` pointer to the
+string backing store, and an `int` giving the `len` of the string.
+
+A struct type `struct { f1 t1; ...; fM tM }` is laid out as the
+sequence t1, ..., tM, tP, where tP is either:
+
+- Type `byte` if sizeof(tM) = 0 and any of sizeof(t*i*) ≠ 0.
+- Empty (size 0 and align 1) otherwise.
+
+The padding byte prevents creating a past-the-end pointer by taking
+the address of the final, empty fN field.
+
+Note that user-written assembly code should generally not depend on Go
+type layout and should instead use the constants defined in
+[`go_asm.h`](/doc/asm.html#data-offsets).
+
+## Function call argument and result passing
+
+Function calls pass arguments and results using a combination of the
+stack and machine registers.
+Each argument or result is passed either entirely in registers or
+entirely on the stack.
+Because access to registers is generally faster than access to the
+stack, arguments and results are preferentially passed in registers.
+However, any argument or result that contains a non-trivial array or
+does not fit entirely in the remaining available registers is passed
+on the stack.
+
+Each architecture defines a sequence of integer registers and a
+sequence of floating-point registers.
+At a high level, arguments and results are recursively broken down
+into values of base types and these base values are assigned to
+registers from these sequences.
+
+Arguments and results can share the same registers, but do not share
+the same stack space.
+Beyond the arguments and results passed on the stack, the caller also
+reserves spill space on the stack for all register-based arguments
+(but does not populate this space).
+
+The receiver, arguments, and results of function or method F are
+assigned to registers or the stack using the following algorithm:
+
+1. Let NI and NFP be the length of integer and floating-point register
+ sequences defined by the architecture.
+ Let I and FP be 0; these are the indexes of the next integer and
+ floating-pointer register.
+ Let S, the type sequence defining the stack frame, be empty.
+1. If F is a method, assign F’s receiver.
+1. For each argument A of F, assign A.
+1. Add a pointer-alignment field to S. This has size 0 and the same
+ alignment as `uintptr`.
+1. Reset I and FP to 0.
+1. For each result R of F, assign R.
+1. Add a pointer-alignment field to S.
+1. For each register-assigned receiver and argument of F, let T be its
+ type and add T to the stack sequence S.
+ This is the argument's (or receiver's) spill space and will be
+ uninitialized at the call.
+1. Add a pointer-alignment field to S.
+
+Assigning a receiver, argument, or result V of underlying type T works
+as follows:
+
+1. Remember I and FP.
+1. Try to register-assign V.
+1. If step 2 failed, reset I and FP to the values from step 1, add T
+ to the stack sequence S, and assign V to this field in S.
+
+Register-assignment of a value V of underlying type T works as follows:
+
+1. If T is a boolean or integral type that fits in an integer
+ register, assign V to register I and increment I.
+1. If T is an integral type that fits in two integer registers, assign
+ the least significant and most significant halves of V to registers
+ I and I+1, respectively, and increment I by 2
+1. If T is a floating-point type and can be represented without loss
+ of precision in a floating-point register, assign V to register FP
+ and increment FP.
+1. If T is a complex type, recursively register-assign its real and
+ imaginary parts.
+1. If T is a pointer type, map type, channel type, or function type,
+ assign V to register I and increment I.
+1. If T is a string type, interface type, or slice type, recursively
+ register-assign V’s components (2 for strings and interfaces, 3 for
+ slices).
+1. If T is a struct type, recursively register-assign each field of V.
+1. If T is an array type of length 0, do nothing.
+1. If T is an array type of length 1, recursively register-assign its
+ one element.
+1. If T is an array type of length > 1, fail.
+1. If I > NI or FP > NFP, fail.
+1. If any recursive assignment above fails, fail.
+
+The above algorithm produces an assignment of each receiver, argument,
+and result to registers or to a field in the stack sequence.
+The final stack sequence looks like: stack-assigned receiver,
+stack-assigned arguments, pointer-alignment, stack-assigned results,
+pointer-alignment, spill space for each register-assigned argument,
+pointer-alignment.
+The following diagram shows what this stack frame looks like on the
+stack, using the typical convention where address 0 is at the bottom:
+
+ +------------------------------+
+ | . . . |
+ | 2nd reg argument spill space |
+ | 1st reg argument spill space |
+ | |
+ | . . . |
+ | 2nd stack-assigned result |
+ | 1st stack-assigned result |
+ | |
+ | . . . |
+ | 2nd stack-assigned argument |
+ | 1st stack-assigned argument |
+ | stack-assigned receiver |
+ +------------------------------+ ↓ lower addresses
+
+To perform a call, the caller reserves space starting at the lowest
+address in its stack frame for the call stack frame, stores arguments
+in the registers and argument stack fields determined by the above
+algorithm, and performs the call.
+At the time of a call, spill space, result stack fields, and result
+registers are left uninitialized.
+Upon return, the callee must have stored results to all result
+registers and result stack fields determined by the above algorithm.
+
+There are no callee-save registers, so a call may overwrite any
+register that doesn’t have a fixed meaning, including argument
+registers.
+
+### Example
+
+Consider the function `func f(a1 uint8, a2 [2]uintptr, a3 uint8) (r1
+struct { x uintptr; y [2]uintptr }, r2 string)` on a 64-bit
+architecture with hypothetical integer registers R0–R9.
+
+On entry, `a1` is assigned to `R0`, `a3` is assigned to `R1` and the
+stack frame is laid out in the following sequence:
+
+ a2 [2]uintptr
+ r1.x uintptr
+ r1.y [2]uintptr
+ a1Spill uint8
+ a2Spill uint8
+ _ [6]uint8 // alignment padding
+
+In the stack frame, only the `a2` field is initialized on entry; the
+rest of the frame is left uninitialized.
+
+On exit, `r2.base` is assigned to `R0`, `r2.len` is assigned to `R1`,
+and `r1.x` and `r1.y` are initialized in the stack frame.
+
+There are several things to note in this example.
+First, `a2` and `r1` are stack-assigned because they contain arrays.
+The other arguments and results are register-assigned.
+Result `r2` is decomposed into its components, which are individually
+register-assigned.
+On the stack, the stack-assigned arguments appear at lower addresses
+than the stack-assigned results, which appear at lower addresses than
+the argument spill area.
+Only arguments, not results, are assigned a spill area on the stack.
+
+### Rationale
+
+Each base value is assigned to its own register to optimize
+construction and access.
+An alternative would be to pack multiple sub-word values into
+registers, or to simply map an argument's in-memory layout to
+registers (this is common in C ABIs), but this typically adds cost to
+pack and unpack these values.
+Modern architectures have more than enough registers to pass all
+arguments and results this way for nearly all functions (see the
+appendix), so there’s little downside to spreading base values across
+registers.
+
+Arguments that can’t be fully assigned to registers are passed
+entirely on the stack in case the callee takes the address of that
+argument.
+If an argument could be split across the stack and registers and the
+callee took its address, it would need to be reconstructed in memory,
+a process that would be proportional to the size of the argument.
+
+Non-trivial arrays are always passed on the stack because indexing
+into an array typically requires a computed offset, which generally
+isn’t possible with registers.
+Arrays in general are rare in function signatures (only 0.7% of
+functions in the Go 1.15 standard library and 0.2% in kubelet).
+We considered allowing array fields to be passed on the stack while
+the rest of an argument’s fields are passed in registers, but this
+creates the same problems as other large structs if the callee takes
+the address of an argument, and would benefit <0.1% of functions in
+kubelet (and even these very little).
+
+We make exceptions for 0 and 1-element arrays because these don’t
+require computed offsets, and 1-element arrays are already decomposed
+in the compiler’s SSA representation.
+
+The ABI assignment algorithm above is equivalent to Go’s stack-based
+ABI0 calling convention if there are zero architecture registers.
+This is intended to ease the transition to the register-based internal
+ABI and make it easy for the compiler to generate either calling
+convention.
+An architecture may still define register meanings that aren’t
+compatible with ABI0, but these differences should be easy to account
+for in the compiler.
+
+The algorithm reserves spill space for arguments in the caller’s frame
+so that the compiler can generate a stack growth path that spills into
+this reserved space.
+If the callee has to grow the stack, it may not be able to reserve
+enough additional stack space in its own frame to spill these, which
+is why it’s important that the caller do so.
+These slots also act as the home location if these arguments need to
+be spilled for any other reason, which simplifies traceback printing.
+
+There are several options for how to lay out the argument spill space.
+We chose to lay out each argument according to its type's usual memory
+layout but to separate the spill space from the regular argument
+space.
+Using the usual memory layout simplifies the compiler because it
+already understands this layout.
+Also, if a function takes the address of a register-assigned argument,
+the compiler must spill that argument to memory in its usual memory
+layout and it's more convenient to use the argument spill space for
+this purpose.
+
+Alternatively, the spill space could be structured around argument
+registers.
+In this approach, the stack growth spill path would spill each
+argument register to a register-sized stack word.
+However, if the function takes the address of a register-assigned
+argument, the compiler would have to reconstruct it in memory layout
+elsewhere on the stack.
+
+The spill space could also be interleaved with the stack-assigned
+arguments so the arguments appear in order whether they are register-
+or stack-assigned.
+This would be close to ABI0, except that register-assigned arguments
+would be uninitialized on the stack and there's no need to reserve
+stack space for register-assigned results.
+We expect separating the spill space to perform better because of
+memory locality.
+Separating the space is also potentially simpler for `reflect` calls
+because this allows `reflect` to summarize the spill space as a single
+number.
+Finally, the long-term intent is to remove reserved spill slots
+entirely – allowing most functions to be called without any stack
+setup and easing the introduction of callee-save registers – and
+separating the spill space makes that transition easier.
+
+## Closures
+
+A func value (e.g., `var x func()`) is a pointer to a closure object.
+A closure object begins with a pointer-sized program counter
+representing the entry point of the function, followed by zero or more
+bytes containing the closed-over environment.
+
+Closure calls follow the same conventions as static function and
+method calls, with one addition. Each architecture specifies a
+*closure context pointer* register and calls to closures store the
+address of the closure object in the closure context pointer register
+prior to the call.
+
+## Software floating-point mode
+
+In "softfloat" mode, the ABI simply treats the hardware as having zero
+floating-point registers.
+As a result, any arguments containing floating-point values will be
+passed on the stack.
+
+*Rationale*: Softfloat mode is about compatibility over performance
+and is not commonly used.
+Hence, we keep the ABI as simple as possible in this case, rather than
+adding additional rules for passing floating-point values in integer
+registers.
+
+## Architecture specifics
+
+This section describes per-architecture register mappings, as well as
+other per-architecture special cases.
+
+### amd64 architecture
+
+The amd64 architecture uses the following sequence of 9 registers for
+integer arguments and results:
+
+ RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11
+
+It uses X0 – X14 for floating-point arguments and results.
+
+*Rationale*: These sequences are chosen from the available registers
+to be relatively easy to remember.
+
+Registers R12 and R13 are permanent scratch registers.
+R15 is a scratch register except in dynamically linked binaries.
+
+*Rationale*: Some operations such as stack growth and reflection calls
+need dedicated scratch registers in order to manipulate call frames
+without corrupting arguments or results.
+
+Special-purpose registers are as follows:
+
+| Register | Call meaning | Body meaning |
+| --- | --- | --- |
+| RSP | Stack pointer | Fixed |
+| RBP | Frame pointer | Fixed |
+| RDX | Closure context pointer | Scratch |
+| R12 | None | Scratch |
+| R13 | None | Scratch |
+| R14 | Current goroutine | Scratch |
+| R15 | GOT reference temporary | Fixed if dynlink |
+| X15 | Zero value | Fixed |
+
+TODO: We may start with the existing TLS-based g and move to R14
+later.
+
+*Rationale*: These register meanings are compatible with Go’s
+stack-based calling convention except for R14 and X15, which will have
+to be restored on transitions from ABI0 code to ABIInternal code.
+In ABI0, these are undefined, so transitions from ABIInternal to ABI0
+can ignore these registers.
+
+*Rationale*: For the current goroutine pointer, we chose a register
+that requires an additional REX byte.
+While this adds one byte to every function prologue, it is hardly ever
+accessed outside the function prologue and we expect making more
+single-byte registers available to be a net win.
+
+*Rationale*: We designate X15 as a fixed zero register because
+functions often have to bulk zero their stack frames, and this is more
+efficient with a designated zero register.
+
+#### Stack layout
+
+The stack pointer, RSP, grows down and is always aligned to 8 bytes.
+
+The amd64 architecture does not use a link register.
+
+A function's stack frame is laid out as follows:
+
+ +------------------------------+
+ | return PC |
+ | RBP on entry |
+ | ... locals ... |
+ | ... outgoing arguments ... |
+ +------------------------------+ ↓ lower addresses
+
+The "return PC" is pushed as part of the standard amd64 `CALL`
+operation.
+On entry, a function subtracts from RSP to open its stack frame and
+saves the value of RBP directly below the return PC.
+A leaf function that does not require any stack space may omit the
+saved RBP.
+
+The Go ABI's use of RBP as a frame pointer register is compatible with
+amd64 platform conventions so that Go can inter-operate with platform
+debuggers and profilers.
+
+#### Flags
+
+The direction flag (D) is always cleared (set to the “forward”
+direction) at a call.
+The arithmetic status flags are treated like scratch registers and not
+preserved across calls.
+All other bits in RFLAGS are system flags.
+
+The CPU is always in MMX technology state (not x87 mode).
+
+*Rationale*: Go on amd64 uses the XMM registers and never uses the x87
+registers, so it makes sense to assume the CPU is in MMX mode.
+Otherwise, any function that used the XMM registers would have to
+execute an EMMS instruction before calling another function or
+returning (this is the case in the SysV ABI).
+
+At calls, the MXCSR control bits are always set as follows:
+
+| Flag | Bit | Value | Meaning |
+| --- | --- | --- | --- |
+| FZ | 15 | 0 | Do not flush to zero |
+| RC | 14/13 | 0 (RN) | Round to nearest |
+| PM | 12 | 1 | Precision masked |
+| UM | 11 | 1 | Underflow masked |
+| OM | 10 | 1 | Overflow masked |
+| ZM | 9 | 1 | Divide-by-zero masked |
+| DM | 8 | 1 | Denormal operations masked |
+| IM | 7 | 1 | Invalid operations masked |
+| DAZ | 6 | 0 | Do not zero de-normals |
+
+The MXCSR status bits are callee-save.
+
+*Rationale*: Having a fixed MXCSR control configuration allows Go
+functions to use SSE operations without modifying or saving the MXCSR.
+Functions are allowed to modify it between calls (as long as they
+restore it), but as of this writing Go code never does.
+The above fixed configuration matches the process initialization
+control bits specified by the ELF AMD64 ABI.
+
+The x87 floating-point control word is not used by Go on amd64.
+
+## Future directions
+
+### Spill path improvements
+
+The ABI currently reserves spill space for argument registers so the
+compiler can statically generate an argument spill path before calling
+into `runtime.morestack` to grow the stack.
+This ensures there will be sufficient spill space even when the stack
+is nearly exhausted and keeps stack growth and stack scanning
+essentially unchanged from ABI0.
+
+However, this wastes stack space (the median wastage is 16 bytes per
+call), resulting in larger stacks and increased cache footprint.
+A better approach would be to reserve stack space only when spilling.
+One way to ensure enough space is available to spill would be for
+every function to ensure there is enough space for the function's own
+frame *as well as* the spill space of all functions it calls.
+For most functions, this would change the threshold for the prologue
+stack growth check.
+For `nosplit` functions, this would change the threshold used in the
+linker's static stack size check.
+
+Allocating spill space in the callee rather than the caller may also
+allow for faster reflection calls in the common case where a function
+takes only register arguments, since it would allow reflection to make
+these calls directly without allocating any frame.
+
+The statically-generated spill path also increases code size.
+It is possible to instead have a generic spill path in the runtime, as
+part of `morestack`.
+However, this complicates reserving the spill space, since spilling
+all possible register arguments would, in most cases, take
+significantly more space than spilling only those used by a particular
+function.
+Some options are to spill to a temporary space and copy back only the
+registers used by the function, or to grow the stack if necessary
+before spilling to it (using a temporary space if necessary), or to
+use a heap-allocated space if insufficient stack space is available.
+These options all add enough complexity that we will have to make this
+decision based on the actual code size growth caused by the static
+spill paths.
+
+### Clobber sets
+
+As defined, the ABI does not use callee-save registers.
+This significantly simplifies the garbage collector and the compiler's
+register allocator, but at some performance cost.
+A potentially better balance for Go code would be to use *clobber
+sets*: for each function, the compiler records the set of registers it
+clobbers (including those clobbered by functions it calls) and any
+register not clobbered by function F can remain live across calls to
+F.
+
+This is generally a good fit for Go because Go's package DAG allows
+function metadata like the clobber set to flow up the call graph, even
+across package boundaries.
+Clobber sets would require relatively little change to the garbage
+collector, unlike general callee-save registers.
+One disadvantage of clobber sets over callee-save registers is that
+they don't help with indirect function calls or interface method
+calls, since static information isn't available in these cases.
+
+### Large aggregates
+
+Go encourages passing composite values by value, and this simplifies
+reasoning about mutation and races.
+However, this comes at a performance cost for large composite values.
+It may be possible to instead transparently pass large composite
+values by reference and delay copying until it is actually necessary.
+
+## Appendix: Register usage analysis
+
+In order to understand the impacts of the above design on register
+usage, we
+[analyzed](https://github.com/aclements/go-misc/tree/master/abi) the
+impact of the above ABI on a large code base: cmd/kubelet from
+[Kubernetes](https://github.com/kubernetes/kubernetes) at tag v1.18.8.
+
+The following table shows the impact of different numbers of available
+integer and floating-point registers on argument assignment:
+
+```
+| | | | stack args | spills | stack total |
+| ints | floats | % fit | p50 | p95 | p99 | p50 | p95 | p99 | p50 | p95 | p99 |
+| 0 | 0 | 6.3% | 32 | 152 | 256 | 0 | 0 | 0 | 32 | 152 | 256 |
+| 0 | 8 | 6.4% | 32 | 152 | 256 | 0 | 0 | 0 | 32 | 152 | 256 |
+| 1 | 8 | 21.3% | 24 | 144 | 248 | 8 | 8 | 8 | 32 | 152 | 256 |
+| 2 | 8 | 38.9% | 16 | 128 | 224 | 8 | 16 | 16 | 24 | 136 | 240 |
+| 3 | 8 | 57.0% | 0 | 120 | 224 | 16 | 24 | 24 | 24 | 136 | 240 |
+| 4 | 8 | 73.0% | 0 | 120 | 216 | 16 | 32 | 32 | 24 | 136 | 232 |
+| 5 | 8 | 83.3% | 0 | 112 | 216 | 16 | 40 | 40 | 24 | 136 | 232 |
+| 6 | 8 | 87.5% | 0 | 112 | 208 | 16 | 48 | 48 | 24 | 136 | 232 |
+| 7 | 8 | 89.8% | 0 | 112 | 208 | 16 | 48 | 56 | 24 | 136 | 232 |
+| 8 | 8 | 91.3% | 0 | 112 | 200 | 16 | 56 | 64 | 24 | 136 | 232 |
+| 9 | 8 | 92.1% | 0 | 112 | 192 | 16 | 56 | 72 | 24 | 136 | 232 |
+| 10 | 8 | 92.6% | 0 | 104 | 192 | 16 | 56 | 72 | 24 | 136 | 232 |
+| 11 | 8 | 93.1% | 0 | 104 | 184 | 16 | 56 | 80 | 24 | 128 | 232 |
+| 12 | 8 | 93.4% | 0 | 104 | 176 | 16 | 56 | 88 | 24 | 128 | 232 |
+| 13 | 8 | 94.0% | 0 | 88 | 176 | 16 | 56 | 96 | 24 | 128 | 232 |
+| 14 | 8 | 94.4% | 0 | 80 | 152 | 16 | 64 | 104 | 24 | 128 | 232 |
+| 15 | 8 | 94.6% | 0 | 80 | 152 | 16 | 64 | 112 | 24 | 128 | 232 |
+| 16 | 8 | 94.9% | 0 | 16 | 152 | 16 | 64 | 112 | 24 | 128 | 232 |
+| ∞ | 8 | 99.8% | 0 | 0 | 0 | 24 | 112 | 216 | 24 | 120 | 216 |
+```
+
+The first two columns show the number of available integer and
+floating-point registers.
+The first row shows the results for 0 integer and 0 floating-point
+registers, which is equivalent to ABI0.
+We found that any reasonable number of floating-point registers has
+the same effect, so we fixed it at 8 for all other rows.
+
+The “% fit” column gives the fraction of functions where all arguments
+and results are register-assigned and no arguments are passed on the
+stack.
+The three “stack args” columns give the median, 95th and 99th
+percentile number of bytes of stack arguments.
+The “spills” columns likewise summarize the number of bytes in
+on-stack spill space.
+And “stack total” summarizes the sum of stack arguments and on-stack
+spill slots.
+Note that these are three different distributions; for example,
+there’s no single function that takes 0 stack argument bytes, 16 spill
+bytes, and 24 total stack bytes.
+
+From this, we can see that the fraction of functions that fit entirely
+in registers grows very slowly once it reaches about 90%, though
+curiously there is a small minority of functions that could benefit
+from a huge number of registers.
+Making 9 integer registers available on amd64 puts it in this realm.
+We also see that the stack space required for most functions is fairly
+small.
+While the increasing space required for spills largely balances out
+the decreasing space required for stack arguments as the number of
+available registers increases, there is a general reduction in the
+total stack space required with more available registers.
+This does, however, suggest that eliminating spill slots in the future
+would noticeably reduce stack requirements.
diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go
new file mode 100644
index 0000000000000..e935821802dd2
--- /dev/null
+++ b/src/cmd/compile/internal/abi/abiutils.go
@@ -0,0 +1,461 @@
+// 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 abi
+
+import (
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+ "fmt"
+ "sync"
+)
+
+//......................................................................
+//
+// Public/exported bits of the ABI utilities.
+//
+
+// ABIParamResultInfo stores the results of processing a given
+// function type to compute stack layout and register assignments. For
+// each input and output parameter we capture whether the param was
+// register-assigned (and to which register(s)) or the stack offset
+// for the param if is not going to be passed in registers according
+// to the rules in the Go internal ABI specification (1.17).
+type ABIParamResultInfo struct {
+ inparams []ABIParamAssignment // Includes receiver for method calls. Does NOT include hidden closure pointer.
+ outparams []ABIParamAssignment
+ offsetToSpillArea int64
+ spillAreaSize int64
+ config *ABIConfig // to enable String() method
+}
+
+func (a *ABIParamResultInfo) InParams() []ABIParamAssignment {
+ return a.inparams
+}
+
+func (a *ABIParamResultInfo) OutParams() []ABIParamAssignment {
+ return a.outparams
+}
+
+func (a *ABIParamResultInfo) InParam(i int) ABIParamAssignment {
+ return a.inparams[i]
+}
+
+func (a *ABIParamResultInfo) OutParam(i int) ABIParamAssignment {
+ return a.outparams[i]
+}
+
+func (a *ABIParamResultInfo) SpillAreaOffset() int64 {
+ return a.offsetToSpillArea
+}
+
+func (a *ABIParamResultInfo) SpillAreaSize() int64 {
+ return a.spillAreaSize
+}
+
+// RegIndex stores the index into the set of machine registers used by
+// the ABI on a specific architecture for parameter passing. RegIndex
+// values 0 through N-1 (where N is the number of integer registers
+// used for param passing according to the ABI rules) describe integer
+// registers; values N through M (where M is the number of floating
+// point registers used). Thus if the ABI says there are 5 integer
+// registers and 7 floating point registers, then RegIndex value of 4
+// indicates the 5th integer register, and a RegIndex value of 11
+// indicates the 7th floating point register.
+type RegIndex uint8
+
+// ABIParamAssignment holds information about how a specific param or
+// result will be passed: in registers (in which case 'Registers' is
+// populated) or on the stack (in which case 'Offset' is set to a
+// non-negative stack offset. The values in 'Registers' are indices (as
+// described above), not architected registers.
+type ABIParamAssignment struct {
+ Type *types.Type
+ Registers []RegIndex
+ offset int32
+}
+
+// Offset returns the stack offset for addressing the parameter that "a" describes.
+// This will panic if "a" describes a register-allocated parameter.
+func (a *ABIParamAssignment) Offset() int32 {
+ if len(a.Registers) > 0 {
+ panic("Register allocated parameters have no offset")
+ }
+ return a.offset
+}
+
+// SpillOffset returns the offset *within the spill area* for the parameter that "a" describes.
+// Registers will be spilled here; if a memory home is needed (for a pointer method e.g.)
+// then that will be the address.
+// This will panic if "a" describes a stack-allocated parameter.
+func (a *ABIParamAssignment) SpillOffset() int32 {
+ if len(a.Registers) == 0 {
+ panic("Stack-allocated parameters have no spill offset")
+ }
+ return a.offset
+}
+
+// RegAmounts holds a specified number of integer/float registers.
+type RegAmounts struct {
+ intRegs int
+ floatRegs int
+}
+
+// ABIConfig captures the number of registers made available
+// by the ABI rules for parameter passing and result returning.
+type ABIConfig struct {
+ // Do we need anything more than this?
+ regAmounts RegAmounts
+ regsForTypeCache map[*types.Type]int
+}
+
+// NewABIConfig returns a new ABI configuration for an architecture with
+// iRegsCount integer/pointer registers and fRegsCount floating point registers.
+func NewABIConfig(iRegsCount, fRegsCount int) *ABIConfig {
+ return &ABIConfig{regAmounts: RegAmounts{iRegsCount, fRegsCount}, regsForTypeCache: make(map[*types.Type]int)}
+}
+
+// NumParamRegs returns the number of parameter registers used for a given type,
+// without regard for the number available.
+func (a *ABIConfig) NumParamRegs(t *types.Type) int {
+ if n, ok := a.regsForTypeCache[t]; ok {
+ return n
+ }
+
+ if t.IsScalar() || t.IsPtrShaped() {
+ var n int
+ if t.IsComplex() {
+ n = 2
+ } else {
+ n = (int(t.Size()) + types.RegSize - 1) / types.RegSize
+ }
+ a.regsForTypeCache[t] = n
+ return n
+ }
+ typ := t.Kind()
+ n := 0
+ switch typ {
+ case types.TARRAY:
+ n = a.NumParamRegs(t.Elem()) * int(t.NumElem())
+ case types.TSTRUCT:
+ for _, f := range t.FieldSlice() {
+ n += a.NumParamRegs(f.Type)
+ }
+ case types.TSLICE:
+ n = a.NumParamRegs(synthSlice)
+ case types.TSTRING:
+ n = a.NumParamRegs(synthString)
+ case types.TINTER:
+ n = a.NumParamRegs(synthIface)
+ }
+ a.regsForTypeCache[t] = n
+ return n
+}
+
+// ABIAnalyze takes a function type 't' and an ABI rules description
+// 'config' and analyzes the function to determine how its parameters
+// and results will be passed (in registers or on the stack), returning
+// an ABIParamResultInfo object that holds the results of the analysis.
+func (config *ABIConfig) ABIAnalyze(t *types.Type) ABIParamResultInfo {
+ setup()
+ s := assignState{
+ rTotal: config.regAmounts,
+ }
+ result := ABIParamResultInfo{config: config}
+
+ // Receiver
+ ft := t.FuncType()
+ if t.NumRecvs() != 0 {
+ rfsl := ft.Receiver.FieldSlice()
+ result.inparams = append(result.inparams,
+ s.assignParamOrReturn(rfsl[0].Type, false))
+ }
+
+ // Inputs
+ ifsl := ft.Params.FieldSlice()
+ for _, f := range ifsl {
+ result.inparams = append(result.inparams,
+ s.assignParamOrReturn(f.Type, false))
+ }
+ s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize))
+
+ // Outputs
+ s.rUsed = RegAmounts{}
+ ofsl := ft.Results.FieldSlice()
+ for _, f := range ofsl {
+ result.outparams = append(result.outparams, s.assignParamOrReturn(f.Type, true))
+ }
+ // The spill area is at a register-aligned offset and its size is rounded up to a register alignment.
+ // TODO in theory could align offset only to minimum required by spilled data types.
+ result.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize)
+ result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
+
+ return result
+}
+
+//......................................................................
+//
+// Non-public portions.
+
+// regString produces a human-readable version of a RegIndex.
+func (c *RegAmounts) regString(r RegIndex) string {
+ if int(r) < c.intRegs {
+ return fmt.Sprintf("I%d", int(r))
+ } else if int(r) < c.intRegs+c.floatRegs {
+ return fmt.Sprintf("F%d", int(r)-c.intRegs)
+ }
+ return fmt.Sprintf(">%d", r)
+}
+
+// toString method renders an ABIParamAssignment in human-readable
+// form, suitable for debugging or unit testing.
+func (ri *ABIParamAssignment) toString(config *ABIConfig) string {
+ regs := "R{"
+ offname := "spilloffset" // offset is for spill for register(s)
+ if len(ri.Registers) == 0 {
+ offname = "offset" // offset is for memory arg
+ }
+ for _, r := range ri.Registers {
+ regs += " " + config.regAmounts.regString(r)
+ }
+ return fmt.Sprintf("%s } %s: %d typ: %v", regs, offname, ri.offset, ri.Type)
+}
+
+// toString method renders an ABIParamResultInfo in human-readable
+// form, suitable for debugging or unit testing.
+func (ri *ABIParamResultInfo) String() string {
+ res := ""
+ for k, p := range ri.inparams {
+ res += fmt.Sprintf("IN %d: %s\n", k, p.toString(ri.config))
+ }
+ for k, r := range ri.outparams {
+ res += fmt.Sprintf("OUT %d: %s\n", k, r.toString(ri.config))
+ }
+ res += fmt.Sprintf("offsetToSpillArea: %d spillAreaSize: %d",
+ ri.offsetToSpillArea, ri.spillAreaSize)
+ return res
+}
+
+// assignState holds intermediate state during the register assigning process
+// for a given function signature.
+type assignState struct {
+ rTotal RegAmounts // total reg amounts from ABI rules
+ rUsed RegAmounts // regs used by params completely assigned so far
+ pUsed RegAmounts // regs used by the current param (or pieces therein)
+ stackOffset int64 // current stack offset
+ spillOffset int64 // current spill offset
+}
+
+// align returns a rounded up to t's alignment
+func align(a int64, t *types.Type) int64 {
+ return alignTo(a, int(t.Align))
+}
+
+// alignTo returns a rounded up to t, where t must be 0 or a power of 2.
+func alignTo(a int64, t int) int64 {
+ if t == 0 {
+ return a
+ }
+ return types.Rnd(a, int64(t))
+}
+
+// stackSlot returns a stack offset for a param or result of the
+// specified type.
+func (state *assignState) stackSlot(t *types.Type) int64 {
+ rv := align(state.stackOffset, t)
+ state.stackOffset = rv + t.Width
+ return rv
+}
+
+// allocateRegs returns a set of register indices for a parameter or result
+// that we've just determined to be register-assignable. The number of registers
+// needed is assumed to be stored in state.pUsed.
+func (state *assignState) allocateRegs() []RegIndex {
+ regs := []RegIndex{}
+
+ // integer
+ for r := state.rUsed.intRegs; r < state.rUsed.intRegs+state.pUsed.intRegs; r++ {
+ regs = append(regs, RegIndex(r))
+ }
+ state.rUsed.intRegs += state.pUsed.intRegs
+
+ // floating
+ for r := state.rUsed.floatRegs; r < state.rUsed.floatRegs+state.pUsed.floatRegs; r++ {
+ regs = append(regs, RegIndex(r+state.rTotal.intRegs))
+ }
+ state.rUsed.floatRegs += state.pUsed.floatRegs
+
+ return regs
+}
+
+// regAllocate creates a register ABIParamAssignment object for a param
+// or result with the specified type, as a final step (this assumes
+// that all of the safety/suitability analysis is complete).
+func (state *assignState) regAllocate(t *types.Type, isReturn bool) ABIParamAssignment {
+ spillLoc := int64(-1)
+ if !isReturn {
+ // Spill for register-resident t must be aligned for storage of a t.
+ spillLoc = align(state.spillOffset, t)
+ state.spillOffset = spillLoc + t.Size()
+ }
+ return ABIParamAssignment{
+ Type: t,
+ Registers: state.allocateRegs(),
+ offset: int32(spillLoc),
+ }
+}
+
+// stackAllocate creates a stack memory ABIParamAssignment object for
+// a param or result with the specified type, as a final step (this
+// assumes that all of the safety/suitability analysis is complete).
+func (state *assignState) stackAllocate(t *types.Type) ABIParamAssignment {
+ return ABIParamAssignment{
+ Type: t,
+ offset: int32(state.stackSlot(t)),
+ }
+}
+
+// intUsed returns the number of integer registers consumed
+// at a given point within an assignment stage.
+func (state *assignState) intUsed() int {
+ return state.rUsed.intRegs + state.pUsed.intRegs
+}
+
+// floatUsed returns the number of floating point registers consumed at
+// a given point within an assignment stage.
+func (state *assignState) floatUsed() int {
+ return state.rUsed.floatRegs + state.pUsed.floatRegs
+}
+
+// regassignIntegral examines a param/result of integral type 't' to
+// determines whether it can be register-assigned. Returns TRUE if we
+// can register allocate, FALSE otherwise (and updates state
+// accordingly).
+func (state *assignState) regassignIntegral(t *types.Type) bool {
+ regsNeeded := int(types.Rnd(t.Width, int64(types.PtrSize)) / int64(types.PtrSize))
+ if t.IsComplex() {
+ regsNeeded = 2
+ }
+
+ // Floating point and complex.
+ if t.IsFloat() || t.IsComplex() {
+ if regsNeeded+state.floatUsed() > state.rTotal.floatRegs {
+ // not enough regs
+ return false
+ }
+ state.pUsed.floatRegs += regsNeeded
+ return true
+ }
+
+ // Non-floating point
+ if regsNeeded+state.intUsed() > state.rTotal.intRegs {
+ // not enough regs
+ return false
+ }
+ state.pUsed.intRegs += regsNeeded
+ return true
+}
+
+// regassignArray processes an array type (or array component within some
+// other enclosing type) to determine if it can be register assigned.
+// Returns TRUE if we can register allocate, FALSE otherwise.
+func (state *assignState) regassignArray(t *types.Type) bool {
+
+ nel := t.NumElem()
+ if nel == 0 {
+ return true
+ }
+ if nel > 1 {
+ // Not an array of length 1: stack assign
+ return false
+ }
+ // Visit element
+ return state.regassign(t.Elem())
+}
+
+// regassignStruct processes a struct type (or struct component within
+// some other enclosing type) to determine if it can be register
+// assigned. Returns TRUE if we can register allocate, FALSE otherwise.
+func (state *assignState) regassignStruct(t *types.Type) bool {
+ for _, field := range t.FieldSlice() {
+ if !state.regassign(field.Type) {
+ return false
+ }
+ }
+ return true
+}
+
+// synthOnce ensures that we only create the synth* fake types once.
+var synthOnce sync.Once
+
+// synthSlice, synthString, and syncIface are synthesized struct types
+// meant to capture the underlying implementations of string/slice/interface.
+var synthSlice *types.Type
+var synthString *types.Type
+var synthIface *types.Type
+
+// setup performs setup for the register assignment utilities, manufacturing
+// a small set of synthesized types that we'll need along the way.
+func setup() {
+ synthOnce.Do(func() {
+ fname := types.BuiltinPkg.Lookup
+ nxp := src.NoXPos
+ unsp := types.Types[types.TUNSAFEPTR]
+ ui := types.Types[types.TUINTPTR]
+ synthSlice = types.NewStruct(types.NoPkg, []*types.Field{
+ types.NewField(nxp, fname("ptr"), unsp),
+ types.NewField(nxp, fname("len"), ui),
+ types.NewField(nxp, fname("cap"), ui),
+ })
+ synthString = types.NewStruct(types.NoPkg, []*types.Field{
+ types.NewField(nxp, fname("data"), unsp),
+ types.NewField(nxp, fname("len"), ui),
+ })
+ synthIface = types.NewStruct(types.NoPkg, []*types.Field{
+ types.NewField(nxp, fname("f1"), unsp),
+ types.NewField(nxp, fname("f2"), unsp),
+ })
+ })
+}
+
+// regassign examines a given param type (or component within some
+// composite) to determine if it can be register assigned. Returns
+// TRUE if we can register allocate, FALSE otherwise.
+func (state *assignState) regassign(pt *types.Type) bool {
+ typ := pt.Kind()
+ if pt.IsScalar() || pt.IsPtrShaped() {
+ return state.regassignIntegral(pt)
+ }
+ switch typ {
+ case types.TARRAY:
+ return state.regassignArray(pt)
+ case types.TSTRUCT:
+ return state.regassignStruct(pt)
+ case types.TSLICE:
+ return state.regassignStruct(synthSlice)
+ case types.TSTRING:
+ return state.regassignStruct(synthString)
+ case types.TINTER:
+ return state.regassignStruct(synthIface)
+ default:
+ panic("not expected")
+ }
+}
+
+// assignParamOrReturn processes a given receiver, param, or result
+// of type 'pt' to determine whether it can be register assigned.
+// The result of the analysis is recorded in the result
+// ABIParamResultInfo held in 'state'.
+func (state *assignState) assignParamOrReturn(pt *types.Type, isReturn bool) ABIParamAssignment {
+ state.pUsed = RegAmounts{}
+ if pt.Width == types.BADWIDTH {
+ panic("should never happen")
+ } else if pt.Width == 0 {
+ return state.stackAllocate(pt)
+ } else if state.regassign(pt) {
+ return state.regAllocate(pt, isReturn)
+ } else {
+ return state.stackAllocate(pt)
+ }
+}
diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go
index af58440502e00..ce1c402902f7e 100644
--- a/src/cmd/compile/internal/amd64/galign.go
+++ b/src/cmd/compile/internal/amd64/galign.go
@@ -5,13 +5,13 @@
package amd64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/x86"
)
var leaptr = x86.ALEAQ
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &x86.Linkamd64
arch.REGSP = x86.REGSP
arch.MAXWIDTH = 1 << 50
diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go
index 0c1456f4d0ded..aefdb14a69b12 100644
--- a/src/cmd/compile/internal/amd64/ggen.go
+++ b/src/cmd/compile/internal/amd64/ggen.go
@@ -5,7 +5,10 @@
package amd64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"cmd/internal/objabi"
@@ -19,8 +22,8 @@ var isPlan9 = objabi.GOOS == "plan9"
const (
dzBlocks = 16 // number of MOV/ADD blocks
dzBlockLen = 4 // number of clears per block
- dzBlockSize = 19 // size of instructions in a single block
- dzMovSize = 4 // size of single MOV instruction w/ offset
+ dzBlockSize = 23 // size of instructions in a single block
+ dzMovSize = 5 // size of single MOV instruction w/ offset
dzLeaqSize = 4 // size of single LEAQ instruction
dzClearStep = 16 // number of bytes cleared by each MOV instruction
@@ -51,7 +54,7 @@ func dzDI(b int64) int64 {
return -dzClearStep * (dzBlockLen - tailSteps)
}
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
const (
ax = 1 << iota
x0
@@ -61,67 +64,67 @@ func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Pr
return p
}
- if cnt%int64(gc.Widthreg) != 0 {
+ if cnt%int64(types.RegSize) != 0 {
// should only happen with nacl
- if cnt%int64(gc.Widthptr) != 0 {
- gc.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
+ if cnt%int64(types.PtrSize) != 0 {
+ base.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
}
if *state&ax == 0 {
- p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+ p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
*state |= ax
}
- p = pp.Appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
- off += int64(gc.Widthptr)
- cnt -= int64(gc.Widthptr)
+ p = pp.Append(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
+ off += int64(types.PtrSize)
+ cnt -= int64(types.PtrSize)
}
if cnt == 8 {
if *state&ax == 0 {
- p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+ p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
*state |= ax
}
- p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
- } else if !isPlan9 && cnt <= int64(8*gc.Widthreg) {
+ p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
+ } else if !isPlan9 && cnt <= int64(8*types.RegSize) {
if *state&x0 == 0 {
- p = pp.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
+ p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*state |= x0
}
for i := int64(0); i < cnt/16; i++ {
- p = pp.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16)
+ p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16)
}
if cnt%16 != 0 {
- p = pp.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
+ p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
}
- } else if !isPlan9 && (cnt <= int64(128*gc.Widthreg)) {
+ } else if !isPlan9 && (cnt <= int64(128*types.RegSize)) {
if *state&x0 == 0 {
- p = pp.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
+ p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*state |= x0
}
- p = pp.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
- p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
- p.To.Sym = gc.Duffzero
+ p = pp.Append(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
+ p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
+ p.To.Sym = ir.Syms.Duffzero
if cnt%16 != 0 {
- p = pp.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8))
+ p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8))
}
} else {
if *state&ax == 0 {
- p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+ p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
*state |= ax
}
- p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0)
- p = pp.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
- p = pp.Appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
- p = pp.Appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+ p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(types.RegSize), obj.TYPE_REG, x86.REG_CX, 0)
+ p = pp.Append(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
+ p = pp.Append(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+ p = pp.Append(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
// This is a hardware nop (1-byte 0x90) instruction,
// even though we describe it as an explicit XCHGL here.
// Particularly, this does not zero the high 32 bits
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 5ff05a0edd16c..4938e4b0e3519 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -8,16 +8,18 @@ import (
"fmt"
"math"
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
-func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
flive := b.FlagsLiveAtEnd
for _, c := range b.ControlValues() {
flive = c.Type.IsFlags() || flive
@@ -110,7 +112,7 @@ func moveByType(t *types.Type) obj.As {
// dest := dest(To) op src(From)
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
-func opregreg(s *gc.SSAGenState, op obj.As, dest, src int16) *obj.Prog {
+func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
p := s.Prog(op)
p.From.Type = obj.TYPE_REG
p.To.Type = obj.TYPE_REG
@@ -164,7 +166,35 @@ func duff(size int64) (int64, int64) {
return off, adj
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func getgFromTLS(s *ssagen.State, r int16) {
+ // See the comments in cmd/internal/obj/x86/obj6.go
+ // near CanUse1InsnTLS for a detailed explanation of these instructions.
+ if x86.CanUse1InsnTLS(base.Ctxt) {
+ // MOVQ (TLS), r
+ p := s.Prog(x86.AMOVQ)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = x86.REG_TLS
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = r
+ } else {
+ // MOVQ TLS, r
+ // MOVQ (r)(TLS*1), r
+ p := s.Prog(x86.AMOVQ)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = x86.REG_TLS
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = r
+ q := s.Prog(x86.AMOVQ)
+ q.From.Type = obj.TYPE_MEM
+ q.From.Reg = r
+ q.From.Index = x86.REG_TLS
+ q.From.Scale = 1
+ q.To.Type = obj.TYPE_REG
+ q.To.Reg = r
+ }
+}
+
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpAMD64VFMADD231SD:
p := s.Prog(v.Op.Asm())
@@ -630,12 +660,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = o
}
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
case ssa.OpAMD64LEAQ, ssa.OpAMD64LEAL, ssa.OpAMD64LEAW:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64CMPQ, ssa.OpAMD64CMPL, ssa.OpAMD64CMPW, ssa.OpAMD64CMPB,
@@ -671,7 +701,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Args[1].Reg()
case ssa.OpAMD64CMPQconstload, ssa.OpAMD64CMPLconstload, ssa.OpAMD64CMPWconstload, ssa.OpAMD64CMPBconstload:
@@ -679,20 +709,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.From, v, sc.Off())
+ ssagen.AddAux2(&p.From, v, sc.Off())
p.To.Type = obj.TYPE_CONST
p.To.Offset = sc.Val()
case ssa.OpAMD64CMPQloadidx8, ssa.OpAMD64CMPQloadidx1, ssa.OpAMD64CMPLloadidx4, ssa.OpAMD64CMPLloadidx1, ssa.OpAMD64CMPWloadidx2, ssa.OpAMD64CMPWloadidx1, ssa.OpAMD64CMPBloadidx1:
p := s.Prog(v.Op.Asm())
memIdx(&p.From, v)
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Args[2].Reg()
case ssa.OpAMD64CMPQconstloadidx8, ssa.OpAMD64CMPQconstloadidx1, ssa.OpAMD64CMPLconstloadidx4, ssa.OpAMD64CMPLconstloadidx1, ssa.OpAMD64CMPWconstloadidx2, ssa.OpAMD64CMPWconstloadidx1, ssa.OpAMD64CMPBconstloadidx1:
sc := v.AuxValAndOff()
p := s.Prog(v.Op.Asm())
memIdx(&p.From, v)
- gc.AddAux2(&p.From, v, sc.Off())
+ ssagen.AddAux2(&p.From, v, sc.Off())
p.To.Type = obj.TYPE_CONST
p.To.Offset = sc.Val()
case ssa.OpAMD64MOVLconst, ssa.OpAMD64MOVQconst:
@@ -732,14 +762,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1,
ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8, ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4, ssa.OpAMD64MOVWloadidx2:
p := s.Prog(v.Op.Asm())
memIdx(&p.From, v)
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore,
@@ -751,7 +781,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpAMD64MOVBstoreidx1, ssa.OpAMD64MOVWstoreidx1, ssa.OpAMD64MOVLstoreidx1, ssa.OpAMD64MOVQstoreidx1, ssa.OpAMD64MOVSSstoreidx1, ssa.OpAMD64MOVSDstoreidx1,
ssa.OpAMD64MOVQstoreidx8, ssa.OpAMD64MOVSDstoreidx8, ssa.OpAMD64MOVLstoreidx8, ssa.OpAMD64MOVSSstoreidx4, ssa.OpAMD64MOVLstoreidx4, ssa.OpAMD64MOVWstoreidx2,
ssa.OpAMD64ADDLmodifyidx1, ssa.OpAMD64ADDLmodifyidx4, ssa.OpAMD64ADDLmodifyidx8, ssa.OpAMD64ADDQmodifyidx1, ssa.OpAMD64ADDQmodifyidx8,
@@ -763,7 +793,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
memIdx(&p.To, v)
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpAMD64ADDQconstmodify, ssa.OpAMD64ADDLconstmodify:
sc := v.AuxValAndOff()
off := sc.Off()
@@ -786,7 +816,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(asm)
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, off)
+ ssagen.AddAux2(&p.To, v, off)
break
}
fallthrough
@@ -801,7 +831,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = val
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, off)
+ ssagen.AddAux2(&p.To, v, off)
case ssa.OpAMD64MOVQstoreconst, ssa.OpAMD64MOVLstoreconst, ssa.OpAMD64MOVWstoreconst, ssa.OpAMD64MOVBstoreconst:
p := s.Prog(v.Op.Asm())
@@ -810,7 +840,21 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = sc.Val()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, sc.Off())
+ ssagen.AddAux2(&p.To, v, sc.Off())
+ case ssa.OpAMD64MOVOstorezero:
+ if s.ABI != obj.ABIInternal {
+ v.Fatalf("MOVOstorezero can be only used in ABIInternal functions")
+ }
+ if !base.Flag.ABIWrap {
+ // zeroing X15 manually if wrappers are not used
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ }
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = x86.REG_X15
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = v.Args[0].Reg()
+ ssagen.AddAux(&p.To, v)
case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx2, ssa.OpAMD64MOVBstoreconstidx1,
ssa.OpAMD64ADDLconstmodifyidx1, ssa.OpAMD64ADDLconstmodifyidx4, ssa.OpAMD64ADDLconstmodifyidx8, ssa.OpAMD64ADDQconstmodifyidx1, ssa.OpAMD64ADDQconstmodifyidx8,
ssa.OpAMD64ANDLconstmodifyidx1, ssa.OpAMD64ANDLconstmodifyidx4, ssa.OpAMD64ANDLconstmodifyidx8, ssa.OpAMD64ANDQconstmodifyidx1, ssa.OpAMD64ANDQconstmodifyidx8,
@@ -835,7 +879,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Type = obj.TYPE_NONE
}
memIdx(&p.To, v)
- gc.AddAux2(&p.To, v, sc.Off())
+ ssagen.AddAux2(&p.To, v, sc.Off())
case ssa.OpAMD64MOVLQSX, ssa.OpAMD64MOVWQSX, ssa.OpAMD64MOVBQSX, ssa.OpAMD64MOVLQZX, ssa.OpAMD64MOVWQZX, ssa.OpAMD64MOVBQZX,
ssa.OpAMD64CVTTSS2SL, ssa.OpAMD64CVTTSD2SL, ssa.OpAMD64CVTTSS2SQ, ssa.OpAMD64CVTTSD2SQ,
ssa.OpAMD64CVTSS2SD, ssa.OpAMD64CVTSD2SS:
@@ -865,7 +909,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[1].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
if v.Reg() != v.Args[0].Reg() {
@@ -891,13 +935,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r
p.From.Index = i
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
if v.Reg() != v.Args[0].Reg() {
v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
case ssa.OpAMD64DUFFZERO:
+ if s.ABI != obj.ABIInternal {
+ v.Fatalf("MOVOconst can be only used in ABIInternal functions")
+ }
+ if !base.Flag.ABIWrap {
+ // zeroing X15 manually if wrappers are not used
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ }
off := duffStart(v.AuxInt)
adj := duffAdj(v.AuxInt)
var p *obj.Prog
@@ -911,18 +962,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
p = s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_ADDR
- p.To.Sym = gc.Duffzero
+ p.To.Sym = ir.Syms.Duffzero
p.To.Offset = off
- case ssa.OpAMD64MOVOconst:
- if v.AuxInt != 0 {
- v.Fatalf("MOVOconst can only do constant=0")
- }
- r := v.Reg()
- opregreg(s, x86.AXORPS, r, r)
case ssa.OpAMD64DUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_ADDR
- p.To.Sym = gc.Duffcopy
+ p.To.Sym = ir.Syms.Duffcopy
if v.AuxInt%16 != 0 {
v.Fatalf("bad DUFFCOPY AuxInt %v", v.AuxInt)
}
@@ -949,7 +994,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -961,44 +1006,37 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpAMD64LoweredHasCPUFeature:
p := s.Prog(x86.AMOVBQZX)
p.From.Type = obj.TYPE_MEM
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64LoweredGetClosurePtr:
// Closure pointer is DX.
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpAMD64LoweredGetG:
+ if base.Flag.ABIWrap {
+ v.Fatalf("LoweredGetG should not appear in new ABI")
+ }
r := v.Reg()
- // See the comments in cmd/internal/obj/x86/obj6.go
- // near CanUse1InsnTLS for a detailed explanation of these instructions.
- if x86.CanUse1InsnTLS(gc.Ctxt) {
- // MOVQ (TLS), r
- p := s.Prog(x86.AMOVQ)
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = x86.REG_TLS
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- } else {
- // MOVQ TLS, r
- // MOVQ (r)(TLS*1), r
- p := s.Prog(x86.AMOVQ)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_TLS
- p.To.Type = obj.TYPE_REG
- p.To.Reg = r
- q := s.Prog(x86.AMOVQ)
- q.From.Type = obj.TYPE_MEM
- q.From.Reg = r
- q.From.Index = x86.REG_TLS
- q.From.Scale = 1
- q.To.Type = obj.TYPE_REG
- q.To.Reg = r
+ getgFromTLS(s, r)
+ case ssa.OpAMD64CALLstatic:
+ if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
+ // zeroing X15 when entering ABIInternal from ABI0
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ // set G register from TLS
+ getgFromTLS(s, x86.REG_R14)
+ }
+ s.Call(v)
+ if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
+ // zeroing X15 when entering ABIInternal from ABI0
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ // set G register from TLS
+ getgFromTLS(s, x86.REG_R14)
}
- case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLclosure, ssa.OpAMD64CALLinter:
+ case ssa.OpAMD64CALLclosure, ssa.OpAMD64CALLinter:
s.Call(v)
case ssa.OpAMD64LoweredGetCallerPC:
@@ -1012,12 +1050,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpAMD64LoweredGetCallerSP:
// caller's SP is the address of the first arg
mov := x86.AMOVQ
- if gc.Widthptr == 4 {
+ if types.PtrSize == 4 {
mov = x86.AMOVL
}
p := s.Prog(mov)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize() // 0 on amd64, just to be consistent with other architectures
+ p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on amd64, just to be consistent with other architectures
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -1027,14 +1065,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
// arg0 is in DI. Set sym to match where regalloc put arg1.
- p.To.Sym = gc.GCWriteBarrierReg[v.Args[1].Reg()]
+ p.To.Sym = ssagen.GCWriteBarrierReg[v.Args[1].Reg()]
case ssa.OpAMD64LoweredPanicBoundsA, ssa.OpAMD64LoweredPanicBoundsB, ssa.OpAMD64LoweredPanicBoundsC:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(int64(2 * gc.Widthptr)) // space used in callee args area by assembly stubs
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
+ s.UseArgs(int64(2 * types.PtrSize)) // space used in callee args area by assembly stubs
case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL,
ssa.OpAMD64BSWAPQ, ssa.OpAMD64BSWAPL,
@@ -1115,7 +1153,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpAMD64SETNEF:
p := s.Prog(v.Op.Asm())
@@ -1164,14 +1202,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpAMD64MOVBatomicload, ssa.OpAMD64MOVLatomicload, ssa.OpAMD64MOVQatomicload:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpAMD64XCHGB, ssa.OpAMD64XCHGL, ssa.OpAMD64XCHGQ:
@@ -1184,7 +1222,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[1].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpAMD64XADDLlock, ssa.OpAMD64XADDQlock:
r := v.Reg0()
if r != v.Args[0].Reg() {
@@ -1196,7 +1234,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[1].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpAMD64CMPXCHGLlock, ssa.OpAMD64CMPXCHGQlock:
if v.Args[1].Reg() != x86.REG_AX {
v.Fatalf("input[1] not in AX %s", v.LongString())
@@ -1207,7 +1245,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
p = s.Prog(x86.ASETEQ)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
@@ -1218,20 +1256,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpClobber:
p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 0xdeaddead
p.To.Type = obj.TYPE_MEM
p.To.Reg = x86.REG_SP
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
p = s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 0xdeaddead
p.To.Type = obj.TYPE_MEM
p.To.Reg = x86.REG_SP
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
p.To.Offset += 4
default:
v.Fatalf("genValue not implemented: %s", v.LongString())
@@ -1257,22 +1295,22 @@ var blockJump = [...]struct {
ssa.BlockAMD64NAN: {x86.AJPS, x86.AJPC},
}
-var eqfJumps = [2][2]gc.IndexJump{
+var eqfJumps = [2][2]ssagen.IndexJump{
{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPS, Index: 1}}, // next == b.Succs[0]
{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPC, Index: 0}}, // next == b.Succs[1]
}
-var nefJumps = [2][2]gc.IndexJump{
+var nefJumps = [2][2]ssagen.IndexJump{
{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPC, Index: 1}}, // next == b.Succs[0]
{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPS, Index: 0}}, // next == b.Succs[1]
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
// defer returns in rax:
@@ -1285,16 +1323,22 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.To.Reg = x86.REG_AX
p = s.Prog(x86.AJNE)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
s.Prog(obj.ARET)
case ssa.BlockRetJmp:
+ if s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal {
+ // zeroing X15 when entering ABIInternal from ABI0
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ // set G register from TLS
+ getgFromTLS(s, x86.REG_R14)
+ }
p := s.Prog(obj.ARET)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go
index 20e2f43a91c0d..81959ae0abcb3 100644
--- a/src/cmd/compile/internal/arm/galign.go
+++ b/src/cmd/compile/internal/arm/galign.go
@@ -5,13 +5,13 @@
package arm
import (
- "cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/arm"
"cmd/internal/objabi"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &arm.Linkarm
arch.REGSP = arm.REGSP
arch.MAXWIDTH = (1 << 32) - 1
@@ -20,7 +20,7 @@ func Init(arch *gc.Arch) {
arch.Ginsnop = ginsnop
arch.Ginsnopdefer = ginsnop
- arch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+ arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
}
diff --git a/src/cmd/compile/internal/arm/ggen.go b/src/cmd/compile/internal/arm/ggen.go
index bd8d7ff40b8f8..f2c676300a93a 100644
--- a/src/cmd/compile/internal/arm/ggen.go
+++ b/src/cmd/compile/internal/arm/ggen.go
@@ -5,49 +5,51 @@
package arm
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm"
)
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, r0 *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, r0 *uint32) *obj.Prog {
if cnt == 0 {
return p
}
if *r0 == 0 {
- p = pp.Appendpp(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0)
+ p = pp.Append(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0)
*r0 = 1
}
- if cnt < int64(4*gc.Widthptr) {
- for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
- p = pp.Appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, 4+off+i)
+ if cnt < int64(4*types.PtrSize) {
+ for i := int64(0); i < cnt; i += int64(types.PtrSize) {
+ p = pp.Append(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, 4+off+i)
}
- } else if cnt <= int64(128*gc.Widthptr) {
- p = pp.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
+ } else if cnt <= int64(128*types.PtrSize) {
+ p = pp.Append(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
p.Reg = arm.REGSP
- p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+ p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
- p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr))
+ p.To.Sym = ir.Syms.Duffzero
+ p.To.Offset = 4 * (128 - cnt/int64(types.PtrSize))
} else {
- p = pp.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
+ p = pp.Append(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
p.Reg = arm.REGSP
- p = pp.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm.REG_R2, 0)
+ p = pp.Append(p, arm.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm.REG_R2, 0)
p.Reg = arm.REG_R1
- p = pp.Appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4)
+ p = pp.Append(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4)
p1 := p
p.Scond |= arm.C_PBIT
- p = pp.Appendpp(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0)
+ p = pp.Append(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0)
p.Reg = arm.REG_R2
- p = pp.Appendpp(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
- gc.Patch(p, p1)
+ p = pp.Append(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+ p.To.SetTarget(p1)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
p := pp.Prog(arm.AAND)
p.From.Type = obj.TYPE_REG
p.From.Reg = arm.REG_R0
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 765a7715465ce..729d2dab2d809 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -9,9 +9,11 @@ import (
"math"
"math/bits"
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm"
@@ -91,7 +93,7 @@ func makeshift(reg int16, typ int64, s int64) shift {
}
// genshift generates a Prog for r = r0 op (r1 shifted by n)
-func genshift(s *gc.SSAGenState, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
+func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
p.From.Offset = int64(makeshift(r1, typ, n))
@@ -109,7 +111,7 @@ func makeregshift(r1 int16, typ int64, r2 int16) shift {
}
// genregshift generates a Prog for r = r0 op (r1 shifted by r2)
-func genregshift(s *gc.SSAGenState, as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
+func genregshift(s *ssagen.State, as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
p.From.Offset = int64(makeregshift(r1, typ, r2))
@@ -143,7 +145,7 @@ func getBFC(v uint32) (uint32, uint32) {
return 0xffffffff, 0
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy, ssa.OpARMMOVWreg:
if v.Type.IsMemory() {
@@ -181,7 +183,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpStoreReg:
@@ -192,7 +194,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpARMADD,
ssa.OpARMADC,
ssa.OpARMSUB,
@@ -543,10 +545,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
- gc.AddAux(&p.From, v)
- case *gc.Node:
+ ssagen.AddAux(&p.From, v)
+ case *ir.Name:
wantreg = "SP"
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVW $off(SP), R
wantreg = "SP"
@@ -566,7 +568,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpARMMOVBstore,
@@ -579,7 +581,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpARMMOVWloadidx, ssa.OpARMMOVBUloadidx, ssa.OpARMMOVBloadidx, ssa.OpARMMOVHUloadidx, ssa.OpARMMOVHloadidx:
// this is just shift 0 bits
fallthrough
@@ -700,7 +702,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Udiv
+ p.To.Sym = ir.Syms.Udiv
case ssa.OpARMLoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -710,39 +712,39 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(8) // space used in callee args area by assembly stubs
case ssa.OpARMLoweredPanicExtendA, ssa.OpARMLoweredPanicExtendB, ssa.OpARMLoweredPanicExtendC:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.ExtendCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
s.UseArgs(12) // space used in callee args area by assembly stubs
case ssa.OpARMDUFFZERO:
p := s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
+ p.To.Sym = ir.Syms.Duffzero
p.To.Offset = v.AuxInt
case ssa.OpARMDUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffcopy
+ p.To.Sym = ir.Syms.Duffcopy
p.To.Offset = v.AuxInt
case ssa.OpARMLoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(arm.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = arm.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpARMLoweredZero:
// MOVW.P Rarg2, 4(R1)
@@ -777,7 +779,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p2.Reg = arm.REG_R1
p3 := s.Prog(arm.ABLE)
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
case ssa.OpARMLoweredMove:
// MOVW.P 4(R1), Rtmp
// MOVW.P Rtmp, 4(R2)
@@ -818,7 +820,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.Reg = arm.REG_R1
p4 := s.Prog(arm.ABLE)
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p)
+ p4.To.SetTarget(p)
case ssa.OpARMEqual,
ssa.OpARMNotEqual,
ssa.OpARMLessThan,
@@ -844,12 +846,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpARMLoweredGetClosurePtr:
// Closure pointer is R7 (arm.REGCTXT).
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpARMLoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(arm.AMOVW)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -899,24 +901,24 @@ var blockJump = map[ssa.BlockKind]struct {
}
// To model a 'LEnoov' ('<=' without overflow checking) branching
-var leJumps = [2][2]gc.IndexJump{
+var leJumps = [2][2]ssagen.IndexJump{
{{Jump: arm.ABEQ, Index: 0}, {Jump: arm.ABPL, Index: 1}}, // next == b.Succs[0]
{{Jump: arm.ABMI, Index: 0}, {Jump: arm.ABEQ, Index: 0}}, // next == b.Succs[1]
}
// To model a 'GTnoov' ('>' without overflow checking) branching
-var gtJumps = [2][2]gc.IndexJump{
+var gtJumps = [2][2]ssagen.IndexJump{
{{Jump: arm.ABMI, Index: 1}, {Jump: arm.ABEQ, Index: 1}}, // next == b.Succs[0]
{{Jump: arm.ABEQ, Index: 1}, {Jump: arm.ABPL, Index: 0}}, // next == b.Succs[1]
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
@@ -929,11 +931,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.Reg = arm.REG_R0
p = s.Prog(arm.ABNE)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go
index 40d6e17ae2296..d3db37e16f43b 100644
--- a/src/cmd/compile/internal/arm64/galign.go
+++ b/src/cmd/compile/internal/arm64/galign.go
@@ -5,12 +5,12 @@
package arm64
import (
- "cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/arm64"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &arm64.Linkarm64
arch.REGSP = arm64.REGSP
arch.MAXWIDTH = 1 << 50
@@ -20,7 +20,7 @@ func Init(arch *gc.Arch) {
arch.Ginsnop = ginsnop
arch.Ginsnopdefer = ginsnop
- arch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+ arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
}
diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go
index f3fec03854f16..8364535f63bb8 100644
--- a/src/cmd/compile/internal/arm64/ggen.go
+++ b/src/cmd/compile/internal/arm64/ggen.go
@@ -5,7 +5,9 @@
package arm64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
"cmd/internal/objabi"
@@ -22,52 +24,52 @@ func padframe(frame int64) int64 {
return frame
}
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
if cnt == 0 {
return p
}
- if cnt < int64(4*gc.Widthptr) {
- for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
- p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off+i)
+ if cnt < int64(4*types.PtrSize) {
+ for i := int64(0); i < cnt; i += int64(types.PtrSize) {
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off+i)
}
- } else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
- if cnt%(2*int64(gc.Widthptr)) != 0 {
- p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off)
- off += int64(gc.Widthptr)
- cnt -= int64(gc.Widthptr)
+ } else if cnt <= int64(128*types.PtrSize) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
+ if cnt%(2*int64(types.PtrSize)) != 0 {
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off)
+ off += int64(types.PtrSize)
+ cnt -= int64(types.PtrSize)
}
- p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REG_R20, 0)
- p = pp.Appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+off, obj.TYPE_REG, arm64.REG_R20, 0)
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REG_R20, 0)
+ p = pp.Append(p, arm64.AADD, obj.TYPE_CONST, 0, 8+off, obj.TYPE_REG, arm64.REG_R20, 0)
p.Reg = arm64.REG_R20
- p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+ p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
- p.To.Offset = 4 * (64 - cnt/(2*int64(gc.Widthptr)))
+ p.To.Sym = ir.Syms.Duffzero
+ p.To.Offset = 4 * (64 - cnt/(2*int64(types.PtrSize)))
} else {
// Not using REGTMP, so this is async preemptible (async preemption clobbers REGTMP).
// We are at the function entry, where no register is live, so it is okay to clobber
// other registers
const rtmp = arm64.REG_R20
- p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, rtmp, 0)
- p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
- p = pp.Appendpp(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT1, 0)
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, rtmp, 0)
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
+ p = pp.Append(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT1, 0)
p.Reg = arm64.REGRT1
- p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, rtmp, 0)
- p = pp.Appendpp(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT2, 0)
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, rtmp, 0)
+ p = pp.Append(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT2, 0)
p.Reg = arm64.REGRT1
- p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr))
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(types.PtrSize))
p.Scond = arm64.C_XPRE
p1 := p
- p = pp.Appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
+ p = pp.Append(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
p.Reg = arm64.REGRT2
- p = pp.Appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
- gc.Patch(p, p1)
+ p = pp.Append(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+ p.To.SetTarget(p1)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
p := pp.Prog(arm64.AHINT)
p.From.Type = obj.TYPE_CONST
return p
diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go
index 43588511ab3b1..73e74e12199b5 100644
--- a/src/cmd/compile/internal/arm64/ssa.go
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -7,9 +7,11 @@ package arm64
import (
"math"
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
@@ -81,7 +83,7 @@ func makeshift(reg int16, typ int64, s int64) int64 {
}
// genshift generates a Prog for r = r0 op (r1 shifted by n)
-func genshift(s *gc.SSAGenState, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
+func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
p.From.Offset = makeshift(r1, typ, n)
@@ -110,7 +112,7 @@ func genIndexedOperand(v *ssa.Value) obj.Addr {
return mop
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy, ssa.OpARM64MOVDreg:
if v.Type.IsMemory() {
@@ -148,7 +150,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpStoreReg:
@@ -159,7 +161,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpARM64ADD,
ssa.OpARM64SUB,
ssa.OpARM64AND,
@@ -393,10 +395,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
- gc.AddAux(&p.From, v)
- case *gc.Node:
+ ssagen.AddAux(&p.From, v)
+ case *ir.Name:
wantreg = "SP"
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVD $off(SP), R
wantreg = "SP"
@@ -417,7 +419,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpARM64MOVBloadidx,
@@ -444,7 +446,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpARM64MOVBstore,
@@ -461,7 +463,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpARM64MOVBstoreidx,
ssa.OpARM64MOVHstoreidx,
ssa.OpARM64MOVWstoreidx,
@@ -482,7 +484,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = int64(v.Args[2].Reg())
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpARM64MOVBstorezero,
ssa.OpARM64MOVHstorezero,
ssa.OpARM64MOVWstorezero,
@@ -492,7 +494,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = arm64.REGZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpARM64MOVBstorezeroidx,
ssa.OpARM64MOVHstorezeroidx,
ssa.OpARM64MOVWstorezeroidx,
@@ -511,7 +513,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = int64(arm64.REGZERO)
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpARM64BFI,
ssa.OpARM64BFXIL:
r := v.Reg()
@@ -580,7 +582,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p2.From.Type = obj.TYPE_REG
p2.From.Reg = arm64.REGTMP
p2.To.Type = obj.TYPE_BRANCH
- gc.Patch(p2, p)
+ p2.To.SetTarget(p)
case ssa.OpARM64LoweredAtomicExchange64Variant,
ssa.OpARM64LoweredAtomicExchange32Variant:
swap := arm64.ASWPALD
@@ -634,7 +636,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = arm64.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
case ssa.OpARM64LoweredAtomicAdd64Variant,
ssa.OpARM64LoweredAtomicAdd32Variant:
// LDADDAL Rarg1, (Rarg0), Rout
@@ -698,13 +700,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p4.From.Type = obj.TYPE_REG
p4.From.Reg = arm64.REGTMP
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p)
+ p4.To.SetTarget(p)
p5 := s.Prog(arm64.ACSET)
p5.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
p5.From.Reg = arm64.COND_EQ
p5.To.Type = obj.TYPE_REG
p5.To.Reg = out
- gc.Patch(p2, p5)
+ p2.To.SetTarget(p5)
case ssa.OpARM64LoweredAtomicCas64Variant,
ssa.OpARM64LoweredAtomicCas32Variant:
// Rarg0: ptr
@@ -792,7 +794,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = arm64.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
case ssa.OpARM64LoweredAtomicAnd8Variant,
ssa.OpARM64LoweredAtomicAnd32Variant:
atomic_clear := arm64.ALDCLRALW
@@ -959,7 +961,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
+ p.To.Sym = ir.Syms.Duffzero
p.To.Offset = v.AuxInt
case ssa.OpARM64LoweredZero:
// STP.P (ZR,ZR), 16(R16)
@@ -980,12 +982,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p2.Reg = arm64.REG_R16
p3 := s.Prog(arm64.ABLE)
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
case ssa.OpARM64DUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffcopy
+ p.To.Sym = ir.Syms.Duffcopy
p.To.Offset = v.AuxInt
case ssa.OpARM64LoweredMove:
// MOVD.P 8(R16), Rtmp
@@ -1013,7 +1015,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.Reg = arm64.REG_R16
p4 := s.Prog(arm64.ABLE)
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p)
+ p4.To.SetTarget(p)
case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
s.Call(v)
case ssa.OpARM64LoweredWB:
@@ -1025,21 +1027,21 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpARM64LoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(arm64.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = arm64.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Line==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Line==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpARM64Equal,
ssa.OpARM64NotEqual,
@@ -1067,12 +1069,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpARM64LoweredGetClosurePtr:
// Closure pointer is R26 (arm64.REGCTXT).
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpARM64LoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(arm64.AMOVD)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -1142,24 +1144,24 @@ var blockJump = map[ssa.BlockKind]struct {
}
// To model a 'LEnoov' ('<=' without overflow checking) branching
-var leJumps = [2][2]gc.IndexJump{
+var leJumps = [2][2]ssagen.IndexJump{
{{Jump: arm64.ABEQ, Index: 0}, {Jump: arm64.ABPL, Index: 1}}, // next == b.Succs[0]
{{Jump: arm64.ABMI, Index: 0}, {Jump: arm64.ABEQ, Index: 0}}, // next == b.Succs[1]
}
// To model a 'GTnoov' ('>' without overflow checking) branching
-var gtJumps = [2][2]gc.IndexJump{
+var gtJumps = [2][2]ssagen.IndexJump{
{{Jump: arm64.ABMI, Index: 1}, {Jump: arm64.ABEQ, Index: 1}}, // next == b.Succs[0]
{{Jump: arm64.ABEQ, Index: 1}, {Jump: arm64.ABPL, Index: 0}}, // next == b.Succs[1]
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
@@ -1172,11 +1174,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.Reg = arm64.REG_R0
p = s.Prog(arm64.ABNE)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/base/base.go
similarity index 55%
rename from src/cmd/compile/internal/gc/racewalk.go
rename to src/cmd/compile/internal/base/base.go
index 35526174010fa..3b9bc3a8af2d5 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/base/base.go
@@ -1,15 +1,43 @@
-// Copyright 2012 The Go Authors. All rights reserved.
+// Copyright 2009 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 gc
+package base
import (
- "cmd/compile/internal/types"
- "cmd/internal/src"
- "cmd/internal/sys"
+ "os"
)
+var atExitFuncs []func()
+
+func AtExit(f func()) {
+ atExitFuncs = append(atExitFuncs, f)
+}
+
+func Exit(code int) {
+ for i := len(atExitFuncs) - 1; i >= 0; i-- {
+ f := atExitFuncs[i]
+ atExitFuncs = atExitFuncs[:i]
+ f()
+ }
+ os.Exit(code)
+}
+
+// To enable tracing support (-t flag), set EnableTrace to true.
+const EnableTrace = false
+
+func Compiling(pkgs []string) bool {
+ if Ctxt.Pkgpath != "" {
+ for _, p := range pkgs {
+ if Ctxt.Pkgpath == p {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
// The racewalk pass is currently handled in three parts.
//
// First, for flag_race, it inserts calls to racefuncenter and
@@ -32,7 +60,7 @@ import (
// Do not instrument the following packages at all,
// at best instrumentation would cause infinite recursion.
-var omit_pkgs = []string{
+var NoInstrumentPkgs = []string{
"runtime/internal/atomic",
"runtime/internal/sys",
"runtime/internal/math",
@@ -44,50 +72,4 @@ var omit_pkgs = []string{
// Don't insert racefuncenterfp/racefuncexit into the following packages.
// Memory accesses in the packages are either uninteresting or will cause false positives.
-var norace_inst_pkgs = []string{"sync", "sync/atomic"}
-
-func ispkgin(pkgs []string) bool {
- if myimportpath != "" {
- for _, p := range pkgs {
- if myimportpath == p {
- return true
- }
- }
- }
-
- return false
-}
-
-func instrument(fn *Node) {
- if fn.Func.Pragma&Norace != 0 {
- return
- }
-
- if !flag_race || !ispkgin(norace_inst_pkgs) {
- fn.Func.SetInstrumentBody(true)
- }
-
- if flag_race {
- lno := lineno
- lineno = src.NoXPos
-
- if thearch.LinkArch.Arch.Family != sys.AMD64 {
- fn.Func.Enter.Prepend(mkcall("racefuncenterfp", nil, nil))
- fn.Func.Exit.Append(mkcall("racefuncexit", nil, nil))
- } else {
-
- // nodpc is the PC of the caller as extracted by
- // getcallerpc. We use -widthptr(FP) for x86.
- // This only works for amd64. This will not
- // work on arm or others that might support
- // race in the future.
- nodpc := nodfp.copy()
- nodpc.Type = types.Types[TUINTPTR]
- nodpc.Xoffset = int64(-Widthptr)
- fn.Func.Dcl = append(fn.Func.Dcl, nodpc)
- fn.Func.Enter.Prepend(mkcall("racefuncenter", nil, nil, nodpc))
- fn.Func.Exit.Append(mkcall("racefuncexit", nil, nil))
- }
- lineno = lno
- }
-}
+var NoRacePkgs = []string{"sync", "sync/atomic"}
diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go
new file mode 100644
index 0000000000000..164941bb26c43
--- /dev/null
+++ b/src/cmd/compile/internal/base/debug.go
@@ -0,0 +1,194 @@
+// Copyright 2009 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.
+
+// Debug arguments, set by -d flag.
+
+package base
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "cmd/internal/objabi"
+)
+
+// Debug holds the parsed debugging configuration values.
+var Debug = DebugFlags{
+ Fieldtrack: &objabi.Fieldtrack_enabled,
+}
+
+// DebugFlags defines the debugging configuration values (see var Debug).
+// Each struct field is a different value, named for the lower-case of the field name.
+// Each field must be an int or string and must have a `help` struct tag.
+//
+// The -d option takes a comma-separated list of settings.
+// Each setting is name=value; for ints, name is short for name=1.
+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"`
+ DclStack int `help:"run internal dclstack check"`
+ Defer int `help:"print information about defer compilation"`
+ DisableNil int `help:"disable nil checks"`
+ DumpPtrs int `help:"show Node pointers values in dump output"`
+ DwarfInl int `help:"print information about DWARF inlined function creation"`
+ Export int `help:"print export data"`
+ Fieldtrack *int `help:"enable field tracking"`
+ GCProg int `help:"print dump of GC programs"`
+ Libfuzzer int `help:"enable coverage instrumentation for libfuzzer"`
+ LocationLists int `help:"print information about DWARF location list creation"`
+ Nil int `help:"print information about nil checks"`
+ PCTab string `help:"print named pc-value table"`
+ Panic int `help:"show all compiler panics"`
+ Slice int `help:"print information about slice compilation"`
+ SoftFloat int `help:"force compiler to emit soft-float code"`
+ TypeAssert int `help:"print information about type assertion inlining"`
+ TypecheckInl int `help:"eager typechecking of inline function bodies"`
+ WB int `help:"print information about write barriers"`
+ ABIWrap int `help:"print information about ABI wrapper generation"`
+
+ any bool // set when any of the values have been set
+}
+
+// Any reports whether any of the debug flags have been set.
+func (d *DebugFlags) Any() bool { return d.any }
+
+type debugField struct {
+ name string
+ help string
+ val interface{} // *int or *string
+}
+
+var debugTab []debugField
+
+func init() {
+ v := reflect.ValueOf(&Debug).Elem()
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if f.Name == "any" {
+ continue
+ }
+ name := strings.ToLower(f.Name)
+ help := f.Tag.Get("help")
+ if help == "" {
+ panic(fmt.Sprintf("base.Debug.%s is missing help text", f.Name))
+ }
+ ptr := v.Field(i).Addr().Interface()
+ switch ptr.(type) {
+ default:
+ panic(fmt.Sprintf("base.Debug.%s has invalid type %v (must be int or string)", f.Name, f.Type))
+ case *int, *string:
+ // ok
+ case **int:
+ ptr = *ptr.(**int) // record the *int itself
+ }
+ debugTab = append(debugTab, debugField{name, help, ptr})
+ }
+}
+
+// DebugSSA is called to set a -d ssa/... option.
+// If nil, those options are reported as invalid options.
+// If DebugSSA returns a non-empty string, that text is reported as a compiler error.
+var DebugSSA func(phase, flag string, val int, valString string) string
+
+// parseDebug parses the -d debug string argument.
+func parseDebug(debugstr string) {
+ // parse -d argument
+ if debugstr == "" {
+ return
+ }
+ Debug.any = true
+Split:
+ for _, name := range strings.Split(debugstr, ",") {
+ if name == "" {
+ continue
+ }
+ // display help about the -d option itself and quit
+ if name == "help" {
+ fmt.Print(debugHelpHeader)
+ maxLen := len("ssa/help")
+ for _, t := range debugTab {
+ if len(t.name) > maxLen {
+ maxLen = len(t.name)
+ }
+ }
+ for _, t := range debugTab {
+ fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help)
+ }
+ // ssa options have their own help
+ fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
+ fmt.Print(debugHelpFooter)
+ os.Exit(0)
+ }
+ val, valstring, haveInt := 1, "", true
+ if i := strings.IndexAny(name, "=:"); i >= 0 {
+ var err error
+ name, valstring = name[:i], name[i+1:]
+ val, err = strconv.Atoi(valstring)
+ if err != nil {
+ val, haveInt = 1, false
+ }
+ }
+ for _, t := range debugTab {
+ if t.name != name {
+ continue
+ }
+ switch vp := t.val.(type) {
+ case nil:
+ // Ignore
+ case *string:
+ *vp = valstring
+ case *int:
+ if !haveInt {
+ log.Fatalf("invalid debug value %v", name)
+ }
+ *vp = val
+ default:
+ panic("bad debugtab type")
+ }
+ continue Split
+ }
+ // special case for ssa for now
+ if DebugSSA != nil && strings.HasPrefix(name, "ssa/") {
+ // expect form ssa/phase/flag
+ // e.g. -d=ssa/generic_cse/time
+ // _ in phase name also matches space
+ phase := name[4:]
+ flag := "debug" // default flag is debug
+ if i := strings.Index(phase, "/"); i >= 0 {
+ flag = phase[i+1:]
+ phase = phase[:i]
+ }
+ err := DebugSSA(phase, flag, val, valstring)
+ if err != "" {
+ log.Fatalf(err)
+ }
+ continue Split
+ }
+ log.Fatalf("unknown debug key -d %s\n", name)
+ }
+}
+
+const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=]
+
+ is one of:
+
+`
+
+const debugHelpFooter = `
+ is key-specific.
+
+Key "checkptr" supports values:
+ "0": instrumentation disabled
+ "1": conversions involving unsafe.Pointer are instrumented
+ "2": conversions to unsafe.Pointer force heap allocation
+
+Key "pctab" supports values:
+ "pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata"
+`
diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go
new file mode 100644
index 0000000000000..c38bbe627210d
--- /dev/null
+++ b/src/cmd/compile/internal/base/flag.go
@@ -0,0 +1,459 @@
+// Copyright 2009 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 base
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "reflect"
+ "runtime"
+ "strings"
+
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
+ objabi.Flagprint(os.Stderr)
+ Exit(2)
+}
+
+// Flag holds the parsed command-line flags.
+// See ParseFlag for non-zero defaults.
+var Flag CmdFlags
+
+// A CountFlag is a counting integer flag.
+// It accepts -name=value to set the value directly,
+// but it also accepts -name with no =value to increment the count.
+type CountFlag int
+
+// CmdFlags defines the command-line flags (see var Flag).
+// Each struct field is a different flag, by default named for the lower-case of the field name.
+// If the flag name is a single letter, the default flag name is left upper-case.
+// If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter.
+//
+// If this default flag name can't be made right, the `flag` struct tag can be used to replace it,
+// but this should be done only in exceptional circumstances: it helps everyone if the flag name
+// is obvious from the field name when the flag is used elsewhere in the compiler sources.
+// The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly.
+//
+// Each field must have a `help` struct tag giving the flag help message.
+//
+// The allowed field types are bool, int, string, pointers to those (for values stored elsewhere),
+// CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing).
+type CmdFlags struct {
+ // Single letters
+ B CountFlag "help:\"disable bounds checking\""
+ C CountFlag "help:\"disable printing of columns in error messages\""
+ D string "help:\"set relative `path` for local imports\""
+ E CountFlag "help:\"debug symbol export\""
+ I func(string) "help:\"add `directory` to import search path\""
+ K CountFlag "help:\"debug missing line numbers\""
+ L CountFlag "help:\"show full file names in error messages\""
+ N CountFlag "help:\"disable optimizations\""
+ S CountFlag "help:\"print assembly listing\""
+ // V is added by objabi.AddVersionFlag
+ W CountFlag "help:\"debug parse tree after type checking\""
+
+ LowerC int "help:\"concurrency during compilation (1 means no concurrency)\""
+ LowerD func(string) "help:\"enable debugging settings; try -d help\""
+ LowerE CountFlag "help:\"no limit on number of errors reported\""
+ LowerH CountFlag "help:\"halt on error\""
+ LowerJ CountFlag "help:\"debug runtime-initialized variables\""
+ LowerL CountFlag "help:\"disable inlining\""
+ LowerM CountFlag "help:\"print optimization decisions\""
+ LowerO string "help:\"write output to `file`\""
+ LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
+ LowerR CountFlag "help:\"debug generated wrappers\""
+ LowerT bool "help:\"enable tracing for debugging the compiler\""
+ LowerW CountFlag "help:\"debug type checking\""
+ LowerV *bool "help:\"increase debug verbosity\""
+
+ // Special characters
+ Percent int "flag:\"%\" help:\"debug non-static initializers\""
+ CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
+
+ // Longer names
+ ABIWrap bool "help:\"enable generation of ABI wrappers\""
+ ABIWrapLimit int "help:\"emit at most N ABI wrappers (for debugging)\""
+ AsmHdr string "help:\"write assembly header to `file`\""
+ Bench string "help:\"append benchmark times to `file`\""
+ BlockProfile string "help:\"write block profile to `file`\""
+ BuildID string "help:\"record `id` as the build id in the export metadata\""
+ CPUProfile string "help:\"write cpu profile to `file`\""
+ Complete bool "help:\"compiling complete package (no C or assembly)\""
+ Dwarf bool "help:\"generate DWARF symbols\""
+ DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\"" // &Ctxt.UseBASEntries, set below
+ DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\"" // &Ctxt.Flag_locationlists, set below
+ Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below
+ EmbedCfg func(string) "help:\"read go:embed configuration from `file`\""
+ GenDwarfInl int "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals
+ GoVersion string "help:\"required version of the runtime\""
+ ImportCfg func(string) "help:\"read import configuration from `file`\""
+ ImportMap func(string) "help:\"add `definition` of the form source=actual to import map\""
+ InstallSuffix string "help:\"set pkg directory `suffix`\""
+ JSON string "help:\"version,file for JSON compiler/optimizer detail output\""
+ Lang string "help:\"Go language version source code expects\""
+ LinkObj string "help:\"write linker-specific object to `file`\""
+ LinkShared *bool "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below
+ Live CountFlag "help:\"debug liveness analysis\""
+ MSan bool "help:\"build code compatible with C/C++ memory sanitizer\""
+ MemProfile string "help:\"write memory profile to `file`\""
+ MemProfileRate int64 "help:\"set runtime.MemProfileRate to `rate`\""
+ MutexProfile string "help:\"write mutex profile to `file`\""
+ NoLocalImports bool "help:\"reject local (relative) imports\""
+ Pack bool "help:\"write to file.a instead of file.o\""
+ Race bool "help:\"enable race detector\""
+ Shared *bool "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
+ SmallFrames bool "help:\"reduce the size limit for stack allocated objects\"" // small stacks, to diagnose GC latency; see golang.org/issue/27732
+ Spectre string "help:\"enable spectre mitigations in `list` (all, index, ret)\""
+ Std bool "help:\"compiling standard library\""
+ SymABIs string "help:\"read symbol ABIs from `file`\""
+ TraceProfile string "help:\"write an execution trace to `file`\""
+ TrimPath string "help:\"remove `prefix` from recorded source file paths\""
+ WB bool "help:\"enable write barrier\"" // TODO: remove
+
+ // Configuration derived from flags; not a flag itself.
+ Cfg struct {
+ Embed struct { // set by -embedcfg
+ Patterns map[string][]string
+ Files map[string]string
+ }
+ ImportDirs []string // appended to by -I
+ ImportMap map[string]string // set by -importmap OR -importcfg
+ PackageFile map[string]string // set by -importcfg; nil means not in use
+ SpectreIndex bool // set by -spectre=index or -spectre=all
+ // Whether we are adding any sort of code instrumentation, such as
+ // when the race detector is enabled.
+ Instrumenting bool
+ }
+}
+
+// ParseFlags parses the command-line flags into Flag.
+func ParseFlags() {
+ Flag.I = addImportDir
+
+ Flag.LowerC = 1
+ Flag.LowerD = parseDebug
+ Flag.LowerP = &Ctxt.Pkgpath
+ Flag.LowerV = &Ctxt.Debugvlog
+
+ Flag.ABIWrap = objabi.Regabi_enabled != 0
+ Flag.Dwarf = objabi.GOARCH != "wasm"
+ Flag.DwarfBASEntries = &Ctxt.UseBASEntries
+ Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
+ *Flag.DwarfLocationLists = true
+ Flag.Dynlink = &Ctxt.Flag_dynlink
+ Flag.EmbedCfg = readEmbedCfg
+ Flag.GenDwarfInl = 2
+ Flag.ImportCfg = readImportCfg
+ Flag.ImportMap = addImportMap
+ Flag.LinkShared = &Ctxt.Flag_linkshared
+ Flag.Shared = &Ctxt.Flag_shared
+ Flag.WB = true
+
+ Flag.Cfg.ImportMap = make(map[string]string)
+
+ objabi.AddVersionFlag() // -V
+ registerFlags()
+ objabi.Flagparse(usage)
+
+ if Flag.MSan && !sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
+ log.Fatalf("%s/%s does not support -msan", objabi.GOOS, objabi.GOARCH)
+ }
+ if Flag.Race && !sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) {
+ log.Fatalf("%s/%s does not support -race", objabi.GOOS, objabi.GOARCH)
+ }
+ if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) {
+ log.Fatalf("%s/%s does not support -shared", objabi.GOOS, objabi.GOARCH)
+ }
+ parseSpectre(Flag.Spectre) // left as string for RecordFlags
+
+ Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
+ Ctxt.Flag_optimize = Flag.N == 0
+ Ctxt.Debugasm = int(Flag.S)
+
+ if flag.NArg() < 1 {
+ usage()
+ }
+
+ if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
+ fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
+ Exit(2)
+ }
+
+ if Flag.LowerO == "" {
+ p := flag.Arg(0)
+ if i := strings.LastIndex(p, "/"); i >= 0 {
+ p = p[i+1:]
+ }
+ if runtime.GOOS == "windows" {
+ if i := strings.LastIndex(p, `\`); i >= 0 {
+ p = p[i+1:]
+ }
+ }
+ if i := strings.LastIndex(p, "."); i >= 0 {
+ p = p[:i]
+ }
+ suffix := ".o"
+ if Flag.Pack {
+ suffix = ".a"
+ }
+ Flag.LowerO = p + suffix
+ }
+
+ if Flag.Race && Flag.MSan {
+ log.Fatal("cannot use both -race and -msan")
+ }
+ if Flag.Race || Flag.MSan {
+ // -race and -msan imply -d=checkptr for now.
+ Debug.Checkptr = 1
+ }
+
+ if Flag.CompilingRuntime && Flag.N != 0 {
+ log.Fatal("cannot disable optimizations while compiling runtime")
+ }
+ if Flag.LowerC < 1 {
+ log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
+ }
+ if Flag.LowerC > 1 && !concurrentBackendAllowed() {
+ log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
+ }
+
+ if Flag.CompilingRuntime {
+ // Runtime can't use -d=checkptr, at least not yet.
+ Debug.Checkptr = 0
+
+ // Fuzzing the runtime isn't interesting either.
+ Debug.Libfuzzer = 0
+ }
+
+ // set via a -d flag
+ Ctxt.Debugpcln = Debug.PCTab
+}
+
+// registerFlags adds flag registrations for all the fields in Flag.
+// See the comment on type CmdFlags for the rules.
+func registerFlags() {
+ var (
+ boolType = reflect.TypeOf(bool(false))
+ intType = reflect.TypeOf(int(0))
+ stringType = reflect.TypeOf(string(""))
+ ptrBoolType = reflect.TypeOf(new(bool))
+ ptrIntType = reflect.TypeOf(new(int))
+ ptrStringType = reflect.TypeOf(new(string))
+ countType = reflect.TypeOf(CountFlag(0))
+ funcType = reflect.TypeOf((func(string))(nil))
+ )
+
+ v := reflect.ValueOf(&Flag).Elem()
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if f.Name == "Cfg" {
+ continue
+ }
+
+ var name string
+ if len(f.Name) == 1 {
+ name = f.Name
+ } else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
+ name = string(rune(f.Name[5] + 'a' - 'A'))
+ } else {
+ name = strings.ToLower(f.Name)
+ }
+ if tag := f.Tag.Get("flag"); tag != "" {
+ name = tag
+ }
+
+ help := f.Tag.Get("help")
+ if help == "" {
+ panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
+ }
+
+ if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
+ panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
+ }
+
+ switch f.Type {
+ case boolType:
+ p := v.Field(i).Addr().Interface().(*bool)
+ flag.BoolVar(p, name, *p, help)
+ case intType:
+ p := v.Field(i).Addr().Interface().(*int)
+ flag.IntVar(p, name, *p, help)
+ case stringType:
+ p := v.Field(i).Addr().Interface().(*string)
+ flag.StringVar(p, name, *p, help)
+ case ptrBoolType:
+ p := v.Field(i).Interface().(*bool)
+ flag.BoolVar(p, name, *p, help)
+ case ptrIntType:
+ p := v.Field(i).Interface().(*int)
+ flag.IntVar(p, name, *p, help)
+ case ptrStringType:
+ p := v.Field(i).Interface().(*string)
+ flag.StringVar(p, name, *p, help)
+ case countType:
+ p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
+ objabi.Flagcount(name, help, p)
+ case funcType:
+ f := v.Field(i).Interface().(func(string))
+ objabi.Flagfn1(name, help, f)
+ }
+ }
+}
+
+// concurrentFlagOk reports whether the current compiler flags
+// are compatible with concurrent compilation.
+func concurrentFlagOk() bool {
+ // TODO(rsc): Many of these are fine. Remove them.
+ return Flag.Percent == 0 &&
+ Flag.E == 0 &&
+ Flag.K == 0 &&
+ Flag.L == 0 &&
+ Flag.LowerH == 0 &&
+ Flag.LowerJ == 0 &&
+ Flag.LowerM == 0 &&
+ Flag.LowerR == 0
+}
+
+func concurrentBackendAllowed() bool {
+ if !concurrentFlagOk() {
+ return false
+ }
+
+ // Debug.S by itself is ok, because all printing occurs
+ // while writing the object file, and that is non-concurrent.
+ // Adding Debug_vlog, however, causes Debug.S to also print
+ // while flushing the plist, which happens concurrently.
+ if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 {
+ return false
+ }
+ // TODO: Test and delete this condition.
+ if objabi.Fieldtrack_enabled != 0 {
+ return false
+ }
+ // TODO: fix races and enable the following flags
+ if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race {
+ return false
+ }
+ return true
+}
+
+func addImportDir(dir string) {
+ if dir != "" {
+ Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
+ }
+}
+
+func addImportMap(s string) {
+ if Flag.Cfg.ImportMap == nil {
+ Flag.Cfg.ImportMap = make(map[string]string)
+ }
+ if strings.Count(s, "=") != 1 {
+ log.Fatal("-importmap argument must be of the form source=actual")
+ }
+ i := strings.Index(s, "=")
+ source, actual := s[:i], s[i+1:]
+ if source == "" || actual == "" {
+ log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
+ }
+ Flag.Cfg.ImportMap[source] = actual
+}
+
+func readImportCfg(file string) {
+ if Flag.Cfg.ImportMap == nil {
+ Flag.Cfg.ImportMap = make(map[string]string)
+ }
+ Flag.Cfg.PackageFile = map[string]string{}
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-importcfg: %v", err)
+ }
+
+ for lineNum, line := range strings.Split(string(data), "\n") {
+ lineNum++ // 1-based
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+
+ var verb, args string
+ if i := strings.Index(line, " "); i < 0 {
+ verb = line
+ } else {
+ verb, args = line[:i], strings.TrimSpace(line[i+1:])
+ }
+ var before, after string
+ if i := strings.Index(args, "="); i >= 0 {
+ before, after = args[:i], args[i+1:]
+ }
+ switch verb {
+ default:
+ log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
+ case "importmap":
+ if before == "" || after == "" {
+ log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
+ }
+ Flag.Cfg.ImportMap[before] = after
+ case "packagefile":
+ if before == "" || after == "" {
+ log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
+ }
+ Flag.Cfg.PackageFile[before] = after
+ }
+ }
+}
+
+func readEmbedCfg(file string) {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-embedcfg: %v", err)
+ }
+ if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
+ log.Fatalf("%s: %v", file, err)
+ }
+ if Flag.Cfg.Embed.Patterns == nil {
+ log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
+ }
+ if Flag.Cfg.Embed.Files == nil {
+ log.Fatalf("%s: invalid embedcfg: missing Files", file)
+ }
+}
+
+// parseSpectre parses the spectre configuration from the string s.
+func parseSpectre(s string) {
+ for _, f := range strings.Split(s, ",") {
+ f = strings.TrimSpace(f)
+ switch f {
+ default:
+ log.Fatalf("unknown setting -spectre=%s", f)
+ case "":
+ // nothing
+ case "all":
+ Flag.Cfg.SpectreIndex = true
+ Ctxt.Retpoline = true
+ case "index":
+ Flag.Cfg.SpectreIndex = true
+ case "ret":
+ Ctxt.Retpoline = true
+ }
+ }
+
+ if Flag.Cfg.SpectreIndex {
+ switch objabi.GOARCH {
+ case "amd64":
+ // ok
+ default:
+ log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/base/link.go b/src/cmd/compile/internal/base/link.go
new file mode 100644
index 0000000000000..49fe4352b2f65
--- /dev/null
+++ b/src/cmd/compile/internal/base/link.go
@@ -0,0 +1,36 @@
+// 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 base
+
+import (
+ "cmd/internal/obj"
+)
+
+var Ctxt *obj.Link
+
+// TODO(mdempsky): These should probably be obj.Link methods.
+
+// PkgLinksym returns the linker symbol for name within the given
+// package prefix. For user packages, prefix should be the package
+// path encoded with objabi.PathToPrefix.
+func PkgLinksym(prefix, name string, abi obj.ABI) *obj.LSym {
+ if name == "_" {
+ // TODO(mdempsky): Cleanup callers and Fatalf instead.
+ return linksym(prefix, "_", abi)
+ }
+ return linksym(prefix, prefix+"."+name, abi)
+}
+
+// Linkname returns the linker symbol for the given name as it might
+// appear within a //go:linkname directive.
+func Linkname(name string, abi obj.ABI) *obj.LSym {
+ return linksym("_", name, abi)
+}
+
+// linksym is an internal helper function for implementing the above
+// exported APIs.
+func linksym(pkg, name string, abi obj.ABI) *obj.LSym {
+ return Ctxt.LookupABIInit(name, abi, func(r *obj.LSym) { r.Pkg = pkg })
+}
diff --git a/src/cmd/compile/internal/base/print.go b/src/cmd/compile/internal/base/print.go
new file mode 100644
index 0000000000000..668c600d31774
--- /dev/null
+++ b/src/cmd/compile/internal/base/print.go
@@ -0,0 +1,264 @@
+// 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 base
+
+import (
+ "fmt"
+ "os"
+ "runtime/debug"
+ "sort"
+ "strings"
+
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+// An errorMsg is a queued error message, waiting to be printed.
+type errorMsg struct {
+ pos src.XPos
+ msg string
+}
+
+// Pos is the current source position being processed,
+// printed by Errorf, ErrorfLang, Fatalf, and Warnf.
+var Pos src.XPos
+
+var (
+ errorMsgs []errorMsg
+ numErrors int // number of entries in errorMsgs that are errors (as opposed to warnings)
+ numSyntaxErrors int
+)
+
+// Errors returns the number of errors reported.
+func Errors() int {
+ return numErrors
+}
+
+// SyntaxErrors returns the number of syntax errors reported
+func SyntaxErrors() int {
+ return numSyntaxErrors
+}
+
+// addErrorMsg adds a new errorMsg (which may be a warning) to errorMsgs.
+func addErrorMsg(pos src.XPos, format string, args ...interface{}) {
+ msg := fmt.Sprintf(format, args...)
+ // Only add the position if know the position.
+ // See issue golang.org/issue/11361.
+ if pos.IsKnown() {
+ msg = fmt.Sprintf("%v: %s", FmtPos(pos), msg)
+ }
+ errorMsgs = append(errorMsgs, errorMsg{
+ pos: pos,
+ msg: msg + "\n",
+ })
+}
+
+// FmtPos formats pos as a file:line string.
+func FmtPos(pos src.XPos) string {
+ if Ctxt == nil {
+ return "???"
+ }
+ return Ctxt.OutermostPos(pos).Format(Flag.C == 0, Flag.L == 1)
+}
+
+// byPos sorts errors by source position.
+type byPos []errorMsg
+
+func (x byPos) Len() int { return len(x) }
+func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) }
+func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+// FlushErrors sorts errors seen so far by line number, prints them to stdout,
+// and empties the errors array.
+func FlushErrors() {
+ if Ctxt != nil && Ctxt.Bso != nil {
+ Ctxt.Bso.Flush()
+ }
+ if len(errorMsgs) == 0 {
+ return
+ }
+ sort.Stable(byPos(errorMsgs))
+ for i, err := range errorMsgs {
+ if i == 0 || err.msg != errorMsgs[i-1].msg {
+ fmt.Printf("%s", err.msg)
+ }
+ }
+ errorMsgs = errorMsgs[:0]
+}
+
+// lasterror keeps track of the most recently issued error,
+// to avoid printing multiple error messages on the same line.
+var lasterror struct {
+ syntax src.XPos // source position of last syntax error
+ other src.XPos // source position of last non-syntax error
+ msg string // error message of last non-syntax error
+}
+
+// sameline reports whether two positions a, b are on the same line.
+func sameline(a, b src.XPos) bool {
+ p := Ctxt.PosTable.Pos(a)
+ q := Ctxt.PosTable.Pos(b)
+ return p.Base() == q.Base() && p.Line() == q.Line()
+}
+
+// Errorf reports a formatted error at the current line.
+func Errorf(format string, args ...interface{}) {
+ ErrorfAt(Pos, format, args...)
+}
+
+// ErrorfAt reports a formatted error message at pos.
+func ErrorfAt(pos src.XPos, format string, args ...interface{}) {
+ msg := fmt.Sprintf(format, args...)
+
+ if strings.HasPrefix(msg, "syntax error") {
+ numSyntaxErrors++
+ // only one syntax error per line, no matter what error
+ if sameline(lasterror.syntax, pos) {
+ return
+ }
+ lasterror.syntax = pos
+ } else {
+ // only one of multiple equal non-syntax errors per line
+ // (FlushErrors shows only one of them, so we filter them
+ // here as best as we can (they may not appear in order)
+ // so that we don't count them here and exit early, and
+ // then have nothing to show for.)
+ if sameline(lasterror.other, pos) && lasterror.msg == msg {
+ return
+ }
+ lasterror.other = pos
+ lasterror.msg = msg
+ }
+
+ addErrorMsg(pos, "%s", msg)
+ numErrors++
+
+ hcrash()
+ if numErrors >= 10 && Flag.LowerE == 0 {
+ FlushErrors()
+ fmt.Printf("%v: too many errors\n", FmtPos(pos))
+ ErrorExit()
+ }
+}
+
+// ErrorfVers reports that a language feature (format, args) requires a later version of Go.
+func ErrorfVers(lang string, format string, args ...interface{}) {
+ Errorf("%s requires %s or later (-lang was set to %s; check go.mod)", fmt.Sprintf(format, args...), lang, Flag.Lang)
+}
+
+// UpdateErrorDot is a clumsy hack that rewrites the last error,
+// if it was "LINE: undefined: NAME", to be "LINE: undefined: NAME in EXPR".
+// It is used to give better error messages for dot (selector) expressions.
+func UpdateErrorDot(line string, name, expr string) {
+ if len(errorMsgs) == 0 {
+ return
+ }
+ e := &errorMsgs[len(errorMsgs)-1]
+ if strings.HasPrefix(e.msg, line) && e.msg == fmt.Sprintf("%v: undefined: %v\n", line, name) {
+ e.msg = fmt.Sprintf("%v: undefined: %v in %v\n", line, name, expr)
+ }
+}
+
+// Warnf reports a formatted warning at the current line.
+// In general the Go compiler does NOT generate warnings,
+// so this should be used only when the user has opted in
+// to additional output by setting a particular flag.
+func Warn(format string, args ...interface{}) {
+ WarnfAt(Pos, format, args...)
+}
+
+// WarnfAt reports a formatted warning at pos.
+// In general the Go compiler does NOT generate warnings,
+// so this should be used only when the user has opted in
+// to additional output by setting a particular flag.
+func WarnfAt(pos src.XPos, format string, args ...interface{}) {
+ addErrorMsg(pos, format, args...)
+ if Flag.LowerM != 0 {
+ FlushErrors()
+ }
+}
+
+// Fatalf reports a fatal error - an internal problem - at the current line and exits.
+// If other errors have already been printed, then Fatalf just quietly exits.
+// (The internal problem may have been caused by incomplete information
+// after the already-reported errors, so best to let users fix those and
+// try again without being bothered about a spurious internal error.)
+//
+// But if no errors have been printed, or if -d panic has been specified,
+// Fatalf prints the error as an "internal compiler error". In a released build,
+// it prints an error asking to file a bug report. In development builds, it
+// prints a stack trace.
+//
+// If -h has been specified, Fatalf panics to force the usual runtime info dump.
+func Fatalf(format string, args ...interface{}) {
+ FatalfAt(Pos, format, args...)
+}
+
+// FatalfAt reports a fatal error - an internal problem - at pos and exits.
+// If other errors have already been printed, then FatalfAt just quietly exits.
+// (The internal problem may have been caused by incomplete information
+// after the already-reported errors, so best to let users fix those and
+// try again without being bothered about a spurious internal error.)
+//
+// But if no errors have been printed, or if -d panic has been specified,
+// FatalfAt prints the error as an "internal compiler error". In a released build,
+// it prints an error asking to file a bug report. In development builds, it
+// prints a stack trace.
+//
+// If -h has been specified, FatalfAt panics to force the usual runtime info dump.
+func FatalfAt(pos src.XPos, format string, args ...interface{}) {
+ FlushErrors()
+
+ if Debug.Panic != 0 || numErrors == 0 {
+ fmt.Printf("%v: internal compiler error: ", FmtPos(pos))
+ fmt.Printf(format, args...)
+ fmt.Printf("\n")
+
+ // If this is a released compiler version, ask for a bug report.
+ if strings.HasPrefix(objabi.Version, "go") {
+ fmt.Printf("\n")
+ fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
+ fmt.Printf("https://golang.org/issue/new\n")
+ } else {
+ // Not a release; dump a stack trace, too.
+ fmt.Println()
+ os.Stdout.Write(debug.Stack())
+ fmt.Println()
+ }
+ }
+
+ hcrash()
+ ErrorExit()
+}
+
+// hcrash crashes the compiler when -h is set, to find out where a message is generated.
+func hcrash() {
+ if Flag.LowerH != 0 {
+ FlushErrors()
+ if Flag.LowerO != "" {
+ os.Remove(Flag.LowerO)
+ }
+ panic("-h")
+ }
+}
+
+// ErrorExit handles an error-status exit.
+// It flushes any pending errors, removes the output file, and exits.
+func ErrorExit() {
+ FlushErrors()
+ if Flag.LowerO != "" {
+ os.Remove(Flag.LowerO)
+ }
+ os.Exit(2)
+}
+
+// ExitIfErrors calls ErrorExit if any errors have been reported.
+func ExitIfErrors() {
+ if Errors() > 0 {
+ ErrorExit()
+ }
+}
+
+var AutogeneratedPos src.XPos
diff --git a/src/cmd/compile/internal/gc/timings.go b/src/cmd/compile/internal/base/timings.go
similarity index 99%
rename from src/cmd/compile/internal/gc/timings.go
rename to src/cmd/compile/internal/base/timings.go
index 56b3899e2f6ae..f599f4e05f633 100644
--- a/src/cmd/compile/internal/gc/timings.go
+++ b/src/cmd/compile/internal/base/timings.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.
-package gc
+package base
import (
"fmt"
@@ -11,6 +11,8 @@ import (
"time"
)
+var Timer Timings
+
// Timings collects the execution times of labeled phases
// which are added trough a sequence of Start/Stop calls.
// Events may be associated with each phase via AddEvent.
diff --git a/src/cmd/compile/internal/bitvec/bv.go b/src/cmd/compile/internal/bitvec/bv.go
new file mode 100644
index 0000000000000..bcac1fe351fac
--- /dev/null
+++ b/src/cmd/compile/internal/bitvec/bv.go
@@ -0,0 +1,190 @@
+// Copyright 2013 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 bitvec
+
+import (
+ "math/bits"
+
+ "cmd/compile/internal/base"
+)
+
+const (
+ wordBits = 32
+ wordMask = wordBits - 1
+ wordShift = 5
+)
+
+// A BitVec is a bit vector.
+type BitVec struct {
+ N int32 // number of bits in vector
+ B []uint32 // words holding bits
+}
+
+func New(n int32) BitVec {
+ nword := (n + wordBits - 1) / wordBits
+ return BitVec{n, make([]uint32, nword)}
+}
+
+type Bulk struct {
+ words []uint32
+ nbit int32
+ nword int32
+}
+
+func NewBulk(nbit int32, count int32) Bulk {
+ nword := (nbit + wordBits - 1) / wordBits
+ size := int64(nword) * int64(count)
+ if int64(int32(size*4)) != size*4 {
+ base.Fatalf("NewBulk too big: nbit=%d count=%d nword=%d size=%d", nbit, count, nword, size)
+ }
+ return Bulk{
+ words: make([]uint32, size),
+ nbit: nbit,
+ nword: nword,
+ }
+}
+
+func (b *Bulk) Next() BitVec {
+ out := BitVec{b.nbit, b.words[:b.nword]}
+ b.words = b.words[b.nword:]
+ return out
+}
+
+func (bv1 BitVec) Eq(bv2 BitVec) bool {
+ if bv1.N != bv2.N {
+ base.Fatalf("bvequal: lengths %d and %d are not equal", bv1.N, bv2.N)
+ }
+ for i, x := range bv1.B {
+ if x != bv2.B[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func (dst BitVec) Copy(src BitVec) {
+ copy(dst.B, src.B)
+}
+
+func (bv BitVec) Get(i int32) bool {
+ if i < 0 || i >= bv.N {
+ base.Fatalf("bvget: index %d is out of bounds with length %d\n", i, bv.N)
+ }
+ mask := uint32(1 << uint(i%wordBits))
+ return bv.B[i>>wordShift]&mask != 0
+}
+
+func (bv BitVec) Set(i int32) {
+ if i < 0 || i >= bv.N {
+ base.Fatalf("bvset: index %d is out of bounds with length %d\n", i, bv.N)
+ }
+ mask := uint32(1 << uint(i%wordBits))
+ bv.B[i/wordBits] |= mask
+}
+
+func (bv BitVec) Unset(i int32) {
+ if i < 0 || i >= bv.N {
+ base.Fatalf("bvunset: index %d is out of bounds with length %d\n", i, bv.N)
+ }
+ mask := uint32(1 << uint(i%wordBits))
+ bv.B[i/wordBits] &^= mask
+}
+
+// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
+// If there is no such index, bvnext returns -1.
+func (bv BitVec) Next(i int32) int32 {
+ if i >= bv.N {
+ return -1
+ }
+
+ // Jump i ahead to next word with bits.
+ if bv.B[i>>wordShift]>>uint(i&wordMask) == 0 {
+ i &^= wordMask
+ i += wordBits
+ for i < bv.N && bv.B[i>>wordShift] == 0 {
+ i += wordBits
+ }
+ }
+
+ if i >= bv.N {
+ return -1
+ }
+
+ // Find 1 bit.
+ w := bv.B[i>>wordShift] >> uint(i&wordMask)
+ i += int32(bits.TrailingZeros32(w))
+
+ return i
+}
+
+func (bv BitVec) IsEmpty() bool {
+ for _, x := range bv.B {
+ if x != 0 {
+ return false
+ }
+ }
+ return true
+}
+
+func (bv BitVec) Not() {
+ for i, x := range bv.B {
+ bv.B[i] = ^x
+ }
+}
+
+// union
+func (dst BitVec) Or(src1, src2 BitVec) {
+ if len(src1.B) == 0 {
+ return
+ }
+ _, _ = dst.B[len(src1.B)-1], src2.B[len(src1.B)-1] // hoist bounds checks out of the loop
+
+ for i, x := range src1.B {
+ dst.B[i] = x | src2.B[i]
+ }
+}
+
+// intersection
+func (dst BitVec) And(src1, src2 BitVec) {
+ if len(src1.B) == 0 {
+ return
+ }
+ _, _ = dst.B[len(src1.B)-1], src2.B[len(src1.B)-1] // hoist bounds checks out of the loop
+
+ for i, x := range src1.B {
+ dst.B[i] = x & src2.B[i]
+ }
+}
+
+// difference
+func (dst BitVec) AndNot(src1, src2 BitVec) {
+ if len(src1.B) == 0 {
+ return
+ }
+ _, _ = dst.B[len(src1.B)-1], src2.B[len(src1.B)-1] // hoist bounds checks out of the loop
+
+ for i, x := range src1.B {
+ dst.B[i] = x &^ src2.B[i]
+ }
+}
+
+func (bv BitVec) String() string {
+ s := make([]byte, 2+bv.N)
+ copy(s, "#*")
+ for i := int32(0); i < bv.N; i++ {
+ ch := byte('0')
+ if bv.Get(i) {
+ ch = '1'
+ }
+ s[2+i] = ch
+ }
+ return string(s)
+}
+
+func (bv BitVec) Clear() {
+ for i := range bv.B {
+ bv.B[i] = 0
+ }
+}
diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go
new file mode 100644
index 0000000000000..520203787f02d
--- /dev/null
+++ b/src/cmd/compile/internal/deadcode/deadcode.go
@@ -0,0 +1,152 @@
+// Copyright 2009 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 deadcode
+
+import (
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+)
+
+func Func(fn *ir.Func) {
+ stmts(&fn.Body)
+
+ if len(fn.Body) == 0 {
+ return
+ }
+
+ for _, n := range fn.Body {
+ if len(n.Init()) > 0 {
+ return
+ }
+ switch n.Op() {
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ if !ir.IsConst(n.Cond, constant.Bool) || len(n.Body) > 0 || len(n.Else) > 0 {
+ return
+ }
+ case ir.OFOR:
+ n := n.(*ir.ForStmt)
+ if !ir.IsConst(n.Cond, constant.Bool) || ir.BoolVal(n.Cond) {
+ return
+ }
+ default:
+ return
+ }
+ }
+
+ fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)}
+}
+
+func stmts(nn *ir.Nodes) {
+ var lastLabel = -1
+ for i, n := range *nn {
+ if n != nil && n.Op() == ir.OLABEL {
+ lastLabel = i
+ }
+ }
+ for i, n := range *nn {
+ // Cut is set to true when all nodes after i'th position
+ // should be removed.
+ // In other words, it marks whole slice "tail" as dead.
+ cut := false
+ if n == nil {
+ continue
+ }
+ if n.Op() == ir.OIF {
+ n := n.(*ir.IfStmt)
+ n.Cond = expr(n.Cond)
+ if ir.IsConst(n.Cond, constant.Bool) {
+ var body ir.Nodes
+ if ir.BoolVal(n.Cond) {
+ n.Else = ir.Nodes{}
+ body = n.Body
+ } else {
+ n.Body = ir.Nodes{}
+ body = n.Else
+ }
+ // If "then" or "else" branch ends with panic or return statement,
+ // it is safe to remove all statements after this node.
+ // isterminating is not used to avoid goto-related complications.
+ // We must be careful not to deadcode-remove labels, as they
+ // might be the target of a goto. See issue 28616.
+ if body := body; len(body) != 0 {
+ switch body[(len(body) - 1)].Op() {
+ case ir.ORETURN, ir.OTAILCALL, ir.OPANIC:
+ if i > lastLabel {
+ cut = true
+ }
+ }
+ }
+ }
+ }
+
+ if len(n.Init()) != 0 {
+ stmts(n.(ir.InitNode).PtrInit())
+ }
+ switch n.Op() {
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
+ stmts(&n.List)
+ case ir.OFOR:
+ n := n.(*ir.ForStmt)
+ stmts(&n.Body)
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ stmts(&n.Body)
+ stmts(&n.Else)
+ case ir.ORANGE:
+ n := n.(*ir.RangeStmt)
+ stmts(&n.Body)
+ case ir.OSELECT:
+ n := n.(*ir.SelectStmt)
+ for _, cas := range n.Cases {
+ stmts(&cas.Body)
+ }
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+ for _, cas := range n.Cases {
+ stmts(&cas.Body)
+ }
+ }
+
+ if cut {
+ *nn = (*nn)[:i+1]
+ break
+ }
+ }
+}
+
+func expr(n ir.Node) ir.Node {
+ // Perform dead-code elimination on short-circuited boolean
+ // expressions involving constants with the intent of
+ // producing a constant 'if' condition.
+ switch n.Op() {
+ case ir.OANDAND:
+ n := n.(*ir.LogicalExpr)
+ n.X = expr(n.X)
+ n.Y = expr(n.Y)
+ if ir.IsConst(n.X, constant.Bool) {
+ if ir.BoolVal(n.X) {
+ return n.Y // true && x => x
+ } else {
+ return n.X // false && x => false
+ }
+ }
+ case ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ n.X = expr(n.X)
+ n.Y = expr(n.Y)
+ if ir.IsConst(n.X, constant.Bool) {
+ if ir.BoolVal(n.X) {
+ return n.X // true || x => true
+ } else {
+ return n.Y // false || x => x
+ }
+ }
+ }
+ return n
+}
diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go
new file mode 100644
index 0000000000000..60ba208d0876b
--- /dev/null
+++ b/src/cmd/compile/internal/devirtualize/devirtualize.go
@@ -0,0 +1,85 @@
+// 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 devirtualize implements a simple "devirtualization"
+// optimization pass, which replaces interface method calls with
+// direct concrete-type method calls where possible.
+package devirtualize
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+)
+
+// Func devirtualizes calls within fn where possible.
+func Func(fn *ir.Func) {
+ ir.CurFunc = fn
+ ir.VisitList(fn.Body, func(n ir.Node) {
+ if call, ok := n.(*ir.CallExpr); ok {
+ Call(call)
+ }
+ })
+}
+
+// Call devirtualizes the given call if possible.
+func Call(call *ir.CallExpr) {
+ if call.Op() != ir.OCALLINTER {
+ return
+ }
+ sel := call.X.(*ir.SelectorExpr)
+ r := ir.StaticValue(sel.X)
+ if r.Op() != ir.OCONVIFACE {
+ return
+ }
+ recv := r.(*ir.ConvExpr)
+
+ typ := recv.X.Type()
+ if typ.IsInterface() {
+ return
+ }
+
+ dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil)
+ dt.SetType(typ)
+ x := typecheck.Callee(ir.NewSelectorExpr(sel.Pos(), ir.OXDOT, dt, sel.Sel))
+ switch x.Op() {
+ case ir.ODOTMETH:
+ x := x.(*ir.SelectorExpr)
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ)
+ }
+ call.SetOp(ir.OCALLMETH)
+ call.X = x
+ case ir.ODOTINTER:
+ // Promoted method from embedded interface-typed field (#42279).
+ x := x.(*ir.SelectorExpr)
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ)
+ }
+ call.SetOp(ir.OCALLINTER)
+ call.X = x
+ default:
+ // TODO(mdempsky): Turn back into Fatalf after more testing.
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(call.Pos(), "failed to devirtualize %v (%v)", x, x.Op())
+ }
+ return
+ }
+
+ // Duplicated logic from typecheck for function call return
+ // value types.
+ //
+ // Receiver parameter size may have changed; need to update
+ // call.Type to get correct stack offsets for result
+ // parameters.
+ types.CheckSize(x.Type())
+ switch ft := x.Type(); ft.NumResults() {
+ case 0:
+ case 1:
+ call.SetType(ft.Results().Field(0).Type)
+ default:
+ call.SetType(ft.Results())
+ }
+}
diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go
new file mode 100644
index 0000000000000..dd22c033ccd57
--- /dev/null
+++ b/src/cmd/compile/internal/dwarfgen/dwarf.go
@@ -0,0 +1,458 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dwarfgen
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "sort"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/types"
+ "cmd/internal/dwarf"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) {
+ fn := curfn.(*ir.Func)
+
+ if fn.Nname != nil {
+ expect := fn.Linksym()
+ if fnsym.ABI() == obj.ABI0 {
+ expect = fn.LinksymABI(obj.ABI0)
+ }
+ if fnsym != expect {
+ base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
+ }
+ }
+
+ // Back when there were two different *Funcs for a function, this code
+ // was not consistent about whether a particular *Node being processed
+ // was an ODCLFUNC or ONAME node. Partly this is because inlined function
+ // bodies have no ODCLFUNC node, which was it's own inconsistency.
+ // In any event, the handling of the two different nodes for DWARF purposes
+ // was subtly different, likely in unintended ways. CL 272253 merged the
+ // two nodes' Func fields, so that code sees the same *Func whether it is
+ // holding the ODCLFUNC or the ONAME. This resulted in changes in the
+ // DWARF output. To preserve the existing DWARF output and leave an
+ // intentional change for a future CL, this code does the following when
+ // fn.Op == ONAME:
+ //
+ // 1. Disallow use of createComplexVars in createDwarfVars.
+ // It was not possible to reach that code for an ONAME before,
+ // because the DebugInfo was set only on the ODCLFUNC Func.
+ // Calling into it in the ONAME case causes an index out of bounds panic.
+ //
+ // 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func,
+ // not the ONAME Func. Populating apdecls for the ONAME case results
+ // in selected being populated after createSimpleVars is called in
+ // createDwarfVars, and then that causes the loop to skip all the entries
+ // in dcl, meaning that the RecordAutoType calls don't happen.
+ //
+ // These two adjustments keep toolstash -cmp working for now.
+ // Deciding the right answer is, as they say, future work.
+ //
+ // We can tell the difference between the old ODCLFUNC and ONAME
+ // cases by looking at the infosym.Name. If it's empty, DebugInfo is
+ // being called from (*obj.Link).populateDWARF, which used to use
+ // the ODCLFUNC. If it's non-empty (the name will end in $abstract),
+ // DebugInfo is being called from (*obj.Link).DwarfAbstractFunc,
+ // which used to use the ONAME form.
+ isODCLFUNC := infosym.Name == ""
+
+ var apdecls []*ir.Name
+ // Populate decls for fn.
+ if isODCLFUNC {
+ for _, n := range fn.Dcl {
+ if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL
+ continue
+ }
+ switch n.Class {
+ case ir.PAUTO:
+ if !n.Used() {
+ // Text == nil -> generating abstract function
+ if fnsym.Func().Text != nil {
+ base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
+ }
+ continue
+ }
+ case ir.PPARAM, ir.PPARAMOUT:
+ default:
+ continue
+ }
+ apdecls = append(apdecls, n)
+ fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
+ }
+ }
+
+ decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls)
+
+ // For each type referenced by the functions auto vars but not
+ // already referenced by a dwarf var, attach an R_USETYPE relocation to
+ // the function symbol to insure that the type included in DWARF
+ // processing during linking.
+ typesyms := []*obj.LSym{}
+ for t, _ := range fnsym.Func().Autot {
+ typesyms = append(typesyms, t)
+ }
+ sort.Sort(obj.BySymName(typesyms))
+ for _, sym := range typesyms {
+ r := obj.Addrel(infosym)
+ r.Sym = sym
+ r.Type = objabi.R_USETYPE
+ }
+ fnsym.Func().Autot = nil
+
+ var varScopes []ir.ScopeID
+ for _, decl := range decls {
+ pos := declPos(decl)
+ varScopes = append(varScopes, findScope(fn.Marks, pos))
+ }
+
+ scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes)
+ var inlcalls dwarf.InlCalls
+ if base.Flag.GenDwarfInl > 0 {
+ inlcalls = assembleInlines(fnsym, dwarfVars)
+ }
+ return scopes, inlcalls
+}
+
+func declPos(decl *ir.Name) src.XPos {
+ return decl.Canonical().Pos()
+}
+
+// createDwarfVars process fn, returning a list of DWARF variables and the
+// Nodes they represent.
+func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) {
+ // Collect a raw list of DWARF vars.
+ var vars []*dwarf.Var
+ var decls []*ir.Name
+ var selected ir.NameSet
+ if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK {
+ decls, vars, selected = createComplexVars(fnsym, fn)
+ } else {
+ decls, vars, selected = createSimpleVars(fnsym, apDecls)
+ }
+
+ dcl := apDecls
+ if fnsym.WasInlined() {
+ dcl = preInliningDcls(fnsym)
+ }
+
+ // If optimization is enabled, the list above will typically be
+ // missing some of the original pre-optimization variables in the
+ // function (they may have been promoted to registers, folded into
+ // constants, dead-coded away, etc). Input arguments not eligible
+ // for SSA optimization are also missing. Here we add back in entries
+ // for selected missing vars. Note that the recipe below creates a
+ // conservative location. The idea here is that we want to
+ // communicate to the user that "yes, there is a variable named X
+ // in this function, but no, I don't have enough information to
+ // reliably report its contents."
+ // For non-SSA-able arguments, however, the correct information
+ // is known -- they have a single home on the stack.
+ for _, n := range dcl {
+ if selected.Has(n) {
+ continue
+ }
+ c := n.Sym().Name[0]
+ if c == '.' || n.Type().IsUntyped() {
+ continue
+ }
+ if n.Class == ir.PPARAM && !ssagen.TypeOK(n.Type()) {
+ // SSA-able args get location lists, and may move in and
+ // out of registers, so those are handled elsewhere.
+ // Autos and named output params seem to get handled
+ // with VARDEF, which creates location lists.
+ // Args not of SSA-able type are treated here; they
+ // are homed on the stack in a single place for the
+ // entire call.
+ vars = append(vars, createSimpleVar(fnsym, n))
+ decls = append(decls, n)
+ continue
+ }
+ typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
+ decls = append(decls, n)
+ abbrev := dwarf.DW_ABRV_AUTO_LOCLIST
+ isReturnValue := (n.Class == ir.PPARAMOUT)
+ if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+ abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ }
+ if n.Esc() == ir.EscHeap {
+ // The variable in question has been promoted to the heap.
+ // Its address is in n.Heapaddr.
+ // TODO(thanm): generate a better location expression
+ }
+ inlIndex := 0
+ if base.Flag.GenDwarfInl > 1 {
+ if n.InlFormal() || n.InlLocal() {
+ inlIndex = posInlIndex(n.Pos()) + 1
+ if n.InlFormal() {
+ abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ }
+ }
+ }
+ declpos := base.Ctxt.InnermostPos(n.Pos())
+ vars = append(vars, &dwarf.Var{
+ Name: n.Sym().Name,
+ IsReturnValue: isReturnValue,
+ Abbrev: abbrev,
+ StackOffset: int32(n.FrameOffset()),
+ Type: base.Ctxt.Lookup(typename),
+ DeclFile: declpos.RelFilename(),
+ DeclLine: declpos.RelLine(),
+ DeclCol: declpos.Col(),
+ InlIndex: int32(inlIndex),
+ ChildIndex: -1,
+ })
+ // Record go type of to insure that it gets emitted by the linker.
+ fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
+ }
+
+ return decls, vars
+}
+
+// Given a function that was inlined at some point during the
+// compilation, return a sorted list of nodes corresponding to the
+// autos/locals in that function prior to inlining. If this is a
+// function that is not local to the package being compiled, then the
+// names of the variables may have been "versioned" to avoid conflicts
+// with local vars; disregard this versioning when sorting.
+func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
+ fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func)
+ var rdcl []*ir.Name
+ for _, n := range fn.Inl.Dcl {
+ c := n.Sym().Name[0]
+ // Avoid reporting "_" parameters, since if there are more than
+ // one, it can result in a collision later on, as in #23179.
+ if unversion(n.Sym().Name) == "_" || c == '.' || n.Type().IsUntyped() {
+ continue
+ }
+ rdcl = append(rdcl, n)
+ }
+ return rdcl
+}
+
+// createSimpleVars creates a DWARF entry for every variable declared in the
+// function, claiming that they are permanently on the stack.
+func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
+ var vars []*dwarf.Var
+ var decls []*ir.Name
+ var selected ir.NameSet
+ for _, n := range apDecls {
+ if ir.IsAutoTmp(n) {
+ continue
+ }
+
+ decls = append(decls, n)
+ vars = append(vars, createSimpleVar(fnsym, n))
+ selected.Add(n)
+ }
+ return decls, vars, selected
+}
+
+func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
+ var abbrev int
+ var offs int64
+
+ switch n.Class {
+ case ir.PAUTO:
+ offs = n.FrameOffset()
+ abbrev = dwarf.DW_ABRV_AUTO
+ if base.Ctxt.FixedFrameSize() == 0 {
+ offs -= int64(types.PtrSize)
+ }
+ if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
+ // There is a word space for FP on ARM64 even if the frame pointer is disabled
+ offs -= int64(types.PtrSize)
+ }
+
+ case ir.PPARAM, ir.PPARAMOUT:
+ abbrev = dwarf.DW_ABRV_PARAM
+ offs = n.FrameOffset() + base.Ctxt.FixedFrameSize()
+ default:
+ base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n)
+ }
+
+ typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
+ delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type()))
+ inlIndex := 0
+ if base.Flag.GenDwarfInl > 1 {
+ if n.InlFormal() || n.InlLocal() {
+ inlIndex = posInlIndex(n.Pos()) + 1
+ if n.InlFormal() {
+ abbrev = dwarf.DW_ABRV_PARAM
+ }
+ }
+ }
+ declpos := base.Ctxt.InnermostPos(declPos(n))
+ return &dwarf.Var{
+ Name: n.Sym().Name,
+ IsReturnValue: n.Class == ir.PPARAMOUT,
+ IsInlFormal: n.InlFormal(),
+ Abbrev: abbrev,
+ StackOffset: int32(offs),
+ Type: base.Ctxt.Lookup(typename),
+ DeclFile: declpos.RelFilename(),
+ DeclLine: declpos.RelLine(),
+ DeclCol: declpos.Col(),
+ InlIndex: int32(inlIndex),
+ ChildIndex: -1,
+ }
+}
+
+// createComplexVars creates recomposed DWARF vars with location lists,
+// suitable for describing optimized code.
+func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
+ debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
+
+ // Produce a DWARF variable entry for each user variable.
+ var decls []*ir.Name
+ var vars []*dwarf.Var
+ var ssaVars ir.NameSet
+
+ for varID, dvar := range debugInfo.Vars {
+ n := dvar
+ ssaVars.Add(n)
+ for _, slot := range debugInfo.VarSlots[varID] {
+ ssaVars.Add(debugInfo.Slots[slot].N)
+ }
+
+ if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil {
+ decls = append(decls, n)
+ vars = append(vars, dvar)
+ }
+ }
+
+ return decls, vars, ssaVars
+}
+
+// createComplexVar builds a single DWARF variable entry and location list.
+func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var {
+ debug := fn.DebugInfo.(*ssa.FuncDebug)
+ n := debug.Vars[varID]
+
+ var abbrev int
+ switch n.Class {
+ case ir.PAUTO:
+ abbrev = dwarf.DW_ABRV_AUTO_LOCLIST
+ case ir.PPARAM, ir.PPARAMOUT:
+ abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ default:
+ return nil
+ }
+
+ gotype := reflectdata.TypeLinksym(n.Type())
+ delete(fnsym.Func().Autot, gotype)
+ typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
+ inlIndex := 0
+ if base.Flag.GenDwarfInl > 1 {
+ if n.InlFormal() || n.InlLocal() {
+ inlIndex = posInlIndex(n.Pos()) + 1
+ if n.InlFormal() {
+ abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ }
+ }
+ }
+ declpos := base.Ctxt.InnermostPos(n.Pos())
+ dvar := &dwarf.Var{
+ Name: n.Sym().Name,
+ IsReturnValue: n.Class == ir.PPARAMOUT,
+ IsInlFormal: n.InlFormal(),
+ Abbrev: abbrev,
+ Type: base.Ctxt.Lookup(typename),
+ // The stack offset is used as a sorting key, so for decomposed
+ // variables just give it the first one. It's not used otherwise.
+ // This won't work well if the first slot hasn't been assigned a stack
+ // location, but it's not obvious how to do better.
+ StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]),
+ DeclFile: declpos.RelFilename(),
+ DeclLine: declpos.RelLine(),
+ DeclCol: declpos.Col(),
+ InlIndex: int32(inlIndex),
+ ChildIndex: -1,
+ }
+ list := debug.LocationLists[varID]
+ if len(list) != 0 {
+ dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
+ debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
+ }
+ }
+ return dvar
+}
+
+// RecordFlags records the specified command-line flags to be placed
+// in the DWARF info.
+func RecordFlags(flags ...string) {
+ if base.Ctxt.Pkgpath == "" {
+ // We can't record the flags if we don't know what the
+ // package name is.
+ return
+ }
+
+ type BoolFlag interface {
+ IsBoolFlag() bool
+ }
+ type CountFlag interface {
+ IsCountFlag() bool
+ }
+ var cmd bytes.Buffer
+ for _, name := range flags {
+ f := flag.Lookup(name)
+ if f == nil {
+ continue
+ }
+ getter := f.Value.(flag.Getter)
+ if getter.String() == f.DefValue {
+ // Flag has default value, so omit it.
+ continue
+ }
+ if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
+ val, ok := getter.Get().(bool)
+ if ok && val {
+ fmt.Fprintf(&cmd, " -%s", f.Name)
+ continue
+ }
+ }
+ if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
+ val, ok := getter.Get().(int)
+ if ok && val == 1 {
+ fmt.Fprintf(&cmd, " -%s", f.Name)
+ continue
+ }
+ }
+ fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
+ }
+
+ if cmd.Len() == 0 {
+ return
+ }
+ s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath)
+ s.Type = objabi.SDWARFCUINFO
+ // Sometimes (for example when building tests) we can link
+ // together two package main archives. So allow dups.
+ s.Set(obj.AttrDuplicateOK, true)
+ base.Ctxt.Data = append(base.Ctxt.Data, s)
+ s.P = cmd.Bytes()[1:]
+}
+
+// RecordPackageName records the name of the package being
+// compiled, so that the linker can save it in the compile unit's DIE.
+func RecordPackageName() {
+ s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath)
+ s.Type = objabi.SDWARFCUINFO
+ // Sometimes (for example when building tests) we can link
+ // together two package main archives. So allow dups.
+ s.Set(obj.AttrDuplicateOK, true)
+ base.Ctxt.Data = append(base.Ctxt.Data, s)
+ s.P = []byte(types.LocalPkg.Name)
+}
diff --git a/src/cmd/compile/internal/gc/dwinl.go b/src/cmd/compile/internal/dwarfgen/dwinl.go
similarity index 86%
rename from src/cmd/compile/internal/gc/dwinl.go
rename to src/cmd/compile/internal/dwarfgen/dwinl.go
index bb5ae61cbb3b4..d5687cb1d72bb 100644
--- a/src/cmd/compile/internal/gc/dwinl.go
+++ b/src/cmd/compile/internal/dwarfgen/dwinl.go
@@ -2,14 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package dwarfgen
import (
+ "fmt"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/src"
- "fmt"
- "strings"
)
// To identify variables by original source position.
@@ -26,8 +29,8 @@ type varPos struct {
func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
var inlcalls dwarf.InlCalls
- if Debug_gendwarfinl != 0 {
- Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name)
+ if base.Debug.DwarfInl != 0 {
+ base.Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name)
}
// This maps inline index (from Ctxt.InlTree) to index in inlcalls.Calls
@@ -106,7 +109,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
}
m = makePreinlineDclMap(fnsym)
} else {
- ifnlsym := Ctxt.InlTree.InlinedFunction(int(ii - 1))
+ ifnlsym := base.Ctxt.InlTree.InlinedFunction(int(ii - 1))
m = makePreinlineDclMap(ifnlsym)
}
@@ -181,7 +184,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
}
// Debugging
- if Debug_gendwarfinl != 0 {
+ if base.Debug.DwarfInl != 0 {
dumpInlCalls(inlcalls)
dumpInlVars(dwVars)
}
@@ -204,16 +207,17 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
// late in the compilation when it is determined that we need an
// abstract function DIE for an inlined routine imported from a
// previously compiled package.
-func genAbstractFunc(fn *obj.LSym) {
- ifn := Ctxt.DwFixups.GetPrecursorFunc(fn)
+func AbstractFunc(fn *obj.LSym) {
+ ifn := base.Ctxt.DwFixups.GetPrecursorFunc(fn)
if ifn == nil {
- Ctxt.Diag("failed to locate precursor fn for %v", fn)
+ base.Ctxt.Diag("failed to locate precursor fn for %v", fn)
return
}
- if Debug_gendwarfinl != 0 {
- Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
+ _ = ifn.(*ir.Func)
+ if base.Debug.DwarfInl != 0 {
+ base.Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
}
- Ctxt.DwarfAbstractFunc(ifn, fn, myimportpath)
+ base.Ctxt.DwarfAbstractFunc(ifn, fn, base.Ctxt.Pkgpath)
}
// Undo any versioning performed when a name was written
@@ -235,15 +239,15 @@ func makePreinlineDclMap(fnsym *obj.LSym) map[varPos]int {
dcl := preInliningDcls(fnsym)
m := make(map[varPos]int)
for i, n := range dcl {
- pos := Ctxt.InnermostPos(n.Pos)
+ pos := base.Ctxt.InnermostPos(n.Pos())
vp := varPos{
- DeclName: unversion(n.Sym.Name),
+ DeclName: unversion(n.Sym().Name),
DeclFile: pos.RelFilename(),
DeclLine: pos.RelLine(),
DeclCol: pos.Col(),
}
if _, found := m[vp]; found {
- Fatalf("child dcl collision on symbol %s within %v\n", n.Sym.Name, fnsym.Name)
+ base.Fatalf("child dcl collision on symbol %s within %v\n", n.Sym().Name, fnsym.Name)
}
m[vp] = i
}
@@ -260,17 +264,17 @@ func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
// is one. We do this first so that parents appear before their
// children in the resulting table.
parCallIdx := -1
- parInlIdx := Ctxt.InlTree.Parent(inlIdx)
+ parInlIdx := base.Ctxt.InlTree.Parent(inlIdx)
if parInlIdx >= 0 {
parCallIdx = insertInlCall(dwcalls, parInlIdx, imap)
}
// Create new entry for this inline
- inlinedFn := Ctxt.InlTree.InlinedFunction(inlIdx)
- callXPos := Ctxt.InlTree.CallPos(inlIdx)
- absFnSym := Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
- pb := Ctxt.PosTable.Pos(callXPos).Base()
- callFileSym := Ctxt.Lookup(pb.SymFilename())
+ inlinedFn := base.Ctxt.InlTree.InlinedFunction(inlIdx)
+ callXPos := base.Ctxt.InlTree.CallPos(inlIdx)
+ absFnSym := base.Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
+ pb := base.Ctxt.PosTable.Pos(callXPos).Base()
+ callFileSym := base.Ctxt.Lookup(pb.SymFilename())
ic := dwarf.InlCall{
InlIndex: inlIdx,
CallFile: callFileSym,
@@ -298,7 +302,7 @@ func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
// the index for a node from the inlined body of D will refer to the
// call to D from C. Whew.
func posInlIndex(xpos src.XPos) int {
- pos := Ctxt.PosTable.Pos(xpos)
+ pos := base.Ctxt.PosTable.Pos(xpos)
if b := pos.Base(); b != nil {
ii := b.InliningIndex()
if ii >= 0 {
@@ -324,7 +328,7 @@ func addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int)
// Append range to correct inlined call
callIdx, found := imap[ii]
if !found {
- Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start)
+ base.Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start)
}
call := &calls[callIdx]
call.Ranges = append(call.Ranges, dwarf.Range{Start: start, End: end})
@@ -332,23 +336,23 @@ func addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int)
func dumpInlCall(inlcalls dwarf.InlCalls, idx, ilevel int) {
for i := 0; i < ilevel; i++ {
- Ctxt.Logf(" ")
+ base.Ctxt.Logf(" ")
}
ic := inlcalls.Calls[idx]
- callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex)
- Ctxt.Logf(" %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
+ callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex)
+ base.Ctxt.Logf(" %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
for _, f := range ic.InlVars {
- Ctxt.Logf(" %v", f.Name)
+ base.Ctxt.Logf(" %v", f.Name)
}
- Ctxt.Logf(" ) C: (")
+ base.Ctxt.Logf(" ) C: (")
for _, k := range ic.Children {
- Ctxt.Logf(" %v", k)
+ base.Ctxt.Logf(" %v", k)
}
- Ctxt.Logf(" ) R:")
+ base.Ctxt.Logf(" ) R:")
for _, r := range ic.Ranges {
- Ctxt.Logf(" [%d,%d)", r.Start, r.End)
+ base.Ctxt.Logf(" [%d,%d)", r.Start, r.End)
}
- Ctxt.Logf("\n")
+ base.Ctxt.Logf("\n")
for _, k := range ic.Children {
dumpInlCall(inlcalls, k, ilevel+1)
}
@@ -373,7 +377,7 @@ func dumpInlVars(dwvars []*dwarf.Var) {
if dwv.IsInAbstract {
ia = 1
}
- Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
+ base.Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
}
}
@@ -410,7 +414,7 @@ func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx,
// Callee
ic := inlCalls.Calls[idx]
- callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
+ callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
calleeRanges := ic.Ranges
// Caller
@@ -418,14 +422,14 @@ func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx,
parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}}
if parentIdx != -1 {
pic := inlCalls.Calls[parentIdx]
- caller = Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
+ caller = base.Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
parentRanges = pic.Ranges
}
// Callee ranges contained in caller ranges?
c, m := rangesContainsAll(parentRanges, calleeRanges)
if !c {
- Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
+ base.Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
}
// Now visit kids
diff --git a/src/cmd/compile/internal/dwarfgen/marker.go b/src/cmd/compile/internal/dwarfgen/marker.go
new file mode 100644
index 0000000000000..ec6ce45a900bc
--- /dev/null
+++ b/src/cmd/compile/internal/dwarfgen/marker.go
@@ -0,0 +1,94 @@
+// 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 dwarfgen
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/internal/src"
+)
+
+// A ScopeMarker tracks scope nesting and boundaries for later use
+// during DWARF generation.
+type ScopeMarker struct {
+ parents []ir.ScopeID
+ marks []ir.Mark
+}
+
+// checkPos validates the given position and returns the current scope.
+func (m *ScopeMarker) checkPos(pos src.XPos) ir.ScopeID {
+ if !pos.IsKnown() {
+ base.Fatalf("unknown scope position")
+ }
+
+ if len(m.marks) == 0 {
+ return 0
+ }
+
+ last := &m.marks[len(m.marks)-1]
+ if xposBefore(pos, last.Pos) {
+ base.FatalfAt(pos, "non-monotonic scope positions\n\t%v: previous scope position", base.FmtPos(last.Pos))
+ }
+ return last.Scope
+}
+
+// Push records a transition to a new child scope of the current scope.
+func (m *ScopeMarker) Push(pos src.XPos) {
+ current := m.checkPos(pos)
+
+ m.parents = append(m.parents, current)
+ child := ir.ScopeID(len(m.parents))
+
+ m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: child})
+}
+
+// Pop records a transition back to the current scope's parent.
+func (m *ScopeMarker) Pop(pos src.XPos) {
+ current := m.checkPos(pos)
+
+ parent := m.parents[current-1]
+
+ m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: parent})
+}
+
+// Unpush removes the current scope, which must be empty.
+func (m *ScopeMarker) Unpush() {
+ i := len(m.marks) - 1
+ current := m.marks[i].Scope
+
+ if current != ir.ScopeID(len(m.parents)) {
+ base.FatalfAt(m.marks[i].Pos, "current scope is not empty")
+ }
+
+ m.parents = m.parents[:current-1]
+ m.marks = m.marks[:i]
+}
+
+// WriteTo writes the recorded scope marks to the given function,
+// and resets the marker for reuse.
+func (m *ScopeMarker) WriteTo(fn *ir.Func) {
+ m.compactMarks()
+
+ fn.Parents = make([]ir.ScopeID, len(m.parents))
+ copy(fn.Parents, m.parents)
+ m.parents = m.parents[:0]
+
+ fn.Marks = make([]ir.Mark, len(m.marks))
+ copy(fn.Marks, m.marks)
+ m.marks = m.marks[:0]
+}
+
+func (m *ScopeMarker) compactMarks() {
+ n := 0
+ for _, next := range m.marks {
+ if n > 0 && next.Pos == m.marks[n-1].Pos {
+ m.marks[n-1].Scope = next.Scope
+ continue
+ }
+ m.marks[n] = next
+ n++
+ }
+ m.marks = m.marks[:n]
+}
diff --git a/src/cmd/compile/internal/gc/scope.go b/src/cmd/compile/internal/dwarfgen/scope.go
similarity index 78%
rename from src/cmd/compile/internal/gc/scope.go
rename to src/cmd/compile/internal/dwarfgen/scope.go
index e66b859e10069..1c040edc28423 100644
--- a/src/cmd/compile/internal/gc/scope.go
+++ b/src/cmd/compile/internal/dwarfgen/scope.go
@@ -2,21 +2,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package dwarfgen
import (
+ "sort"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/src"
- "sort"
)
// See golang.org/issue/20390.
func xposBefore(p, q src.XPos) bool {
- return Ctxt.PosTable.Pos(p).Before(Ctxt.PosTable.Pos(q))
+ return base.Ctxt.PosTable.Pos(p).Before(base.Ctxt.PosTable.Pos(q))
}
-func findScope(marks []Mark, pos src.XPos) ScopeID {
+func findScope(marks []ir.Mark, pos src.XPos) ir.ScopeID {
i := sort.Search(len(marks), func(i int) bool {
return xposBefore(pos, marks[i].Pos)
})
@@ -26,20 +29,20 @@ func findScope(marks []Mark, pos src.XPos) ScopeID {
return marks[i-1].Scope
}
-func assembleScopes(fnsym *obj.LSym, fn *Node, dwarfVars []*dwarf.Var, varScopes []ScopeID) []dwarf.Scope {
+func assembleScopes(fnsym *obj.LSym, fn *ir.Func, dwarfVars []*dwarf.Var, varScopes []ir.ScopeID) []dwarf.Scope {
// Initialize the DWARF scope tree based on lexical scopes.
- dwarfScopes := make([]dwarf.Scope, 1+len(fn.Func.Parents))
- for i, parent := range fn.Func.Parents {
+ dwarfScopes := make([]dwarf.Scope, 1+len(fn.Parents))
+ for i, parent := range fn.Parents {
dwarfScopes[i+1].Parent = int32(parent)
}
scopeVariables(dwarfVars, varScopes, dwarfScopes)
- scopePCs(fnsym, fn.Func.Marks, dwarfScopes)
+ scopePCs(fnsym, fn.Marks, dwarfScopes)
return compactScopes(dwarfScopes)
}
// scopeVariables assigns DWARF variable records to their scopes.
-func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ScopeID, dwarfScopes []dwarf.Scope) {
+func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ir.ScopeID, dwarfScopes []dwarf.Scope) {
sort.Stable(varsByScopeAndOffset{dwarfVars, varScopes})
i0 := 0
@@ -56,7 +59,7 @@ func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ScopeID, dwarfScopes []d
}
// scopePCs assigns PC ranges to their scopes.
-func scopePCs(fnsym *obj.LSym, marks []Mark, dwarfScopes []dwarf.Scope) {
+func scopePCs(fnsym *obj.LSym, marks []ir.Mark, dwarfScopes []dwarf.Scope) {
// If there aren't any child scopes (in particular, when scope
// tracking is disabled), we can skip a whole lot of work.
if len(marks) == 0 {
@@ -89,7 +92,7 @@ func compactScopes(dwarfScopes []dwarf.Scope) []dwarf.Scope {
type varsByScopeAndOffset struct {
vars []*dwarf.Var
- scopes []ScopeID
+ scopes []ir.ScopeID
}
func (v varsByScopeAndOffset) Len() int {
diff --git a/src/cmd/compile/internal/gc/scope_test.go b/src/cmd/compile/internal/dwarfgen/scope_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/scope_test.go
rename to src/cmd/compile/internal/dwarfgen/scope_test.go
index b0e038d27f5db..fcfcf85f84c2a 100644
--- a/src/cmd/compile/internal/gc/scope_test.go
+++ b/src/cmd/compile/internal/dwarfgen/scope_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc_test
+package dwarfgen
import (
- "cmd/internal/objfile"
"debug/dwarf"
"fmt"
"internal/testenv"
@@ -18,6 +17,8 @@ import (
"strconv"
"strings"
"testing"
+
+ "cmd/internal/objfile"
)
type testline struct {
diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go
new file mode 100644
index 0000000000000..58cad73c76230
--- /dev/null
+++ b/src/cmd/compile/internal/escape/escape.go
@@ -0,0 +1,2137 @@
+// 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.
+
+package escape
+
+import (
+ "fmt"
+ "math"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/logopt"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// Escape analysis.
+//
+// Here we analyze functions to determine which Go variables
+// (including implicit allocations such as calls to "new" or "make",
+// composite literals, etc.) can be allocated on the stack. The two
+// key invariants we have to ensure are: (1) pointers to stack objects
+// cannot be stored in the heap, and (2) pointers to a stack object
+// cannot outlive that object (e.g., because the declaring function
+// returned and destroyed the object's stack frame, or its space is
+// reused across loop iterations for logically distinct variables).
+//
+// We implement this with a static data-flow analysis of the AST.
+// First, we construct a directed weighted graph where vertices
+// (termed "locations") represent variables allocated by statements
+// and expressions, and edges represent assignments between variables
+// (with weights representing addressing/dereference counts).
+//
+// Next we walk the graph looking for assignment paths that might
+// violate the invariants stated above. If a variable v's address is
+// stored in the heap or elsewhere that may outlive it, then v is
+// marked as requiring heap allocation.
+//
+// To support interprocedural analysis, we also record data-flow from
+// each function's parameters to the heap and to its result
+// parameters. This information is summarized as "parameter tags",
+// which are used at static call sites to improve escape analysis of
+// function arguments.
+
+// Constructing the location graph.
+//
+// Every allocating statement (e.g., variable declaration) or
+// expression (e.g., "new" or "make") is first mapped to a unique
+// "location."
+//
+// We also model every Go assignment as a directed edges between
+// locations. The number of dereference operations minus the number of
+// addressing operations is recorded as the edge's weight (termed
+// "derefs"). For example:
+//
+// p = &q // -1
+// p = q // 0
+// p = *q // 1
+// p = **q // 2
+//
+// p = **&**&q // 2
+//
+// Note that the & operator can only be applied to addressable
+// expressions, and the expression &x itself is not addressable, so
+// derefs cannot go below -1.
+//
+// Every Go language construct is lowered into this representation,
+// generally without sensitivity to flow, path, or context; and
+// without distinguishing elements within a compound variable. For
+// example:
+//
+// var x struct { f, g *int }
+// var u []*int
+//
+// x.f = u[0]
+//
+// is modeled simply as
+//
+// x = *u
+//
+// That is, we don't distinguish x.f from x.g, or u[0] from u[1],
+// u[2], etc. However, we do record the implicit dereference involved
+// in indexing a slice.
+
+// A batch holds escape analysis state that's shared across an entire
+// batch of functions being analyzed at once.
+type batch struct {
+ allLocs []*location
+ closures []closure
+
+ heapLoc location
+ blankLoc location
+}
+
+// A closure holds a closure expression and its spill hole (i.e.,
+// where the hole representing storing into its closure record).
+type closure struct {
+ k hole
+ clo *ir.ClosureExpr
+}
+
+// An escape holds state specific to a single function being analyzed
+// within a batch.
+type escape struct {
+ *batch
+
+ curfn *ir.Func // function being analyzed
+
+ labels map[*types.Sym]labelState // known labels
+
+ // loopDepth counts the current loop nesting depth within
+ // curfn. It increments within each "for" loop and at each
+ // label with a corresponding backwards "goto" (i.e.,
+ // unstructured loop).
+ loopDepth int
+}
+
+// An location represents an abstract location that stores a Go
+// variable.
+type location struct {
+ n ir.Node // represented variable or expression, if any
+ curfn *ir.Func // enclosing function
+ 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
+ walkgen uint32
+
+ // dst and dstEdgeindex track the next immediate assignment
+ // destination location during walkone, along with the index
+ // of the edge pointing back to this location.
+ dst *location
+ dstEdgeIdx int
+
+ // queued is used by walkAll to track whether this location is
+ // in the walk queue.
+ queued bool
+
+ // escapes reports whether the represented variable's address
+ // escapes; that is, whether the variable must be heap
+ // allocated.
+ escapes bool
+
+ // transient reports whether the represented expression's
+ // address does not outlive the statement; that is, whether
+ // its storage can be immediately reused.
+ transient bool
+
+ // paramEsc records the represented parameter's leak set.
+ paramEsc leaks
+
+ captured bool // has a closure captured this variable?
+ reassigned bool // has this variable been reassigned?
+ addrtaken bool // has this variable's address been taken?
+}
+
+// An edge represents an assignment edge between two Go variables.
+type edge struct {
+ src *location
+ derefs int // >= -1
+ notes *note
+}
+
+// Fmt is called from node printing to print information about escape analysis results.
+func Fmt(n ir.Node) string {
+ text := ""
+ switch n.Esc() {
+ case ir.EscUnknown:
+ break
+
+ case ir.EscHeap:
+ text = "esc(h)"
+
+ case ir.EscNone:
+ text = "esc(no)"
+
+ case ir.EscNever:
+ text = "esc(N)"
+
+ default:
+ text = fmt.Sprintf("esc(%d)", n.Esc())
+ }
+
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
+ if text != "" {
+ text += " "
+ }
+ text += fmt.Sprintf("ld(%d)", loc.loopDepth)
+ }
+ }
+
+ return text
+}
+
+// Batch performs escape analysis on a minimal batch of
+// functions.
+func Batch(fns []*ir.Func, recursive bool) {
+ for _, fn := range fns {
+ if fn.Op() != ir.ODCLFUNC {
+ base.Fatalf("unexpected node: %v", fn)
+ }
+ }
+
+ var b batch
+ b.heapLoc.escapes = true
+
+ // Construct data-flow graph from syntax trees.
+ for _, fn := range fns {
+ if base.Flag.W > 1 {
+ s := fmt.Sprintf("\nbefore escape %v", fn)
+ ir.Dump(s, fn)
+ }
+ b.initFunc(fn)
+ }
+ for _, fn := range fns {
+ if !fn.IsHiddenClosure() {
+ b.walkFunc(fn)
+ }
+ }
+
+ // We've walked the function bodies, so we've seen everywhere a
+ // variable might be reassigned or have it's address taken. Now we
+ // can decide whether closures should capture their free variables
+ // by value or reference.
+ for _, closure := range b.closures {
+ b.flowClosure(closure.k, closure.clo)
+ }
+ b.closures = nil
+
+ for _, loc := range b.allLocs {
+ if why := HeapAllocReason(loc.n); why != "" {
+ b.flow(b.heapHole().addr(loc.n, why), loc)
+ }
+ }
+
+ b.walkAll()
+ b.finish(fns)
+}
+
+func (b *batch) with(fn *ir.Func) *escape {
+ return &escape{
+ batch: b,
+ curfn: fn,
+ loopDepth: 1,
+ }
+}
+
+func (b *batch) initFunc(fn *ir.Func) {
+ e := b.with(fn)
+ if fn.Esc() != escFuncUnknown {
+ base.Fatalf("unexpected node: %v", fn)
+ }
+ fn.SetEsc(escFuncPlanned)
+ if base.Flag.LowerM > 3 {
+ ir.Dump("escAnalyze", fn)
+ }
+
+ // Allocate locations for local variables.
+ 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) {
+ e := b.with(fn)
+ fn.SetEsc(escFuncStarted)
+
+ // Identify labels that mark the head of an unstructured loop.
+ ir.Visit(fn, func(n ir.Node) {
+ switch n.Op() {
+ case ir.OLABEL:
+ n := n.(*ir.LabelStmt)
+ if e.labels == nil {
+ e.labels = make(map[*types.Sym]labelState)
+ }
+ e.labels[n.Label] = nonlooping
+
+ case ir.OGOTO:
+ // If we visited the label before the goto,
+ // then this is a looping label.
+ n := n.(*ir.BranchStmt)
+ if e.labels[n.Label] == nonlooping {
+ e.labels[n.Label] = looping
+ }
+ }
+ })
+
+ e.block(fn.Body)
+
+ if len(e.labels) != 0 {
+ base.FatalfAt(fn.Pos(), "leftover labels after walkFunc")
+ }
+}
+
+func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) {
+ for _, cv := range clo.Func.ClosureVars {
+ n := cv.Canonical()
+ loc := b.oldLoc(cv)
+ if !loc.captured {
+ base.FatalfAt(cv.Pos(), "closure variable never captured: %v", cv)
+ }
+
+ // Capture by value for variables <= 128 bytes that are never reassigned.
+ n.SetByval(!loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128)
+ if !n.Byval() {
+ n.SetAddrtaken(true)
+ }
+
+ if base.Flag.LowerM > 1 {
+ how := "ref"
+ if n.Byval() {
+ how = "value"
+ }
+ base.WarnfAt(n.Pos(), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", n.Curfn, how, n, loc.addrtaken, loc.reassigned, n.Type().Size())
+ }
+
+ // Flow captured variables to closure.
+ k := k
+ if !cv.Byval() {
+ k = k.addr(cv, "reference")
+ }
+ b.flow(k.note(cv, "captured by a closure"), loc)
+ }
+}
+
+// Below we implement the methods for walking the AST and recording
+// data flow edges. Note that because a sub-expression might have
+// side-effects, it's important to always visit the entire AST.
+//
+// For example, write either:
+//
+// if x {
+// e.discard(n.Left)
+// } else {
+// e.value(k, n.Left)
+// }
+//
+// or
+//
+// if x {
+// k = e.discardHole()
+// }
+// e.value(k, n.Left)
+//
+// Do NOT write:
+//
+// // BAD: possibly loses side-effects within n.Left
+// if !x {
+// e.value(k, n.Left)
+// }
+
+// stmt evaluates a single Go statement.
+func (e *escape) stmt(n ir.Node) {
+ if n == nil {
+ return
+ }
+
+ lno := ir.SetPos(n)
+ defer func() {
+ base.Pos = lno
+ }()
+
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n)
+ }
+
+ e.stmts(n.Init())
+
+ switch n.Op() {
+ default:
+ base.Fatalf("unexpected stmt: %v", n)
+
+ case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK:
+ // nop
+
+ case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
+ // TODO(mdempsky): Handle dead code?
+
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
+ e.stmts(n.List)
+
+ case ir.ODCL:
+ // Record loop depth at declaration.
+ n := n.(*ir.Decl)
+ if !ir.IsBlank(n.X) {
+ e.dcl(n.X)
+ }
+
+ case ir.OLABEL:
+ n := n.(*ir.LabelStmt)
+ switch e.labels[n.Label] {
+ case nonlooping:
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n)
+ }
+ case looping:
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n)
+ }
+ e.loopDepth++
+ default:
+ base.Fatalf("label missing tag")
+ }
+ delete(e.labels, n.Label)
+
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ e.discard(n.Cond)
+ e.block(n.Body)
+ e.block(n.Else)
+
+ case ir.OFOR, ir.OFORUNTIL:
+ n := n.(*ir.ForStmt)
+ e.loopDepth++
+ e.discard(n.Cond)
+ e.stmt(n.Post)
+ e.block(n.Body)
+ e.loopDepth--
+
+ case ir.ORANGE:
+ // for Key, Value = range X { Body }
+ n := n.(*ir.RangeStmt)
+
+ // X is evaluated outside the loop.
+ tmp := e.newLoc(nil, false)
+ e.expr(tmp.asHole(), n.X)
+
+ e.loopDepth++
+ ks := e.addrs([]ir.Node{n.Key, n.Value})
+ if n.X.Type().IsArray() {
+ e.flow(ks[1].note(n, "range"), tmp)
+ } else {
+ e.flow(ks[1].deref(n, "range-deref"), tmp)
+ }
+ e.reassigned(ks, n)
+
+ e.block(n.Body)
+ e.loopDepth--
+
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+
+ if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
+ var ks []hole
+ if guard.Tag != nil {
+ for _, cas := range n.Cases {
+ cv := cas.Var
+ k := e.dcl(cv) // type switch variables have no ODCL.
+ if cv.Type().HasPointers() {
+ ks = append(ks, k.dotType(cv.Type(), cas, "switch case"))
+ }
+ }
+ }
+ e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X)
+ } else {
+ e.discard(n.Tag)
+ }
+
+ for _, cas := range n.Cases {
+ e.discards(cas.List)
+ e.block(cas.Body)
+ }
+
+ case ir.OSELECT:
+ n := n.(*ir.SelectStmt)
+ for _, cas := range n.Cases {
+ e.stmt(cas.Comm)
+ e.block(cas.Body)
+ }
+ case ir.ORECV:
+ // TODO(mdempsky): Consider e.discard(n.Left).
+ n := n.(*ir.UnaryExpr)
+ e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ e.discard(n.Chan)
+ e.assignHeap(n.Value, "send", n)
+
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ // TODO(mdempsky): Worry about OLSH/ORSH?
+ e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
+ case ir.OAS2:
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair", n)
+
+ case ir.OAS2DOTTYPE: // v, ok = x.(type)
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n)
+ case ir.OAS2MAPR: // v, ok = m[k]
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n)
+ case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n)
+
+ case ir.OAS2FUNC:
+ n := n.(*ir.AssignListStmt)
+ e.stmts(n.Rhs[0].Init())
+ ks := e.addrs(n.Lhs)
+ e.call(ks, n.Rhs[0], nil)
+ e.reassigned(ks, n)
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ results := e.curfn.Type().Results().FieldSlice()
+ dsts := make([]ir.Node, len(results))
+ for i, res := range results {
+ dsts[i] = res.Nname.(*ir.Name)
+ }
+ e.assignList(dsts, n.Results, "return", n)
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ e.call(nil, n, nil)
+ case ir.OGO, ir.ODEFER:
+ n := n.(*ir.GoDeferStmt)
+ e.stmts(n.Call.Init())
+ e.call(nil, n.Call, n)
+
+ case ir.OTAILCALL:
+ // TODO(mdempsky): Treat like a normal call? esc.go used to just ignore it.
+ }
+}
+
+func (e *escape) stmts(l ir.Nodes) {
+ for _, n := range l {
+ e.stmt(n)
+ }
+}
+
+// block is like stmts, but preserves loopDepth.
+func (e *escape) block(l ir.Nodes) {
+ old := e.loopDepth
+ e.stmts(l)
+ e.loopDepth = old
+}
+
+// expr models evaluating an expression n and flowing the result into
+// hole k.
+func (e *escape) expr(k hole, n ir.Node) {
+ if n == nil {
+ return
+ }
+ e.stmts(n.Init())
+ e.exprSkipInit(k, n)
+}
+
+func (e *escape) exprSkipInit(k hole, n ir.Node) {
+ if n == nil {
+ return
+ }
+
+ lno := ir.SetPos(n)
+ defer func() {
+ base.Pos = lno
+ }()
+
+ uintptrEscapesHack := k.uintptrEscapesHack
+ k.uintptrEscapesHack = false
+
+ if uintptrEscapesHack && n.Op() == ir.OCONVNOP && n.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
+ // nop
+ } else if k.derefs >= 0 && !n.Type().HasPointers() {
+ k.dst = &e.blankLoc
+ }
+
+ switch n.Op() {
+ default:
+ base.Fatalf("unexpected expr: %v", n)
+
+ case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OTYPE, ir.OMETHEXPR, ir.OLINKSYMOFFSET:
+ // nop
+
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ 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.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT:
+ n := n.(*ir.UnaryExpr)
+ e.discard(n.X)
+ case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ n := n.(*ir.BinaryExpr)
+ e.discard(n.X)
+ e.discard(n.Y)
+ case ir.OANDAND, ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ e.discard(n.X)
+ e.discard(n.Y)
+ case ir.OADDR:
+ n := n.(*ir.AddrExpr)
+ e.expr(k.addr(n, "address-of"), n.X) // "address-of"
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ e.expr(k.deref(n, "indirection"), n.X) // "indirection"
+ case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER:
+ n := n.(*ir.SelectorExpr)
+ e.expr(k.note(n, "dot"), n.X)
+ case ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ e.expr(k.deref(n, "dot of pointer"), n.X) // "dot of pointer"
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ n := n.(*ir.TypeAssertExpr)
+ e.expr(k.dotType(n.Type(), n, "dot"), n.X)
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ if n.X.Type().IsArray() {
+ e.expr(k.note(n, "fixed-array-index-of"), n.X)
+ } else {
+ // TODO(mdempsky): Fix why reason text.
+ e.expr(k.deref(n, "dot of pointer"), n.X)
+ }
+ e.discard(n.Index)
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ e.discard(n.X)
+ e.discard(n.Index)
+ case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR:
+ n := n.(*ir.SliceExpr)
+ e.expr(k.note(n, "slice"), n.X)
+ e.discard(n.Low)
+ e.discard(n.High)
+ e.discard(n.Max)
+
+ case ir.OCONV, ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ if ir.ShouldCheckPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
+ // When -d=checkptr=2 is enabled, treat
+ // conversions to unsafe.Pointer as an
+ // escaping operation. This allows better
+ // runtime instrumentation, since we can more
+ // easily detect object boundaries on the heap
+ // than the stack.
+ e.assignHeap(n.X, "conversion to unsafe.Pointer", n)
+ } else if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() {
+ e.unsafeValue(k, n.X)
+ } else {
+ e.expr(k, n.X)
+ }
+ case ir.OCONVIFACE:
+ n := n.(*ir.ConvExpr)
+ if !n.X.Type().IsInterface() && !types.IsDirectIface(n.X.Type()) {
+ k = e.spill(k, n)
+ }
+ e.expr(k.note(n, "interface-converted"), n.X)
+
+ case ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ e.discard(n.X)
+
+ case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY:
+ e.call([]hole{k}, n, nil)
+
+ case ir.ONEW:
+ n := n.(*ir.UnaryExpr)
+ e.spill(k, n)
+
+ case ir.OMAKESLICE:
+ n := n.(*ir.MakeExpr)
+ e.spill(k, n)
+ e.discard(n.Len)
+ e.discard(n.Cap)
+ case ir.OMAKECHAN:
+ n := n.(*ir.MakeExpr)
+ e.discard(n.Len)
+ case ir.OMAKEMAP:
+ n := n.(*ir.MakeExpr)
+ e.spill(k, n)
+ e.discard(n.Len)
+
+ case ir.ORECOVER:
+ // nop
+
+ case ir.OCALLPART:
+ // Flow the receiver argument to both the closure and
+ // to the receiver parameter.
+
+ n := n.(*ir.SelectorExpr)
+ closureK := e.spill(k, n)
+
+ m := n.Selection
+
+ // We don't know how the method value will be called
+ // later, so conservatively assume the result
+ // parameters all flow to the heap.
+ //
+ // TODO(mdempsky): Change ks into a callback, so that
+ // we don't have to create this slice?
+ var ks []hole
+ for i := m.Type.NumResults(); i > 0; i-- {
+ ks = append(ks, e.heapHole())
+ }
+ name, _ := m.Nname.(*ir.Name)
+ paramK := e.tagHole(ks, name, m.Type.Recv())
+
+ e.expr(e.teeHole(paramK, closureK), n.X)
+
+ case ir.OPTRLIT:
+ n := n.(*ir.AddrExpr)
+ e.expr(e.spill(k, n), n.X)
+
+ case ir.OARRAYLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elt := range n.List {
+ if elt.Op() == ir.OKEY {
+ elt = elt.(*ir.KeyExpr).Value
+ }
+ e.expr(k.note(n, "array literal element"), elt)
+ }
+
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ k = e.spill(k, n)
+ k.uintptrEscapesHack = uintptrEscapesHack // for ...uintptr parameters
+
+ for _, elt := range n.List {
+ if elt.Op() == ir.OKEY {
+ elt = elt.(*ir.KeyExpr).Value
+ }
+ e.expr(k.note(n, "slice-literal-element"), elt)
+ }
+
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elt := range n.List {
+ e.expr(k.note(n, "struct literal element"), elt.(*ir.StructKeyExpr).Value)
+ }
+
+ case ir.OMAPLIT:
+ n := n.(*ir.CompLitExpr)
+ e.spill(k, n)
+
+ // Map keys and values are always stored in the heap.
+ for _, elt := range n.List {
+ elt := elt.(*ir.KeyExpr)
+ e.assignHeap(elt.Key, "map literal key", n)
+ e.assignHeap(elt.Value, "map literal value", n)
+ }
+
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ k = e.spill(k, n)
+ e.closures = append(e.closures, closure{k, n})
+
+ if fn := n.Func; fn.IsHiddenClosure() {
+ for _, cv := range fn.ClosureVars {
+ if loc := e.oldLoc(cv); !loc.captured {
+ loc.captured = true
+
+ // Ignore reassignments to the variable in straightline code
+ // preceding the first capture by a closure.
+ if loc.loopDepth == e.loopDepth {
+ loc.reassigned = false
+ }
+ }
+ }
+
+ for _, n := range fn.Dcl {
+ // Add locations for local variables of the
+ // closure, if needed, in case we're not including
+ // the closure func in the batch for escape
+ // analysis (happens for escape analysis called
+ // from reflectdata.methodWrapper)
+ if n.Op() == ir.ONAME && n.Opt == nil {
+ e.with(fn).newLoc(n, false)
+ }
+ }
+ e.walkFunc(fn)
+ }
+
+ case ir.ORUNES2STR, ir.OBYTES2STR, ir.OSTR2RUNES, ir.OSTR2BYTES, ir.ORUNESTR:
+ n := n.(*ir.ConvExpr)
+ e.spill(k, n)
+ e.discard(n.X)
+
+ case ir.OADDSTR:
+ n := n.(*ir.AddStringExpr)
+ e.spill(k, n)
+
+ // Arguments of OADDSTR never escape;
+ // runtime.concatstrings makes sure of that.
+ e.discards(n.List)
+ }
+}
+
+// unsafeValue evaluates a uintptr-typed arithmetic expression looking
+// for conversions from an unsafe.Pointer.
+func (e *escape) unsafeValue(k hole, n ir.Node) {
+ if n.Type().Kind() != types.TUINTPTR {
+ base.Fatalf("unexpected type %v for %v", n.Type(), n)
+ }
+ if k.addrtaken {
+ base.Fatalf("unexpected addrtaken")
+ }
+
+ e.stmts(n.Init())
+
+ switch n.Op() {
+ case ir.OCONV, ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ if n.X.Type().IsUnsafePtr() {
+ e.expr(k, n.X)
+ } else {
+ e.discard(n.X)
+ }
+ case ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ if ir.IsReflectHeaderDataField(n) {
+ e.expr(k.deref(n, "reflect.Header.Data"), n.X)
+ } else {
+ e.discard(n.X)
+ }
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT:
+ n := n.(*ir.UnaryExpr)
+ e.unsafeValue(k, n.X)
+ case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OAND, ir.OANDNOT:
+ n := n.(*ir.BinaryExpr)
+ e.unsafeValue(k, n.X)
+ e.unsafeValue(k, n.Y)
+ case ir.OLSH, ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ e.unsafeValue(k, n.X)
+ // RHS need not be uintptr-typed (#32959) and can't meaningfully
+ // flow pointers anyway.
+ e.discard(n.Y)
+ default:
+ e.exprSkipInit(e.discardHole(), n)
+ }
+}
+
+// discard evaluates an expression n for side-effects, but discards
+// its value.
+func (e *escape) discard(n ir.Node) {
+ e.expr(e.discardHole(), n)
+}
+
+func (e *escape) discards(l ir.Nodes) {
+ for _, n := range l {
+ e.discard(n)
+ }
+}
+
+// addr evaluates an addressable expression n and returns a hole
+// that represents storing into the represented location.
+func (e *escape) addr(n ir.Node) hole {
+ if n == nil || ir.IsBlank(n) {
+ // Can happen in select case, range, maybe others.
+ return e.discardHole()
+ }
+
+ k := e.heapHole()
+
+ switch n.Op() {
+ default:
+ base.Fatalf("unexpected addr: %v", n)
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ if n.Class == ir.PEXTERN {
+ break
+ }
+ k = e.oldLoc(n).asHole()
+ case ir.OLINKSYMOFFSET:
+ break
+ case ir.ODOT:
+ n := n.(*ir.SelectorExpr)
+ k = e.addr(n.X)
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ e.discard(n.Index)
+ if n.X.Type().IsArray() {
+ k = e.addr(n.X)
+ } else {
+ e.discard(n.X)
+ }
+ case ir.ODEREF, ir.ODOTPTR:
+ e.discard(n)
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ e.discard(n.X)
+ e.assignHeap(n.Index, "key of map put", n)
+ }
+
+ return k
+}
+
+func (e *escape) addrs(l ir.Nodes) []hole {
+ var ks []hole
+ for _, n := range l {
+ ks = append(ks, e.addr(n))
+ }
+ return ks
+}
+
+// reassigned marks the locations associated with the given holes as
+// reassigned, unless the location represents a variable declared and
+// assigned exactly once by where.
+func (e *escape) reassigned(ks []hole, where ir.Node) {
+ if as, ok := where.(*ir.AssignStmt); ok && as.Op() == ir.OAS && as.Y == nil {
+ if dst, ok := as.X.(*ir.Name); ok && dst.Op() == ir.ONAME && dst.Defn == nil {
+ // Zero-value assignment for variable declared without an
+ // explicit initial value. Assume this is its initialization
+ // statement.
+ return
+ }
+ }
+
+ for _, k := range ks {
+ loc := k.dst
+ // Variables declared by range statements are assigned on every iteration.
+ if n, ok := loc.n.(*ir.Name); ok && n.Defn == where && where.Op() != ir.ORANGE {
+ continue
+ }
+ loc.reassigned = true
+ }
+}
+
+// assignList evaluates the assignment dsts... = srcs....
+func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) {
+ ks := e.addrs(dsts)
+ for i, k := range ks {
+ var src ir.Node
+ if i < len(srcs) {
+ src = srcs[i]
+ }
+
+ if dst := dsts[i]; dst != nil {
+ // Detect implicit conversion of uintptr to unsafe.Pointer when
+ // storing into reflect.{Slice,String}Header.
+ if dst.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(dst) {
+ e.unsafeValue(e.heapHole().note(where, why), src)
+ continue
+ }
+
+ // Filter out some no-op assignments for escape analysis.
+ if src != nil && isSelfAssign(dst, src) {
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(where.Pos(), "%v ignoring self-assignment in %v", e.curfn, where)
+ }
+ k = e.discardHole()
+ }
+ }
+
+ e.expr(k.note(where, why), src)
+ }
+
+ e.reassigned(ks, where)
+}
+
+func (e *escape) assignHeap(src ir.Node, why string, where ir.Node) {
+ e.expr(e.heapHole().note(where, why), src)
+}
+
+// call evaluates a call expressions, including builtin calls. ks
+// should contain the holes representing where the function callee's
+// results flows; where is the OGO/ODEFER context of the call, if any.
+func (e *escape) call(ks []hole, call, where ir.Node) {
+ topLevelDefer := where != nil && where.Op() == ir.ODEFER && e.loopDepth == 1
+ if topLevelDefer {
+ // force stack allocation of defer record, unless
+ // open-coded defers are used (see ssa.go)
+ where.SetEsc(ir.EscNever)
+ }
+
+ argument := func(k hole, arg ir.Node) {
+ if topLevelDefer {
+ // Top level defers arguments don't escape to
+ // heap, but they do need to last until end of
+ // function.
+ k = e.later(k)
+ } else if where != nil {
+ k = e.heapHole()
+ }
+
+ e.expr(k.note(call, "call parameter"), arg)
+ }
+
+ switch call.Op() {
+ default:
+ ir.Dump("esc", call)
+ base.Fatalf("unexpected call op: %v", call.Op())
+
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
+ call := call.(*ir.CallExpr)
+ typecheck.FixVariadicCall(call)
+
+ // Pick out the function callee, if statically known.
+ var fn *ir.Name
+ switch call.Op() {
+ case ir.OCALLFUNC:
+ switch v := ir.StaticValue(call.X); {
+ case v.Op() == ir.ONAME && v.(*ir.Name).Class == ir.PFUNC:
+ fn = v.(*ir.Name)
+ case v.Op() == ir.OCLOSURE:
+ fn = v.(*ir.ClosureExpr).Func.Nname
+ }
+ case ir.OCALLMETH:
+ fn = ir.MethodExprName(call.X)
+ }
+
+ fntype := call.X.Type()
+ if fn != nil {
+ fntype = fn.Type()
+ }
+
+ if ks != nil && fn != nil && e.inMutualBatch(fn) {
+ for i, result := range fn.Type().Results().FieldSlice() {
+ e.expr(ks[i], ir.AsNode(result.Nname))
+ }
+ }
+
+ if r := fntype.Recv(); r != nil {
+ argument(e.tagHole(ks, fn, r), call.X.(*ir.SelectorExpr).X)
+ } else {
+ // Evaluate callee function expression.
+ argument(e.discardHole(), call.X)
+ }
+
+ args := call.Args
+ for i, param := range fntype.Params().FieldSlice() {
+ argument(e.tagHole(ks, fn, param), args[i])
+ }
+
+ case ir.OAPPEND:
+ call := call.(*ir.CallExpr)
+ args := call.Args
+
+ // Appendee slice may flow directly to the result, if
+ // it has enough capacity. Alternatively, a new heap
+ // slice might be allocated, and all slice elements
+ // might flow to heap.
+ appendeeK := ks[0]
+ if args[0].Type().Elem().HasPointers() {
+ appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
+ }
+ argument(appendeeK, args[0])
+
+ if call.IsDDD {
+ appendedK := e.discardHole()
+ if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() {
+ appendedK = e.heapHole().deref(call, "appended slice...")
+ }
+ argument(appendedK, args[1])
+ } else {
+ for _, arg := range args[1:] {
+ argument(e.heapHole(), arg)
+ }
+ }
+
+ case ir.OCOPY:
+ call := call.(*ir.BinaryExpr)
+ argument(e.discardHole(), call.X)
+
+ copiedK := e.discardHole()
+ if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() {
+ copiedK = e.heapHole().deref(call, "copied slice")
+ }
+ argument(copiedK, call.Y)
+
+ case ir.OPANIC:
+ call := call.(*ir.UnaryExpr)
+ argument(e.heapHole(), call.X)
+
+ case ir.OCOMPLEX:
+ call := call.(*ir.BinaryExpr)
+ argument(e.discardHole(), call.X)
+ argument(e.discardHole(), call.Y)
+ case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ call := call.(*ir.CallExpr)
+ for _, arg := range call.Args {
+ argument(e.discardHole(), arg)
+ }
+ case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
+ call := call.(*ir.UnaryExpr)
+ argument(e.discardHole(), call.X)
+ }
+}
+
+// tagHole returns a hole for evaluating an argument passed to param.
+// ks should contain the holes representing where the function
+// callee's results flows. fn is the statically-known callee function,
+// if any.
+func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole {
+ // If this is a dynamic call, we can't rely on param.Note.
+ if fn == nil {
+ return e.heapHole()
+ }
+
+ if e.inMutualBatch(fn) {
+ return e.addr(ir.AsNode(param.Nname))
+ }
+
+ // Call to previously tagged function.
+
+ if param.Note == UintptrEscapesNote {
+ k := e.heapHole()
+ k.uintptrEscapesHack = true
+ return k
+ }
+
+ var tagKs []hole
+
+ esc := parseLeaks(param.Note)
+ if x := esc.Heap(); x >= 0 {
+ tagKs = append(tagKs, e.heapHole().shift(x))
+ }
+
+ if ks != nil {
+ for i := 0; i < numEscResults; i++ {
+ if x := esc.Result(i); x >= 0 {
+ tagKs = append(tagKs, ks[i].shift(x))
+ }
+ }
+ }
+
+ return e.teeHole(tagKs...)
+}
+
+// inMutualBatch reports whether function fn is in the batch of
+// mutually recursive functions being analyzed. When this is true,
+// fn has not yet been analyzed, so its parameters and results
+// should be incorporated directly into the flow graph instead of
+// relying on its escape analysis tagging.
+func (e *escape) inMutualBatch(fn *ir.Name) bool {
+ if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged {
+ if fn.Defn.Esc() == escFuncUnknown {
+ base.Fatalf("graph inconsistency: %v", fn)
+ }
+ return true
+ }
+ return false
+}
+
+// An hole represents a context for evaluation a Go
+// expression. E.g., when evaluating p in "x = **p", we'd have a hole
+// with dst==x and derefs==2.
+type hole struct {
+ dst *location
+ derefs int // >= -1
+ notes *note
+
+ // addrtaken indicates whether this context is taking the address of
+ // the expression, independent of whether the address will actually
+ // be stored into a variable.
+ addrtaken bool
+
+ // uintptrEscapesHack indicates this context is evaluating an
+ // argument for a //go:uintptrescapes function.
+ uintptrEscapesHack bool
+}
+
+type note struct {
+ next *note
+ where ir.Node
+ why string
+}
+
+func (k hole) note(where ir.Node, why string) hole {
+ if where == nil || why == "" {
+ base.Fatalf("note: missing where/why")
+ }
+ if base.Flag.LowerM >= 2 || logopt.Enabled() {
+ k.notes = ¬e{
+ next: k.notes,
+ where: where,
+ why: why,
+ }
+ }
+ return k
+}
+
+func (k hole) shift(delta int) hole {
+ k.derefs += delta
+ if k.derefs < -1 {
+ base.Fatalf("derefs underflow: %v", k.derefs)
+ }
+ k.addrtaken = delta < 0
+ return k
+}
+
+func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) }
+func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) }
+
+func (k hole) dotType(t *types.Type, where ir.Node, why string) hole {
+ if !t.IsInterface() && !types.IsDirectIface(t) {
+ k = k.shift(1)
+ }
+ return k.note(where, why)
+}
+
+// teeHole returns a new hole that flows into each hole of ks,
+// similar to the Unix tee(1) command.
+func (e *escape) teeHole(ks ...hole) hole {
+ if len(ks) == 0 {
+ return e.discardHole()
+ }
+ if len(ks) == 1 {
+ return ks[0]
+ }
+ // TODO(mdempsky): Optimize if there's only one non-discard hole?
+
+ // Given holes "l1 = _", "l2 = **_", "l3 = *_", ..., create a
+ // new temporary location ltmp, wire it into place, and return
+ // a hole for "ltmp = _".
+ loc := e.newLoc(nil, true)
+ for _, k := range ks {
+ // N.B., "p = &q" and "p = &tmp; tmp = q" are not
+ // semantically equivalent. To combine holes like "l1
+ // = _" and "l2 = &_", we'd need to wire them as "l1 =
+ // *ltmp" and "l2 = ltmp" and return "ltmp = &_"
+ // instead.
+ if k.derefs < 0 {
+ base.Fatalf("teeHole: negative derefs")
+ }
+
+ e.flow(k, loc)
+ }
+ return loc.asHole()
+}
+
+func (e *escape) dcl(n *ir.Name) hole {
+ if n.Curfn != e.curfn || n.IsClosureVar() {
+ base.Fatalf("bad declaration of %v", n)
+ }
+ loc := e.oldLoc(n)
+ loc.loopDepth = e.loopDepth
+ return loc.asHole()
+}
+
+// spill allocates a new location associated with expression n, flows
+// its address to k, and returns a hole that flows values to it. It's
+// intended for use with most expressions that allocate storage.
+func (e *escape) spill(k hole, n ir.Node) hole {
+ loc := e.newLoc(n, true)
+ e.flow(k.addr(n, "spill"), loc)
+ return loc.asHole()
+}
+
+// later returns a new hole that flows into k, but some time later.
+// Its main effect is to prevent immediate reuse of temporary
+// variables introduced during Order.
+func (e *escape) later(k hole) hole {
+ loc := e.newLoc(nil, false)
+ e.flow(k, loc)
+ return loc.asHole()
+}
+
+func (e *escape) newLoc(n ir.Node, transient bool) *location {
+ if e.curfn == nil {
+ base.Fatalf("e.curfn isn't set")
+ }
+ if n != nil && n.Type() != nil && n.Type().NotInHeap() {
+ base.ErrorfAt(n.Pos(), "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type())
+ }
+
+ if n != nil && n.Op() == ir.ONAME {
+ n = n.(*ir.Name).Canonical()
+ }
+ loc := &location{
+ n: n,
+ curfn: e.curfn,
+ loopDepth: e.loopDepth,
+ transient: transient,
+ }
+ e.allLocs = append(e.allLocs, loc)
+ if n != nil {
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ if n.Curfn != e.curfn {
+ base.Fatalf("curfn mismatch: %v != %v", n.Curfn, e.curfn)
+ }
+
+ if n.Opt != nil {
+ base.Fatalf("%v already has a location", n)
+ }
+ n.Opt = loc
+ }
+ }
+ return loc
+}
+
+func (b *batch) oldLoc(n *ir.Name) *location {
+ return n.Canonical().Opt.(*location)
+}
+
+func (l *location) asHole() hole {
+ return hole{dst: l}
+}
+
+func (b *batch) flow(k hole, src *location) {
+ if k.addrtaken {
+ src.addrtaken = true
+ }
+
+ dst := k.dst
+ if dst == &b.blankLoc {
+ return
+ }
+ if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ...
+ return
+ }
+ if dst.escapes && k.derefs < 0 { // dst = &src
+ if base.Flag.LowerM >= 2 || logopt.Enabled() {
+ pos := base.FmtPos(src.n.Pos())
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
+ }
+ explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation)
+ }
+
+ }
+ src.escapes = true
+ return
+ }
+
+ // TODO(mdempsky): Deduplicate edges?
+ dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes})
+}
+
+func (b *batch) heapHole() hole { return b.heapLoc.asHole() }
+func (b *batch) discardHole() hole { return b.blankLoc.asHole() }
+
+// walkAll computes the minimal dereferences between all pairs of
+// locations.
+func (b *batch) walkAll() {
+ // We use a work queue to keep track of locations that we need
+ // to visit, and repeatedly walk until we reach a fixed point.
+ //
+ // We walk once from each location (including the heap), and
+ // then re-enqueue each location on its transition from
+ // transient->!transient and !escapes->escapes, which can each
+ // happen at most once. So we take Θ(len(e.allLocs)) walks.
+
+ // LIFO queue, has enough room for e.allLocs and e.heapLoc.
+ todo := make([]*location, 0, len(b.allLocs)+1)
+ enqueue := func(loc *location) {
+ if !loc.queued {
+ todo = append(todo, loc)
+ loc.queued = true
+ }
+ }
+
+ for _, loc := range b.allLocs {
+ enqueue(loc)
+ }
+ enqueue(&b.heapLoc)
+
+ var walkgen uint32
+ for len(todo) > 0 {
+ root := todo[len(todo)-1]
+ todo = todo[:len(todo)-1]
+ root.queued = false
+
+ walkgen++
+ b.walkOne(root, walkgen, enqueue)
+ }
+}
+
+// walkOne computes the minimal number of dereferences from root to
+// all other locations.
+func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
+ // The data flow graph has negative edges (from addressing
+ // operations), so we use the Bellman-Ford algorithm. However,
+ // we don't have to worry about infinite negative cycles since
+ // we bound intermediate dereference counts to 0.
+
+ root.walkgen = walkgen
+ root.derefs = 0
+ root.dst = nil
+
+ todo := []*location{root} // LIFO queue
+ for len(todo) > 0 {
+ l := todo[len(todo)-1]
+ todo = todo[:len(todo)-1]
+
+ derefs := l.derefs
+
+ // If l.derefs < 0, then l's address flows to root.
+ addressOf := derefs < 0
+ if addressOf {
+ // For a flow path like "root = &l; l = x",
+ // l's address flows to root, but x's does
+ // not. We recognize this by lower bounding
+ // derefs at 0.
+ derefs = 0
+
+ // If l's address flows to a non-transient
+ // location, then l can't be transiently
+ // allocated.
+ if !root.transient && l.transient {
+ l.transient = false
+ enqueue(l)
+ }
+ }
+
+ if b.outlives(root, l) {
+ // l's value flows to root. If l is a function
+ // parameter and root is the heap or a
+ // corresponding result parameter, then record
+ // that value flow for tagging the function
+ // later.
+ if l.isName(ir.PPARAM) {
+ if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes {
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
+ }
+ explanation := b.explainPath(root, l)
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
+ fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation)
+ }
+ }
+ l.leakTo(root, derefs)
+ }
+
+ // If l's address flows somewhere that
+ // outlives it, then l needs to be heap
+ // allocated.
+ if addressOf && !l.escapes {
+ if logopt.Enabled() || base.Flag.LowerM >= 2 {
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
+ }
+ explanation := b.explainPath(root, l)
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
+ }
+ }
+ l.escapes = true
+ enqueue(l)
+ continue
+ }
+ }
+
+ for i, edge := range l.edges {
+ if edge.src.escapes {
+ continue
+ }
+ d := derefs + edge.derefs
+ if edge.src.walkgen != walkgen || edge.src.derefs > d {
+ edge.src.walkgen = walkgen
+ edge.src.derefs = d
+ edge.src.dst = l
+ edge.src.dstEdgeIdx = i
+ todo = append(todo, edge.src)
+ }
+ }
+ }
+}
+
+// explainPath prints an explanation of how src flows to the walk root.
+func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt {
+ visited := make(map[*location]bool)
+ pos := base.FmtPos(src.n.Pos())
+ var explanation []*logopt.LoggedOpt
+ for {
+ // Prevent infinite loop.
+ if visited[src] {
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
+ }
+ break
+ }
+ visited[src] = true
+ dst := src.dst
+ edge := &dst.edges[src.dstEdgeIdx]
+ if edge.src != src {
+ base.Fatalf("path inconsistency: %v != %v", edge.src, src)
+ }
+
+ explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
+
+ if dst == root {
+ break
+ }
+ src = dst
+ }
+
+ return explanation
+}
+
+func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
+ ops := "&"
+ if derefs >= 0 {
+ ops = strings.Repeat("*", derefs)
+ }
+ print := base.Flag.LowerM >= 2
+
+ flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
+ if print {
+ fmt.Printf("%s:%s\n", pos, flow)
+ }
+ if logopt.Enabled() {
+ var epos src.XPos
+ if notes != nil {
+ epos = notes.where.Pos()
+ } else if srcloc != nil && srcloc.n != nil {
+ epos = srcloc.n.Pos()
+ }
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow))
+ }
+
+ for note := notes; note != nil; note = note.next {
+ if print {
+ fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos()))
+ }
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn),
+ fmt.Sprintf(" from %v (%v)", note.where, note.why)))
+ }
+ }
+ return explanation
+}
+
+func (b *batch) explainLoc(l *location) string {
+ if l == &b.heapLoc {
+ return "{heap}"
+ }
+ if l.n == nil {
+ // TODO(mdempsky): Omit entirely.
+ return "{temp}"
+ }
+ if l.n.Op() == ir.ONAME {
+ return fmt.Sprintf("%v", l.n)
+ }
+ return fmt.Sprintf("{storage for %v}", l.n)
+}
+
+// outlives reports whether values stored in l may survive beyond
+// other's lifetime if stack allocated.
+func (b *batch) outlives(l, other *location) bool {
+ // The heap outlives everything.
+ if l.escapes {
+ return true
+ }
+
+ // We don't know what callers do with returned values, so
+ // pessimistically we need to assume they flow to the heap and
+ // outlive everything too.
+ if l.isName(ir.PPARAMOUT) {
+ // Exception: Directly called closures can return
+ // locations allocated outside of them without forcing
+ // them to the heap. For example:
+ //
+ // var u int // okay to stack allocate
+ // *(func() *int { return &u }()) = 42
+ if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() {
+ return false
+ }
+
+ return true
+ }
+
+ // If l and other are within the same function, then l
+ // outlives other if it was declared outside other's loop
+ // scope. For example:
+ //
+ // var l *int
+ // for {
+ // l = new(int)
+ // }
+ if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
+ return true
+ }
+
+ // If other is declared within a child closure of where l is
+ // declared, then l outlives it. For example:
+ //
+ // var l *int
+ // func() {
+ // l = new(int)
+ // }
+ if containsClosure(l.curfn, other.curfn) {
+ return true
+ }
+
+ return false
+}
+
+// containsClosure reports whether c is a closure contained within f.
+func containsClosure(f, c *ir.Func) bool {
+ // Common case.
+ if f == c {
+ return false
+ }
+
+ // Closures within function Foo are named like "Foo.funcN..."
+ // TODO(mdempsky): Better way to recognize this.
+ fn := f.Sym().Name
+ cn := c.Sym().Name
+ return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
+}
+
+// leak records that parameter l leaks to sink.
+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 {
+ ri := sink.resultIndex - 1
+ if ri < numEscResults {
+ // Leak to result parameter.
+ l.paramEsc.AddResult(ri, derefs)
+ return
+ }
+ }
+
+ // Otherwise, record as heap leak.
+ l.paramEsc.AddHeap(derefs)
+}
+
+func (b *batch) finish(fns []*ir.Func) {
+ // Record parameter tags for package export data.
+ for _, fn := range fns {
+ fn.SetEsc(escFuncTagged)
+
+ narg := 0
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(fn.Type()).Fields().Slice() {
+ narg++
+ f.Note = b.paramTag(fn, narg, f)
+ }
+ }
+ }
+
+ for _, loc := range b.allLocs {
+ n := loc.n
+ if n == nil {
+ continue
+ }
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ n.Opt = nil
+ }
+
+ // Update n.Esc based on escape analysis results.
+
+ if loc.escapes {
+ if n.Op() == ir.ONAME {
+ if base.Flag.CompilingRuntime {
+ base.ErrorfAt(n.Pos(), "%v escapes to heap, not allowed in runtime", n)
+ }
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(n.Pos(), "moved to heap: %v", n)
+ }
+ } else {
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(n.Pos(), "%v escapes to heap", n)
+ }
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn))
+ }
+ }
+ n.SetEsc(ir.EscHeap)
+ } else {
+ if base.Flag.LowerM != 0 && n.Op() != ir.ONAME {
+ base.WarnfAt(n.Pos(), "%v does not escape", n)
+ }
+ n.SetEsc(ir.EscNone)
+ if loc.transient {
+ switch n.Op() {
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ n.SetTransient(true)
+ case ir.OCALLPART:
+ n := n.(*ir.SelectorExpr)
+ n.SetTransient(true)
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ n.SetTransient(true)
+ }
+ }
+ }
+ }
+}
+
+func (l *location) isName(c ir.Class) bool {
+ return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
+}
+
+const numEscResults = 7
+
+// An leaks represents a set of assignment flows from a parameter
+// to the heap or to any of its function's (first numEscResults)
+// result parameters.
+type leaks [1 + numEscResults]uint8
+
+// Empty reports whether l is an empty set (i.e., no assignment flows).
+func (l leaks) Empty() bool { return l == leaks{} }
+
+// Heap returns the minimum deref count of any assignment flow from l
+// to the heap. If no such flows exist, Heap returns -1.
+func (l leaks) Heap() int { return l.get(0) }
+
+// Result returns the minimum deref count of any assignment flow from
+// l to its function's i'th result parameter. If no such flows exist,
+// Result returns -1.
+func (l leaks) Result(i int) int { return l.get(1 + i) }
+
+// AddHeap adds an assignment flow from l to the heap.
+func (l *leaks) AddHeap(derefs int) { l.add(0, derefs) }
+
+// AddResult adds an assignment flow from l to its function's i'th
+// result parameter.
+func (l *leaks) AddResult(i, derefs int) { l.add(1+i, derefs) }
+
+func (l *leaks) setResult(i, derefs int) { l.set(1+i, derefs) }
+
+func (l leaks) get(i int) int { return int(l[i]) - 1 }
+
+func (l *leaks) add(i, derefs int) {
+ if old := l.get(i); old < 0 || derefs < old {
+ l.set(i, derefs)
+ }
+}
+
+func (l *leaks) set(i, derefs int) {
+ v := derefs + 1
+ if v < 0 {
+ base.Fatalf("invalid derefs count: %v", derefs)
+ }
+ if v > math.MaxUint8 {
+ v = math.MaxUint8
+ }
+
+ l[i] = uint8(v)
+}
+
+// Optimize removes result flow paths that are equal in length or
+// longer than the shortest heap flow path.
+func (l *leaks) Optimize() {
+ // If we have a path to the heap, then there's no use in
+ // keeping equal or longer paths elsewhere.
+ if x := l.Heap(); x >= 0 {
+ for i := 0; i < numEscResults; i++ {
+ if l.Result(i) >= x {
+ l.setResult(i, -1)
+ }
+ }
+ }
+}
+
+var leakTagCache = map[leaks]string{}
+
+// Encode converts l into a binary string for export data.
+func (l leaks) Encode() string {
+ if l.Heap() == 0 {
+ // Space optimization: empty string encodes more
+ // efficiently in export data.
+ return ""
+ }
+ if s, ok := leakTagCache[l]; ok {
+ return s
+ }
+
+ n := len(l)
+ for n > 0 && l[n-1] == 0 {
+ n--
+ }
+ s := "esc:" + string(l[:n])
+ leakTagCache[l] = s
+ return s
+}
+
+// parseLeaks parses a binary string representing a leaks
+func parseLeaks(s string) leaks {
+ var l leaks
+ if !strings.HasPrefix(s, "esc:") {
+ l.AddHeap(0)
+ return l
+ }
+ copy(l[:], s[4:])
+ return l
+}
+
+func Funcs(all []ir.Node) {
+ ir.VisitFuncsBottomUp(all, Batch)
+}
+
+const (
+ escFuncUnknown = 0 + iota
+ escFuncPlanned
+ escFuncStarted
+ escFuncTagged
+)
+
+// Mark labels that have no backjumps to them as not increasing e.loopdepth.
+type labelState int
+
+const (
+ looping labelState = 1 + iota
+ nonlooping
+)
+
+func isSliceSelfAssign(dst, src ir.Node) bool {
+ // Detect the following special case.
+ //
+ // func (b *Buffer) Foo() {
+ // n, m := ...
+ // b.buf = b.buf[n:m]
+ // }
+ //
+ // This assignment is a no-op for escape analysis,
+ // it does not store any new pointers into b that were not already there.
+ // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
+ // Here we assume that the statement will not contain calls,
+ // that is, that order will move any calls to init.
+ // Otherwise base ONAME value could change between the moments
+ // when we evaluate it for dst and for src.
+
+ // dst is ONAME dereference.
+ var dstX ir.Node
+ switch dst.Op() {
+ default:
+ return false
+ case ir.ODEREF:
+ dst := dst.(*ir.StarExpr)
+ dstX = dst.X
+ case ir.ODOTPTR:
+ dst := dst.(*ir.SelectorExpr)
+ dstX = dst.X
+ }
+ if dstX.Op() != ir.ONAME {
+ return false
+ }
+ // src is a slice operation.
+ switch src.Op() {
+ case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
+ // OK.
+ case ir.OSLICEARR, ir.OSLICE3ARR:
+ // Since arrays are embedded into containing object,
+ // slice of non-pointer array will introduce a new pointer into b that was not already there
+ // (pointer to b itself). After such assignment, if b contents escape,
+ // b escapes as well. If we ignore such OSLICEARR, we will conclude
+ // that b does not escape when b contents do.
+ //
+ // Pointer to an array is OK since it's not stored inside b directly.
+ // For slicing an array (not pointer to array), there is an implicit OADDR.
+ // We check that to determine non-pointer array slicing.
+ src := src.(*ir.SliceExpr)
+ if src.X.Op() == ir.OADDR {
+ return false
+ }
+ default:
+ return false
+ }
+ // slice is applied to ONAME dereference.
+ var baseX ir.Node
+ switch base := src.(*ir.SliceExpr).X; base.Op() {
+ default:
+ return false
+ case ir.ODEREF:
+ base := base.(*ir.StarExpr)
+ baseX = base.X
+ case ir.ODOTPTR:
+ base := base.(*ir.SelectorExpr)
+ baseX = base.X
+ }
+ if baseX.Op() != ir.ONAME {
+ return false
+ }
+ // dst and src reference the same base ONAME.
+ return dstX.(*ir.Name) == baseX.(*ir.Name)
+}
+
+// isSelfAssign reports whether assignment from src to dst can
+// be ignored by the escape analysis as it's effectively a self-assignment.
+func isSelfAssign(dst, src ir.Node) bool {
+ if isSliceSelfAssign(dst, src) {
+ return true
+ }
+
+ // Detect trivial assignments that assign back to the same object.
+ //
+ // It covers these cases:
+ // val.x = val.y
+ // val.x[i] = val.y[j]
+ // val.x1.x2 = val.x1.y2
+ // ... etc
+ //
+ // These assignments do not change assigned object lifetime.
+
+ if dst == nil || src == nil || dst.Op() != src.Op() {
+ return false
+ }
+
+ // The expression prefix must be both "safe" and identical.
+ switch dst.Op() {
+ case ir.ODOT, ir.ODOTPTR:
+ // Safe trailing accessors that are permitted to differ.
+ dst := dst.(*ir.SelectorExpr)
+ src := src.(*ir.SelectorExpr)
+ return ir.SameSafeExpr(dst.X, src.X)
+ case ir.OINDEX:
+ dst := dst.(*ir.IndexExpr)
+ src := src.(*ir.IndexExpr)
+ if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
+ return false
+ }
+ return ir.SameSafeExpr(dst.X, src.X)
+ default:
+ return false
+ }
+}
+
+// mayAffectMemory reports whether evaluation of n may affect the program's
+// memory state. If the expression can't affect memory state, then it can be
+// safely ignored by the escape analysis.
+func mayAffectMemory(n ir.Node) bool {
+ // We may want to use a list of "memory safe" ops instead of generally
+ // "side-effect free", which would include all calls and other ops that can
+ // allocate or change global state. For now, it's safer to start with the latter.
+ //
+ // We're ignoring things like division by zero, index out of range,
+ // and nil pointer dereference here.
+
+ // TODO(rsc): It seems like it should be possible to replace this with
+ // 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.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:
+ n := n.(*ir.BinaryExpr)
+ return mayAffectMemory(n.X) || mayAffectMemory(n.Y)
+
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ return mayAffectMemory(n.X) || mayAffectMemory(n.Index)
+
+ case ir.OCONVNOP, ir.OCONV:
+ n := n.(*ir.ConvExpr)
+ return mayAffectMemory(n.X)
+
+ case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+ n := n.(*ir.UnaryExpr)
+ return mayAffectMemory(n.X)
+
+ case ir.ODOT, ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ return mayAffectMemory(n.X)
+
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ return mayAffectMemory(n.X)
+
+ default:
+ return true
+ }
+}
+
+// HeapAllocReason returns the reason the given Node must be heap
+// allocated, or the empty string if it doesn't.
+func HeapAllocReason(n ir.Node) string {
+ if n == nil || n.Type() == nil {
+ return ""
+ }
+
+ // Parameters are always passed via the stack.
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+ return ""
+ }
+ }
+
+ if n.Type().Width > ir.MaxStackVarSize {
+ return "too large for stack"
+ }
+
+ if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width >= ir.MaxImplicitStackVarSize {
+ return "too large for stack"
+ }
+
+ if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() >= ir.MaxImplicitStackVarSize {
+ return "too large for stack"
+ }
+ if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() >= ir.MaxImplicitStackVarSize {
+ return "too large for stack"
+ }
+
+ if n.Op() == ir.OMAKESLICE {
+ n := n.(*ir.MakeExpr)
+ r := n.Cap
+ if r == nil {
+ r = n.Len
+ }
+ if !ir.IsSmallIntConst(r) {
+ return "non-constant size"
+ }
+ if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) >= ir.MaxImplicitStackVarSize/t.Elem().Width {
+ return "too large for stack"
+ }
+ }
+
+ return ""
+}
+
+// This special tag is applied to uintptr variables
+// that we believe may hold unsafe.Pointers for
+// calls into assembly functions.
+const UnsafeUintptrNote = "unsafe-uintptr"
+
+// This special tag is applied to uintptr parameters of functions
+// marked go:uintptrescapes.
+const UintptrEscapesNote = "uintptr-escapes"
+
+func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
+ name := func() string {
+ if f.Sym != nil {
+ return f.Sym.Name
+ }
+ return fmt.Sprintf("arg#%d", narg)
+ }
+
+ if len(fn.Body) == 0 {
+ // Assume that uintptr arguments must be held live across the call.
+ // This is most important for syscall.Syscall.
+ // See golang.org/issue/13372.
+ // This really doesn't have much to do with escape analysis per se,
+ // but we are reusing the ability to annotate an individual function
+ // argument and pass those annotations along to importing code.
+ if f.Type.IsUintptr() {
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name())
+ }
+ return UnsafeUintptrNote
+ }
+
+ if !f.Type.HasPointers() { // don't bother tagging for scalars
+ return ""
+ }
+
+ var esc leaks
+
+ // External functions are assumed unsafe, unless
+ // //go:noescape is given before the declaration.
+ if fn.Pragma&ir.Noescape != 0 {
+ if base.Flag.LowerM != 0 && f.Sym != nil {
+ base.WarnfAt(f.Pos, "%v does not escape", name())
+ }
+ } else {
+ if base.Flag.LowerM != 0 && f.Sym != nil {
+ base.WarnfAt(f.Pos, "leaking param: %v", name())
+ }
+ esc.AddHeap(0)
+ }
+
+ return esc.Encode()
+ }
+
+ if fn.Pragma&ir.UintptrEscapes != 0 {
+ if f.Type.IsUintptr() {
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name())
+ }
+ return UintptrEscapesNote
+ }
+ if f.IsDDD() && f.Type.Elem().IsUintptr() {
+ // final argument is ...uintptr.
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(f.Pos, "marking %v as escaping ...uintptr", name())
+ }
+ return UintptrEscapesNote
+ }
+ }
+
+ if !f.Type.HasPointers() { // don't bother tagging for scalars
+ return ""
+ }
+
+ // Unnamed parameters are unused and therefore do not escape.
+ if f.Sym == nil || f.Sym.IsBlank() {
+ var esc leaks
+ return esc.Encode()
+ }
+
+ n := f.Nname.(*ir.Name)
+ loc := b.oldLoc(n)
+ esc := loc.paramEsc
+ esc.Optimize()
+
+ if base.Flag.LowerM != 0 && !loc.escapes {
+ if esc.Empty() {
+ base.WarnfAt(f.Pos, "%v does not escape", name())
+ }
+ if x := esc.Heap(); x >= 0 {
+ if x == 0 {
+ base.WarnfAt(f.Pos, "leaking param: %v", name())
+ } else {
+ // TODO(mdempsky): Mention level=x like below?
+ base.WarnfAt(f.Pos, "leaking param content: %v", name())
+ }
+ }
+ for i := 0; i < numEscResults; i++ {
+ if x := esc.Result(i); x >= 0 {
+ res := fn.Type().Results().Field(i).Sym
+ base.WarnfAt(f.Pos, "leaking param: %v to result %v level=%d", name(), res, x)
+ }
+ }
+ }
+
+ return esc.Encode()
+}
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
deleted file mode 100644
index 2f7fa27bb9100..0000000000000
--- a/src/cmd/compile/internal/gc/alg.go
+++ /dev/null
@@ -1,959 +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.
-
-package gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "fmt"
- "sort"
-)
-
-// AlgKind describes the kind of algorithms used for comparing and
-// hashing a Type.
-type AlgKind int
-
-//go:generate stringer -type AlgKind -trimprefix A
-
-const (
- // These values are known by runtime.
- ANOEQ AlgKind = iota
- AMEM0
- AMEM8
- AMEM16
- AMEM32
- AMEM64
- AMEM128
- ASTRING
- AINTER
- ANILINTER
- AFLOAT32
- AFLOAT64
- ACPLX64
- ACPLX128
-
- // Type can be compared/hashed as regular memory.
- AMEM AlgKind = 100
-
- // Type needs special comparison/hashing functions.
- ASPECIAL AlgKind = -1
-)
-
-// IsComparable reports whether t is a comparable type.
-func IsComparable(t *types.Type) bool {
- a, _ := algtype1(t)
- return a != ANOEQ
-}
-
-// IsRegularMemory reports whether t can be compared/hashed as regular memory.
-func IsRegularMemory(t *types.Type) bool {
- a, _ := algtype1(t)
- return a == AMEM
-}
-
-// IncomparableField returns an incomparable Field of struct Type t, if any.
-func IncomparableField(t *types.Type) *types.Field {
- for _, f := range t.FieldSlice() {
- if !IsComparable(f.Type) {
- return f
- }
- }
- return nil
-}
-
-// EqCanPanic reports whether == on type t could panic (has an interface somewhere).
-// t must be comparable.
-func EqCanPanic(t *types.Type) bool {
- switch t.Etype {
- default:
- return false
- case TINTER:
- return true
- case TARRAY:
- return EqCanPanic(t.Elem())
- case TSTRUCT:
- for _, f := range t.FieldSlice() {
- if !f.Sym.IsBlank() && EqCanPanic(f.Type) {
- return true
- }
- }
- return false
- }
-}
-
-// algtype is like algtype1, except it returns the fixed-width AMEMxx variants
-// instead of the general AMEM kind when possible.
-func algtype(t *types.Type) AlgKind {
- a, _ := algtype1(t)
- if a == AMEM {
- switch t.Width {
- case 0:
- return AMEM0
- case 1:
- return AMEM8
- case 2:
- return AMEM16
- case 4:
- return AMEM32
- case 8:
- return AMEM64
- case 16:
- return AMEM128
- }
- }
-
- return a
-}
-
-// algtype1 returns the AlgKind used for comparing and hashing Type t.
-// If it returns ANOEQ, it also returns the component type of t that
-// makes it incomparable.
-func algtype1(t *types.Type) (AlgKind, *types.Type) {
- if t.Broke() {
- return AMEM, nil
- }
- if t.Noalg() {
- return ANOEQ, t
- }
-
- switch t.Etype {
- case TANY, TFORW:
- // will be defined later.
- return ANOEQ, t
-
- case TINT8, TUINT8, TINT16, TUINT16,
- TINT32, TUINT32, TINT64, TUINT64,
- TINT, TUINT, TUINTPTR,
- TBOOL, TPTR,
- TCHAN, TUNSAFEPTR:
- return AMEM, nil
-
- case TFUNC, TMAP:
- return ANOEQ, t
-
- case TFLOAT32:
- return AFLOAT32, nil
-
- case TFLOAT64:
- return AFLOAT64, nil
-
- case TCOMPLEX64:
- return ACPLX64, nil
-
- case TCOMPLEX128:
- return ACPLX128, nil
-
- case TSTRING:
- return ASTRING, nil
-
- case TINTER:
- if t.IsEmptyInterface() {
- return ANILINTER, nil
- }
- return AINTER, nil
-
- case TSLICE:
- return ANOEQ, t
-
- case TARRAY:
- a, bad := algtype1(t.Elem())
- switch a {
- case AMEM:
- return AMEM, nil
- case ANOEQ:
- return ANOEQ, bad
- }
-
- switch t.NumElem() {
- case 0:
- // We checked above that the element type is comparable.
- return AMEM, nil
- case 1:
- // Single-element array is same as its lone element.
- return a, nil
- }
-
- return ASPECIAL, nil
-
- case TSTRUCT:
- fields := t.FieldSlice()
-
- // One-field struct is same as that one field alone.
- if len(fields) == 1 && !fields[0].Sym.IsBlank() {
- return algtype1(fields[0].Type)
- }
-
- ret := AMEM
- for i, f := range fields {
- // All fields must be comparable.
- a, bad := algtype1(f.Type)
- if a == ANOEQ {
- return ANOEQ, bad
- }
-
- // Blank fields, padded fields, fields with non-memory
- // equality need special compare.
- if a != AMEM || f.Sym.IsBlank() || ispaddedfield(t, i) {
- ret = ASPECIAL
- }
- }
-
- return ret, nil
- }
-
- Fatalf("algtype1: unexpected type %v", t)
- return 0, nil
-}
-
-// genhash returns a symbol which is the closure used to compute
-// the hash of a value of type t.
-// Note: the generated function must match runtime.typehash exactly.
-func genhash(t *types.Type) *obj.LSym {
- switch algtype(t) {
- default:
- // genhash is only called for types that have equality
- Fatalf("genhash %v", t)
- case AMEM0:
- return sysClosure("memhash0")
- case AMEM8:
- return sysClosure("memhash8")
- case AMEM16:
- return sysClosure("memhash16")
- case AMEM32:
- return sysClosure("memhash32")
- case AMEM64:
- return sysClosure("memhash64")
- case AMEM128:
- return sysClosure("memhash128")
- case ASTRING:
- return sysClosure("strhash")
- case AINTER:
- return sysClosure("interhash")
- case ANILINTER:
- return sysClosure("nilinterhash")
- case AFLOAT32:
- return sysClosure("f32hash")
- case AFLOAT64:
- return sysClosure("f64hash")
- case ACPLX64:
- return sysClosure("c64hash")
- case ACPLX128:
- return sysClosure("c128hash")
- case AMEM:
- // For other sizes of plain memory, we build a closure
- // that calls memhash_varlen. The size of the memory is
- // encoded in the first slot of the closure.
- closure := typeLookup(fmt.Sprintf(".hashfunc%d", t.Width)).Linksym()
- if len(closure.P) > 0 { // already generated
- return closure
- }
- if memhashvarlen == nil {
- memhashvarlen = sysfunc("memhash_varlen")
- }
- ot := 0
- ot = dsymptr(closure, ot, memhashvarlen, 0)
- ot = duintptr(closure, ot, uint64(t.Width)) // size encoded in closure
- ggloblsym(closure, int32(ot), obj.DUPOK|obj.RODATA)
- return closure
- case ASPECIAL:
- break
- }
-
- closure := typesymprefix(".hashfunc", t).Linksym()
- if len(closure.P) > 0 { // already generated
- return closure
- }
-
- // Generate hash functions for subtypes.
- // There are cases where we might not use these hashes,
- // but in that case they will get dead-code eliminated.
- // (And the closure generated by genhash will also get
- // dead-code eliminated, as we call the subtype hashers
- // directly.)
- switch t.Etype {
- case types.TARRAY:
- genhash(t.Elem())
- case types.TSTRUCT:
- for _, f := range t.FieldSlice() {
- genhash(f.Type)
- }
- }
-
- sym := typesymprefix(".hash", t)
- if Debug.r != 0 {
- fmt.Printf("genhash %v %v %v\n", closure, sym, t)
- }
-
- lineno = autogeneratedPos // less confusing than end of input
- dclcontext = PEXTERN
-
- // func sym(p *T, h uintptr) uintptr
- tfn := nod(OTFUNC, nil, nil)
- tfn.List.Set2(
- namedfield("p", types.NewPtr(t)),
- namedfield("h", types.Types[TUINTPTR]),
- )
- tfn.Rlist.Set1(anonfield(types.Types[TUINTPTR]))
-
- fn := dclfunc(sym, tfn)
- np := asNode(tfn.Type.Params().Field(0).Nname)
- nh := asNode(tfn.Type.Params().Field(1).Nname)
-
- switch t.Etype {
- case types.TARRAY:
- // An array of pure memory would be handled by the
- // standard algorithm, so the element type must not be
- // pure memory.
- hashel := hashfor(t.Elem())
-
- n := nod(ORANGE, nil, nod(ODEREF, np, nil))
- ni := newname(lookup("i"))
- ni.Type = types.Types[TINT]
- n.List.Set1(ni)
- n.SetColas(true)
- colasdefn(n.List.Slice(), n)
- ni = n.List.First()
-
- // h = hashel(&p[i], h)
- call := nod(OCALL, hashel, nil)
-
- nx := nod(OINDEX, np, ni)
- nx.SetBounded(true)
- na := nod(OADDR, nx, nil)
- call.List.Append(na)
- call.List.Append(nh)
- n.Nbody.Append(nod(OAS, nh, call))
-
- fn.Nbody.Append(n)
-
- case types.TSTRUCT:
- // Walk the struct using memhash for runs of AMEM
- // and calling specific hash functions for the others.
- for i, fields := 0, t.FieldSlice(); i < len(fields); {
- f := fields[i]
-
- // Skip blank fields.
- if f.Sym.IsBlank() {
- i++
- continue
- }
-
- // Hash non-memory fields with appropriate hash function.
- if !IsRegularMemory(f.Type) {
- hashel := hashfor(f.Type)
- call := nod(OCALL, hashel, nil)
- nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
- na := nod(OADDR, nx, nil)
- call.List.Append(na)
- call.List.Append(nh)
- fn.Nbody.Append(nod(OAS, nh, call))
- i++
- continue
- }
-
- // Otherwise, hash a maximal length run of raw memory.
- size, next := memrun(t, i)
-
- // h = hashel(&p.first, size, h)
- hashel := hashmem(f.Type)
- call := nod(OCALL, hashel, nil)
- nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
- na := nod(OADDR, nx, nil)
- call.List.Append(na)
- call.List.Append(nh)
- call.List.Append(nodintconst(size))
- fn.Nbody.Append(nod(OAS, nh, call))
-
- i = next
- }
- }
-
- r := nod(ORETURN, nil, nil)
- r.List.Append(nh)
- fn.Nbody.Append(r)
-
- if Debug.r != 0 {
- dumplist("genhash body", fn.Nbody)
- }
-
- funcbody()
-
- fn.Func.SetDupok(true)
- fn = typecheck(fn, ctxStmt)
-
- Curfn = fn
- typecheckslice(fn.Nbody.Slice(), ctxStmt)
- Curfn = nil
-
- if debug_dclstack != 0 {
- testdclstack()
- }
-
- fn.Func.SetNilCheckDisabled(true)
- xtop = append(xtop, fn)
-
- // Build closure. It doesn't close over any variables, so
- // it contains just the function pointer.
- dsymptr(closure, 0, sym.Linksym(), 0)
- ggloblsym(closure, int32(Widthptr), obj.DUPOK|obj.RODATA)
-
- return closure
-}
-
-func hashfor(t *types.Type) *Node {
- var sym *types.Sym
-
- switch a, _ := algtype1(t); a {
- case AMEM:
- Fatalf("hashfor with AMEM type")
- case AINTER:
- sym = Runtimepkg.Lookup("interhash")
- case ANILINTER:
- sym = Runtimepkg.Lookup("nilinterhash")
- case ASTRING:
- sym = Runtimepkg.Lookup("strhash")
- case AFLOAT32:
- sym = Runtimepkg.Lookup("f32hash")
- case AFLOAT64:
- sym = Runtimepkg.Lookup("f64hash")
- case ACPLX64:
- sym = Runtimepkg.Lookup("c64hash")
- case ACPLX128:
- sym = Runtimepkg.Lookup("c128hash")
- default:
- // Note: the caller of hashfor ensured that this symbol
- // exists and has a body by calling genhash for t.
- sym = typesymprefix(".hash", t)
- }
-
- n := newname(sym)
- setNodeNameFunc(n)
- n.Type = functype(nil, []*Node{
- anonfield(types.NewPtr(t)),
- anonfield(types.Types[TUINTPTR]),
- }, []*Node{
- anonfield(types.Types[TUINTPTR]),
- })
- return n
-}
-
-// sysClosure returns a closure which will call the
-// given runtime function (with no closed-over variables).
-func sysClosure(name string) *obj.LSym {
- s := sysvar(name + "·f")
- if len(s.P) == 0 {
- f := sysfunc(name)
- dsymptr(s, 0, f, 0)
- ggloblsym(s, int32(Widthptr), obj.DUPOK|obj.RODATA)
- }
- return s
-}
-
-// geneq returns a symbol which is the closure used to compute
-// equality for two objects of type t.
-func geneq(t *types.Type) *obj.LSym {
- switch algtype(t) {
- case ANOEQ:
- // The runtime will panic if it tries to compare
- // a type with a nil equality function.
- return nil
- case AMEM0:
- return sysClosure("memequal0")
- case AMEM8:
- return sysClosure("memequal8")
- case AMEM16:
- return sysClosure("memequal16")
- case AMEM32:
- return sysClosure("memequal32")
- case AMEM64:
- return sysClosure("memequal64")
- case AMEM128:
- return sysClosure("memequal128")
- case ASTRING:
- return sysClosure("strequal")
- case AINTER:
- return sysClosure("interequal")
- case ANILINTER:
- return sysClosure("nilinterequal")
- case AFLOAT32:
- return sysClosure("f32equal")
- case AFLOAT64:
- return sysClosure("f64equal")
- case ACPLX64:
- return sysClosure("c64equal")
- case ACPLX128:
- return sysClosure("c128equal")
- case AMEM:
- // make equality closure. The size of the type
- // is encoded in the closure.
- closure := typeLookup(fmt.Sprintf(".eqfunc%d", t.Width)).Linksym()
- if len(closure.P) != 0 {
- return closure
- }
- if memequalvarlen == nil {
- memequalvarlen = sysvar("memequal_varlen") // asm func
- }
- ot := 0
- ot = dsymptr(closure, ot, memequalvarlen, 0)
- ot = duintptr(closure, ot, uint64(t.Width))
- ggloblsym(closure, int32(ot), obj.DUPOK|obj.RODATA)
- return closure
- case ASPECIAL:
- break
- }
-
- closure := typesymprefix(".eqfunc", t).Linksym()
- if len(closure.P) > 0 { // already generated
- return closure
- }
- sym := typesymprefix(".eq", t)
- if Debug.r != 0 {
- fmt.Printf("geneq %v\n", t)
- }
-
- // Autogenerate code for equality of structs and arrays.
-
- lineno = autogeneratedPos // less confusing than end of input
- dclcontext = PEXTERN
-
- // func sym(p, q *T) bool
- tfn := nod(OTFUNC, nil, nil)
- tfn.List.Set2(
- namedfield("p", types.NewPtr(t)),
- namedfield("q", types.NewPtr(t)),
- )
- tfn.Rlist.Set1(namedfield("r", types.Types[TBOOL]))
-
- fn := dclfunc(sym, tfn)
- np := asNode(tfn.Type.Params().Field(0).Nname)
- nq := asNode(tfn.Type.Params().Field(1).Nname)
- nr := asNode(tfn.Type.Results().Field(0).Nname)
-
- // Label to jump to if an equality test fails.
- neq := autolabel(".neq")
-
- // We reach here only for types that have equality but
- // cannot be handled by the standard algorithms,
- // so t must be either an array or a struct.
- switch t.Etype {
- default:
- Fatalf("geneq %v", t)
-
- case TARRAY:
- nelem := t.NumElem()
-
- // checkAll generates code to check the equality of all array elements.
- // If unroll is greater than nelem, checkAll generates:
- //
- // if eq(p[0], q[0]) && eq(p[1], q[1]) && ... {
- // } else {
- // return
- // }
- //
- // And so on.
- //
- // Otherwise it generates:
- //
- // for i := 0; i < nelem; i++ {
- // if eq(p[i], q[i]) {
- // } else {
- // goto neq
- // }
- // }
- //
- // TODO(josharian): consider doing some loop unrolling
- // for larger nelem as well, processing a few elements at a time in a loop.
- checkAll := func(unroll int64, last bool, eq func(pi, qi *Node) *Node) {
- // checkIdx generates a node to check for equality at index i.
- checkIdx := func(i *Node) *Node {
- // pi := p[i]
- pi := nod(OINDEX, np, i)
- pi.SetBounded(true)
- pi.Type = t.Elem()
- // qi := q[i]
- qi := nod(OINDEX, nq, i)
- qi.SetBounded(true)
- qi.Type = t.Elem()
- return eq(pi, qi)
- }
-
- if nelem <= unroll {
- if last {
- // Do last comparison in a different manner.
- nelem--
- }
- // Generate a series of checks.
- for i := int64(0); i < nelem; i++ {
- // if check {} else { goto neq }
- nif := nod(OIF, checkIdx(nodintconst(i)), nil)
- nif.Rlist.Append(nodSym(OGOTO, nil, neq))
- fn.Nbody.Append(nif)
- }
- if last {
- fn.Nbody.Append(nod(OAS, nr, checkIdx(nodintconst(nelem))))
- }
- } else {
- // Generate a for loop.
- // for i := 0; i < nelem; i++
- i := temp(types.Types[TINT])
- init := nod(OAS, i, nodintconst(0))
- cond := nod(OLT, i, nodintconst(nelem))
- post := nod(OAS, i, nod(OADD, i, nodintconst(1)))
- loop := nod(OFOR, cond, post)
- loop.Ninit.Append(init)
- // if eq(pi, qi) {} else { goto neq }
- nif := nod(OIF, checkIdx(i), nil)
- nif.Rlist.Append(nodSym(OGOTO, nil, neq))
- loop.Nbody.Append(nif)
- fn.Nbody.Append(loop)
- if last {
- fn.Nbody.Append(nod(OAS, nr, nodbool(true)))
- }
- }
- }
-
- switch t.Elem().Etype {
- case TSTRING:
- // Do two loops. First, check that all the lengths match (cheap).
- // Second, check that all the contents match (expensive).
- // TODO: when the array size is small, unroll the length match checks.
- checkAll(3, false, func(pi, qi *Node) *Node {
- // Compare lengths.
- eqlen, _ := eqstring(pi, qi)
- return eqlen
- })
- checkAll(1, true, func(pi, qi *Node) *Node {
- // Compare contents.
- _, eqmem := eqstring(pi, qi)
- return eqmem
- })
- case TFLOAT32, TFLOAT64:
- checkAll(2, true, func(pi, qi *Node) *Node {
- // p[i] == q[i]
- return nod(OEQ, pi, qi)
- })
- // TODO: pick apart structs, do them piecemeal too
- default:
- checkAll(1, true, func(pi, qi *Node) *Node {
- // p[i] == q[i]
- return nod(OEQ, pi, qi)
- })
- }
-
- case TSTRUCT:
- // Build a list of conditions to satisfy.
- // The conditions are a list-of-lists. Conditions are reorderable
- // within each inner list. The outer lists must be evaluated in order.
- var conds [][]*Node
- conds = append(conds, []*Node{})
- and := func(n *Node) {
- i := len(conds) - 1
- conds[i] = append(conds[i], n)
- }
-
- // Walk the struct using memequal for runs of AMEM
- // and calling specific equality tests for the others.
- for i, fields := 0, t.FieldSlice(); i < len(fields); {
- f := fields[i]
-
- // Skip blank-named fields.
- if f.Sym.IsBlank() {
- i++
- continue
- }
-
- // Compare non-memory fields with field equality.
- if !IsRegularMemory(f.Type) {
- if EqCanPanic(f.Type) {
- // Enforce ordering by starting a new set of reorderable conditions.
- conds = append(conds, []*Node{})
- }
- p := nodSym(OXDOT, np, f.Sym)
- q := nodSym(OXDOT, nq, f.Sym)
- switch {
- case f.Type.IsString():
- eqlen, eqmem := eqstring(p, q)
- and(eqlen)
- and(eqmem)
- default:
- and(nod(OEQ, p, q))
- }
- if EqCanPanic(f.Type) {
- // Also enforce ordering after something that can panic.
- conds = append(conds, []*Node{})
- }
- i++
- continue
- }
-
- // Find maximal length run of memory-only fields.
- size, next := memrun(t, i)
-
- // TODO(rsc): All the calls to newname are wrong for
- // cross-package unexported fields.
- if s := fields[i:next]; len(s) <= 2 {
- // Two or fewer fields: use plain field equality.
- for _, f := range s {
- and(eqfield(np, nq, f.Sym))
- }
- } else {
- // More than two fields: use memequal.
- and(eqmem(np, nq, f.Sym, size))
- }
- i = next
- }
-
- // Sort conditions to put runtime calls last.
- // Preserve the rest of the ordering.
- var flatConds []*Node
- for _, c := range conds {
- isCall := func(n *Node) bool {
- return n.Op == OCALL || n.Op == OCALLFUNC
- }
- sort.SliceStable(c, func(i, j int) bool {
- return !isCall(c[i]) && isCall(c[j])
- })
- flatConds = append(flatConds, c...)
- }
-
- if len(flatConds) == 0 {
- fn.Nbody.Append(nod(OAS, nr, nodbool(true)))
- } else {
- for _, c := range flatConds[:len(flatConds)-1] {
- // if cond {} else { goto neq }
- n := nod(OIF, c, nil)
- n.Rlist.Append(nodSym(OGOTO, nil, neq))
- fn.Nbody.Append(n)
- }
- fn.Nbody.Append(nod(OAS, nr, flatConds[len(flatConds)-1]))
- }
- }
-
- // ret:
- // return
- ret := autolabel(".ret")
- fn.Nbody.Append(nodSym(OLABEL, nil, ret))
- fn.Nbody.Append(nod(ORETURN, nil, nil))
-
- // neq:
- // r = false
- // return (or goto ret)
- fn.Nbody.Append(nodSym(OLABEL, nil, neq))
- fn.Nbody.Append(nod(OAS, nr, nodbool(false)))
- if EqCanPanic(t) || hasCall(fn) {
- // Epilogue is large, so share it with the equal case.
- fn.Nbody.Append(nodSym(OGOTO, nil, ret))
- } else {
- // Epilogue is small, so don't bother sharing.
- fn.Nbody.Append(nod(ORETURN, nil, nil))
- }
- // TODO(khr): the epilogue size detection condition above isn't perfect.
- // We should really do a generic CL that shares epilogues across
- // the board. See #24936.
-
- if Debug.r != 0 {
- dumplist("geneq body", fn.Nbody)
- }
-
- funcbody()
-
- fn.Func.SetDupok(true)
- fn = typecheck(fn, ctxStmt)
-
- Curfn = fn
- typecheckslice(fn.Nbody.Slice(), ctxStmt)
- Curfn = nil
-
- if debug_dclstack != 0 {
- testdclstack()
- }
-
- // Disable checknils while compiling this code.
- // We are comparing a struct or an array,
- // neither of which can be nil, and our comparisons
- // are shallow.
- fn.Func.SetNilCheckDisabled(true)
- xtop = append(xtop, fn)
-
- // Generate a closure which points at the function we just generated.
- dsymptr(closure, 0, sym.Linksym(), 0)
- ggloblsym(closure, int32(Widthptr), obj.DUPOK|obj.RODATA)
- return closure
-}
-
-func hasCall(n *Node) bool {
- if n.Op == OCALL || n.Op == OCALLFUNC {
- return true
- }
- if n.Left != nil && hasCall(n.Left) {
- return true
- }
- if n.Right != nil && hasCall(n.Right) {
- return true
- }
- for _, x := range n.Ninit.Slice() {
- if hasCall(x) {
- return true
- }
- }
- for _, x := range n.Nbody.Slice() {
- if hasCall(x) {
- return true
- }
- }
- for _, x := range n.List.Slice() {
- if hasCall(x) {
- return true
- }
- }
- for _, x := range n.Rlist.Slice() {
- if hasCall(x) {
- return true
- }
- }
- return false
-}
-
-// eqfield returns the node
-// p.field == q.field
-func eqfield(p *Node, q *Node, field *types.Sym) *Node {
- nx := nodSym(OXDOT, p, field)
- ny := nodSym(OXDOT, q, field)
- ne := nod(OEQ, nx, ny)
- return ne
-}
-
-// eqstring returns the nodes
-// len(s) == len(t)
-// and
-// memequal(s.ptr, t.ptr, len(s))
-// which can be used to construct string equality comparison.
-// eqlen must be evaluated before eqmem, and shortcircuiting is required.
-func eqstring(s, t *Node) (eqlen, eqmem *Node) {
- s = conv(s, types.Types[TSTRING])
- t = conv(t, types.Types[TSTRING])
- sptr := nod(OSPTR, s, nil)
- tptr := nod(OSPTR, t, nil)
- slen := conv(nod(OLEN, s, nil), types.Types[TUINTPTR])
- tlen := conv(nod(OLEN, t, nil), types.Types[TUINTPTR])
-
- fn := syslook("memequal")
- fn = substArgTypes(fn, types.Types[TUINT8], types.Types[TUINT8])
- call := nod(OCALL, fn, nil)
- call.List.Append(sptr, tptr, slen.copy())
- call = typecheck(call, ctxExpr|ctxMultiOK)
-
- cmp := nod(OEQ, slen, tlen)
- cmp = typecheck(cmp, ctxExpr)
- cmp.Type = types.Types[TBOOL]
- return cmp, call
-}
-
-// eqinterface returns the nodes
-// s.tab == t.tab (or s.typ == t.typ, as appropriate)
-// and
-// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate)
-// which can be used to construct interface equality comparison.
-// eqtab must be evaluated before eqdata, and shortcircuiting is required.
-func eqinterface(s, t *Node) (eqtab, eqdata *Node) {
- if !types.Identical(s.Type, t.Type) {
- Fatalf("eqinterface %v %v", s.Type, t.Type)
- }
- // func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
- // func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
- var fn *Node
- if s.Type.IsEmptyInterface() {
- fn = syslook("efaceeq")
- } else {
- fn = syslook("ifaceeq")
- }
-
- stab := nod(OITAB, s, nil)
- ttab := nod(OITAB, t, nil)
- sdata := nod(OIDATA, s, nil)
- tdata := nod(OIDATA, t, nil)
- sdata.Type = types.Types[TUNSAFEPTR]
- tdata.Type = types.Types[TUNSAFEPTR]
- sdata.SetTypecheck(1)
- tdata.SetTypecheck(1)
-
- call := nod(OCALL, fn, nil)
- call.List.Append(stab, sdata, tdata)
- call = typecheck(call, ctxExpr|ctxMultiOK)
-
- cmp := nod(OEQ, stab, ttab)
- cmp = typecheck(cmp, ctxExpr)
- cmp.Type = types.Types[TBOOL]
- return cmp, call
-}
-
-// eqmem returns the node
-// memequal(&p.field, &q.field [, size])
-func eqmem(p *Node, q *Node, field *types.Sym, size int64) *Node {
- nx := nod(OADDR, nodSym(OXDOT, p, field), nil)
- ny := nod(OADDR, nodSym(OXDOT, q, field), nil)
- nx = typecheck(nx, ctxExpr)
- ny = typecheck(ny, ctxExpr)
-
- fn, needsize := eqmemfunc(size, nx.Type.Elem())
- call := nod(OCALL, fn, nil)
- call.List.Append(nx)
- call.List.Append(ny)
- if needsize {
- call.List.Append(nodintconst(size))
- }
-
- return call
-}
-
-func eqmemfunc(size int64, t *types.Type) (fn *Node, needsize bool) {
- switch size {
- default:
- fn = syslook("memequal")
- needsize = true
- case 1, 2, 4, 8, 16:
- buf := fmt.Sprintf("memequal%d", int(size)*8)
- fn = syslook(buf)
- }
-
- fn = substArgTypes(fn, t, t)
- return fn, needsize
-}
-
-// memrun finds runs of struct fields for which memory-only algs are appropriate.
-// t is the parent struct type, and start is the field index at which to start the run.
-// size is the length in bytes of the memory included in the run.
-// next is the index just after the end of the memory run.
-func memrun(t *types.Type, start int) (size int64, next int) {
- next = start
- for {
- next++
- if next == t.NumFields() {
- break
- }
- // Stop run after a padded field.
- if ispaddedfield(t, next-1) {
- break
- }
- // Also, stop before a blank or non-memory field.
- if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) {
- break
- }
- }
- return t.Field(next-1).End() - t.Field(start).Offset, next
-}
-
-// ispaddedfield reports whether the i'th field of struct type t is followed
-// by padding.
-func ispaddedfield(t *types.Type, i int) bool {
- if !t.IsStruct() {
- Fatalf("ispaddedfield called non-struct %v", t)
- }
- end := t.Width
- if i+1 < t.NumFields() {
- end = t.Field(i + 1).Offset
- }
- return t.Field(i).End() != end
-}
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
deleted file mode 100644
index 10f21f86df581..0000000000000
--- a/src/cmd/compile/internal/gc/bexport.go
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2015 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 gc
-
-import (
- "cmd/compile/internal/types"
-)
-
-type exporter struct {
- marked map[*types.Type]bool // types already seen by markType
-}
-
-// markType recursively visits types reachable from t to identify
-// functions whose inline bodies may be needed.
-func (p *exporter) markType(t *types.Type) {
- if p.marked[t] {
- return
- }
- p.marked[t] = true
-
- // If this is a named type, mark all of its associated
- // methods. Skip interface types because t.Methods contains
- // only their unexpanded method set (i.e., exclusive of
- // interface embeddings), and the switch statement below
- // handles their full method set.
- if t.Sym != nil && t.Etype != TINTER {
- for _, m := range t.Methods().Slice() {
- if types.IsExported(m.Sym.Name) {
- p.markType(m.Type)
- }
- }
- }
-
- // Recursively mark any types that can be produced given a
- // value of type t: dereferencing a pointer; indexing or
- // iterating over an array, slice, or map; receiving from a
- // channel; accessing a struct field or interface method; or
- // calling a function.
- //
- // Notably, we don't mark function parameter types, because
- // the user already needs some way to construct values of
- // those types.
- switch t.Etype {
- case TPTR, TARRAY, TSLICE:
- p.markType(t.Elem())
-
- case TCHAN:
- if t.ChanDir().CanRecv() {
- p.markType(t.Elem())
- }
-
- case TMAP:
- p.markType(t.Key())
- p.markType(t.Elem())
-
- case TSTRUCT:
- for _, f := range t.FieldSlice() {
- if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
- p.markType(f.Type)
- }
- }
-
- case TFUNC:
- // If t is the type of a function or method, then
- // t.Nname() is its ONAME. Mark its inline body and
- // any recursively called functions for export.
- inlFlood(asNode(t.Nname()))
-
- for _, f := range t.Results().FieldSlice() {
- p.markType(f.Type)
- }
-
- case TINTER:
- for _, f := range t.FieldSlice() {
- if types.IsExported(f.Sym.Name) {
- p.markType(f.Type)
- }
- }
- }
-}
-
-// ----------------------------------------------------------------------------
-// Export format
-
-// Tags. Must be < 0.
-const (
- // Objects
- packageTag = -(iota + 1)
- constTag
- typeTag
- varTag
- funcTag
- endTag
-
- // Types
- namedTag
- arrayTag
- sliceTag
- dddTag
- structTag
- pointerTag
- signatureTag
- interfaceTag
- mapTag
- chanTag
-
- // Values
- falseTag
- trueTag
- int64Tag
- floatTag
- fractionTag // not used by gc
- complexTag
- stringTag
- nilTag
- unknownTag // not used by gc (only appears in packages with errors)
-
- // Type aliases
- aliasTag
-)
-
-var predecl []*types.Type // initialized lazily
-
-func predeclared() []*types.Type {
- if predecl == nil {
- // initialize lazily to be sure that all
- // elements have been initialized before
- predecl = []*types.Type{
- // basic types
- types.Types[TBOOL],
- types.Types[TINT],
- types.Types[TINT8],
- types.Types[TINT16],
- types.Types[TINT32],
- types.Types[TINT64],
- types.Types[TUINT],
- types.Types[TUINT8],
- types.Types[TUINT16],
- types.Types[TUINT32],
- types.Types[TUINT64],
- types.Types[TUINTPTR],
- types.Types[TFLOAT32],
- types.Types[TFLOAT64],
- types.Types[TCOMPLEX64],
- types.Types[TCOMPLEX128],
- types.Types[TSTRING],
-
- // basic type aliases
- types.Bytetype,
- types.Runetype,
-
- // error
- types.Errortype,
-
- // untyped types
- types.UntypedBool,
- types.UntypedInt,
- types.UntypedRune,
- types.UntypedFloat,
- types.UntypedComplex,
- types.UntypedString,
- types.Types[TNIL],
-
- // package unsafe
- types.Types[TUNSAFEPTR],
-
- // invalid type (package contains errors)
- types.Types[Txxx],
-
- // any type, for builtin export data
- types.Types[TANY],
- }
- }
- return predecl
-}
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
deleted file mode 100644
index 911ac4c0dc7b6..0000000000000
--- a/src/cmd/compile/internal/gc/bimport.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2015 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 gc
-
-import (
- "cmd/internal/src"
-)
-
-// numImport tracks how often a package with a given name is imported.
-// It is used to provide a better error message (by using the package
-// path to disambiguate) if a package that appears multiple times with
-// the same name appears in an error message.
-var numImport = make(map[string]int)
-
-func npos(pos src.XPos, n *Node) *Node {
- n.Pos = pos
- return n
-}
-
-func builtinCall(op Op) *Node {
- return nod(OCALL, mkname(builtinpkg.Lookup(goopnames[op])), nil)
-}
diff --git a/src/cmd/compile/internal/gc/bootstrap.go b/src/cmd/compile/internal/gc/bootstrap.go
index 967f75a9ac3a7..2e13d6b57acd4 100644
--- a/src/cmd/compile/internal/gc/bootstrap.go
+++ b/src/cmd/compile/internal/gc/bootstrap.go
@@ -6,8 +6,11 @@
package gc
-import "runtime"
+import (
+ "cmd/compile/internal/base"
+ "runtime"
+)
func startMutexProfiling() {
- Fatalf("mutex profiling unavailable in version %v", runtime.Version())
+ base.Fatalf("mutex profiling unavailable in version %v", runtime.Version())
}
diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
deleted file mode 100644
index e04f23e2294ed..0000000000000
--- a/src/cmd/compile/internal/gc/builtin.go
+++ /dev/null
@@ -1,340 +0,0 @@
-// Code generated by mkbuiltin.go. DO NOT EDIT.
-
-package gc
-
-import "cmd/compile/internal/types"
-
-var runtimeDecls = [...]struct {
- name string
- tag int
- typ int
-}{
- {"newobject", funcTag, 4},
- {"mallocgc", funcTag, 8},
- {"panicdivide", funcTag, 9},
- {"panicshift", funcTag, 9},
- {"panicmakeslicelen", funcTag, 9},
- {"panicmakeslicecap", funcTag, 9},
- {"throwinit", funcTag, 9},
- {"panicwrap", funcTag, 9},
- {"gopanic", funcTag, 11},
- {"gorecover", funcTag, 14},
- {"goschedguarded", funcTag, 9},
- {"goPanicIndex", funcTag, 16},
- {"goPanicIndexU", funcTag, 18},
- {"goPanicSliceAlen", funcTag, 16},
- {"goPanicSliceAlenU", funcTag, 18},
- {"goPanicSliceAcap", funcTag, 16},
- {"goPanicSliceAcapU", funcTag, 18},
- {"goPanicSliceB", funcTag, 16},
- {"goPanicSliceBU", funcTag, 18},
- {"goPanicSlice3Alen", funcTag, 16},
- {"goPanicSlice3AlenU", funcTag, 18},
- {"goPanicSlice3Acap", funcTag, 16},
- {"goPanicSlice3AcapU", funcTag, 18},
- {"goPanicSlice3B", funcTag, 16},
- {"goPanicSlice3BU", funcTag, 18},
- {"goPanicSlice3C", funcTag, 16},
- {"goPanicSlice3CU", funcTag, 18},
- {"printbool", funcTag, 19},
- {"printfloat", funcTag, 21},
- {"printint", funcTag, 23},
- {"printhex", funcTag, 25},
- {"printuint", funcTag, 25},
- {"printcomplex", funcTag, 27},
- {"printstring", funcTag, 29},
- {"printpointer", funcTag, 30},
- {"printuintptr", funcTag, 31},
- {"printiface", funcTag, 30},
- {"printeface", funcTag, 30},
- {"printslice", funcTag, 30},
- {"printnl", funcTag, 9},
- {"printsp", funcTag, 9},
- {"printlock", funcTag, 9},
- {"printunlock", funcTag, 9},
- {"concatstring2", funcTag, 34},
- {"concatstring3", funcTag, 35},
- {"concatstring4", funcTag, 36},
- {"concatstring5", funcTag, 37},
- {"concatstrings", funcTag, 39},
- {"cmpstring", funcTag, 40},
- {"intstring", funcTag, 43},
- {"slicebytetostring", funcTag, 44},
- {"slicebytetostringtmp", funcTag, 45},
- {"slicerunetostring", funcTag, 48},
- {"stringtoslicebyte", funcTag, 50},
- {"stringtoslicerune", funcTag, 53},
- {"slicecopy", 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},
- {"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},
- {"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, 31},
- {"racefuncenterfp", funcTag, 9},
- {"racefuncexit", funcTag, 9},
- {"raceread", funcTag, 31},
- {"racewrite", funcTag, 31},
- {"racereadrange", funcTag, 121},
- {"racewriterange", funcTag, 121},
- {"msanread", funcTag, 121},
- {"msanwrite", funcTag, 121},
- {"msanmove", 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},
- {"x86HasPOPCNT", varTag, 6},
- {"x86HasSSE41", varTag, 6},
- {"x86HasFMA", varTag, 6},
- {"armHasVFPv4", varTag, 6},
- {"arm64HasATOMICS", varTag, 6},
-}
-
-func runtimeTypes() []*types.Type {
- var typs [132]*types.Type
- typs[0] = types.Bytetype
- typs[1] = types.NewPtr(typs[0])
- typs[2] = types.Types[TANY]
- typs[3] = types.NewPtr(typs[2])
- typs[4] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[3])})
- typs[5] = types.Types[TUINTPTR]
- typs[6] = types.Types[TBOOL]
- typs[7] = types.Types[TUNSAFEPTR]
- typs[8] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[1]), anonfield(typs[6])}, []*Node{anonfield(typs[7])})
- typs[9] = functype(nil, nil, nil)
- typs[10] = types.Types[TINTER]
- typs[11] = functype(nil, []*Node{anonfield(typs[10])}, nil)
- typs[12] = types.Types[TINT32]
- typs[13] = types.NewPtr(typs[12])
- typs[14] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[10])})
- typs[15] = types.Types[TINT]
- typs[16] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, nil)
- typs[17] = types.Types[TUINT]
- typs[18] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[15])}, nil)
- typs[19] = functype(nil, []*Node{anonfield(typs[6])}, nil)
- typs[20] = types.Types[TFLOAT64]
- typs[21] = functype(nil, []*Node{anonfield(typs[20])}, nil)
- typs[22] = types.Types[TINT64]
- typs[23] = functype(nil, []*Node{anonfield(typs[22])}, nil)
- typs[24] = types.Types[TUINT64]
- typs[25] = functype(nil, []*Node{anonfield(typs[24])}, nil)
- typs[26] = types.Types[TCOMPLEX128]
- typs[27] = functype(nil, []*Node{anonfield(typs[26])}, nil)
- typs[28] = types.Types[TSTRING]
- typs[29] = functype(nil, []*Node{anonfield(typs[28])}, nil)
- typs[30] = functype(nil, []*Node{anonfield(typs[2])}, nil)
- typs[31] = functype(nil, []*Node{anonfield(typs[5])}, nil)
- typs[32] = types.NewArray(typs[0], 32)
- typs[33] = types.NewPtr(typs[32])
- typs[34] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[35] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[36] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[37] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
- typs[38] = types.NewSlice(typs[28])
- typs[39] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[38])}, []*Node{anonfield(typs[28])})
- typs[40] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[15])})
- typs[41] = types.NewArray(typs[0], 4)
- typs[42] = types.NewPtr(typs[41])
- typs[43] = functype(nil, []*Node{anonfield(typs[42]), anonfield(typs[22])}, []*Node{anonfield(typs[28])})
- typs[44] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
- typs[45] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
- typs[46] = types.Runetype
- typs[47] = types.NewSlice(typs[46])
- typs[48] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[47])}, []*Node{anonfield(typs[28])})
- typs[49] = types.NewSlice(typs[0])
- typs[50] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28])}, []*Node{anonfield(typs[49])})
- typs[51] = types.NewArray(typs[46], 32)
- typs[52] = types.NewPtr(typs[51])
- typs[53] = functype(nil, []*Node{anonfield(typs[52]), anonfield(typs[28])}, []*Node{anonfield(typs[47])})
- typs[54] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])})
- typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[46]), 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]), anonfield(typs[5])}, nil)
- typs[122] = functype(nil, []*Node{anonfield(typs[5]), 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)
- return typs[:]
-}
diff --git a/src/cmd/compile/internal/gc/bv.go b/src/cmd/compile/internal/gc/bv.go
deleted file mode 100644
index e32ab97ad52d2..0000000000000
--- a/src/cmd/compile/internal/gc/bv.go
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright 2013 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 gc
-
-import (
- "math/bits"
-)
-
-const (
- wordBits = 32
- wordMask = wordBits - 1
- wordShift = 5
-)
-
-// A bvec is a bit vector.
-type bvec struct {
- n int32 // number of bits in vector
- b []uint32 // words holding bits
-}
-
-func bvalloc(n int32) bvec {
- nword := (n + wordBits - 1) / wordBits
- return bvec{n, make([]uint32, nword)}
-}
-
-type bulkBvec struct {
- words []uint32
- nbit int32
- nword int32
-}
-
-func bvbulkalloc(nbit int32, count int32) bulkBvec {
- nword := (nbit + wordBits - 1) / wordBits
- size := int64(nword) * int64(count)
- if int64(int32(size*4)) != size*4 {
- Fatalf("bvbulkalloc too big: nbit=%d count=%d nword=%d size=%d", nbit, count, nword, size)
- }
- return bulkBvec{
- words: make([]uint32, size),
- nbit: nbit,
- nword: nword,
- }
-}
-
-func (b *bulkBvec) next() bvec {
- out := bvec{b.nbit, b.words[:b.nword]}
- b.words = b.words[b.nword:]
- return out
-}
-
-func (bv1 bvec) Eq(bv2 bvec) bool {
- if bv1.n != bv2.n {
- Fatalf("bvequal: lengths %d and %d are not equal", bv1.n, bv2.n)
- }
- for i, x := range bv1.b {
- if x != bv2.b[i] {
- return false
- }
- }
- return true
-}
-
-func (dst bvec) Copy(src bvec) {
- copy(dst.b, src.b)
-}
-
-func (bv bvec) Get(i int32) bool {
- if i < 0 || i >= bv.n {
- Fatalf("bvget: index %d is out of bounds with length %d\n", i, bv.n)
- }
- mask := uint32(1 << uint(i%wordBits))
- return bv.b[i>>wordShift]&mask != 0
-}
-
-func (bv bvec) Set(i int32) {
- if i < 0 || i >= bv.n {
- Fatalf("bvset: index %d is out of bounds with length %d\n", i, bv.n)
- }
- mask := uint32(1 << uint(i%wordBits))
- bv.b[i/wordBits] |= mask
-}
-
-func (bv bvec) Unset(i int32) {
- if i < 0 || i >= bv.n {
- Fatalf("bvunset: index %d is out of bounds with length %d\n", i, bv.n)
- }
- mask := uint32(1 << uint(i%wordBits))
- bv.b[i/wordBits] &^= mask
-}
-
-// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
-// If there is no such index, bvnext returns -1.
-func (bv bvec) Next(i int32) int32 {
- if i >= bv.n {
- return -1
- }
-
- // Jump i ahead to next word with bits.
- if bv.b[i>>wordShift]>>uint(i&wordMask) == 0 {
- i &^= wordMask
- i += wordBits
- for i < bv.n && bv.b[i>>wordShift] == 0 {
- i += wordBits
- }
- }
-
- if i >= bv.n {
- return -1
- }
-
- // Find 1 bit.
- w := bv.b[i>>wordShift] >> uint(i&wordMask)
- i += int32(bits.TrailingZeros32(w))
-
- return i
-}
-
-func (bv bvec) IsEmpty() bool {
- for _, x := range bv.b {
- if x != 0 {
- return false
- }
- }
- return true
-}
-
-func (bv bvec) Not() {
- for i, x := range bv.b {
- bv.b[i] = ^x
- }
-}
-
-// union
-func (dst bvec) Or(src1, src2 bvec) {
- if len(src1.b) == 0 {
- return
- }
- _, _ = dst.b[len(src1.b)-1], src2.b[len(src1.b)-1] // hoist bounds checks out of the loop
-
- for i, x := range src1.b {
- dst.b[i] = x | src2.b[i]
- }
-}
-
-// intersection
-func (dst bvec) And(src1, src2 bvec) {
- if len(src1.b) == 0 {
- return
- }
- _, _ = dst.b[len(src1.b)-1], src2.b[len(src1.b)-1] // hoist bounds checks out of the loop
-
- for i, x := range src1.b {
- dst.b[i] = x & src2.b[i]
- }
-}
-
-// difference
-func (dst bvec) AndNot(src1, src2 bvec) {
- if len(src1.b) == 0 {
- return
- }
- _, _ = dst.b[len(src1.b)-1], src2.b[len(src1.b)-1] // hoist bounds checks out of the loop
-
- for i, x := range src1.b {
- dst.b[i] = x &^ src2.b[i]
- }
-}
-
-func (bv bvec) String() string {
- s := make([]byte, 2+bv.n)
- copy(s, "#*")
- for i := int32(0); i < bv.n; i++ {
- ch := byte('0')
- if bv.Get(i) {
- ch = '1'
- }
- s[2+i] = ch
- }
- return string(s)
-}
-
-func (bv bvec) Clear() {
- for i := range bv.b {
- bv.b[i] = 0
- }
-}
-
-// FNV-1 hash function constants.
-const (
- H0 = 2166136261
- Hp = 16777619
-)
-
-func hashbitmap(h uint32, bv bvec) uint32 {
- n := int((bv.n + 31) / 32)
- for i := 0; i < n; i++ {
- w := bv.b[i]
- h = (h * Hp) ^ (w & 0xff)
- h = (h * Hp) ^ ((w >> 8) & 0xff)
- h = (h * Hp) ^ ((w >> 16) & 0xff)
- h = (h * Hp) ^ ((w >> 24) & 0xff)
- }
-
- return h
-}
-
-// bvecSet is a set of bvecs, in initial insertion order.
-type bvecSet struct {
- index []int // hash -> uniq index. -1 indicates empty slot.
- uniq []bvec // unique bvecs, in insertion order
-}
-
-func (m *bvecSet) grow() {
- // Allocate new index.
- n := len(m.index) * 2
- if n == 0 {
- n = 32
- }
- newIndex := make([]int, n)
- for i := range newIndex {
- newIndex[i] = -1
- }
-
- // Rehash into newIndex.
- for i, bv := range m.uniq {
- h := hashbitmap(H0, bv) % uint32(len(newIndex))
- for {
- j := newIndex[h]
- if j < 0 {
- newIndex[h] = i
- break
- }
- h++
- if h == uint32(len(newIndex)) {
- h = 0
- }
- }
- }
- m.index = newIndex
-}
-
-// add adds bv to the set and returns its index in m.extractUniqe.
-// The caller must not modify bv after this.
-func (m *bvecSet) add(bv bvec) int {
- if len(m.uniq)*4 >= len(m.index) {
- m.grow()
- }
-
- index := m.index
- h := hashbitmap(H0, bv) % uint32(len(index))
- for {
- j := index[h]
- if j < 0 {
- // New bvec.
- index[h] = len(m.uniq)
- m.uniq = append(m.uniq, bv)
- return len(m.uniq) - 1
- }
- jlive := m.uniq[j]
- if bv.Eq(jlive) {
- // Existing bvec.
- return j
- }
-
- h++
- if h == uint32(len(index)) {
- h = 0
- }
- }
-}
-
-// extractUniqe returns this slice of unique bit vectors in m, as
-// indexed by the result of bvecSet.add.
-func (m *bvecSet) extractUniqe() []bvec {
- return m.uniq
-}
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
deleted file mode 100644
index bd350f696e8ce..0000000000000
--- a/src/cmd/compile/internal/gc/closure.go
+++ /dev/null
@@ -1,594 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/syntax"
- "cmd/compile/internal/types"
- "fmt"
-)
-
-func (p *noder) funcLit(expr *syntax.FuncLit) *Node {
- xtype := p.typeExpr(expr.Type)
- ntype := p.typeExpr(expr.Type)
-
- xfunc := p.nod(expr, ODCLFUNC, nil, nil)
- xfunc.Func.SetIsHiddenClosure(Curfn != nil)
- xfunc.Func.Nname = newfuncnamel(p.pos(expr), nblank.Sym) // filled in by typecheckclosure
- xfunc.Func.Nname.Name.Param.Ntype = xtype
- xfunc.Func.Nname.Name.Defn = xfunc
-
- clo := p.nod(expr, OCLOSURE, nil, nil)
- clo.Func.Ntype = ntype
-
- xfunc.Func.Closure = clo
- clo.Func.Closure = xfunc
-
- p.funcBody(xfunc, 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 xfunc.Func.Cvars.Slice() {
- // Unlink from v1; see comment in syntax.go type Param for these fields.
- v1 := v.Name.Defn
- v1.Name.Param.Innermost = v.Name.Param.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.Name.Param.Outer = oldname(v.Sym)
- }
-
- return clo
-}
-
-// typecheckclosure typechecks an OCLOSURE node. It also creates the named
-// function associated with the closure.
-// TODO: This creation of the named function should probably really be done in a
-// separate pass from type-checking.
-func typecheckclosure(clo *Node, top int) {
- xfunc := clo.Func.Closure
- // Set current associated iota value, so iota can be used inside
- // function in ConstSpec, see issue #22344
- if x := getIotaValue(); x >= 0 {
- xfunc.SetIota(x)
- }
-
- clo.Func.Ntype = typecheck(clo.Func.Ntype, ctxType)
- clo.Type = clo.Func.Ntype.Type
- clo.Func.Top = top
-
- // Do not typecheck xfunc twice, otherwise, we will end up pushing
- // xfunc to xtop multiple times, causing initLSym called twice.
- // See #30709
- if xfunc.Typecheck() == 1 {
- return
- }
-
- for _, ln := range xfunc.Func.Cvars.Slice() {
- n := ln.Name.Defn
- if !n.Name.Captured() {
- n.Name.SetCaptured(true)
- if n.Name.Decldepth == 0 {
- Fatalf("typecheckclosure: var %S does not have decldepth assigned", n)
- }
-
- // Ignore assignments to the variable in straightline code
- // preceding the first capturing by a closure.
- if n.Name.Decldepth == decldepth {
- n.Name.SetAssigned(false)
- }
- }
- }
-
- xfunc.Func.Nname.Sym = closurename(Curfn)
- setNodeNameFunc(xfunc.Func.Nname)
- xfunc = typecheck(xfunc, ctxStmt)
-
- // 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
- // ready to type check code yet; we'll check it later, because the
- // underlying closure function we create is added to xtop.
- if Curfn != nil && clo.Type != nil {
- oldfn := Curfn
- Curfn = xfunc
- olddd := decldepth
- decldepth = 1
- typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
- decldepth = olddd
- Curfn = oldfn
- }
-
- xtop = append(xtop, xfunc)
-}
-
-// globClosgen is like Func.Closgen, but for the global scope.
-var globClosgen int
-
-// closurename generates a new unique name for a closure within
-// outerfunc.
-func closurename(outerfunc *Node) *types.Sym {
- outer := "glob."
- prefix := "func"
- gen := &globClosgen
-
- if outerfunc != nil {
- if outerfunc.Func.Closure != nil {
- prefix = ""
- }
-
- outer = outerfunc.funcname()
-
- // There may be multiple functions named "_". In those
- // cases, we can't use their individual Closgens as it
- // would lead to name clashes.
- if !outerfunc.Func.Nname.isBlank() {
- gen = &outerfunc.Func.Closgen
- }
- }
-
- *gen++
- return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
-}
-
-// capturevarscomplete is set to true when the capturevars phase is done.
-var capturevarscomplete bool
-
-// capturevars is called in a separate phase after all typechecking is done.
-// It decides whether each variable captured by a closure should be captured
-// by value or by reference.
-// We use value capturing for values <= 128 bytes that are never reassigned
-// after capturing (effectively constant).
-func capturevars(xfunc *Node) {
- lno := lineno
- lineno = xfunc.Pos
-
- clo := xfunc.Func.Closure
- cvars := xfunc.Func.Cvars.Slice()
- out := cvars[:0]
- for _, v := range cvars {
- if v.Type == nil {
- // If v.Type is nil, it means v looked like it
- // was going to be used in the closure, but
- // isn't. This happens in struct literals like
- // s{f: x} where we can't distinguish whether
- // f is a field identifier or expression until
- // resolving s.
- continue
- }
- out = append(out, v)
-
- // type check the & of closed variables outside the closure,
- // so that the outer frame also grabs them and knows they escape.
- dowidth(v.Type)
-
- outer := v.Name.Param.Outer
- outermost := v.Name.Defn
-
- // out parameters will be assigned to implicitly upon return.
- if outermost.Class() != PPARAMOUT && !outermost.Name.Addrtaken() && !outermost.Name.Assigned() && v.Type.Width <= 128 {
- v.Name.SetByval(true)
- } else {
- outermost.Name.SetAddrtaken(true)
- outer = nod(OADDR, outer, nil)
- }
-
- if Debug.m > 1 {
- var name *types.Sym
- if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil {
- name = v.Name.Curfn.Func.Nname.Sym
- }
- how := "ref"
- if v.Name.Byval() {
- how = "value"
- }
- Warnl(v.Pos, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Name.Addrtaken(), outermost.Name.Assigned(), int32(v.Type.Width))
- }
-
- outer = typecheck(outer, ctxExpr)
- clo.Func.Enter.Append(outer)
- }
-
- xfunc.Func.Cvars.Set(out)
- lineno = lno
-}
-
-// transformclosure is called in a separate phase after escape analysis.
-// It transform closure bodies to properly reference captured variables.
-func transformclosure(xfunc *Node) {
- lno := lineno
- lineno = xfunc.Pos
- clo := xfunc.Func.Closure
-
- if clo.Func.Top&ctxCallee != 0 {
- // 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 := xfunc.Func.Nname
-
- // We are going to insert captured variables before input args.
- var params []*types.Field
- var decls []*Node
- for _, v := range xfunc.Func.Cvars.Slice() {
- if !v.Name.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 := newname(lookup("&" + v.Sym.Name))
- addr.Type = types.NewPtr(v.Type)
- v.Name.Param.Heapaddr = addr
- v = addr
- }
-
- v.SetClass(PPARAM)
- decls = append(decls, v)
-
- fld := types.NewField()
- fld.Nname = asTypesNode(v)
- fld.Type = v.Type
- fld.Sym = v.Sym
- params = append(params, fld)
- }
-
- if len(params) > 0 {
- // Prepend params and decls.
- f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...))
- xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...)
- }
-
- dowidth(f.Type)
- xfunc.Type = f.Type // update type of ODCLFUNC
- } else {
- // The closure is not called, so it is going to stay as closure.
- var body []*Node
- offset := int64(Widthptr)
- for _, v := range xfunc.Func.Cvars.Slice() {
- // cv refers to the field inside of closure OSTRUCTLIT.
- cv := nod(OCLOSUREVAR, nil, nil)
-
- cv.Type = v.Type
- if !v.Name.Byval() {
- cv.Type = types.NewPtr(v.Type)
- }
- offset = Rnd(offset, int64(cv.Type.Align))
- cv.Xoffset = offset
- offset += cv.Type.Width
-
- if v.Name.Byval() && v.Type.Width <= int64(2*Widthptr) {
- // If it is a small variable captured by value, downgrade it to PAUTO.
- v.SetClass(PAUTO)
- xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
- body = append(body, nod(OAS, v, cv))
- } else {
- // Declare variable holding addresses taken from closure
- // and initialize in entry prologue.
- addr := newname(lookup("&" + v.Sym.Name))
- addr.Type = types.NewPtr(v.Type)
- addr.SetClass(PAUTO)
- addr.Name.SetUsed(true)
- addr.Name.Curfn = xfunc
- xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
- v.Name.Param.Heapaddr = addr
- if v.Name.Byval() {
- cv = nod(OADDR, cv, nil)
- }
- body = append(body, nod(OAS, addr, cv))
- }
- }
-
- if len(body) > 0 {
- typecheckslice(body, ctxStmt)
- xfunc.Func.Enter.Set(body)
- xfunc.Func.SetNeedctxt(true)
- }
- }
-
- lineno = lno
-}
-
-// hasemptycvars reports whether closure clo has an
-// empty list of captured vars.
-func hasemptycvars(clo *Node) bool {
- xfunc := clo.Func.Closure
- return xfunc.Func.Cvars.Len() == 0
-}
-
-// closuredebugruntimecheck applies boilerplate checks for debug flags
-// and compiling runtime
-func closuredebugruntimecheck(clo *Node) {
- if Debug_closure > 0 {
- xfunc := clo.Func.Closure
- if clo.Esc == EscHeap {
- Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars)
- } else {
- Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars)
- }
- }
- if compiling_runtime && clo.Esc == EscHeap {
- yyerrorl(clo.Pos, "heap-allocated closure, not allowed in runtime")
- }
-}
-
-// closureType returns the struct type used to hold all the information
-// needed in the closure for clo (clo must be a OCLOSURE node).
-// The address of a variable of the returned type can be cast to a func.
-func closureType(clo *Node) *types.Type {
- // Create closure in the form of a composite literal.
- // supposing the closure captures an int i and a string s
- // and has one float64 argument and no results,
- // the generated code looks like:
- //
- // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
- //
- // The use of the struct provides type information to the garbage
- // collector so that it can walk the closure. We could use (in this case)
- // [3]unsafe.Pointer instead, but that would leave the gc in the dark.
- // The information appears in the binary in the form of type descriptors;
- // the struct is unnamed so that closures in multiple packages with the
- // same struct type can share the descriptor.
- fields := []*Node{
- namedfield(".F", types.Types[TUINTPTR]),
- }
- for _, v := range clo.Func.Closure.Func.Cvars.Slice() {
- typ := v.Type
- if !v.Name.Byval() {
- typ = types.NewPtr(typ)
- }
- fields = append(fields, symfield(v.Sym, typ))
- }
- typ := tostruct(fields)
- typ.SetNoalg(true)
- return typ
-}
-
-func walkclosure(clo *Node, init *Nodes) *Node {
- xfunc := clo.Func.Closure
-
- // If no closure vars, don't bother wrapping.
- if hasemptycvars(clo) {
- if Debug_closure > 0 {
- Warnl(clo.Pos, "closure converted to global")
- }
- return xfunc.Func.Nname
- }
- closuredebugruntimecheck(clo)
-
- typ := closureType(clo)
-
- clos := nod(OCOMPLIT, nil, typenod(typ))
- clos.Esc = clo.Esc
- clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...))
-
- clos = nod(OADDR, clos, nil)
- clos.Esc = clo.Esc
-
- // Force type conversion from *struct to the func type.
- clos = convnop(clos, clo.Type)
-
- // non-escaping temp to use, if any.
- if x := prealloc[clo]; x != nil {
- if !types.Identical(typ, x.Type) {
- panic("closure type does not match order's assigned type")
- }
- clos.Left.Right = x
- delete(prealloc, clo)
- }
-
- return walkexpr(clos, init)
-}
-
-func typecheckpartialcall(fn *Node, sym *types.Sym) {
- switch fn.Op {
- case ODOTINTER, ODOTMETH:
- break
-
- default:
- Fatalf("invalid typecheckpartialcall")
- }
-
- // Create top-level function.
- xfunc := makepartialcall(fn, fn.Type, sym)
- fn.Func = xfunc.Func
- fn.Func.SetWrapper(true)
- fn.Right = newname(sym)
- fn.Op = OCALLPART
- fn.Type = xfunc.Type
-}
-
-// makepartialcall returns a DCLFUNC node representing the wrapper function (*-fm) needed
-// for partial calls.
-func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
- rcvrtype := fn.Left.Type
- sym := methodSymSuffix(rcvrtype, meth, "-fm")
-
- if sym.Uniq() {
- return asNode(sym.Def)
- }
- sym.SetUniq(true)
-
- savecurfn := Curfn
- saveLineNo := lineno
- Curfn = nil
-
- // Set line number equal to the line number where the method is declared.
- var m *types.Field
- if lookdot0(meth, rcvrtype, &m, false) == 1 && m.Pos.IsKnown() {
- lineno = m.Pos
- }
- // Note: !m.Pos.IsKnown() happens for method expressions where
- // the method is implicitly declared. The Error method of the
- // built-in error type is one such method. We leave the line
- // number at the use of the method expression in this
- // case. See issue 29389.
-
- tfn := nod(OTFUNC, nil, nil)
- tfn.List.Set(structargs(t0.Params(), true))
- tfn.Rlist.Set(structargs(t0.Results(), false))
-
- xfunc := dclfunc(sym, tfn)
- xfunc.Func.SetDupok(true)
- xfunc.Func.SetNeedctxt(true)
-
- tfn.Type.SetPkg(t0.Pkg())
-
- // Declare and initialize variable holding receiver.
-
- cv := nod(OCLOSUREVAR, nil, nil)
- cv.Type = rcvrtype
- cv.Xoffset = Rnd(int64(Widthptr), int64(cv.Type.Align))
-
- ptr := newname(lookup(".this"))
- declare(ptr, PAUTO)
- ptr.Name.SetUsed(true)
- var body []*Node
- if rcvrtype.IsPtr() || rcvrtype.IsInterface() {
- ptr.Type = rcvrtype
- body = append(body, nod(OAS, ptr, cv))
- } else {
- ptr.Type = types.NewPtr(rcvrtype)
- body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil)))
- }
-
- call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil)
- call.List.Set(paramNnames(tfn.Type))
- call.SetIsDDD(tfn.Type.IsVariadic())
- if t0.NumResults() != 0 {
- n := nod(ORETURN, nil, nil)
- n.List.Set1(call)
- call = n
- }
- body = append(body, call)
-
- xfunc.Nbody.Set(body)
- funcbody()
-
- xfunc = typecheck(xfunc, ctxStmt)
- // Need to typecheck the body of the just-generated wrapper.
- // typecheckslice() requires that Curfn is set when processing an ORETURN.
- Curfn = xfunc
- typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
- sym.Def = asTypesNode(xfunc)
- xtop = append(xtop, xfunc)
- Curfn = savecurfn
- lineno = saveLineNo
-
- return xfunc
-}
-
-// partialCallType returns the struct type used to hold all the information
-// needed in the closure for n (n must be a OCALLPART node).
-// The address of a variable of the returned type can be cast to a func.
-func partialCallType(n *Node) *types.Type {
- t := tostruct([]*Node{
- namedfield("F", types.Types[TUINTPTR]),
- namedfield("R", n.Left.Type),
- })
- t.SetNoalg(true)
- return t
-}
-
-func walkpartialcall(n *Node, init *Nodes) *Node {
- // Create closure in the form of a composite literal.
- // For x.M with receiver (x) type T, the generated code looks like:
- //
- // clos = &struct{F uintptr; R T}{T.M·f, x}
- //
- // Like walkclosure above.
-
- if n.Left.Type.IsInterface() {
- // Trigger panic for method on nil interface now.
- // Otherwise it happens in the wrapper and is confusing.
- n.Left = cheapexpr(n.Left, init)
- n.Left = walkexpr(n.Left, nil)
-
- tab := nod(OITAB, n.Left, nil)
- tab = typecheck(tab, ctxExpr)
-
- c := nod(OCHECKNIL, tab, nil)
- c.SetTypecheck(1)
- init.Append(c)
- }
-
- typ := partialCallType(n)
-
- clos := nod(OCOMPLIT, nil, typenod(typ))
- clos.Esc = n.Esc
- clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left)
-
- clos = nod(OADDR, clos, nil)
- clos.Esc = n.Esc
-
- // Force type conversion from *struct to the func type.
- clos = convnop(clos, n.Type)
-
- // non-escaping temp to use, if any.
- if x := prealloc[n]; x != nil {
- if !types.Identical(typ, x.Type) {
- panic("partial call type does not match order's assigned type")
- }
- clos.Left.Right = x
- delete(prealloc, n)
- }
-
- return walkexpr(clos, init)
-}
-
-// callpartMethod returns the *types.Field representing the method
-// referenced by method value n.
-func callpartMethod(n *Node) *types.Field {
- if n.Op != OCALLPART {
- Fatalf("expected OCALLPART, got %v", n)
- }
-
- // TODO(mdempsky): Optimize this. If necessary,
- // makepartialcall could save m for us somewhere.
- var m *types.Field
- if lookdot0(n.Right.Sym, n.Left.Type, &m, false) != 1 {
- Fatalf("failed to find field for OCALLPART")
- }
-
- return m
-}
diff --git a/src/cmd/compile/internal/gc/compile.go b/src/cmd/compile/internal/gc/compile.go
new file mode 100644
index 0000000000000..ba67c58c45587
--- /dev/null
+++ b/src/cmd/compile/internal/gc/compile.go
@@ -0,0 +1,147 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gc
+
+import (
+ "internal/race"
+ "math/rand"
+ "sort"
+ "sync"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/liveness"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/walk"
+)
+
+// "Portable" code generation.
+
+var (
+ compilequeue []*ir.Func // functions waiting to be compiled
+)
+
+func enqueueFunc(fn *ir.Func) {
+ if ir.CurFunc != nil {
+ base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
+ }
+
+ if ir.FuncName(fn) == "_" {
+ // Skip compiling blank functions.
+ // Frontend already reported any spec-mandated errors (#29870).
+ return
+ }
+
+ 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.
+ ssagen.InitLSym(fn, false)
+ liveness.WriteFuncMap(fn)
+ return
+ }
+
+ 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)
+}
+
+// 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)
+
+ // Calculate parameter offsets.
+ types.CalcSize(fn.Type())
+
+ typecheck.DeclContext = ir.PAUTO
+ ir.CurFunc = fn
+ walk.Walk(fn)
+ ir.CurFunc = nil // enforce no further uses of CurFunc
+ typecheck.DeclContext = ir.PEXTERN
+}
+
+// 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 {
+ 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]
+ }
+ 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
+ }
+
+ 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()
+ }()
+ }
+
+ types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
+ base.Ctxt.InParallel = true
+ for _, fn := range compilequeue {
+ asyncCompile(fn)
+ }
+ compilequeue = nil
+ wg.Wait()
+ base.Ctxt.InParallel = false
+ types.CalcSizeDisabled = false
+}
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
deleted file mode 100644
index b92c8d66b5afb..0000000000000
--- a/src/cmd/compile/internal/gc/const.go
+++ /dev/null
@@ -1,1323 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/src"
- "fmt"
- "math/big"
- "strings"
-)
-
-// Ctype describes the constant kind of an "ideal" (untyped) constant.
-type Ctype uint8
-
-const (
- CTxxx Ctype = iota
-
- CTINT
- CTRUNE
- CTFLT
- CTCPLX
- CTSTR
- CTBOOL
- CTNIL
-)
-
-type Val struct {
- // U contains one of:
- // bool bool when Ctype() == CTBOOL
- // *Mpint int when Ctype() == CTINT, rune when Ctype() == CTRUNE
- // *Mpflt float when Ctype() == CTFLT
- // *Mpcplx pair of floats when Ctype() == CTCPLX
- // string string when Ctype() == CTSTR
- // *Nilval when Ctype() == CTNIL
- U interface{}
-}
-
-func (v Val) Ctype() Ctype {
- switch x := v.U.(type) {
- default:
- Fatalf("unexpected Ctype for %T", v.U)
- panic("unreachable")
- case nil:
- return CTxxx
- case *NilVal:
- return CTNIL
- case bool:
- return CTBOOL
- case *Mpint:
- if x.Rune {
- return CTRUNE
- }
- return CTINT
- case *Mpflt:
- return CTFLT
- case *Mpcplx:
- return CTCPLX
- case string:
- return CTSTR
- }
-}
-
-func eqval(a, b Val) bool {
- if a.Ctype() != b.Ctype() {
- return false
- }
- switch x := a.U.(type) {
- default:
- Fatalf("unexpected Ctype for %T", a.U)
- panic("unreachable")
- case *NilVal:
- return true
- case bool:
- y := b.U.(bool)
- return x == y
- case *Mpint:
- y := b.U.(*Mpint)
- return x.Cmp(y) == 0
- case *Mpflt:
- y := b.U.(*Mpflt)
- return x.Cmp(y) == 0
- case *Mpcplx:
- y := b.U.(*Mpcplx)
- return x.Real.Cmp(&y.Real) == 0 && x.Imag.Cmp(&y.Imag) == 0
- case string:
- y := b.U.(string)
- return x == y
- }
-}
-
-// Interface returns the constant value stored in v as an interface{}.
-// It returns int64s for ints and runes, float64s for floats,
-// complex128s for complex values, and nil for constant nils.
-func (v Val) Interface() interface{} {
- switch x := v.U.(type) {
- default:
- Fatalf("unexpected Interface for %T", v.U)
- panic("unreachable")
- case *NilVal:
- return nil
- case bool, string:
- return x
- case *Mpint:
- return x.Int64()
- case *Mpflt:
- return x.Float64()
- case *Mpcplx:
- return complex(x.Real.Float64(), x.Imag.Float64())
- }
-}
-
-type NilVal struct{}
-
-// Int64Val returns n as an int64.
-// n must be an integer or rune constant.
-func (n *Node) Int64Val() int64 {
- if !Isconst(n, CTINT) {
- Fatalf("Int64Val(%v)", n)
- }
- return n.Val().U.(*Mpint).Int64()
-}
-
-// CanInt64 reports whether it is safe to call Int64Val() on n.
-func (n *Node) CanInt64() bool {
- if !Isconst(n, CTINT) {
- return false
- }
-
- // if the value inside n cannot be represented as an int64, the
- // return value of Int64 is undefined
- return n.Val().U.(*Mpint).CmpInt64(n.Int64Val()) == 0
-}
-
-// BoolVal returns n as a bool.
-// n must be a boolean constant.
-func (n *Node) BoolVal() bool {
- if !Isconst(n, CTBOOL) {
- Fatalf("BoolVal(%v)", n)
- }
- return n.Val().U.(bool)
-}
-
-// StringVal returns the value of a literal string Node as a string.
-// n must be a string constant.
-func (n *Node) StringVal() string {
- if !Isconst(n, CTSTR) {
- Fatalf("StringVal(%v)", n)
- }
- return n.Val().U.(string)
-}
-
-// truncate float literal fv to 32-bit or 64-bit precision
-// according to type; return truncated value.
-func truncfltlit(oldv *Mpflt, t *types.Type) *Mpflt {
- if t == nil {
- return oldv
- }
-
- if overflow(Val{oldv}, t) {
- // If there was overflow, simply continuing would set the
- // value to Inf which in turn would lead to spurious follow-on
- // errors. Avoid this by returning the existing value.
- return oldv
- }
-
- fv := newMpflt()
-
- // convert large precision literal floating
- // into limited precision (float64 or float32)
- switch t.Etype {
- case types.TFLOAT32:
- fv.SetFloat64(oldv.Float32())
- case types.TFLOAT64:
- fv.SetFloat64(oldv.Float64())
- default:
- Fatalf("truncfltlit: unexpected Etype %v", t.Etype)
- }
-
- return fv
-}
-
-// truncate Real and Imag parts of Mpcplx to 32-bit or 64-bit
-// precision, according to type; return truncated value. In case of
-// overflow, calls yyerror but does not truncate the input value.
-func trunccmplxlit(oldv *Mpcplx, t *types.Type) *Mpcplx {
- if t == nil {
- return oldv
- }
-
- if overflow(Val{oldv}, t) {
- // If there was overflow, simply continuing would set the
- // value to Inf which in turn would lead to spurious follow-on
- // errors. Avoid this by returning the existing value.
- return oldv
- }
-
- cv := newMpcmplx()
-
- switch t.Etype {
- case types.TCOMPLEX64:
- cv.Real.SetFloat64(oldv.Real.Float32())
- cv.Imag.SetFloat64(oldv.Imag.Float32())
- case types.TCOMPLEX128:
- cv.Real.SetFloat64(oldv.Real.Float64())
- cv.Imag.SetFloat64(oldv.Imag.Float64())
- default:
- Fatalf("trunccplxlit: unexpected Etype %v", t.Etype)
- }
-
- return cv
-}
-
-// TODO(mdempsky): Replace these with better APIs.
-func convlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) }
-func defaultlit(n *Node, t *types.Type) *Node { return convlit1(n, t, false, nil) }
-
-// convlit1 converts an untyped expression n to type t. If n already
-// has a type, convlit1 has no effect.
-//
-// For explicit conversions, t must be non-nil, and integer-to-string
-// conversions are allowed.
-//
-// For implicit conversions (e.g., assignments), t may be nil; if so,
-// n is converted to its default type.
-//
-// If there's an error converting n to t, context is used in the error
-// message.
-func convlit1(n *Node, t *types.Type, explicit bool, context func() string) *Node {
- if explicit && t == nil {
- Fatalf("explicit conversion missing type")
- }
- if t != nil && t.IsUntyped() {
- Fatalf("bad conversion to untyped: %v", t)
- }
-
- if n == nil || n.Type == nil {
- // Allow sloppy callers.
- return n
- }
- if !n.Type.IsUntyped() {
- // Already typed; nothing to do.
- return n
- }
-
- if n.Op == OLITERAL {
- // Can't always set n.Type directly on OLITERAL nodes.
- // See discussion on CL 20813.
- n = n.rawcopy()
- }
-
- // Nil is technically not a constant, so handle it specially.
- if n.Type.Etype == TNIL {
- if t == nil {
- yyerror("use of untyped nil")
- n.SetDiag(true)
- n.Type = nil
- return n
- }
-
- if !t.HasNil() {
- // Leave for caller to handle.
- return n
- }
-
- n.Type = t
- return n
- }
-
- if t == nil || !okforconst[t.Etype] {
- t = defaultType(n.Type)
- }
-
- switch n.Op {
- default:
- Fatalf("unexpected untyped expression: %v", n)
-
- case OLITERAL:
- v := convertVal(n.Val(), t, explicit)
- if v.U == nil {
- break
- }
- n.SetVal(v)
- n.Type = t
- return n
-
- case OPLUS, ONEG, OBITNOT, ONOT, OREAL, OIMAG:
- ot := operandType(n.Op, t)
- if ot == nil {
- n = defaultlit(n, nil)
- break
- }
-
- n.Left = convlit(n.Left, ot)
- if n.Left.Type == nil {
- n.Type = nil
- return n
- }
- n.Type = t
- return n
-
- case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND, OCOMPLEX:
- ot := operandType(n.Op, t)
- if ot == nil {
- n = defaultlit(n, nil)
- break
- }
-
- n.Left = convlit(n.Left, ot)
- n.Right = convlit(n.Right, ot)
- if n.Left.Type == nil || n.Right.Type == nil {
- n.Type = nil
- return n
- }
- if !types.Identical(n.Left.Type, n.Right.Type) {
- yyerror("invalid operation: %v (mismatched types %v and %v)", n, n.Left.Type, n.Right.Type)
- n.Type = nil
- return n
- }
-
- n.Type = t
- return n
-
- case OEQ, ONE, OLT, OLE, OGT, OGE:
- if !t.IsBoolean() {
- break
- }
- n.Type = t
- return n
-
- case OLSH, ORSH:
- n.Left = convlit1(n.Left, t, explicit, nil)
- n.Type = n.Left.Type
- if n.Type != nil && !n.Type.IsInteger() {
- yyerror("invalid operation: %v (shift of type %v)", n, n.Type)
- n.Type = nil
- }
- return n
- }
-
- if !n.Diag() {
- if !t.Broke() {
- if explicit {
- yyerror("cannot convert %L to type %v", n, t)
- } else if context != nil {
- yyerror("cannot use %L as type %v in %s", n, t, context())
- } else {
- yyerror("cannot use %L as type %v", n, t)
- }
- }
- n.SetDiag(true)
- }
- n.Type = nil
- return n
-}
-
-func operandType(op Op, t *types.Type) *types.Type {
- switch op {
- case OCOMPLEX:
- if t.IsComplex() {
- return floatForComplex(t)
- }
- case OREAL, OIMAG:
- if t.IsFloat() {
- return complexForFloat(t)
- }
- default:
- if okfor[op][t.Etype] {
- return t
- }
- }
- return nil
-}
-
-// convertVal converts v into a representation appropriate for t. If
-// no such representation exists, it returns Val{} instead.
-//
-// If explicit is true, then conversions from integer to string are
-// also allowed.
-func convertVal(v Val, t *types.Type, explicit bool) Val {
- switch ct := v.Ctype(); ct {
- case CTBOOL:
- if t.IsBoolean() {
- return v
- }
-
- case CTSTR:
- if t.IsString() {
- return v
- }
-
- case CTINT, CTRUNE:
- if explicit && t.IsString() {
- return tostr(v)
- }
- fallthrough
- case CTFLT, CTCPLX:
- switch {
- case t.IsInteger():
- v = toint(v)
- overflow(v, t)
- return v
- case t.IsFloat():
- v = toflt(v)
- v = Val{truncfltlit(v.U.(*Mpflt), t)}
- return v
- case t.IsComplex():
- v = tocplx(v)
- v = Val{trunccmplxlit(v.U.(*Mpcplx), t)}
- return v
- }
- }
-
- return Val{}
-}
-
-func tocplx(v Val) Val {
- switch u := v.U.(type) {
- case *Mpint:
- c := newMpcmplx()
- c.Real.SetInt(u)
- c.Imag.SetFloat64(0.0)
- v.U = c
-
- case *Mpflt:
- c := newMpcmplx()
- c.Real.Set(u)
- c.Imag.SetFloat64(0.0)
- v.U = c
- }
-
- return v
-}
-
-func toflt(v Val) Val {
- switch u := v.U.(type) {
- case *Mpint:
- f := newMpflt()
- f.SetInt(u)
- v.U = f
-
- case *Mpcplx:
- f := newMpflt()
- f.Set(&u.Real)
- if u.Imag.CmpFloat64(0) != 0 {
- yyerror("constant %v truncated to real", u.GoString())
- }
- v.U = f
- }
-
- return v
-}
-
-func toint(v Val) Val {
- switch u := v.U.(type) {
- case *Mpint:
- if u.Rune {
- i := new(Mpint)
- i.Set(u)
- v.U = i
- }
-
- case *Mpflt:
- i := new(Mpint)
- if !i.SetFloat(u) {
- if i.checkOverflow(0) {
- yyerror("integer too large")
- } else {
- // The value of u cannot be represented as an integer;
- // so we need to print an error message.
- // Unfortunately some float values cannot be
- // reasonably formatted for inclusion in an error
- // message (example: 1 + 1e-100), so first we try to
- // format the float; if the truncation resulted in
- // something that looks like an integer we omit the
- // value from the error message.
- // (See issue #11371).
- var t big.Float
- t.Parse(u.GoString(), 10)
- if t.IsInt() {
- yyerror("constant truncated to integer")
- } else {
- yyerror("constant %v truncated to integer", u.GoString())
- }
- }
- }
- v.U = i
-
- case *Mpcplx:
- i := new(Mpint)
- if !i.SetFloat(&u.Real) || u.Imag.CmpFloat64(0) != 0 {
- yyerror("constant %v truncated to integer", u.GoString())
- }
-
- v.U = i
- }
-
- return v
-}
-
-func doesoverflow(v Val, t *types.Type) bool {
- switch u := v.U.(type) {
- case *Mpint:
- if !t.IsInteger() {
- Fatalf("overflow: %v integer constant", t)
- }
- return u.Cmp(minintval[t.Etype]) < 0 || u.Cmp(maxintval[t.Etype]) > 0
-
- case *Mpflt:
- if !t.IsFloat() {
- Fatalf("overflow: %v floating-point constant", t)
- }
- return u.Cmp(minfltval[t.Etype]) <= 0 || u.Cmp(maxfltval[t.Etype]) >= 0
-
- case *Mpcplx:
- if !t.IsComplex() {
- Fatalf("overflow: %v complex constant", t)
- }
- return u.Real.Cmp(minfltval[t.Etype]) <= 0 || u.Real.Cmp(maxfltval[t.Etype]) >= 0 ||
- u.Imag.Cmp(minfltval[t.Etype]) <= 0 || u.Imag.Cmp(maxfltval[t.Etype]) >= 0
- }
-
- return false
-}
-
-func overflow(v Val, t *types.Type) bool {
- // v has already been converted
- // to appropriate form for t.
- if t == nil || t.Etype == TIDEAL {
- return false
- }
-
- // Only uintptrs may be converted to pointers, which cannot overflow.
- if t.IsPtr() || t.IsUnsafePtr() {
- return false
- }
-
- if doesoverflow(v, t) {
- yyerror("constant %v overflows %v", v, t)
- return true
- }
-
- return false
-
-}
-
-func tostr(v Val) Val {
- switch u := v.U.(type) {
- case *Mpint:
- var r rune = 0xFFFD
- if u.Cmp(minintval[TINT32]) >= 0 && u.Cmp(maxintval[TINT32]) <= 0 {
- r = rune(u.Int64())
- }
- v.U = string(r)
- }
-
- return v
-}
-
-func consttype(n *Node) Ctype {
- if n == nil || n.Op != OLITERAL {
- return CTxxx
- }
- return n.Val().Ctype()
-}
-
-func Isconst(n *Node, ct Ctype) bool {
- t := consttype(n)
-
- // If the caller is asking for CTINT, allow CTRUNE too.
- // Makes life easier for back ends.
- return t == ct || (ct == CTINT && t == CTRUNE)
-}
-
-// evconst rewrites constant expressions into OLITERAL nodes.
-func evconst(n *Node) {
- nl, nr := n.Left, n.Right
-
- // Pick off just the opcodes that can be constant evaluated.
- switch op := n.Op; op {
- case OPLUS, ONEG, OBITNOT, ONOT:
- if nl.Op == OLITERAL {
- setconst(n, unaryOp(op, nl.Val(), n.Type))
- }
-
- case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND:
- if nl.Op == OLITERAL && nr.Op == OLITERAL {
- setconst(n, binaryOp(nl.Val(), op, nr.Val()))
- }
-
- case OEQ, ONE, OLT, OLE, OGT, OGE:
- if nl.Op == OLITERAL && nr.Op == OLITERAL {
- setboolconst(n, compareOp(nl.Val(), op, nr.Val()))
- }
-
- case OLSH, ORSH:
- if nl.Op == OLITERAL && nr.Op == OLITERAL {
- setconst(n, shiftOp(nl.Val(), op, nr.Val()))
- }
-
- case OCONV, ORUNESTR:
- if okforconst[n.Type.Etype] && nl.Op == OLITERAL {
- setconst(n, convertVal(nl.Val(), n.Type, true))
- }
-
- case OCONVNOP:
- if okforconst[n.Type.Etype] && nl.Op == OLITERAL {
- // set so n.Orig gets OCONV instead of OCONVNOP
- n.Op = OCONV
- setconst(n, nl.Val())
- }
-
- case OADDSTR:
- // Merge adjacent constants in the argument list.
- s := n.List.Slice()
- for i1 := 0; i1 < len(s); i1++ {
- if Isconst(s[i1], CTSTR) && i1+1 < len(s) && Isconst(s[i1+1], CTSTR) {
- // merge from i1 up to but not including i2
- var strs []string
- i2 := i1
- for i2 < len(s) && Isconst(s[i2], CTSTR) {
- strs = append(strs, s[i2].StringVal())
- i2++
- }
-
- nl := *s[i1]
- nl.Orig = &nl
- nl.SetVal(Val{strings.Join(strs, "")})
- s[i1] = &nl
- s = append(s[:i1+1], s[i2:]...)
- }
- }
-
- if len(s) == 1 && Isconst(s[0], CTSTR) {
- n.Op = OLITERAL
- n.SetVal(s[0].Val())
- } else {
- n.List.Set(s)
- }
-
- case OCAP, OLEN:
- switch nl.Type.Etype {
- case TSTRING:
- if Isconst(nl, CTSTR) {
- setintconst(n, int64(len(nl.StringVal())))
- }
- case TARRAY:
- if !hascallchan(nl) {
- setintconst(n, nl.Type.NumElem())
- }
- }
-
- case OALIGNOF, OOFFSETOF, OSIZEOF:
- setintconst(n, evalunsafe(n))
-
- case OREAL, OIMAG:
- if nl.Op == OLITERAL {
- var re, im *Mpflt
- switch u := nl.Val().U.(type) {
- case *Mpint:
- re = newMpflt()
- re.SetInt(u)
- // im = 0
- case *Mpflt:
- re = u
- // im = 0
- case *Mpcplx:
- re = &u.Real
- im = &u.Imag
- default:
- Fatalf("impossible")
- }
- if n.Op == OIMAG {
- if im == nil {
- im = newMpflt()
- }
- re = im
- }
- setconst(n, Val{re})
- }
-
- case OCOMPLEX:
- if nl.Op == OLITERAL && nr.Op == OLITERAL {
- // make it a complex literal
- c := newMpcmplx()
- c.Real.Set(toflt(nl.Val()).U.(*Mpflt))
- c.Imag.Set(toflt(nr.Val()).U.(*Mpflt))
- setconst(n, Val{c})
- }
- }
-}
-
-func match(x, y Val) (Val, Val) {
- switch {
- case x.Ctype() == CTCPLX || y.Ctype() == CTCPLX:
- return tocplx(x), tocplx(y)
- case x.Ctype() == CTFLT || y.Ctype() == CTFLT:
- return toflt(x), toflt(y)
- }
-
- // Mixed int/rune are fine.
- return x, y
-}
-
-func compareOp(x Val, op Op, y Val) bool {
- x, y = match(x, y)
-
- switch x.Ctype() {
- case CTBOOL:
- x, y := x.U.(bool), y.U.(bool)
- switch op {
- case OEQ:
- return x == y
- case ONE:
- return x != y
- }
-
- case CTINT, CTRUNE:
- x, y := x.U.(*Mpint), y.U.(*Mpint)
- return cmpZero(x.Cmp(y), op)
-
- case CTFLT:
- x, y := x.U.(*Mpflt), y.U.(*Mpflt)
- return cmpZero(x.Cmp(y), op)
-
- case CTCPLX:
- x, y := x.U.(*Mpcplx), y.U.(*Mpcplx)
- eq := x.Real.Cmp(&y.Real) == 0 && x.Imag.Cmp(&y.Imag) == 0
- switch op {
- case OEQ:
- return eq
- case ONE:
- return !eq
- }
-
- case CTSTR:
- x, y := x.U.(string), y.U.(string)
- switch op {
- case OEQ:
- return x == y
- case ONE:
- return x != y
- case OLT:
- return x < y
- case OLE:
- return x <= y
- case OGT:
- return x > y
- case OGE:
- return x >= y
- }
- }
-
- Fatalf("compareOp: bad comparison: %v %v %v", x, op, y)
- panic("unreachable")
-}
-
-func cmpZero(x int, op Op) bool {
- switch op {
- case OEQ:
- return x == 0
- case ONE:
- return x != 0
- case OLT:
- return x < 0
- case OLE:
- return x <= 0
- case OGT:
- return x > 0
- case OGE:
- return x >= 0
- }
-
- Fatalf("cmpZero: want comparison operator, got %v", op)
- panic("unreachable")
-}
-
-func binaryOp(x Val, op Op, y Val) Val {
- x, y = match(x, y)
-
-Outer:
- switch x.Ctype() {
- case CTBOOL:
- x, y := x.U.(bool), y.U.(bool)
- switch op {
- case OANDAND:
- return Val{U: x && y}
- case OOROR:
- return Val{U: x || y}
- }
-
- case CTINT, CTRUNE:
- x, y := x.U.(*Mpint), y.U.(*Mpint)
-
- u := new(Mpint)
- u.Rune = x.Rune || y.Rune
- u.Set(x)
- switch op {
- case OADD:
- u.Add(y)
- case OSUB:
- u.Sub(y)
- case OMUL:
- u.Mul(y)
- case ODIV:
- if y.CmpInt64(0) == 0 {
- yyerror("division by zero")
- return Val{}
- }
- u.Quo(y)
- case OMOD:
- if y.CmpInt64(0) == 0 {
- yyerror("division by zero")
- return Val{}
- }
- u.Rem(y)
- case OOR:
- u.Or(y)
- case OAND:
- u.And(y)
- case OANDNOT:
- u.AndNot(y)
- case OXOR:
- u.Xor(y)
- default:
- break Outer
- }
- return Val{U: u}
-
- case CTFLT:
- x, y := x.U.(*Mpflt), y.U.(*Mpflt)
-
- u := newMpflt()
- u.Set(x)
- switch op {
- case OADD:
- u.Add(y)
- case OSUB:
- u.Sub(y)
- case OMUL:
- u.Mul(y)
- case ODIV:
- if y.CmpFloat64(0) == 0 {
- yyerror("division by zero")
- return Val{}
- }
- u.Quo(y)
- default:
- break Outer
- }
- return Val{U: u}
-
- case CTCPLX:
- x, y := x.U.(*Mpcplx), y.U.(*Mpcplx)
-
- u := newMpcmplx()
- u.Real.Set(&x.Real)
- u.Imag.Set(&x.Imag)
- switch op {
- case OADD:
- u.Real.Add(&y.Real)
- u.Imag.Add(&y.Imag)
- case OSUB:
- u.Real.Sub(&y.Real)
- u.Imag.Sub(&y.Imag)
- case OMUL:
- u.Mul(y)
- case ODIV:
- if !u.Div(y) {
- yyerror("complex division by zero")
- return Val{}
- }
- default:
- break Outer
- }
- return Val{U: u}
- }
-
- Fatalf("binaryOp: bad operation: %v %v %v", x, op, y)
- panic("unreachable")
-}
-
-func unaryOp(op Op, x Val, t *types.Type) Val {
- switch op {
- case OPLUS:
- switch x.Ctype() {
- case CTINT, CTRUNE, CTFLT, CTCPLX:
- return x
- }
-
- case ONEG:
- switch x.Ctype() {
- case CTINT, CTRUNE:
- x := x.U.(*Mpint)
- u := new(Mpint)
- u.Rune = x.Rune
- u.Set(x)
- u.Neg()
- return Val{U: u}
-
- case CTFLT:
- x := x.U.(*Mpflt)
- u := newMpflt()
- u.Set(x)
- u.Neg()
- return Val{U: u}
-
- case CTCPLX:
- x := x.U.(*Mpcplx)
- u := newMpcmplx()
- u.Real.Set(&x.Real)
- u.Imag.Set(&x.Imag)
- u.Real.Neg()
- u.Imag.Neg()
- return Val{U: u}
- }
-
- case OBITNOT:
- switch x.Ctype() {
- case CTINT, CTRUNE:
- x := x.U.(*Mpint)
-
- u := new(Mpint)
- u.Rune = x.Rune
- if t.IsSigned() || t.IsUntyped() {
- // Signed values change sign.
- u.SetInt64(-1)
- } else {
- // Unsigned values invert their bits.
- u.Set(maxintval[t.Etype])
- }
- u.Xor(x)
- return Val{U: u}
- }
-
- case ONOT:
- return Val{U: !x.U.(bool)}
- }
-
- Fatalf("unaryOp: bad operation: %v %v", op, x)
- panic("unreachable")
-}
-
-func shiftOp(x Val, op Op, y Val) Val {
- if x.Ctype() != CTRUNE {
- x = toint(x)
- }
- y = toint(y)
-
- u := new(Mpint)
- u.Set(x.U.(*Mpint))
- u.Rune = x.U.(*Mpint).Rune
- switch op {
- case OLSH:
- u.Lsh(y.U.(*Mpint))
- case ORSH:
- u.Rsh(y.U.(*Mpint))
- default:
- Fatalf("shiftOp: bad operator: %v", op)
- panic("unreachable")
- }
- return Val{U: u}
-}
-
-// setconst rewrites n as an OLITERAL with value v.
-func setconst(n *Node, v Val) {
- // If constant folding failed, mark n as broken and give up.
- if v.U == nil {
- n.Type = nil
- return
- }
-
- // Ensure n.Orig still points to a semantically-equivalent
- // expression after we rewrite n into a constant.
- if n.Orig == n {
- n.Orig = n.sepcopy()
- }
-
- *n = Node{
- Op: OLITERAL,
- Pos: n.Pos,
- Orig: n.Orig,
- Type: n.Type,
- Xoffset: BADWIDTH,
- }
- n.SetVal(v)
- if vt := idealType(v.Ctype()); n.Type.IsUntyped() && n.Type != vt {
- Fatalf("untyped type mismatch, have: %v, want: %v", n.Type, vt)
- }
-
- // Check range.
- lno := setlineno(n)
- overflow(v, n.Type)
- lineno = lno
-
- if !n.Type.IsUntyped() {
- switch v.Ctype() {
- // Truncate precision for non-ideal float.
- case CTFLT:
- n.SetVal(Val{truncfltlit(v.U.(*Mpflt), n.Type)})
- // Truncate precision for non-ideal complex.
- case CTCPLX:
- n.SetVal(Val{trunccmplxlit(v.U.(*Mpcplx), n.Type)})
- }
- }
-}
-
-func setboolconst(n *Node, v bool) {
- setconst(n, Val{U: v})
-}
-
-func setintconst(n *Node, v int64) {
- u := new(Mpint)
- u.SetInt64(v)
- setconst(n, Val{u})
-}
-
-// nodlit returns a new untyped constant with value v.
-func nodlit(v Val) *Node {
- n := nod(OLITERAL, nil, nil)
- n.SetVal(v)
- n.Type = idealType(v.Ctype())
- return n
-}
-
-func idealType(ct Ctype) *types.Type {
- switch ct {
- case CTSTR:
- return types.UntypedString
- case CTBOOL:
- return types.UntypedBool
- case CTINT:
- return types.UntypedInt
- case CTRUNE:
- return types.UntypedRune
- case CTFLT:
- return types.UntypedFloat
- case CTCPLX:
- return types.UntypedComplex
- case CTNIL:
- return types.Types[TNIL]
- }
- Fatalf("unexpected Ctype: %v", ct)
- return nil
-}
-
-// defaultlit on both nodes simultaneously;
-// if they're both ideal going in they better
-// get the same type going out.
-// force means must assign concrete (non-ideal) type.
-// The results of defaultlit2 MUST be assigned back to l and r, e.g.
-// n.Left, n.Right = defaultlit2(n.Left, n.Right, force)
-func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) {
- if l.Type == nil || r.Type == nil {
- return l, r
- }
- if !l.Type.IsUntyped() {
- r = convlit(r, l.Type)
- return l, r
- }
-
- if !r.Type.IsUntyped() {
- l = convlit(l, r.Type)
- return l, r
- }
-
- if !force {
- return l, r
- }
-
- // Can't mix bool with non-bool, string with non-string, or nil with anything (untyped).
- if l.Type.IsBoolean() != r.Type.IsBoolean() {
- return l, r
- }
- if l.Type.IsString() != r.Type.IsString() {
- return l, r
- }
- if l.isNil() || r.isNil() {
- return l, r
- }
-
- t := defaultType(mixUntyped(l.Type, r.Type))
- l = convlit(l, t)
- r = convlit(r, t)
- return l, r
-}
-
-func ctype(t *types.Type) Ctype {
- switch t {
- case types.UntypedBool:
- return CTBOOL
- case types.UntypedString:
- return CTSTR
- case types.UntypedInt:
- return CTINT
- case types.UntypedRune:
- return CTRUNE
- case types.UntypedFloat:
- return CTFLT
- case types.UntypedComplex:
- return CTCPLX
- }
- Fatalf("bad type %v", t)
- panic("unreachable")
-}
-
-func mixUntyped(t1, t2 *types.Type) *types.Type {
- t := t1
- if ctype(t2) > ctype(t1) {
- t = t2
- }
- return t
-}
-
-func defaultType(t *types.Type) *types.Type {
- if !t.IsUntyped() || t.Etype == TNIL {
- return t
- }
-
- switch t {
- case types.UntypedBool:
- return types.Types[TBOOL]
- case types.UntypedString:
- return types.Types[TSTRING]
- case types.UntypedInt:
- return types.Types[TINT]
- case types.UntypedRune:
- return types.Runetype
- case types.UntypedFloat:
- return types.Types[TFLOAT64]
- case types.UntypedComplex:
- return types.Types[TCOMPLEX128]
- }
-
- Fatalf("bad type %v", t)
- return nil
-}
-
-func smallintconst(n *Node) bool {
- if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil {
- switch simtype[n.Type.Etype] {
- case TINT8,
- TUINT8,
- TINT16,
- TUINT16,
- TINT32,
- TUINT32,
- TBOOL:
- return true
-
- case TIDEAL, TINT64, TUINT64, TPTR:
- v, ok := n.Val().U.(*Mpint)
- if ok && v.Cmp(minintval[TINT32]) >= 0 && v.Cmp(maxintval[TINT32]) <= 0 {
- return true
- }
- }
- }
-
- return false
-}
-
-// indexconst checks if Node n contains a constant expression
-// representable as a non-negative int and returns its value.
-// If n is not a constant expression, not representable as an
-// integer, or negative, it returns -1. If n is too large, it
-// returns -2.
-func indexconst(n *Node) int64 {
- if n.Op != OLITERAL {
- return -1
- }
-
- v := toint(n.Val()) // toint returns argument unchanged if not representable as an *Mpint
- vi, ok := v.U.(*Mpint)
- if !ok || vi.CmpInt64(0) < 0 {
- return -1
- }
- if vi.Cmp(maxintval[TINT]) > 0 {
- return -2
- }
-
- return vi.Int64()
-}
-
-// isGoConst reports whether n is a Go language constant (as opposed to a
-// compile-time constant).
-//
-// Expressions derived from nil, like string([]byte(nil)), while they
-// may be known at compile time, are not Go language constants.
-func (n *Node) isGoConst() bool {
- return n.Op == OLITERAL && n.Val().Ctype() != CTNIL
-}
-
-func hascallchan(n *Node) bool {
- if n == nil {
- return false
- }
- switch n.Op {
- case OAPPEND,
- OCALL,
- OCALLFUNC,
- OCALLINTER,
- OCALLMETH,
- OCAP,
- OCLOSE,
- OCOMPLEX,
- OCOPY,
- ODELETE,
- OIMAG,
- OLEN,
- OMAKE,
- ONEW,
- OPANIC,
- OPRINT,
- OPRINTN,
- OREAL,
- ORECOVER,
- ORECV:
- return true
- }
-
- if hascallchan(n.Left) || hascallchan(n.Right) {
- return true
- }
- for _, n1 := range n.List.Slice() {
- if hascallchan(n1) {
- return true
- }
- }
- for _, n2 := range n.Rlist.Slice() {
- if hascallchan(n2) {
- return true
- }
- }
-
- return false
-}
-
-// A constSet represents a set of Go constant expressions.
-type constSet struct {
- m map[constSetKey]src.XPos
-}
-
-type constSetKey struct {
- typ *types.Type
- val interface{}
-}
-
-// add adds constant expression n to s. If a constant expression of
-// equal value and identical type has already been added, then add
-// reports an error about the duplicate value.
-//
-// pos provides position information for where expression n occurred
-// (in case n does not have its own position information). what and
-// where are used in the error message.
-//
-// n must not be an untyped constant.
-func (s *constSet) add(pos src.XPos, n *Node, what, where string) {
- if n.Op == OCONVIFACE && n.Implicit() {
- n = n.Left
- }
-
- if !n.isGoConst() {
- return
- }
- if n.Type.IsUntyped() {
- Fatalf("%v is untyped", n)
- }
-
- // Consts are only duplicates if they have the same value and
- // identical types.
- //
- // In general, we have to use types.Identical to test type
- // identity, because == gives false negatives for anonymous
- // types and the byte/uint8 and rune/int32 builtin type
- // aliases. However, this is not a problem here, because
- // constant expressions are always untyped or have a named
- // type, and we explicitly handle the builtin type aliases
- // below.
- //
- // This approach may need to be revisited though if we fix
- // #21866 by treating all type aliases like byte/uint8 and
- // rune/int32.
-
- typ := n.Type
- switch typ {
- case types.Bytetype:
- typ = types.Types[TUINT8]
- case types.Runetype:
- typ = types.Types[TINT32]
- }
- k := constSetKey{typ, n.Val().Interface()}
-
- if hasUniquePos(n) {
- pos = n.Pos
- }
-
- if s.m == nil {
- s.m = make(map[constSetKey]src.XPos)
- }
-
- if prevPos, isDup := s.m[k]; isDup {
- yyerrorl(pos, "duplicate %s %s in %s\n\tprevious %s at %v",
- what, nodeAndVal(n), where,
- what, linestr(prevPos))
- } else {
- s.m[k] = pos
- }
-}
-
-// nodeAndVal reports both an expression and its constant value, if
-// the latter is non-obvious.
-//
-// TODO(mdempsky): This could probably be a fmt.go flag.
-func nodeAndVal(n *Node) string {
- show := n.String()
- val := n.Val().Interface()
- if s := fmt.Sprintf("%#v", val); show != s {
- show += " (value " + s + ")"
- }
- return show
-}
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
deleted file mode 100644
index 6e90eb4d65bcd..0000000000000
--- a/src/cmd/compile/internal/gc/dcl.go
+++ /dev/null
@@ -1,1185 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "bytes"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/src"
- "fmt"
- "strings"
-)
-
-// Declaration stack & operations
-
-var externdcl []*Node
-
-func testdclstack() {
- if !types.IsDclstackValid() {
- if nerrors != 0 {
- errorexit()
- }
- Fatalf("mark left on the dclstack")
- }
-}
-
-// redeclare emits a diagnostic about symbol s being redeclared at pos.
-func redeclare(pos src.XPos, s *types.Sym, where string) {
- if !s.Lastlineno.IsKnown() {
- pkg := s.Origpkg
- if pkg == nil {
- pkg = s.Pkg
- }
- yyerrorl(pos, "%v redeclared %s\n"+
- "\tprevious declaration during import %q", s, where, pkg.Path)
- } else {
- prevPos := s.Lastlineno
-
- // When an import and a declaration collide in separate files,
- // present the import as the "redeclared", because the declaration
- // is visible where the import is, but not vice versa.
- // See issue 4510.
- if s.Def == nil {
- pos, prevPos = prevPos, pos
- }
-
- yyerrorl(pos, "%v redeclared %s\n"+
- "\tprevious declaration at %v", s, where, linestr(prevPos))
- }
-}
-
-var vargen int
-
-// declare individual names - var, typ, const
-
-var declare_typegen int
-
-// declare records that Node n declares symbol n.Sym in the specified
-// declaration context.
-func declare(n *Node, ctxt Class) {
- if n.isBlank() {
- return
- }
-
- if n.Name == nil {
- // named OLITERAL needs Name; most OLITERALs don't.
- n.Name = new(Name)
- }
-
- s := n.Sym
-
- // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later.
- if !inimport && !typecheckok && s.Pkg != localpkg {
- yyerrorl(n.Pos, "cannot declare name %v", s)
- }
-
- gen := 0
- if ctxt == PEXTERN {
- if s.Name == "init" {
- yyerrorl(n.Pos, "cannot declare init - must be func")
- }
- if s.Name == "main" && s.Pkg.Name == "main" {
- yyerrorl(n.Pos, "cannot declare main - must be func")
- }
- externdcl = append(externdcl, n)
- } else {
- if Curfn == nil && ctxt == PAUTO {
- lineno = n.Pos
- Fatalf("automatic outside function")
- }
- if Curfn != nil && ctxt != PFUNC {
- Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
- }
- if n.Op == OTYPE {
- declare_typegen++
- gen = declare_typegen
- } else if n.Op == ONAME && ctxt == PAUTO && !strings.Contains(s.Name, "·") {
- vargen++
- gen = vargen
- }
- types.Pushdcl(s)
- n.Name.Curfn = Curfn
- }
-
- if ctxt == PAUTO {
- n.Xoffset = 0
- }
-
- if s.Block == types.Block {
- // functype will print errors about duplicate function arguments.
- // Don't repeat the error here.
- if ctxt != PPARAM && ctxt != PPARAMOUT {
- redeclare(n.Pos, s, "in this block")
- }
- }
-
- s.Block = types.Block
- s.Lastlineno = lineno
- s.Def = asTypesNode(n)
- n.Name.Vargen = int32(gen)
- n.SetClass(ctxt)
- if ctxt == PFUNC {
- n.Sym.SetFunc(true)
- }
-
- autoexport(n, ctxt)
-}
-
-func addvar(n *Node, t *types.Type, ctxt Class) {
- if n == nil || n.Sym == nil || (n.Op != ONAME && n.Op != ONONAME) || t == nil {
- Fatalf("addvar: n=%v t=%v nil", n, t)
- }
-
- n.Op = ONAME
- declare(n, ctxt)
- n.Type = t
-}
-
-// declare variables from grammar
-// new_name_list (type | [type] = expr_list)
-func variter(vl []*Node, t *Node, el []*Node) []*Node {
- var init []*Node
- doexpr := len(el) > 0
-
- if len(el) == 1 && len(vl) > 1 {
- e := el[0]
- as2 := nod(OAS2, nil, nil)
- as2.List.Set(vl)
- as2.Rlist.Set1(e)
- for _, v := range vl {
- v.Op = ONAME
- declare(v, dclcontext)
- v.Name.Param.Ntype = t
- v.Name.Defn = as2
- if Curfn != nil {
- init = append(init, nod(ODCL, v, nil))
- }
- }
-
- return append(init, as2)
- }
-
- nel := len(el)
- for _, v := range vl {
- var e *Node
- if doexpr {
- if len(el) == 0 {
- yyerror("assignment mismatch: %d variables but %d values", len(vl), nel)
- break
- }
- e = el[0]
- el = el[1:]
- }
-
- v.Op = ONAME
- declare(v, dclcontext)
- v.Name.Param.Ntype = t
-
- if e != nil || Curfn != nil || v.isBlank() {
- if Curfn != nil {
- init = append(init, nod(ODCL, v, nil))
- }
- e = nod(OAS, v, e)
- init = append(init, e)
- if e.Right != nil {
- v.Name.Defn = e
- }
- }
- }
-
- if len(el) != 0 {
- yyerror("assignment mismatch: %d variables but %d values", len(vl), nel)
- }
- return init
-}
-
-// newnoname returns a new ONONAME Node associated with symbol s.
-func newnoname(s *types.Sym) *Node {
- if s == nil {
- Fatalf("newnoname nil")
- }
- n := nod(ONONAME, nil, nil)
- n.Sym = s
- n.Xoffset = 0
- return n
-}
-
-// newfuncnamel generates a new name node for a function or method.
-// TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360.
-func newfuncnamel(pos src.XPos, s *types.Sym) *Node {
- n := newnamel(pos, s)
- n.Func = new(Func)
- n.Func.SetIsHiddenClosure(Curfn != nil)
- return n
-}
-
-// this generates a new name node for a name
-// being declared.
-func dclname(s *types.Sym) *Node {
- n := newname(s)
- n.Op = ONONAME // caller will correct it
- return n
-}
-
-func typenod(t *types.Type) *Node {
- return typenodl(src.NoXPos, t)
-}
-
-func typenodl(pos src.XPos, t *types.Type) *Node {
- // if we copied another type with *t = *u
- // then t->nod might be out of date, so
- // check t->nod->type too
- if asNode(t.Nod) == nil || asNode(t.Nod).Type != t {
- t.Nod = asTypesNode(nodl(pos, OTYPE, nil, nil))
- asNode(t.Nod).Type = t
- asNode(t.Nod).Sym = t.Sym
- }
-
- return asNode(t.Nod)
-}
-
-func anonfield(typ *types.Type) *Node {
- return symfield(nil, typ)
-}
-
-func namedfield(s string, typ *types.Type) *Node {
- return symfield(lookup(s), typ)
-}
-
-func symfield(s *types.Sym, typ *types.Type) *Node {
- n := nodSym(ODCLFIELD, nil, s)
- n.Type = typ
- return n
-}
-
-// oldname returns the Node that declares symbol s in the current scope.
-// If no such Node currently exists, an ONONAME Node is returned instead.
-// Automatically creates a new closure variable if the referenced symbol was
-// declared in a different (containing) function.
-func oldname(s *types.Sym) *Node {
- n := asNode(s.Def)
- if n == nil {
- // Maybe a top-level declaration will come along later to
- // define s. resolve will check s.Def again once all input
- // source has been processed.
- return newnoname(s)
- }
-
- if Curfn != nil && n.Op == ONAME && n.Name.Curfn != nil && n.Name.Curfn != Curfn {
- // Inner func is referring to var in outer func.
- //
- // 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.
- c := n.Name.Param.Innermost
- if c == nil || c.Name.Curfn != Curfn {
- // Do not have a closure var for the active closure yet; make one.
- c = newname(s)
- c.SetClass(PAUTOHEAP)
- c.Name.SetIsClosureVar(true)
- c.SetIsDDD(n.IsDDD())
- c.Name.Defn = n
-
- // Link into list of active closure variables.
- // Popped from list in func funcLit.
- c.Name.Param.Outer = n.Name.Param.Innermost
- n.Name.Param.Innermost = c
-
- Curfn.Func.Cvars.Append(c)
- }
-
- // return ref to closure var, not original
- return c
- }
-
- return n
-}
-
-// importName is like oldname, but it reports an error if sym is from another package and not exported.
-func importName(sym *types.Sym) *Node {
- n := oldname(sym)
- if !types.IsExported(sym.Name) && sym.Pkg != localpkg {
- n.SetDiag(true)
- yyerror("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
- }
- return n
-}
-
-// := declarations
-func colasname(n *Node) bool {
- switch n.Op {
- case ONAME,
- ONONAME,
- OPACK,
- OTYPE,
- OLITERAL:
- return n.Sym != nil
- }
-
- return false
-}
-
-func colasdefn(left []*Node, defn *Node) {
- for _, n := range left {
- if n.Sym != nil {
- n.Sym.SetUniq(true)
- }
- }
-
- var nnew, nerr int
- for i, n := range left {
- if n.isBlank() {
- continue
- }
- if !colasname(n) {
- yyerrorl(defn.Pos, "non-name %v on left side of :=", n)
- nerr++
- continue
- }
-
- if !n.Sym.Uniq() {
- yyerrorl(defn.Pos, "%v repeated on left side of :=", n.Sym)
- n.SetDiag(true)
- nerr++
- continue
- }
-
- n.Sym.SetUniq(false)
- if n.Sym.Block == types.Block {
- continue
- }
-
- nnew++
- n = newname(n.Sym)
- declare(n, dclcontext)
- n.Name.Defn = defn
- defn.Ninit.Append(nod(ODCL, n, nil))
- left[i] = n
- }
-
- if nnew == 0 && nerr == 0 {
- yyerrorl(defn.Pos, "no new variables on left side of :=")
- }
-}
-
-// declare the arguments in an
-// interface field declaration.
-func ifacedcl(n *Node) {
- if n.Op != ODCLFIELD || n.Left == nil {
- Fatalf("ifacedcl")
- }
-
- if n.Sym.IsBlank() {
- yyerror("methods must have a unique non-blank name")
- }
-}
-
-// declare the function proper
-// and declare the arguments.
-// called in extern-declaration context
-// returns in auto-declaration context.
-func funchdr(n *Node) {
- // change the declaration context from extern to auto
- funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext})
- Curfn = n
- dclcontext = PAUTO
-
- types.Markdcl()
-
- if n.Func.Nname != nil {
- funcargs(n.Func.Nname.Name.Param.Ntype)
- } else if n.Func.Ntype != nil {
- funcargs(n.Func.Ntype)
- } else {
- funcargs2(n.Type)
- }
-}
-
-func funcargs(nt *Node) {
- if nt.Op != OTFUNC {
- 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 = nt.Rlist.Len()
-
- // declare the receiver and in arguments.
- if nt.Left != nil {
- funcarg(nt.Left, PPARAM)
- }
- for _, n := range nt.List.Slice() {
- funcarg(n, PPARAM)
- }
-
- oldvargen := vargen
- vargen = 0
-
- // declare the out arguments.
- gen := nt.List.Len()
- for _, n := range nt.Rlist.Slice() {
- if n.Sym == nil {
- // Name so that escape analysis can track it. ~r stands for 'result'.
- n.Sym = lookupN("~r", gen)
- gen++
- }
- if n.Sym.IsBlank() {
- // Give it a name so we can assign to it during return. ~b stands for 'blank'.
- // The name must be different from ~r above because if you have
- // func f() (_ int)
- // func g() int
- // f is allowed to use a plain 'return' with no arguments, while g is not.
- // So the two cases must be distinguished.
- n.Sym = lookupN("~b", gen)
- gen++
- }
-
- funcarg(n, PPARAMOUT)
- }
-
- vargen = oldvargen
-}
-
-func funcarg(n *Node, ctxt Class) {
- if n.Op != ODCLFIELD {
- Fatalf("funcarg %v", n.Op)
- }
- if n.Sym == nil {
- return
- }
-
- n.Right = newnamel(n.Pos, n.Sym)
- n.Right.Name.Param.Ntype = n.Left
- n.Right.SetIsDDD(n.IsDDD())
- declare(n.Right, ctxt)
-
- vargen++
- n.Right.Name.Vargen = int32(vargen)
-}
-
-// Same as funcargs, except run over an already constructed TFUNC.
-// This happens during import, where the hidden_fndcl rule has
-// used functype directly to parse the function's type.
-func funcargs2(t *types.Type) {
- if t.Etype != TFUNC {
- Fatalf("funcargs2 %v", t)
- }
-
- for _, f := range t.Recvs().Fields().Slice() {
- funcarg2(f, PPARAM)
- }
- for _, f := range t.Params().Fields().Slice() {
- funcarg2(f, PPARAM)
- }
- for _, f := range t.Results().Fields().Slice() {
- funcarg2(f, PPARAMOUT)
- }
-}
-
-func funcarg2(f *types.Field, ctxt Class) {
- if f.Sym == nil {
- return
- }
- n := newnamel(f.Pos, f.Sym)
- f.Nname = asTypesNode(n)
- n.Type = f.Type
- n.SetIsDDD(f.IsDDD())
- declare(n, ctxt)
-}
-
-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 previous context
- types.Popdcl()
- var e funcStackEnt
- funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1]
- Curfn, dclcontext = e.curfn, e.dclcontext
-}
-
-// structs, functions, and methods.
-// they don't belong here, but where do they belong?
-func checkembeddedtype(t *types.Type) {
- if t == nil {
- return
- }
-
- if t.Sym == nil && t.IsPtr() {
- t = t.Elem()
- if t.IsInterface() {
- yyerror("embedded type cannot be a pointer to interface")
- }
- }
-
- if t.IsPtr() || t.IsUnsafePtr() {
- yyerror("embedded type cannot be a pointer")
- } else if t.Etype == TFORW && !t.ForwardType().Embedlineno.IsKnown() {
- t.ForwardType().Embedlineno = lineno
- }
-}
-
-func structfield(n *Node) *types.Field {
- lno := lineno
- lineno = n.Pos
-
- if n.Op != ODCLFIELD {
- Fatalf("structfield: oops %v\n", n)
- }
-
- f := types.NewField()
- f.Pos = n.Pos
- f.Sym = n.Sym
-
- if n.Left != nil {
- n.Left = typecheck(n.Left, ctxType)
- n.Type = n.Left.Type
- n.Left = nil
- }
-
- f.Type = n.Type
- if f.Type == nil {
- f.SetBroke(true)
- }
-
- if n.Embedded() {
- checkembeddedtype(n.Type)
- f.Embedded = 1
- } else {
- f.Embedded = 0
- }
-
- switch u := n.Val().U.(type) {
- case string:
- f.Note = u
- default:
- yyerror("field tag must be a string")
- case nil:
- // no-op
- }
-
- lineno = lno
- return f
-}
-
-// checkdupfields emits errors for duplicately named fields or methods in
-// a list of struct or interface types.
-func checkdupfields(what string, fss ...[]*types.Field) {
- seen := make(map[*types.Sym]bool)
- for _, fs := range fss {
- for _, f := range fs {
- if f.Sym == nil || f.Sym.IsBlank() {
- continue
- }
- if seen[f.Sym] {
- yyerrorl(f.Pos, "duplicate %s %s", what, f.Sym.Name)
- continue
- }
- seen[f.Sym] = true
- }
- }
-}
-
-// convert a parsed id/type list into
-// a type for struct/interface/arglist
-func tostruct(l []*Node) *types.Type {
- t := types.New(TSTRUCT)
-
- fields := make([]*types.Field, len(l))
- for i, n := range l {
- f := structfield(n)
- if f.Broke() {
- t.SetBroke(true)
- }
- fields[i] = f
- }
- t.SetFields(fields)
-
- checkdupfields("field", t.FieldSlice())
-
- if !t.Broke() {
- checkwidth(t)
- }
-
- return t
-}
-
-func tofunargs(l []*Node, funarg types.Funarg) *types.Type {
- t := types.New(TSTRUCT)
- t.StructType().Funarg = funarg
-
- fields := make([]*types.Field, len(l))
- for i, n := range l {
- f := structfield(n)
- f.SetIsDDD(n.IsDDD())
- if n.Right != nil {
- n.Right.Type = f.Type
- f.Nname = asTypesNode(n.Right)
- }
- if f.Broke() {
- t.SetBroke(true)
- }
- fields[i] = f
- }
- t.SetFields(fields)
- return t
-}
-
-func tofunargsfield(fields []*types.Field, funarg types.Funarg) *types.Type {
- t := types.New(TSTRUCT)
- t.StructType().Funarg = funarg
- t.SetFields(fields)
- return t
-}
-
-func interfacefield(n *Node) *types.Field {
- lno := lineno
- lineno = n.Pos
-
- if n.Op != ODCLFIELD {
- Fatalf("interfacefield: oops %v\n", n)
- }
-
- if n.Val().Ctype() != CTxxx {
- yyerror("interface method cannot have annotation")
- }
-
- // MethodSpec = MethodName Signature | InterfaceTypeName .
- //
- // If Sym != nil, then Sym is MethodName and Left is Signature.
- // Otherwise, Left is InterfaceTypeName.
-
- if n.Left != nil {
- n.Left = typecheck(n.Left, ctxType)
- n.Type = n.Left.Type
- n.Left = nil
- }
-
- f := types.NewField()
- f.Pos = n.Pos
- f.Sym = n.Sym
- f.Type = n.Type
- if f.Type == nil {
- f.SetBroke(true)
- }
-
- lineno = lno
- return f
-}
-
-func tointerface(l []*Node) *types.Type {
- if len(l) == 0 {
- return types.Types[TINTER]
- }
- t := types.New(TINTER)
- var fields []*types.Field
- for _, n := range l {
- f := interfacefield(n)
- if f.Broke() {
- t.SetBroke(true)
- }
- fields = append(fields, f)
- }
- t.SetInterface(fields)
- return t
-}
-
-func fakeRecv() *Node {
- return anonfield(types.FakeRecvType())
-}
-
-func fakeRecvField() *types.Field {
- f := types.NewField()
- f.Type = types.FakeRecvType()
- return f
-}
-
-// isifacemethod reports whether (field) m is
-// an interface method. Such methods have the
-// special receiver type types.FakeRecvType().
-func isifacemethod(f *types.Type) bool {
- return f.Recv().Type == types.FakeRecvType()
-}
-
-// turn a parsed function declaration into a type
-func functype(this *Node, in, out []*Node) *types.Type {
- t := types.New(TFUNC)
-
- var rcvr []*Node
- if this != nil {
- rcvr = []*Node{this}
- }
- t.FuncType().Receiver = tofunargs(rcvr, types.FunargRcvr)
- t.FuncType().Params = tofunargs(in, types.FunargParams)
- t.FuncType().Results = tofunargs(out, types.FunargResults)
-
- checkdupfields("argument", t.Recvs().FieldSlice(), t.Params().FieldSlice(), t.Results().FieldSlice())
-
- if t.Recvs().Broke() || t.Results().Broke() || t.Params().Broke() {
- t.SetBroke(true)
- }
-
- t.FuncType().Outnamed = t.NumResults() > 0 && origSym(t.Results().Field(0).Sym) != nil
-
- return t
-}
-
-func functypefield(this *types.Field, in, out []*types.Field) *types.Type {
- t := types.New(TFUNC)
-
- var rcvr []*types.Field
- if this != nil {
- rcvr = []*types.Field{this}
- }
- t.FuncType().Receiver = tofunargsfield(rcvr, types.FunargRcvr)
- t.FuncType().Params = tofunargsfield(in, types.FunargParams)
- t.FuncType().Results = tofunargsfield(out, types.FunargResults)
-
- t.FuncType().Outnamed = t.NumResults() > 0 && origSym(t.Results().Field(0).Sym) != nil
-
- return t
-}
-
-// origSym returns the original symbol written by the user.
-func origSym(s *types.Sym) *types.Sym {
- if s == nil {
- return nil
- }
-
- if len(s.Name) > 1 && s.Name[0] == '~' {
- switch s.Name[1] {
- case 'r': // originally an unnamed result
- return nil
- case 'b': // originally the blank identifier _
- // TODO(mdempsky): Does s.Pkg matter here?
- return nblank.Sym
- }
- return s
- }
-
- if strings.HasPrefix(s.Name, ".anon") {
- // originally an unnamed or _ name (see subr.go: structargs)
- return nil
- }
-
- return s
-}
-
-// methodSym returns the method symbol representing a method name
-// associated with a specific receiver type.
-//
-// Method symbols can be used to distinguish the same method appearing
-// in different method sets. For example, T.M and (*T).M have distinct
-// method symbols.
-//
-// The returned symbol will be marked as a function.
-func methodSym(recv *types.Type, msym *types.Sym) *types.Sym {
- sym := methodSymSuffix(recv, msym, "")
- sym.SetFunc(true)
- return sym
-}
-
-// methodSymSuffix is like methodsym, but allows attaching a
-// distinguisher suffix. To avoid collisions, the suffix must not
-// start with a letter, number, or period.
-func methodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sym {
- if msym.IsBlank() {
- Fatalf("blank method name")
- }
-
- rsym := recv.Sym
- if recv.IsPtr() {
- if rsym != nil {
- Fatalf("declared pointer receiver type: %v", recv)
- }
- rsym = recv.Elem().Sym
- }
-
- // Find the package the receiver type appeared in. For
- // anonymous receiver types (i.e., anonymous structs with
- // embedded fields), use the "go" pseudo-package instead.
- rpkg := gopkg
- if rsym != nil {
- rpkg = rsym.Pkg
- }
-
- var b bytes.Buffer
- if recv.IsPtr() {
- // The parentheses aren't really necessary, but
- // they're pretty traditional at this point.
- fmt.Fprintf(&b, "(%-S)", recv)
- } else {
- fmt.Fprintf(&b, "%-S", recv)
- }
-
- // A particular receiver type may have multiple non-exported
- // methods with the same name. To disambiguate them, include a
- // package qualifier for names that came from a different
- // package than the receiver type.
- if !types.IsExported(msym.Name) && msym.Pkg != rpkg {
- b.WriteString(".")
- b.WriteString(msym.Pkg.Prefix)
- }
-
- b.WriteString(".")
- b.WriteString(msym.Name)
- b.WriteString(suffix)
-
- return rpkg.LookupBytes(b.Bytes())
-}
-
-// Add a method, declared as a function.
-// - msym is the method symbol
-// - t is function type (with receiver)
-// Returns a pointer to the existing or added Field; or nil if there's an error.
-func addmethod(msym *types.Sym, t *types.Type, local, nointerface bool) *types.Field {
- if msym == nil {
- Fatalf("no method symbol")
- }
-
- // get parent type sym
- rf := t.Recv() // ptr to this structure
- if rf == nil {
- yyerror("missing receiver")
- return nil
- }
-
- mt := methtype(rf.Type)
- if mt == nil || mt.Sym == nil {
- pa := rf.Type
- t := pa
- if t != nil && t.IsPtr() {
- if t.Sym != nil {
- yyerror("invalid receiver type %v (%v is a pointer type)", pa, t)
- return nil
- }
- t = t.Elem()
- }
-
- switch {
- case t == nil || t.Broke():
- // rely on typecheck having complained before
- case t.Sym == nil:
- yyerror("invalid receiver type %v (%v is not a defined type)", pa, t)
- case t.IsPtr():
- yyerror("invalid receiver type %v (%v is a pointer type)", pa, t)
- case t.IsInterface():
- yyerror("invalid receiver type %v (%v is an interface type)", pa, t)
- default:
- // Should have picked off all the reasons above,
- // but just in case, fall back to generic error.
- yyerror("invalid receiver type %v (%L / %L)", pa, pa, t)
- }
- return nil
- }
-
- if local && mt.Sym.Pkg != localpkg {
- yyerror("cannot define new methods on non-local type %v", mt)
- return nil
- }
-
- if msym.IsBlank() {
- return nil
- }
-
- if mt.IsStruct() {
- for _, f := range mt.Fields().Slice() {
- if f.Sym == msym {
- yyerror("type %v has both field and method named %v", mt, msym)
- f.SetBroke(true)
- return nil
- }
- }
- }
-
- for _, f := range mt.Methods().Slice() {
- if msym.Name != f.Sym.Name {
- continue
- }
- // types.Identical only checks that incoming and result parameters match,
- // so explicitly check that the receiver parameters match too.
- if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) {
- yyerror("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t)
- }
- return f
- }
-
- f := types.NewField()
- f.Pos = lineno
- f.Sym = msym
- f.Type = t
- f.SetNointerface(nointerface)
-
- mt.Methods().Append(f)
- return f
-}
-
-func funcsymname(s *types.Sym) string {
- return s.Name + "·f"
-}
-
-// funcsym returns s·f.
-func funcsym(s *types.Sym) *types.Sym {
- // funcsymsmu here serves to protect not just mutations of funcsyms (below),
- // but also the package lookup of the func sym name,
- // since this function gets called concurrently from the backend.
- // There are no other concurrent package lookups in the backend,
- // except for the types package, which is protected separately.
- // Reusing funcsymsmu to also cover this package lookup
- // avoids a general, broader, expensive package lookup mutex.
- // Note makefuncsym also does package look-up of func sym names,
- // but that it is only called serially, from the front end.
- funcsymsmu.Lock()
- sf, existed := s.Pkg.LookupOK(funcsymname(s))
- // Don't export s·f when compiling for dynamic linking.
- // When dynamically linking, the necessary function
- // symbols will be created explicitly with makefuncsym.
- // See the makefuncsym comment for details.
- if !Ctxt.Flag_dynlink && !existed {
- funcsyms = append(funcsyms, s)
- }
- funcsymsmu.Unlock()
- return sf
-}
-
-// makefuncsym ensures that s·f is exported.
-// It is only used with -dynlink.
-// When not compiling for dynamic linking,
-// the funcsyms are created as needed by
-// the packages that use them.
-// Normally we emit the s·f stubs as DUPOK syms,
-// but DUPOK doesn't work across shared library boundaries.
-// So instead, when dynamic linking, we only create
-// the s·f stubs in s's package.
-func makefuncsym(s *types.Sym) {
- if !Ctxt.Flag_dynlink {
- Fatalf("makefuncsym dynlink")
- }
- if s.IsBlank() {
- return
- }
- if compiling_runtime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") {
- // runtime.getg(), getclosureptr(), getcallerpc(), and
- // getcallersp() are not real functions and so do not
- // get funcsyms.
- return
- }
- if _, existed := s.Pkg.LookupOK(funcsymname(s)); !existed {
- funcsyms = append(funcsyms, s)
- }
-}
-
-// setNodeNameFunc marks a node as a function.
-func setNodeNameFunc(n *Node) {
- if n.Op != ONAME || n.Class() != Pxxx {
- Fatalf("expected ONAME/Pxxx node, got %v", n)
- }
-
- n.SetClass(PFUNC)
- n.Sym.SetFunc(true)
-}
-
-func dclfunc(sym *types.Sym, tfn *Node) *Node {
- if tfn.Op != OTFUNC {
- Fatalf("expected OTFUNC node, got %v", tfn)
- }
-
- fn := nod(ODCLFUNC, nil, nil)
- fn.Func.Nname = newfuncnamel(lineno, sym)
- fn.Func.Nname.Name.Defn = fn
- fn.Func.Nname.Name.Param.Ntype = tfn
- setNodeNameFunc(fn.Func.Nname)
- funchdr(fn)
- fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, ctxType)
- return fn
-}
-
-type nowritebarrierrecChecker struct {
- // extraCalls contains extra function calls that may not be
- // visible during later analysis. It maps from the ODCLFUNC of
- // the caller to a list of callees.
- extraCalls map[*Node][]nowritebarrierrecCall
-
- // curfn is the current function during AST walks.
- curfn *Node
-}
-
-type nowritebarrierrecCall struct {
- target *Node // ODCLFUNC of caller or callee
- lineno src.XPos // line of call
-}
-
-type nowritebarrierrecCallSym struct {
- target *obj.LSym // LSym of callee
- lineno src.XPos // line of call
-}
-
-// newNowritebarrierrecChecker creates a nowritebarrierrecChecker. It
-// must be called before transformclosure and walk.
-func newNowritebarrierrecChecker() *nowritebarrierrecChecker {
- c := &nowritebarrierrecChecker{
- extraCalls: make(map[*Node][]nowritebarrierrecCall),
- }
-
- // Find all systemstack calls and record their targets. In
- // general, flow analysis can't see into systemstack, but it's
- // important to handle it for this check, so we model it
- // directly. This has to happen before transformclosure since
- // it's a lot harder to work out the argument after.
- for _, n := range xtop {
- if n.Op != ODCLFUNC {
- continue
- }
- c.curfn = n
- inspect(n, c.findExtraCalls)
- }
- c.curfn = nil
- return c
-}
-
-func (c *nowritebarrierrecChecker) findExtraCalls(n *Node) bool {
- if n.Op != OCALLFUNC {
- return true
- }
- fn := n.Left
- if fn == nil || fn.Op != ONAME || fn.Class() != PFUNC || fn.Name.Defn == nil {
- return true
- }
- if !isRuntimePkg(fn.Sym.Pkg) || fn.Sym.Name != "systemstack" {
- return true
- }
-
- var callee *Node
- arg := n.List.First()
- switch arg.Op {
- case ONAME:
- callee = arg.Name.Defn
- case OCLOSURE:
- callee = arg.Func.Closure
- default:
- Fatalf("expected ONAME or OCLOSURE node, got %+v", arg)
- }
- if callee.Op != ODCLFUNC {
- Fatalf("expected ODCLFUNC node, got %+v", callee)
- }
- c.extraCalls[c.curfn] = append(c.extraCalls[c.curfn], nowritebarrierrecCall{callee, n.Pos})
- return true
-}
-
-// recordCall records a call from ODCLFUNC node "from", to function
-// symbol "to" at position pos.
-//
-// This should be done as late as possible during compilation to
-// capture precise call graphs. The target of the call is an LSym
-// because that's all we know after we start SSA.
-//
-// This can be called concurrently for different from Nodes.
-func (c *nowritebarrierrecChecker) recordCall(from *Node, to *obj.LSym, pos src.XPos) {
- if from.Op != ODCLFUNC {
- Fatalf("expected ODCLFUNC, got %v", from)
- }
- // We record this information on the *Func so this is
- // concurrent-safe.
- fn := from.Func
- if fn.nwbrCalls == nil {
- fn.nwbrCalls = new([]nowritebarrierrecCallSym)
- }
- *fn.nwbrCalls = append(*fn.nwbrCalls, nowritebarrierrecCallSym{to, pos})
-}
-
-func (c *nowritebarrierrecChecker) check() {
- // We walk the call graph as late as possible so we can
- // capture all calls created by lowering, but this means we
- // only get to see the obj.LSyms of calls. symToFunc lets us
- // get back to the ODCLFUNCs.
- symToFunc := make(map[*obj.LSym]*Node)
- // funcs records the back-edges of the BFS call graph walk. It
- // maps from the ODCLFUNC of each function that must not have
- // write barriers to the call that inhibits them. Functions
- // that are directly marked go:nowritebarrierrec are in this
- // map with a zero-valued nowritebarrierrecCall. This also
- // acts as the set of marks for the BFS of the call graph.
- funcs := make(map[*Node]nowritebarrierrecCall)
- // q is the queue of ODCLFUNC Nodes to visit in BFS order.
- var q nodeQueue
-
- for _, n := range xtop {
- if n.Op != ODCLFUNC {
- continue
- }
-
- symToFunc[n.Func.lsym] = n
-
- // Make nowritebarrierrec functions BFS roots.
- if n.Func.Pragma&Nowritebarrierrec != 0 {
- funcs[n] = nowritebarrierrecCall{}
- q.pushRight(n)
- }
- // Check go:nowritebarrier functions.
- if n.Func.Pragma&Nowritebarrier != 0 && n.Func.WBPos.IsKnown() {
- yyerrorl(n.Func.WBPos, "write barrier prohibited")
- }
- }
-
- // Perform a BFS of the call graph from all
- // go:nowritebarrierrec functions.
- enqueue := func(src, target *Node, pos src.XPos) {
- if target.Func.Pragma&Yeswritebarrierrec != 0 {
- // Don't flow into this function.
- return
- }
- if _, ok := funcs[target]; ok {
- // Already found a path to target.
- return
- }
-
- // Record the path.
- funcs[target] = nowritebarrierrecCall{target: src, lineno: pos}
- q.pushRight(target)
- }
- for !q.empty() {
- fn := q.popLeft()
-
- // Check fn.
- if fn.Func.WBPos.IsKnown() {
- var err bytes.Buffer
- call := funcs[fn]
- for call.target != nil {
- fmt.Fprintf(&err, "\n\t%v: called by %v", linestr(call.lineno), call.target.Func.Nname)
- call = funcs[call.target]
- }
- yyerrorl(fn.Func.WBPos, "write barrier prohibited by caller; %v%s", fn.Func.Nname, err.String())
- continue
- }
-
- // Enqueue fn's calls.
- for _, callee := range c.extraCalls[fn] {
- enqueue(fn, callee.target, callee.lineno)
- }
- if fn.Func.nwbrCalls == nil {
- continue
- }
- for _, callee := range *fn.Func.nwbrCalls {
- target := symToFunc[callee.target]
- if target != nil {
- enqueue(fn, target, callee.lineno)
- }
- }
- }
-}
diff --git a/src/cmd/compile/internal/gc/embed.go b/src/cmd/compile/internal/gc/embed.go
deleted file mode 100644
index f45796cc1d806..0000000000000
--- a/src/cmd/compile/internal/gc/embed.go
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
- "cmd/compile/internal/syntax"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "encoding/json"
- "io/ioutil"
- "log"
- "path"
- "sort"
- "strconv"
- "strings"
-)
-
-var embedlist []*Node
-
-var embedCfg struct {
- Patterns map[string][]string
- Files map[string]string
-}
-
-func readEmbedCfg(file string) {
- data, err := ioutil.ReadFile(file)
- if err != nil {
- log.Fatalf("-embedcfg: %v", err)
- }
- if err := json.Unmarshal(data, &embedCfg); err != nil {
- log.Fatalf("%s: %v", file, err)
- }
- if embedCfg.Patterns == nil {
- log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
- }
- if embedCfg.Files == nil {
- log.Fatalf("%s: invalid embedcfg: missing Files", file)
- }
-}
-
-const (
- embedUnknown = iota
- embedBytes
- embedString
- embedFiles
-)
-
-func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) {
- haveEmbed := false
- for _, decl := range p.file.DeclList {
- imp, ok := decl.(*syntax.ImportDecl)
- if !ok {
- // imports always come first
- break
- }
- path, _ := strconv.Unquote(imp.Path.Value)
- if path == "embed" {
- haveEmbed = true
- break
- }
- }
-
- pos := embeds[0].Pos
- if !haveEmbed {
- p.yyerrorpos(pos, "invalid go:embed: missing import \"embed\"")
- return
- }
- if len(names) > 1 {
- p.yyerrorpos(pos, "go:embed cannot apply to multiple vars")
- return
- }
- if len(exprs) > 0 {
- p.yyerrorpos(pos, "go:embed cannot apply to var with initializer")
- return
- }
- if typ == nil {
- // Should not happen, since len(exprs) == 0 now.
- p.yyerrorpos(pos, "go:embed cannot apply to var without type")
- return
- }
- if dclcontext != PEXTERN {
- p.yyerrorpos(pos, "go:embed cannot apply to var inside func")
- return
- }
-
- var list []irEmbed
- for _, e := range embeds {
- list = append(list, irEmbed{Pos: p.makeXPos(e.Pos), Patterns: e.Patterns})
- }
- v := names[0]
- v.Name.Param.SetEmbedList(list)
- embedlist = append(embedlist, v)
-}
-
-func embedFileList(v *Node, kind int) []string {
- // Build list of files to store.
- have := make(map[string]bool)
- var list []string
- for _, e := range v.Name.Param.EmbedList() {
- for _, pattern := range e.Patterns {
- files, ok := embedCfg.Patterns[pattern]
- if !ok {
- yyerrorl(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
- }
- for _, file := range files {
- if embedCfg.Files[file] == "" {
- yyerrorl(e.Pos, "invalid go:embed: build system did not map file: %s", file)
- continue
- }
- if !have[file] {
- have[file] = true
- list = append(list, file)
- }
- if kind == embedFiles {
- for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) {
- have[dir] = true
- list = append(list, dir+"/")
- }
- }
- }
- }
- }
- sort.Slice(list, func(i, j int) bool {
- return embedFileLess(list[i], list[j])
- })
-
- if kind == embedString || kind == embedBytes {
- if len(list) > 1 {
- yyerrorl(v.Pos, "invalid go:embed: multiple files for type %v", v.Type)
- return nil
- }
- }
-
- return list
-}
-
-// embedKind determines the kind of embedding variable.
-func embedKind(typ *types.Type) int {
- if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) {
- return embedFiles
- }
- if typ.Etype == types.TSTRING {
- return embedString
- }
- if typ.Etype == types.TSLICE && typ.Elem().Etype == types.TUINT8 {
- return embedBytes
- }
- return embedUnknown
-}
-
-func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
- if name[len(name)-1] == '/' {
- isDir = true
- name = name[:len(name)-1]
- }
- i := len(name) - 1
- for i >= 0 && name[i] != '/' {
- i--
- }
- if i < 0 {
- return ".", name, isDir
- }
- return name[:i], name[i+1:], isDir
-}
-
-// embedFileLess implements the sort order for a list of embedded files.
-// See the comment inside ../../../../embed/embed.go's Files struct for rationale.
-func embedFileLess(x, y string) bool {
- xdir, xelem, _ := embedFileNameSplit(x)
- ydir, yelem, _ := embedFileNameSplit(y)
- return xdir < ydir || xdir == ydir && xelem < yelem
-}
-
-func dumpembeds() {
- for _, v := range embedlist {
- initEmbed(v)
- }
-}
-
-// initEmbed emits the init data for a //go:embed variable,
-// which is either a string, a []byte, or an embed.FS.
-func initEmbed(v *Node) {
- commentPos := v.Name.Param.EmbedList()[0].Pos
- if !langSupported(1, 16, localpkg) {
- lno := lineno
- lineno = commentPos
- yyerrorv("go1.16", "go:embed")
- lineno = lno
- return
- }
- if embedCfg.Patterns == nil {
- yyerrorl(commentPos, "invalid go:embed: build system did not supply embed configuration")
- return
- }
- kind := embedKind(v.Type)
- if kind == embedUnknown {
- yyerrorl(v.Pos, "go:embed cannot apply to var of type %v", v.Type)
- return
- }
-
- files := embedFileList(v, kind)
- switch kind {
- case embedString, embedBytes:
- file := files[0]
- fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], kind == embedString, nil)
- if err != nil {
- yyerrorl(v.Pos, "embed %s: %v", file, err)
- }
- sym := v.Sym.Linksym()
- off := 0
- off = dsymptr(sym, off, fsym, 0) // data string
- off = duintptr(sym, off, uint64(size)) // len
- if kind == embedBytes {
- duintptr(sym, off, uint64(size)) // cap for slice
- }
-
- case embedFiles:
- slicedata := Ctxt.Lookup(`"".` + v.Sym.Name + `.files`)
- off := 0
- // []files pointed at by Files
- off = dsymptr(slicedata, off, slicedata, 3*Widthptr) // []file, pointing just past slice
- off = duintptr(slicedata, off, uint64(len(files)))
- off = duintptr(slicedata, off, uint64(len(files)))
-
- // embed/embed.go type file is:
- // name string
- // data string
- // hash [16]byte
- // Emit one of these per file in the set.
- const hashSize = 16
- hash := make([]byte, hashSize)
- for _, file := range files {
- off = dsymptr(slicedata, off, stringsym(v.Pos, file), 0) // file string
- off = duintptr(slicedata, off, uint64(len(file)))
- if strings.HasSuffix(file, "/") {
- // entry for directory - no data
- off = duintptr(slicedata, off, 0)
- off = duintptr(slicedata, off, 0)
- off += hashSize
- } else {
- fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], true, hash)
- if err != nil {
- yyerrorl(v.Pos, "embed %s: %v", file, err)
- }
- off = dsymptr(slicedata, off, fsym, 0) // data string
- off = duintptr(slicedata, off, uint64(size))
- off = int(slicedata.WriteBytes(Ctxt, int64(off), hash))
- }
- }
- ggloblsym(slicedata, int32(off), obj.RODATA|obj.LOCAL)
- sym := v.Sym.Linksym()
- dsymptr(sym, 0, slicedata, 0)
- }
-}
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
deleted file mode 100644
index 6f328ab5ea936..0000000000000
--- a/src/cmd/compile/internal/gc/esc.go
+++ /dev/null
@@ -1,472 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
- "cmd/compile/internal/types"
- "fmt"
-)
-
-func escapes(all []*Node) {
- visitBottomUp(all, escapeFuncs)
-}
-
-const (
- EscFuncUnknown = 0 + iota
- EscFuncPlanned
- EscFuncStarted
- EscFuncTagged
-)
-
-func min8(a, b int8) int8 {
- if a < b {
- return a
- }
- return b
-}
-
-func max8(a, b int8) int8 {
- if a > b {
- return a
- }
- return b
-}
-
-const (
- EscUnknown = iota
- EscNone // Does not escape to heap, result, or parameters.
- EscHeap // Reachable from the heap
- EscNever // By construction will not escape.
-)
-
-// funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way.
-func funcSym(fn *Node) *types.Sym {
- if fn == nil || fn.Func.Nname == nil {
- return nil
- }
- return fn.Func.Nname.Sym
-}
-
-// Mark labels that have no backjumps to them as not increasing e.loopdepth.
-// Walk hasn't generated (goto|label).Left.Sym.Label yet, so we'll cheat
-// and set it to one of the following two. Then in esc we'll clear it again.
-var (
- looping Node
- nonlooping Node
-)
-
-func isSliceSelfAssign(dst, src *Node) bool {
- // Detect the following special case.
- //
- // func (b *Buffer) Foo() {
- // n, m := ...
- // b.buf = b.buf[n:m]
- // }
- //
- // This assignment is a no-op for escape analysis,
- // it does not store any new pointers into b that were not already there.
- // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
- // Here we assume that the statement will not contain calls,
- // that is, that order will move any calls to init.
- // Otherwise base ONAME value could change between the moments
- // when we evaluate it for dst and for src.
-
- // dst is ONAME dereference.
- if dst.Op != ODEREF && dst.Op != ODOTPTR || dst.Left.Op != ONAME {
- return false
- }
- // src is a slice operation.
- switch src.Op {
- case OSLICE, OSLICE3, OSLICESTR:
- // OK.
- case OSLICEARR, OSLICE3ARR:
- // Since arrays are embedded into containing object,
- // slice of non-pointer array will introduce a new pointer into b that was not already there
- // (pointer to b itself). After such assignment, if b contents escape,
- // b escapes as well. If we ignore such OSLICEARR, we will conclude
- // that b does not escape when b contents do.
- //
- // Pointer to an array is OK since it's not stored inside b directly.
- // For slicing an array (not pointer to array), there is an implicit OADDR.
- // We check that to determine non-pointer array slicing.
- if src.Left.Op == OADDR {
- return false
- }
- default:
- return false
- }
- // slice is applied to ONAME dereference.
- if src.Left.Op != ODEREF && src.Left.Op != ODOTPTR || src.Left.Left.Op != ONAME {
- return false
- }
- // dst and src reference the same base ONAME.
- return dst.Left == src.Left.Left
-}
-
-// isSelfAssign reports whether assignment from src to dst can
-// be ignored by the escape analysis as it's effectively a self-assignment.
-func isSelfAssign(dst, src *Node) bool {
- if isSliceSelfAssign(dst, src) {
- return true
- }
-
- // Detect trivial assignments that assign back to the same object.
- //
- // It covers these cases:
- // val.x = val.y
- // val.x[i] = val.y[j]
- // val.x1.x2 = val.x1.y2
- // ... etc
- //
- // These assignments do not change assigned object lifetime.
-
- if dst == nil || src == nil || dst.Op != src.Op {
- return false
- }
-
- switch dst.Op {
- case ODOT, ODOTPTR:
- // Safe trailing accessors that are permitted to differ.
- case OINDEX:
- if mayAffectMemory(dst.Right) || mayAffectMemory(src.Right) {
- return false
- }
- default:
- return false
- }
-
- // The expression prefix must be both "safe" and identical.
- return samesafeexpr(dst.Left, src.Left)
-}
-
-// mayAffectMemory reports whether evaluation of n may affect the program's
-// memory state. If the expression can't affect memory state, then it can be
-// safely ignored by the escape analysis.
-func mayAffectMemory(n *Node) bool {
- // We may want to use a list of "memory safe" ops instead of generally
- // "side-effect free", which would include all calls and other ops that can
- // allocate or change global state. For now, it's safer to start with the latter.
- //
- // We're ignoring things like division by zero, index out of range,
- // and nil pointer dereference here.
- switch n.Op {
- case ONAME, OCLOSUREVAR, OLITERAL:
- return false
-
- // Left+Right group.
- case OINDEX, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
- return mayAffectMemory(n.Left) || mayAffectMemory(n.Right)
-
- // Left group.
- case ODOT, ODOTPTR, ODEREF, OCONVNOP, OCONV, OLEN, OCAP,
- ONOT, OBITNOT, OPLUS, ONEG, OALIGNOF, OOFFSETOF, OSIZEOF:
- return mayAffectMemory(n.Left)
-
- default:
- return true
- }
-}
-
-// heapAllocReason returns the reason the given Node must be heap
-// allocated, or the empty string if it doesn't.
-func heapAllocReason(n *Node) string {
- if n.Type == nil {
- return ""
- }
-
- // Parameters are always passed via the stack.
- if n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) {
- return ""
- }
-
- if n.Type.Width > maxStackVarSize {
- return "too large for stack"
- }
-
- if (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize {
- return "too large for stack"
- }
-
- if n.Op == OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize {
- return "too large for stack"
- }
- if n.Op == OCALLPART && partialCallType(n).Size() >= maxImplicitStackVarSize {
- return "too large for stack"
- }
-
- if n.Op == OMAKESLICE {
- r := n.Right
- if r == nil {
- r = n.Left
- }
- if !smallintconst(r) {
- return "non-constant size"
- }
- if t := n.Type; t.Elem().Width != 0 && r.Int64Val() >= maxImplicitStackVarSize/t.Elem().Width {
- return "too large for stack"
- }
- }
-
- return ""
-}
-
-// addrescapes tags node n as having had its address taken
-// by "increasing" the "value" of n.Esc to EscHeap.
-// Storage is allocated as necessary to allow the address
-// to be taken.
-func addrescapes(n *Node) {
- switch n.Op {
- default:
- // Unexpected Op, probably due to a previous type error. Ignore.
-
- case ODEREF, ODOTPTR:
- // Nothing to do.
-
- case ONAME:
- if n == nodfp {
- break
- }
-
- // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
- // on PPARAM it means something different.
- if n.Class() == PAUTO && n.Esc == EscNever {
- break
- }
-
- // If a closure reference escapes, mark the outer variable as escaping.
- if n.Name.IsClosureVar() {
- addrescapes(n.Name.Defn)
- break
- }
-
- if n.Class() != PPARAM && n.Class() != PPARAMOUT && n.Class() != PAUTO {
- break
- }
-
- // This is a plain parameter or local variable that needs to move to the heap,
- // but possibly for the function outside the one we're compiling.
- // That is, if we have:
- //
- // func f(x int) {
- // func() {
- // global = &x
- // }
- // }
- //
- // then we're analyzing the inner closure but we need to move x to the
- // heap in f, not in the inner closure. Flip over to f before calling moveToHeap.
- oldfn := Curfn
- Curfn = n.Name.Curfn
- if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
- Curfn = Curfn.Func.Closure
- }
- ln := lineno
- lineno = Curfn.Pos
- moveToHeap(n)
- Curfn = oldfn
- lineno = ln
-
- // ODOTPTR has already been introduced,
- // so these are the non-pointer ODOT and OINDEX.
- // In &x[0], if x is a slice, then x does not
- // escape--the pointer inside x does, but that
- // is always a heap pointer anyway.
- case ODOT, OINDEX, OPAREN, OCONVNOP:
- if !n.Left.Type.IsSlice() {
- addrescapes(n.Left)
- }
- }
-}
-
-// moveToHeap records the parameter or local variable n as moved to the heap.
-func moveToHeap(n *Node) {
- if Debug.r != 0 {
- Dump("MOVE", n)
- }
- if compiling_runtime {
- yyerror("%v escapes to heap, not allowed in runtime", n)
- }
- if n.Class() == PAUTOHEAP {
- Dump("n", n)
- Fatalf("double move to heap")
- }
-
- // Allocate a local stack variable to hold the pointer to the heap copy.
- // temp will add it to the function declaration list automatically.
- heapaddr := temp(types.NewPtr(n.Type))
- heapaddr.Sym = lookup("&" + n.Sym.Name)
- heapaddr.Orig.Sym = heapaddr.Sym
- heapaddr.Pos = n.Pos
-
- // Unset AutoTemp to persist the &foo variable name through SSA to
- // liveness analysis.
- // TODO(mdempsky/drchase): Cleaner solution?
- heapaddr.Name.SetAutoTemp(false)
-
- // Parameters have a local stack copy used at function start/end
- // in addition to the copy in the heap that may live longer than
- // the function.
- if n.Class() == PPARAM || n.Class() == PPARAMOUT {
- if n.Xoffset == BADWIDTH {
- Fatalf("addrescapes before param assignment")
- }
-
- // We rewrite n below to be a heap variable (indirection of heapaddr).
- // Preserve a copy so we can still write code referring to the original,
- // and substitute that copy into the function declaration list
- // so that analyses of the local (on-stack) variables use it.
- stackcopy := newname(n.Sym)
- stackcopy.Type = n.Type
- stackcopy.Xoffset = n.Xoffset
- stackcopy.SetClass(n.Class())
- stackcopy.Name.Param.Heapaddr = heapaddr
- if n.Class() == PPARAMOUT {
- // Make sure the pointer to the heap copy is kept live throughout the function.
- // The function could panic at any point, and then a defer could recover.
- // Thus, we need the pointer to the heap copy always available so the
- // post-deferreturn code can copy the return value back to the stack.
- // See issue 16095.
- heapaddr.Name.SetIsOutputParamHeapAddr(true)
- }
- n.Name.Param.Stackcopy = stackcopy
-
- // Substitute the stackcopy into the function variable list so that
- // liveness and other analyses use the underlying stack slot
- // and not the now-pseudo-variable n.
- found := false
- for i, d := range Curfn.Func.Dcl {
- if d == n {
- Curfn.Func.Dcl[i] = stackcopy
- found = true
- break
- }
- // Parameters are before locals, so can stop early.
- // This limits the search even in functions with many local variables.
- if d.Class() == PAUTO {
- break
- }
- }
- if !found {
- Fatalf("cannot find %v in local variable list", n)
- }
- Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
- }
-
- // Modify n in place so that uses of n now mean indirection of the heapaddr.
- n.SetClass(PAUTOHEAP)
- n.Xoffset = 0
- n.Name.Param.Heapaddr = heapaddr
- n.Esc = EscHeap
- if Debug.m != 0 {
- Warnl(n.Pos, "moved to heap: %v", n)
- }
-}
-
-// This special tag is applied to uintptr variables
-// that we believe may hold unsafe.Pointers for
-// calls into assembly functions.
-const unsafeUintptrTag = "unsafe-uintptr"
-
-// This special tag is applied to uintptr parameters of functions
-// marked go:uintptrescapes.
-const uintptrEscapesTag = "uintptr-escapes"
-
-func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
- name := func() string {
- if f.Sym != nil {
- return f.Sym.Name
- }
- return fmt.Sprintf("arg#%d", narg)
- }
-
- if fn.Nbody.Len() == 0 {
- // Assume that uintptr arguments must be held live across the call.
- // This is most important for syscall.Syscall.
- // See golang.org/issue/13372.
- // This really doesn't have much to do with escape analysis per se,
- // but we are reusing the ability to annotate an individual function
- // argument and pass those annotations along to importing code.
- if f.Type.IsUintptr() {
- if Debug.m != 0 {
- Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
- }
- return unsafeUintptrTag
- }
-
- if !f.Type.HasPointers() { // don't bother tagging for scalars
- return ""
- }
-
- var esc EscLeaks
-
- // External functions are assumed unsafe, unless
- // //go:noescape is given before the declaration.
- if fn.Func.Pragma&Noescape != 0 {
- if Debug.m != 0 && f.Sym != nil {
- Warnl(f.Pos, "%v does not escape", name())
- }
- } else {
- if Debug.m != 0 && f.Sym != nil {
- Warnl(f.Pos, "leaking param: %v", name())
- }
- esc.AddHeap(0)
- }
-
- return esc.Encode()
- }
-
- if fn.Func.Pragma&UintptrEscapes != 0 {
- if f.Type.IsUintptr() {
- if Debug.m != 0 {
- Warnl(f.Pos, "marking %v as escaping uintptr", name())
- }
- return uintptrEscapesTag
- }
- if f.IsDDD() && f.Type.Elem().IsUintptr() {
- // final argument is ...uintptr.
- if Debug.m != 0 {
- Warnl(f.Pos, "marking %v as escaping ...uintptr", name())
- }
- return uintptrEscapesTag
- }
- }
-
- if !f.Type.HasPointers() { // don't bother tagging for scalars
- return ""
- }
-
- // Unnamed parameters are unused and therefore do not escape.
- if f.Sym == nil || f.Sym.IsBlank() {
- var esc EscLeaks
- return esc.Encode()
- }
-
- n := asNode(f.Nname)
- loc := e.oldLoc(n)
- esc := loc.paramEsc
- esc.Optimize()
-
- if Debug.m != 0 && !loc.escapes {
- if esc.Empty() {
- Warnl(f.Pos, "%v does not escape", name())
- }
- if x := esc.Heap(); x >= 0 {
- if x == 0 {
- Warnl(f.Pos, "leaking param: %v", name())
- } else {
- // TODO(mdempsky): Mention level=x like below?
- Warnl(f.Pos, "leaking param content: %v", name())
- }
- }
- for i := 0; i < numEscResults; i++ {
- if x := esc.Result(i); x >= 0 {
- res := fn.Type.Results().Field(i).Sym
- Warnl(f.Pos, "leaking param: %v to result %v level=%d", name(), res, x)
- }
- }
- }
-
- return esc.Encode()
-}
diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go
deleted file mode 100644
index 618bdf78e259c..0000000000000
--- a/src/cmd/compile/internal/gc/escape.go
+++ /dev/null
@@ -1,1538 +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.
-
-package gc
-
-import (
- "cmd/compile/internal/logopt"
- "cmd/compile/internal/types"
- "cmd/internal/src"
- "fmt"
- "math"
- "strings"
-)
-
-// Escape analysis.
-//
-// Here we analyze functions to determine which Go variables
-// (including implicit allocations such as calls to "new" or "make",
-// composite literals, etc.) can be allocated on the stack. The two
-// key invariants we have to ensure are: (1) pointers to stack objects
-// cannot be stored in the heap, and (2) pointers to a stack object
-// cannot outlive that object (e.g., because the declaring function
-// returned and destroyed the object's stack frame, or its space is
-// reused across loop iterations for logically distinct variables).
-//
-// We implement this with a static data-flow analysis of the AST.
-// First, we construct a directed weighted graph where vertices
-// (termed "locations") represent variables allocated by statements
-// and expressions, and edges represent assignments between variables
-// (with weights representing addressing/dereference counts).
-//
-// Next we walk the graph looking for assignment paths that might
-// violate the invariants stated above. If a variable v's address is
-// stored in the heap or elsewhere that may outlive it, then v is
-// marked as requiring heap allocation.
-//
-// To support interprocedural analysis, we also record data-flow from
-// each function's parameters to the heap and to its result
-// parameters. This information is summarized as "parameter tags",
-// which are used at static call sites to improve escape analysis of
-// function arguments.
-
-// Constructing the location graph.
-//
-// Every allocating statement (e.g., variable declaration) or
-// expression (e.g., "new" or "make") is first mapped to a unique
-// "location."
-//
-// We also model every Go assignment as a directed edges between
-// locations. The number of dereference operations minus the number of
-// addressing operations is recorded as the edge's weight (termed
-// "derefs"). For example:
-//
-// p = &q // -1
-// p = q // 0
-// p = *q // 1
-// p = **q // 2
-//
-// p = **&**&q // 2
-//
-// Note that the & operator can only be applied to addressable
-// expressions, and the expression &x itself is not addressable, so
-// derefs cannot go below -1.
-//
-// Every Go language construct is lowered into this representation,
-// generally without sensitivity to flow, path, or context; and
-// without distinguishing elements within a compound variable. For
-// example:
-//
-// var x struct { f, g *int }
-// var u []*int
-//
-// x.f = u[0]
-//
-// is modeled simply as
-//
-// x = *u
-//
-// That is, we don't distinguish x.f from x.g, or u[0] from u[1],
-// u[2], etc. However, we do record the implicit dereference involved
-// in indexing a slice.
-
-type Escape struct {
- allLocs []*EscLocation
-
- curfn *Node
-
- // loopDepth counts the current loop nesting depth within
- // curfn. It increments within each "for" loop and at each
- // label with a corresponding backwards "goto" (i.e.,
- // unstructured loop).
- loopDepth int
-
- heapLoc EscLocation
- blankLoc EscLocation
-}
-
-// An EscLocation represents an abstract location that stores a Go
-// variable.
-type EscLocation struct {
- n *Node // represented variable or expression, if any
- curfn *Node // enclosing function
- edges []EscEdge // incoming edges
- loopDepth int // loopDepth at declaration
-
- // derefs and walkgen are used during walkOne to track the
- // minimal dereferences from the walk root.
- derefs int // >= -1
- walkgen uint32
-
- // dst and dstEdgeindex track the next immediate assignment
- // destination location during walkone, along with the index
- // of the edge pointing back to this location.
- dst *EscLocation
- dstEdgeIdx int
-
- // queued is used by walkAll to track whether this location is
- // in the walk queue.
- queued bool
-
- // escapes reports whether the represented variable's address
- // escapes; that is, whether the variable must be heap
- // allocated.
- escapes bool
-
- // transient reports whether the represented expression's
- // address does not outlive the statement; that is, whether
- // its storage can be immediately reused.
- transient bool
-
- // paramEsc records the represented parameter's leak set.
- paramEsc EscLeaks
-}
-
-// An EscEdge represents an assignment edge between two Go variables.
-type EscEdge struct {
- src *EscLocation
- derefs int // >= -1
- notes *EscNote
-}
-
-// escapeFuncs performs escape analysis on a minimal batch of
-// functions.
-func escapeFuncs(fns []*Node, recursive bool) {
- for _, fn := range fns {
- if fn.Op != ODCLFUNC {
- Fatalf("unexpected node: %v", fn)
- }
- }
-
- var e Escape
- e.heapLoc.escapes = true
-
- // Construct data-flow graph from syntax trees.
- for _, fn := range fns {
- e.initFunc(fn)
- }
- for _, fn := range fns {
- e.walkFunc(fn)
- }
- e.curfn = nil
-
- e.walkAll()
- e.finish(fns)
-}
-
-func (e *Escape) initFunc(fn *Node) {
- if fn.Op != ODCLFUNC || fn.Esc != EscFuncUnknown {
- Fatalf("unexpected node: %v", fn)
- }
- fn.Esc = EscFuncPlanned
- if Debug.m > 3 {
- Dump("escAnalyze", fn)
- }
-
- e.curfn = fn
- e.loopDepth = 1
-
- // Allocate locations for local variables.
- for _, dcl := range fn.Func.Dcl {
- if dcl.Op == ONAME {
- e.newLoc(dcl, false)
- }
- }
-}
-
-func (e *Escape) walkFunc(fn *Node) {
- fn.Esc = EscFuncStarted
-
- // Identify labels that mark the head of an unstructured loop.
- inspectList(fn.Nbody, func(n *Node) bool {
- switch n.Op {
- case OLABEL:
- n.Sym.Label = asTypesNode(&nonlooping)
-
- case OGOTO:
- // If we visited the label before the goto,
- // then this is a looping label.
- if n.Sym.Label == asTypesNode(&nonlooping) {
- n.Sym.Label = asTypesNode(&looping)
- }
- }
-
- return true
- })
-
- e.curfn = fn
- e.loopDepth = 1
- e.block(fn.Nbody)
-}
-
-// Below we implement the methods for walking the AST and recording
-// data flow edges. Note that because a sub-expression might have
-// side-effects, it's important to always visit the entire AST.
-//
-// For example, write either:
-//
-// if x {
-// e.discard(n.Left)
-// } else {
-// e.value(k, n.Left)
-// }
-//
-// or
-//
-// if x {
-// k = e.discardHole()
-// }
-// e.value(k, n.Left)
-//
-// Do NOT write:
-//
-// // BAD: possibly loses side-effects within n.Left
-// if !x {
-// e.value(k, n.Left)
-// }
-
-// stmt evaluates a single Go statement.
-func (e *Escape) stmt(n *Node) {
- if n == nil {
- return
- }
-
- lno := setlineno(n)
- defer func() {
- lineno = lno
- }()
-
- if Debug.m > 2 {
- fmt.Printf("%v:[%d] %v stmt: %v\n", linestr(lineno), e.loopDepth, funcSym(e.curfn), n)
- }
-
- e.stmts(n.Ninit)
-
- switch n.Op {
- default:
- Fatalf("unexpected stmt: %v", n)
-
- case ODCLCONST, ODCLTYPE, OEMPTY, OFALL, OINLMARK:
- // nop
-
- case OBREAK, OCONTINUE, OGOTO:
- // TODO(mdempsky): Handle dead code?
-
- case OBLOCK:
- e.stmts(n.List)
-
- case ODCL:
- // Record loop depth at declaration.
- if !n.Left.isBlank() {
- e.dcl(n.Left)
- }
-
- case OLABEL:
- switch asNode(n.Sym.Label) {
- case &nonlooping:
- if Debug.m > 2 {
- fmt.Printf("%v:%v non-looping label\n", linestr(lineno), n)
- }
- case &looping:
- if Debug.m > 2 {
- fmt.Printf("%v: %v looping label\n", linestr(lineno), n)
- }
- e.loopDepth++
- default:
- Fatalf("label missing tag")
- }
- n.Sym.Label = nil
-
- case OIF:
- e.discard(n.Left)
- e.block(n.Nbody)
- e.block(n.Rlist)
-
- case OFOR, OFORUNTIL:
- e.loopDepth++
- e.discard(n.Left)
- e.stmt(n.Right)
- e.block(n.Nbody)
- e.loopDepth--
-
- case ORANGE:
- // for List = range Right { Nbody }
- e.loopDepth++
- ks := e.addrs(n.List)
- e.block(n.Nbody)
- e.loopDepth--
-
- // Right is evaluated outside the loop.
- k := e.discardHole()
- if len(ks) >= 2 {
- if n.Right.Type.IsArray() {
- k = ks[1].note(n, "range")
- } else {
- k = ks[1].deref(n, "range-deref")
- }
- }
- e.expr(e.later(k), n.Right)
-
- case OSWITCH:
- typesw := n.Left != nil && n.Left.Op == OTYPESW
-
- var ks []EscHole
- for _, cas := range n.List.Slice() { // cases
- if typesw && n.Left.Left != nil {
- cv := cas.Rlist.First()
- k := e.dcl(cv) // type switch variables have no ODCL.
- if cv.Type.HasPointers() {
- ks = append(ks, k.dotType(cv.Type, cas, "switch case"))
- }
- }
-
- e.discards(cas.List)
- e.block(cas.Nbody)
- }
-
- if typesw {
- e.expr(e.teeHole(ks...), n.Left.Right)
- } else {
- e.discard(n.Left)
- }
-
- case OSELECT:
- for _, cas := range n.List.Slice() {
- e.stmt(cas.Left)
- e.block(cas.Nbody)
- }
- case OSELRECV:
- e.assign(n.Left, n.Right, "selrecv", n)
- case OSELRECV2:
- e.assign(n.Left, n.Right, "selrecv", n)
- e.assign(n.List.First(), nil, "selrecv", n)
- case ORECV:
- // TODO(mdempsky): Consider e.discard(n.Left).
- e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit
- case OSEND:
- e.discard(n.Left)
- e.assignHeap(n.Right, "send", n)
-
- case OAS, OASOP:
- e.assign(n.Left, n.Right, "assign", n)
-
- case OAS2:
- for i, nl := range n.List.Slice() {
- e.assign(nl, n.Rlist.Index(i), "assign-pair", n)
- }
-
- case OAS2DOTTYPE: // v, ok = x.(type)
- e.assign(n.List.First(), n.Right, "assign-pair-dot-type", n)
- e.assign(n.List.Second(), nil, "assign-pair-dot-type", n)
- case OAS2MAPR: // v, ok = m[k]
- e.assign(n.List.First(), n.Right, "assign-pair-mapr", n)
- e.assign(n.List.Second(), nil, "assign-pair-mapr", n)
- case OAS2RECV: // v, ok = <-ch
- e.assign(n.List.First(), n.Right, "assign-pair-receive", n)
- e.assign(n.List.Second(), nil, "assign-pair-receive", n)
-
- case OAS2FUNC:
- e.stmts(n.Right.Ninit)
- e.call(e.addrs(n.List), n.Right, nil)
- case ORETURN:
- results := e.curfn.Type.Results().FieldSlice()
- for i, v := range n.List.Slice() {
- e.assign(asNode(results[i].Nname), v, "return", n)
- }
- case OCALLFUNC, OCALLMETH, OCALLINTER, OCLOSE, OCOPY, ODELETE, OPANIC, OPRINT, OPRINTN, ORECOVER:
- e.call(nil, n, nil)
- case OGO, ODEFER:
- e.stmts(n.Left.Ninit)
- e.call(nil, n.Left, n)
-
- case ORETJMP:
- // TODO(mdempsky): What do? esc.go just ignores it.
- }
-}
-
-func (e *Escape) stmts(l Nodes) {
- for _, n := range l.Slice() {
- e.stmt(n)
- }
-}
-
-// block is like stmts, but preserves loopDepth.
-func (e *Escape) block(l Nodes) {
- old := e.loopDepth
- e.stmts(l)
- e.loopDepth = old
-}
-
-// expr models evaluating an expression n and flowing the result into
-// hole k.
-func (e *Escape) expr(k EscHole, n *Node) {
- if n == nil {
- return
- }
- e.stmts(n.Ninit)
- e.exprSkipInit(k, n)
-}
-
-func (e *Escape) exprSkipInit(k EscHole, n *Node) {
- if n == nil {
- return
- }
-
- lno := setlineno(n)
- defer func() {
- lineno = lno
- }()
-
- uintptrEscapesHack := k.uintptrEscapesHack
- k.uintptrEscapesHack = false
-
- if uintptrEscapesHack && n.Op == OCONVNOP && n.Left.Type.IsUnsafePtr() {
- // nop
- } else if k.derefs >= 0 && !n.Type.HasPointers() {
- k = e.discardHole()
- }
-
- switch n.Op {
- default:
- Fatalf("unexpected expr: %v", n)
-
- case OLITERAL, OGETG, OCLOSUREVAR, OTYPE:
- // nop
-
- case ONAME:
- if n.Class() == PFUNC || n.Class() == PEXTERN {
- return
- }
- e.flow(k, e.oldLoc(n))
-
- case OPLUS, ONEG, OBITNOT, ONOT:
- e.discard(n.Left)
- case OADD, OSUB, OOR, OXOR, OMUL, ODIV, OMOD, OLSH, ORSH, OAND, OANDNOT, OEQ, ONE, OLT, OLE, OGT, OGE, OANDAND, OOROR:
- e.discard(n.Left)
- e.discard(n.Right)
-
- case OADDR:
- e.expr(k.addr(n, "address-of"), n.Left) // "address-of"
- case ODEREF:
- e.expr(k.deref(n, "indirection"), n.Left) // "indirection"
- case ODOT, ODOTMETH, ODOTINTER:
- e.expr(k.note(n, "dot"), n.Left)
- case ODOTPTR:
- e.expr(k.deref(n, "dot of pointer"), n.Left) // "dot of pointer"
- case ODOTTYPE, ODOTTYPE2:
- e.expr(k.dotType(n.Type, n, "dot"), n.Left)
- case OINDEX:
- if n.Left.Type.IsArray() {
- e.expr(k.note(n, "fixed-array-index-of"), n.Left)
- } else {
- // TODO(mdempsky): Fix why reason text.
- e.expr(k.deref(n, "dot of pointer"), n.Left)
- }
- e.discard(n.Right)
- case OINDEXMAP:
- e.discard(n.Left)
- e.discard(n.Right)
- case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
- e.expr(k.note(n, "slice"), n.Left)
- low, high, max := n.SliceBounds()
- e.discard(low)
- e.discard(high)
- e.discard(max)
-
- case OCONV, OCONVNOP:
- if checkPtr(e.curfn, 2) && n.Type.IsUnsafePtr() && n.Left.Type.IsPtr() {
- // When -d=checkptr=2 is enabled, treat
- // conversions to unsafe.Pointer as an
- // escaping operation. This allows better
- // runtime instrumentation, since we can more
- // easily detect object boundaries on the heap
- // than the stack.
- e.assignHeap(n.Left, "conversion to unsafe.Pointer", n)
- } else if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() {
- e.unsafeValue(k, n.Left)
- } else {
- e.expr(k, n.Left)
- }
- case OCONVIFACE:
- if !n.Left.Type.IsInterface() && !isdirectiface(n.Left.Type) {
- k = e.spill(k, n)
- }
- e.expr(k.note(n, "interface-converted"), n.Left)
-
- case ORECV:
- e.discard(n.Left)
-
- case OCALLMETH, OCALLFUNC, OCALLINTER, OLEN, OCAP, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCOPY:
- e.call([]EscHole{k}, n, nil)
-
- case ONEW:
- e.spill(k, n)
-
- case OMAKESLICE:
- e.spill(k, n)
- e.discard(n.Left)
- e.discard(n.Right)
- case OMAKECHAN:
- e.discard(n.Left)
- case OMAKEMAP:
- e.spill(k, n)
- e.discard(n.Left)
-
- case ORECOVER:
- // nop
-
- case OCALLPART:
- // Flow the receiver argument to both the closure and
- // to the receiver parameter.
-
- closureK := e.spill(k, n)
-
- m := callpartMethod(n)
-
- // We don't know how the method value will be called
- // later, so conservatively assume the result
- // parameters all flow to the heap.
- //
- // TODO(mdempsky): Change ks into a callback, so that
- // we don't have to create this dummy slice?
- var ks []EscHole
- for i := m.Type.NumResults(); i > 0; i-- {
- ks = append(ks, e.heapHole())
- }
- paramK := e.tagHole(ks, asNode(m.Type.Nname()), m.Type.Recv())
-
- e.expr(e.teeHole(paramK, closureK), n.Left)
-
- case OPTRLIT:
- e.expr(e.spill(k, n), n.Left)
-
- case OARRAYLIT:
- for _, elt := range n.List.Slice() {
- if elt.Op == OKEY {
- elt = elt.Right
- }
- e.expr(k.note(n, "array literal element"), elt)
- }
-
- case OSLICELIT:
- k = e.spill(k, n)
- k.uintptrEscapesHack = uintptrEscapesHack // for ...uintptr parameters
-
- for _, elt := range n.List.Slice() {
- if elt.Op == OKEY {
- elt = elt.Right
- }
- e.expr(k.note(n, "slice-literal-element"), elt)
- }
-
- case OSTRUCTLIT:
- for _, elt := range n.List.Slice() {
- e.expr(k.note(n, "struct literal element"), elt.Left)
- }
-
- case OMAPLIT:
- e.spill(k, n)
-
- // Map keys and values are always stored in the heap.
- for _, elt := range n.List.Slice() {
- e.assignHeap(elt.Left, "map literal key", n)
- e.assignHeap(elt.Right, "map literal value", n)
- }
-
- case OCLOSURE:
- k = e.spill(k, n)
-
- // Link addresses of captured variables to closure.
- for _, v := range n.Func.Closure.Func.Cvars.Slice() {
- if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
- continue
- }
-
- k := k
- if !v.Name.Byval() {
- k = k.addr(v, "reference")
- }
-
- e.expr(k.note(n, "captured by a closure"), v.Name.Defn)
- }
-
- case ORUNES2STR, OBYTES2STR, OSTR2RUNES, OSTR2BYTES, ORUNESTR:
- e.spill(k, n)
- e.discard(n.Left)
-
- case OADDSTR:
- e.spill(k, n)
-
- // Arguments of OADDSTR never escape;
- // runtime.concatstrings makes sure of that.
- e.discards(n.List)
- }
-}
-
-// unsafeValue evaluates a uintptr-typed arithmetic expression looking
-// for conversions from an unsafe.Pointer.
-func (e *Escape) unsafeValue(k EscHole, n *Node) {
- if n.Type.Etype != TUINTPTR {
- Fatalf("unexpected type %v for %v", n.Type, n)
- }
-
- e.stmts(n.Ninit)
-
- switch n.Op {
- case OCONV, OCONVNOP:
- if n.Left.Type.IsUnsafePtr() {
- e.expr(k, n.Left)
- } else {
- e.discard(n.Left)
- }
- case ODOTPTR:
- if isReflectHeaderDataField(n) {
- e.expr(k.deref(n, "reflect.Header.Data"), n.Left)
- } else {
- e.discard(n.Left)
- }
- case OPLUS, ONEG, OBITNOT:
- e.unsafeValue(k, n.Left)
- case OADD, OSUB, OOR, OXOR, OMUL, ODIV, OMOD, OAND, OANDNOT:
- e.unsafeValue(k, n.Left)
- e.unsafeValue(k, n.Right)
- case OLSH, ORSH:
- e.unsafeValue(k, n.Left)
- // RHS need not be uintptr-typed (#32959) and can't meaningfully
- // flow pointers anyway.
- e.discard(n.Right)
- default:
- e.exprSkipInit(e.discardHole(), n)
- }
-}
-
-// discard evaluates an expression n for side-effects, but discards
-// its value.
-func (e *Escape) discard(n *Node) {
- e.expr(e.discardHole(), n)
-}
-
-func (e *Escape) discards(l Nodes) {
- for _, n := range l.Slice() {
- e.discard(n)
- }
-}
-
-// addr evaluates an addressable expression n and returns an EscHole
-// that represents storing into the represented location.
-func (e *Escape) addr(n *Node) EscHole {
- if n == nil || n.isBlank() {
- // Can happen at least in OSELRECV.
- // TODO(mdempsky): Anywhere else?
- return e.discardHole()
- }
-
- k := e.heapHole()
-
- switch n.Op {
- default:
- Fatalf("unexpected addr: %v", n)
- case ONAME:
- if n.Class() == PEXTERN {
- break
- }
- k = e.oldLoc(n).asHole()
- case ODOT:
- k = e.addr(n.Left)
- case OINDEX:
- e.discard(n.Right)
- if n.Left.Type.IsArray() {
- k = e.addr(n.Left)
- } else {
- e.discard(n.Left)
- }
- case ODEREF, ODOTPTR:
- e.discard(n)
- case OINDEXMAP:
- e.discard(n.Left)
- e.assignHeap(n.Right, "key of map put", n)
- }
-
- if !n.Type.HasPointers() {
- k = e.discardHole()
- }
-
- return k
-}
-
-func (e *Escape) addrs(l Nodes) []EscHole {
- var ks []EscHole
- for _, n := range l.Slice() {
- ks = append(ks, e.addr(n))
- }
- return ks
-}
-
-// assign evaluates the assignment dst = src.
-func (e *Escape) assign(dst, src *Node, why string, where *Node) {
- // Filter out some no-op assignments for escape analysis.
- ignore := dst != nil && src != nil && isSelfAssign(dst, src)
- if ignore && Debug.m != 0 {
- Warnl(where.Pos, "%v ignoring self-assignment in %S", funcSym(e.curfn), where)
- }
-
- k := e.addr(dst)
- if dst != nil && dst.Op == ODOTPTR && isReflectHeaderDataField(dst) {
- e.unsafeValue(e.heapHole().note(where, why), src)
- } else {
- if ignore {
- k = e.discardHole()
- }
- e.expr(k.note(where, why), src)
- }
-}
-
-func (e *Escape) assignHeap(src *Node, why string, where *Node) {
- e.expr(e.heapHole().note(where, why), src)
-}
-
-// call evaluates a call expressions, including builtin calls. ks
-// should contain the holes representing where the function callee's
-// results flows; where is the OGO/ODEFER context of the call, if any.
-func (e *Escape) call(ks []EscHole, call, where *Node) {
- topLevelDefer := where != nil && where.Op == ODEFER && e.loopDepth == 1
- if topLevelDefer {
- // force stack allocation of defer record, unless
- // open-coded defers are used (see ssa.go)
- where.Esc = EscNever
- }
-
- argument := func(k EscHole, arg *Node) {
- if topLevelDefer {
- // Top level defers arguments don't escape to
- // heap, but they do need to last until end of
- // function.
- k = e.later(k)
- } else if where != nil {
- k = e.heapHole()
- }
-
- e.expr(k.note(call, "call parameter"), arg)
- }
-
- switch call.Op {
- default:
- Fatalf("unexpected call op: %v", call.Op)
-
- case OCALLFUNC, OCALLMETH, OCALLINTER:
- fixVariadicCall(call)
-
- // Pick out the function callee, if statically known.
- var fn *Node
- switch call.Op {
- case OCALLFUNC:
- switch v := staticValue(call.Left); {
- case v.Op == ONAME && v.Class() == PFUNC:
- fn = v
- case v.Op == OCLOSURE:
- fn = v.Func.Closure.Func.Nname
- }
- case OCALLMETH:
- fn = asNode(call.Left.Type.FuncType().Nname)
- }
-
- fntype := call.Left.Type
- if fn != nil {
- fntype = fn.Type
- }
-
- if ks != nil && fn != nil && e.inMutualBatch(fn) {
- for i, result := range fn.Type.Results().FieldSlice() {
- e.expr(ks[i], asNode(result.Nname))
- }
- }
-
- if r := fntype.Recv(); r != nil {
- argument(e.tagHole(ks, fn, r), call.Left.Left)
- } else {
- // Evaluate callee function expression.
- argument(e.discardHole(), call.Left)
- }
-
- args := call.List.Slice()
- for i, param := range fntype.Params().FieldSlice() {
- argument(e.tagHole(ks, fn, param), args[i])
- }
-
- case OAPPEND:
- args := call.List.Slice()
-
- // Appendee slice may flow directly to the result, if
- // it has enough capacity. Alternatively, a new heap
- // slice might be allocated, and all slice elements
- // might flow to heap.
- appendeeK := ks[0]
- if args[0].Type.Elem().HasPointers() {
- appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
- }
- argument(appendeeK, args[0])
-
- if call.IsDDD() {
- appendedK := e.discardHole()
- if args[1].Type.IsSlice() && args[1].Type.Elem().HasPointers() {
- appendedK = e.heapHole().deref(call, "appended slice...")
- }
- argument(appendedK, args[1])
- } else {
- for _, arg := range args[1:] {
- argument(e.heapHole(), arg)
- }
- }
-
- case OCOPY:
- argument(e.discardHole(), call.Left)
-
- copiedK := e.discardHole()
- if call.Right.Type.IsSlice() && call.Right.Type.Elem().HasPointers() {
- copiedK = e.heapHole().deref(call, "copied slice")
- }
- argument(copiedK, call.Right)
-
- case OPANIC:
- argument(e.heapHole(), call.Left)
-
- case OCOMPLEX:
- argument(e.discardHole(), call.Left)
- argument(e.discardHole(), call.Right)
- case ODELETE, OPRINT, OPRINTN, ORECOVER:
- for _, arg := range call.List.Slice() {
- argument(e.discardHole(), arg)
- }
- case OLEN, OCAP, OREAL, OIMAG, OCLOSE:
- argument(e.discardHole(), call.Left)
- }
-}
-
-// tagHole returns a hole for evaluating an argument passed to param.
-// ks should contain the holes representing where the function
-// callee's results flows. fn is the statically-known callee function,
-// if any.
-func (e *Escape) tagHole(ks []EscHole, fn *Node, param *types.Field) EscHole {
- // If this is a dynamic call, we can't rely on param.Note.
- if fn == nil {
- return e.heapHole()
- }
-
- if e.inMutualBatch(fn) {
- return e.addr(asNode(param.Nname))
- }
-
- // Call to previously tagged function.
-
- if param.Note == uintptrEscapesTag {
- k := e.heapHole()
- k.uintptrEscapesHack = true
- return k
- }
-
- var tagKs []EscHole
-
- esc := ParseLeaks(param.Note)
- if x := esc.Heap(); x >= 0 {
- tagKs = append(tagKs, e.heapHole().shift(x))
- }
-
- if ks != nil {
- for i := 0; i < numEscResults; i++ {
- if x := esc.Result(i); x >= 0 {
- tagKs = append(tagKs, ks[i].shift(x))
- }
- }
- }
-
- return e.teeHole(tagKs...)
-}
-
-// inMutualBatch reports whether function fn is in the batch of
-// mutually recursive functions being analyzed. When this is true,
-// fn has not yet been analyzed, so its parameters and results
-// should be incorporated directly into the flow graph instead of
-// relying on its escape analysis tagging.
-func (e *Escape) inMutualBatch(fn *Node) bool {
- if fn.Name.Defn != nil && fn.Name.Defn.Esc < EscFuncTagged {
- if fn.Name.Defn.Esc == EscFuncUnknown {
- Fatalf("graph inconsistency")
- }
- return true
- }
- return false
-}
-
-// An EscHole represents a context for evaluation a Go
-// expression. E.g., when evaluating p in "x = **p", we'd have a hole
-// with dst==x and derefs==2.
-type EscHole struct {
- dst *EscLocation
- derefs int // >= -1
- notes *EscNote
-
- // uintptrEscapesHack indicates this context is evaluating an
- // argument for a //go:uintptrescapes function.
- uintptrEscapesHack bool
-}
-
-type EscNote struct {
- next *EscNote
- where *Node
- why string
-}
-
-func (k EscHole) note(where *Node, why string) EscHole {
- if where == nil || why == "" {
- Fatalf("note: missing where/why")
- }
- if Debug.m >= 2 || logopt.Enabled() {
- k.notes = &EscNote{
- next: k.notes,
- where: where,
- why: why,
- }
- }
- return k
-}
-
-func (k EscHole) shift(delta int) EscHole {
- k.derefs += delta
- if k.derefs < -1 {
- Fatalf("derefs underflow: %v", k.derefs)
- }
- return k
-}
-
-func (k EscHole) deref(where *Node, why string) EscHole { return k.shift(1).note(where, why) }
-func (k EscHole) addr(where *Node, why string) EscHole { return k.shift(-1).note(where, why) }
-
-func (k EscHole) dotType(t *types.Type, where *Node, why string) EscHole {
- if !t.IsInterface() && !isdirectiface(t) {
- k = k.shift(1)
- }
- return k.note(where, why)
-}
-
-// teeHole returns a new hole that flows into each hole of ks,
-// similar to the Unix tee(1) command.
-func (e *Escape) teeHole(ks ...EscHole) EscHole {
- if len(ks) == 0 {
- return e.discardHole()
- }
- if len(ks) == 1 {
- return ks[0]
- }
- // TODO(mdempsky): Optimize if there's only one non-discard hole?
-
- // Given holes "l1 = _", "l2 = **_", "l3 = *_", ..., create a
- // new temporary location ltmp, wire it into place, and return
- // a hole for "ltmp = _".
- loc := e.newLoc(nil, true)
- for _, k := range ks {
- // N.B., "p = &q" and "p = &tmp; tmp = q" are not
- // semantically equivalent. To combine holes like "l1
- // = _" and "l2 = &_", we'd need to wire them as "l1 =
- // *ltmp" and "l2 = ltmp" and return "ltmp = &_"
- // instead.
- if k.derefs < 0 {
- Fatalf("teeHole: negative derefs")
- }
-
- e.flow(k, loc)
- }
- return loc.asHole()
-}
-
-func (e *Escape) dcl(n *Node) EscHole {
- loc := e.oldLoc(n)
- loc.loopDepth = e.loopDepth
- return loc.asHole()
-}
-
-// spill allocates a new location associated with expression n, flows
-// its address to k, and returns a hole that flows values to it. It's
-// intended for use with most expressions that allocate storage.
-func (e *Escape) spill(k EscHole, n *Node) EscHole {
- loc := e.newLoc(n, true)
- e.flow(k.addr(n, "spill"), loc)
- return loc.asHole()
-}
-
-// later returns a new hole that flows into k, but some time later.
-// Its main effect is to prevent immediate reuse of temporary
-// variables introduced during Order.
-func (e *Escape) later(k EscHole) EscHole {
- loc := e.newLoc(nil, false)
- e.flow(k, loc)
- return loc.asHole()
-}
-
-// canonicalNode returns the canonical *Node that n logically
-// represents.
-func canonicalNode(n *Node) *Node {
- if n != nil && n.Op == ONAME && n.Name.IsClosureVar() {
- n = n.Name.Defn
- if n.Name.IsClosureVar() {
- Fatalf("still closure var")
- }
- }
-
- return n
-}
-
-func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
- if e.curfn == nil {
- Fatalf("e.curfn isn't set")
- }
- if n != nil && n.Type != nil && n.Type.NotInHeap() {
- yyerrorl(n.Pos, "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type)
- }
-
- n = canonicalNode(n)
- loc := &EscLocation{
- n: n,
- curfn: e.curfn,
- loopDepth: e.loopDepth,
- transient: transient,
- }
- e.allLocs = append(e.allLocs, loc)
- if n != nil {
- if n.Op == ONAME && n.Name.Curfn != e.curfn {
- Fatalf("curfn mismatch: %v != %v", n.Name.Curfn, e.curfn)
- }
-
- if n.HasOpt() {
- Fatalf("%v already has a location", n)
- }
- n.SetOpt(loc)
-
- if why := heapAllocReason(n); why != "" {
- e.flow(e.heapHole().addr(n, why), loc)
- }
- }
- return loc
-}
-
-func (e *Escape) oldLoc(n *Node) *EscLocation {
- n = canonicalNode(n)
- return n.Opt().(*EscLocation)
-}
-
-func (l *EscLocation) asHole() EscHole {
- return EscHole{dst: l}
-}
-
-func (e *Escape) flow(k EscHole, src *EscLocation) {
- dst := k.dst
- if dst == &e.blankLoc {
- return
- }
- if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ...
- return
- }
- if dst.escapes && k.derefs < 0 { // dst = &src
- if Debug.m >= 2 || logopt.Enabled() {
- pos := linestr(src.n.Pos)
- if Debug.m >= 2 {
- fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
- }
- explanation := e.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
- if logopt.Enabled() {
- logopt.LogOpt(src.n.Pos, "escapes", "escape", e.curfn.funcname(), fmt.Sprintf("%v escapes to heap", src.n), explanation)
- }
-
- }
- src.escapes = true
- return
- }
-
- // TODO(mdempsky): Deduplicate edges?
- dst.edges = append(dst.edges, EscEdge{src: src, derefs: k.derefs, notes: k.notes})
-}
-
-func (e *Escape) heapHole() EscHole { return e.heapLoc.asHole() }
-func (e *Escape) discardHole() EscHole { return e.blankLoc.asHole() }
-
-// walkAll computes the minimal dereferences between all pairs of
-// locations.
-func (e *Escape) walkAll() {
- // We use a work queue to keep track of locations that we need
- // to visit, and repeatedly walk until we reach a fixed point.
- //
- // We walk once from each location (including the heap), and
- // then re-enqueue each location on its transition from
- // transient->!transient and !escapes->escapes, which can each
- // happen at most once. So we take Θ(len(e.allLocs)) walks.
-
- // LIFO queue, has enough room for e.allLocs and e.heapLoc.
- todo := make([]*EscLocation, 0, len(e.allLocs)+1)
- enqueue := func(loc *EscLocation) {
- if !loc.queued {
- todo = append(todo, loc)
- loc.queued = true
- }
- }
-
- for _, loc := range e.allLocs {
- enqueue(loc)
- }
- enqueue(&e.heapLoc)
-
- var walkgen uint32
- for len(todo) > 0 {
- root := todo[len(todo)-1]
- todo = todo[:len(todo)-1]
- root.queued = false
-
- walkgen++
- e.walkOne(root, walkgen, enqueue)
- }
-}
-
-// walkOne computes the minimal number of dereferences from root to
-// all other locations.
-func (e *Escape) walkOne(root *EscLocation, walkgen uint32, enqueue func(*EscLocation)) {
- // The data flow graph has negative edges (from addressing
- // operations), so we use the Bellman-Ford algorithm. However,
- // we don't have to worry about infinite negative cycles since
- // we bound intermediate dereference counts to 0.
-
- root.walkgen = walkgen
- root.derefs = 0
- root.dst = nil
-
- todo := []*EscLocation{root} // LIFO queue
- for len(todo) > 0 {
- l := todo[len(todo)-1]
- todo = todo[:len(todo)-1]
-
- base := l.derefs
-
- // If l.derefs < 0, then l's address flows to root.
- addressOf := base < 0
- if addressOf {
- // For a flow path like "root = &l; l = x",
- // l's address flows to root, but x's does
- // not. We recognize this by lower bounding
- // base at 0.
- base = 0
-
- // If l's address flows to a non-transient
- // location, then l can't be transiently
- // allocated.
- if !root.transient && l.transient {
- l.transient = false
- enqueue(l)
- }
- }
-
- if e.outlives(root, l) {
- // l's value flows to root. If l is a function
- // parameter and root is the heap or a
- // corresponding result parameter, then record
- // that value flow for tagging the function
- // later.
- if l.isName(PPARAM) {
- if (logopt.Enabled() || Debug.m >= 2) && !l.escapes {
- if Debug.m >= 2 {
- fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", linestr(l.n.Pos), l.n, e.explainLoc(root), base)
- }
- explanation := e.explainPath(root, l)
- if logopt.Enabled() {
- logopt.LogOpt(l.n.Pos, "leak", "escape", e.curfn.funcname(),
- fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, e.explainLoc(root), base), explanation)
- }
- }
- l.leakTo(root, base)
- }
-
- // If l's address flows somewhere that
- // outlives it, then l needs to be heap
- // allocated.
- if addressOf && !l.escapes {
- if logopt.Enabled() || Debug.m >= 2 {
- if Debug.m >= 2 {
- fmt.Printf("%s: %v escapes to heap:\n", linestr(l.n.Pos), l.n)
- }
- explanation := e.explainPath(root, l)
- if logopt.Enabled() {
- logopt.LogOpt(l.n.Pos, "escape", "escape", e.curfn.funcname(), fmt.Sprintf("%v escapes to heap", l.n), explanation)
- }
- }
- l.escapes = true
- enqueue(l)
- continue
- }
- }
-
- for i, edge := range l.edges {
- if edge.src.escapes {
- continue
- }
- derefs := base + edge.derefs
- if edge.src.walkgen != walkgen || edge.src.derefs > derefs {
- edge.src.walkgen = walkgen
- edge.src.derefs = derefs
- edge.src.dst = l
- edge.src.dstEdgeIdx = i
- todo = append(todo, edge.src)
- }
- }
- }
-}
-
-// explainPath prints an explanation of how src flows to the walk root.
-func (e *Escape) explainPath(root, src *EscLocation) []*logopt.LoggedOpt {
- visited := make(map[*EscLocation]bool)
- pos := linestr(src.n.Pos)
- var explanation []*logopt.LoggedOpt
- for {
- // Prevent infinite loop.
- if visited[src] {
- if Debug.m >= 2 {
- fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
- }
- break
- }
- visited[src] = true
- dst := src.dst
- edge := &dst.edges[src.dstEdgeIdx]
- if edge.src != src {
- Fatalf("path inconsistency: %v != %v", edge.src, src)
- }
-
- explanation = e.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
-
- if dst == root {
- break
- }
- src = dst
- }
-
- return explanation
-}
-
-func (e *Escape) explainFlow(pos string, dst, srcloc *EscLocation, derefs int, notes *EscNote, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
- ops := "&"
- if derefs >= 0 {
- ops = strings.Repeat("*", derefs)
- }
- print := Debug.m >= 2
-
- flow := fmt.Sprintf(" flow: %s = %s%v:", e.explainLoc(dst), ops, e.explainLoc(srcloc))
- if print {
- fmt.Printf("%s:%s\n", pos, flow)
- }
- if logopt.Enabled() {
- var epos src.XPos
- if notes != nil {
- epos = notes.where.Pos
- } else if srcloc != nil && srcloc.n != nil {
- epos = srcloc.n.Pos
- }
- explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", e.curfn.funcname(), flow))
- }
-
- for note := notes; note != nil; note = note.next {
- if print {
- fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, linestr(note.where.Pos))
- }
- if logopt.Enabled() {
- explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos, "escflow", "escape", e.curfn.funcname(),
- fmt.Sprintf(" from %v (%v)", note.where, note.why)))
- }
- }
- return explanation
-}
-
-func (e *Escape) explainLoc(l *EscLocation) string {
- if l == &e.heapLoc {
- return "{heap}"
- }
- if l.n == nil {
- // TODO(mdempsky): Omit entirely.
- return "{temp}"
- }
- if l.n.Op == ONAME {
- return fmt.Sprintf("%v", l.n)
- }
- return fmt.Sprintf("{storage for %v}", l.n)
-}
-
-// outlives reports whether values stored in l may survive beyond
-// other's lifetime if stack allocated.
-func (e *Escape) outlives(l, other *EscLocation) bool {
- // The heap outlives everything.
- if l.escapes {
- return true
- }
-
- // We don't know what callers do with returned values, so
- // pessimistically we need to assume they flow to the heap and
- // outlive everything too.
- if l.isName(PPARAMOUT) {
- // Exception: Directly called closures can return
- // locations allocated outside of them without forcing
- // them to the heap. For example:
- //
- // var u int // okay to stack allocate
- // *(func() *int { return &u }()) = 42
- if containsClosure(other.curfn, l.curfn) && l.curfn.Func.Closure.Func.Top&ctxCallee != 0 {
- return false
- }
-
- return true
- }
-
- // If l and other are within the same function, then l
- // outlives other if it was declared outside other's loop
- // scope. For example:
- //
- // var l *int
- // for {
- // l = new(int)
- // }
- if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
- return true
- }
-
- // If other is declared within a child closure of where l is
- // declared, then l outlives it. For example:
- //
- // var l *int
- // func() {
- // l = new(int)
- // }
- if containsClosure(l.curfn, other.curfn) {
- return true
- }
-
- return false
-}
-
-// containsClosure reports whether c is a closure contained within f.
-func containsClosure(f, c *Node) bool {
- if f.Op != ODCLFUNC || c.Op != ODCLFUNC {
- Fatalf("bad containsClosure: %v, %v", f, c)
- }
-
- // Common case.
- if f == c {
- return false
- }
-
- // Closures within function Foo are named like "Foo.funcN..."
- // TODO(mdempsky): Better way to recognize this.
- fn := f.Func.Nname.Sym.Name
- cn := c.Func.Nname.Sym.Name
- return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
-}
-
-// leak records that parameter l leaks to sink.
-func (l *EscLocation) leakTo(sink *EscLocation, 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(PPARAMOUT) && sink.curfn == l.curfn {
- // TODO(mdempsky): Eliminate dependency on Vargen here.
- ri := int(sink.n.Name.Vargen) - 1
- if ri < numEscResults {
- // Leak to result parameter.
- l.paramEsc.AddResult(ri, derefs)
- return
- }
- }
-
- // Otherwise, record as heap leak.
- l.paramEsc.AddHeap(derefs)
-}
-
-func (e *Escape) finish(fns []*Node) {
- // Record parameter tags for package export data.
- for _, fn := range fns {
- fn.Esc = EscFuncTagged
-
- narg := 0
- for _, fs := range &types.RecvsParams {
- for _, f := range fs(fn.Type).Fields().Slice() {
- narg++
- f.Note = e.paramTag(fn, narg, f)
- }
- }
- }
-
- for _, loc := range e.allLocs {
- n := loc.n
- if n == nil {
- continue
- }
- n.SetOpt(nil)
-
- // Update n.Esc based on escape analysis results.
-
- if loc.escapes {
- if n.Op != ONAME {
- if Debug.m != 0 {
- Warnl(n.Pos, "%S escapes to heap", n)
- }
- if logopt.Enabled() {
- logopt.LogOpt(n.Pos, "escape", "escape", e.curfn.funcname())
- }
- }
- n.Esc = EscHeap
- addrescapes(n)
- } else {
- if Debug.m != 0 && n.Op != ONAME {
- Warnl(n.Pos, "%S does not escape", n)
- }
- n.Esc = EscNone
- if loc.transient {
- n.SetTransient(true)
- }
- }
- }
-}
-
-func (l *EscLocation) isName(c Class) bool {
- return l.n != nil && l.n.Op == ONAME && l.n.Class() == c
-}
-
-const numEscResults = 7
-
-// An EscLeaks represents a set of assignment flows from a parameter
-// to the heap or to any of its function's (first numEscResults)
-// result parameters.
-type EscLeaks [1 + numEscResults]uint8
-
-// Empty reports whether l is an empty set (i.e., no assignment flows).
-func (l EscLeaks) Empty() bool { return l == EscLeaks{} }
-
-// Heap returns the minimum deref count of any assignment flow from l
-// to the heap. If no such flows exist, Heap returns -1.
-func (l EscLeaks) Heap() int { return l.get(0) }
-
-// Result returns the minimum deref count of any assignment flow from
-// l to its function's i'th result parameter. If no such flows exist,
-// Result returns -1.
-func (l EscLeaks) Result(i int) int { return l.get(1 + i) }
-
-// AddHeap adds an assignment flow from l to the heap.
-func (l *EscLeaks) AddHeap(derefs int) { l.add(0, derefs) }
-
-// AddResult adds an assignment flow from l to its function's i'th
-// result parameter.
-func (l *EscLeaks) AddResult(i, derefs int) { l.add(1+i, derefs) }
-
-func (l *EscLeaks) setResult(i, derefs int) { l.set(1+i, derefs) }
-
-func (l EscLeaks) get(i int) int { return int(l[i]) - 1 }
-
-func (l *EscLeaks) add(i, derefs int) {
- if old := l.get(i); old < 0 || derefs < old {
- l.set(i, derefs)
- }
-}
-
-func (l *EscLeaks) set(i, derefs int) {
- v := derefs + 1
- if v < 0 {
- Fatalf("invalid derefs count: %v", derefs)
- }
- if v > math.MaxUint8 {
- v = math.MaxUint8
- }
-
- l[i] = uint8(v)
-}
-
-// Optimize removes result flow paths that are equal in length or
-// longer than the shortest heap flow path.
-func (l *EscLeaks) Optimize() {
- // If we have a path to the heap, then there's no use in
- // keeping equal or longer paths elsewhere.
- if x := l.Heap(); x >= 0 {
- for i := 0; i < numEscResults; i++ {
- if l.Result(i) >= x {
- l.setResult(i, -1)
- }
- }
- }
-}
-
-var leakTagCache = map[EscLeaks]string{}
-
-// Encode converts l into a binary string for export data.
-func (l EscLeaks) Encode() string {
- if l.Heap() == 0 {
- // Space optimization: empty string encodes more
- // efficiently in export data.
- return ""
- }
- if s, ok := leakTagCache[l]; ok {
- return s
- }
-
- n := len(l)
- for n > 0 && l[n-1] == 0 {
- n--
- }
- s := "esc:" + string(l[:n])
- leakTagCache[l] = s
- return s
-}
-
-// ParseLeaks parses a binary string representing an EscLeaks.
-func ParseLeaks(s string) EscLeaks {
- var l EscLeaks
- if !strings.HasPrefix(s, "esc:") {
- l.AddHeap(0)
- return l
- }
- copy(l[:], s[4:])
- return l
-}
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index c6917e0f810dd..356fcfa6719e2 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -5,229 +5,152 @@
package gc
import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/inline"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/bio"
- "cmd/internal/src"
"fmt"
-)
-
-var (
- Debug_export int // if set, print debugging information about export data
+ "go/constant"
)
func exportf(bout *bio.Writer, format string, args ...interface{}) {
fmt.Fprintf(bout, format, args...)
- if Debug_export != 0 {
+ if base.Debug.Export != 0 {
fmt.Printf(format, args...)
}
}
-var asmlist []*Node
-
-// exportsym marks n for export (or reexport).
-func exportsym(n *Node) {
- if n.Sym.OnExportList() {
- return
- }
- n.Sym.SetOnExportList(true)
-
- if Debug.E != 0 {
- fmt.Printf("export symbol %v\n", n.Sym)
- }
-
- exportlist = append(exportlist, n)
-}
-
-func initname(s string) bool {
- return s == "init"
-}
-
-func autoexport(n *Node, ctxt Class) {
- if n.Sym.Pkg != localpkg {
- return
- }
- if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
- return
- }
- if n.Type != nil && n.Type.IsKind(TFUNC) && n.IsMethod() {
- return
+func dumpexport(bout *bio.Writer) {
+ p := &exporter{marked: make(map[*types.Type]bool)}
+ for _, n := range typecheck.Target.Exports {
+ p.markObject(n)
}
- if types.IsExported(n.Sym.Name) || initname(n.Sym.Name) {
- exportsym(n)
- }
- if asmhdr != "" && !n.Sym.Asm() {
- n.Sym.SetAsm(true)
- asmlist = append(asmlist, n)
- }
-}
-
-func dumpexport(bout *bio.Writer) {
// The linker also looks for the $$ marker - use char after $$ to distinguish format.
exportf(bout, "\n$$B\n") // indicate binary export format
off := bout.Offset()
- iexport(bout.Writer)
+ typecheck.WriteExports(bout.Writer)
size := bout.Offset() - off
exportf(bout, "\n$$\n")
- if Debug_export != 0 {
- fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", myimportpath, size)
- }
-}
-
-func importsym(ipkg *types.Pkg, s *types.Sym, op Op) *Node {
- n := asNode(s.PkgDef())
- if n == nil {
- // iimport should have created a stub ONONAME
- // declaration for all imported symbols. The exception
- // is declarations for Runtimepkg, which are populated
- // by loadsys instead.
- if s.Pkg != Runtimepkg {
- Fatalf("missing ONONAME for %v\n", s)
- }
-
- n = dclname(s)
- s.SetPkgDef(asTypesNode(n))
- s.Importdef = ipkg
- }
- if n.Op != ONONAME && n.Op != op {
- redeclare(lineno, s, fmt.Sprintf("during import %q", ipkg.Path))
+ if base.Debug.Export != 0 {
+ fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, size)
}
- return n
}
-// importtype returns the named type declared by symbol s.
-// If no such type has been declared yet, a forward declaration is returned.
-// ipkg is the package being imported
-func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
- n := importsym(ipkg, s, OTYPE)
- if n.Op != OTYPE {
- t := types.New(TFORW)
- t.Sym = s
- t.Nod = asTypesNode(n)
-
- n.Op = OTYPE
- n.Pos = pos
- n.Type = t
- n.SetClass(PEXTERN)
- }
-
- t := n.Type
- if t == nil {
- Fatalf("importtype %v", s)
+func dumpasmhdr() {
+ b, err := bio.Create(base.Flag.AsmHdr)
+ if err != nil {
+ base.Fatalf("%v", err)
}
- return t
-}
+ fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", types.LocalPkg.Name)
+ for _, n := range typecheck.Target.Asms {
+ if n.Sym().IsBlank() {
+ continue
+ }
+ switch n.Op() {
+ case ir.OLITERAL:
+ t := n.Val().Kind()
+ if t == constant.Float || t == constant.Complex {
+ break
+ }
+ fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym().Name, n.Val())
-// importobj declares symbol s as an imported object representable by op.
-// ipkg is the package being imported
-func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op Op, ctxt Class, t *types.Type) *Node {
- n := importsym(ipkg, s, op)
- if n.Op != ONONAME {
- if n.Op == op && (n.Class() != ctxt || !types.Identical(n.Type, t)) {
- redeclare(lineno, s, fmt.Sprintf("during import %q", ipkg.Path))
+ case ir.OTYPE:
+ t := n.Type()
+ if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
+ break
+ }
+ fmt.Fprintf(b, "#define %s__size %d\n", n.Sym().Name, int(t.Width))
+ for _, f := range t.Fields().Slice() {
+ if !f.Sym.IsBlank() {
+ fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym().Name, f.Sym.Name, int(f.Offset))
+ }
+ }
}
- return nil
}
- n.Op = op
- n.Pos = pos
- n.SetClass(ctxt)
- if ctxt == PFUNC {
- n.Sym.SetFunc(true)
- }
- n.Type = t
- return n
+ b.Close()
}
-// importconst declares symbol s as an imported constant with type t and value val.
-// ipkg is the package being imported
-func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val Val) {
- n := importobj(ipkg, pos, s, OLITERAL, PEXTERN, t)
- if n == nil { // TODO: Check that value matches.
- return
- }
-
- n.SetVal(val)
-
- if Debug.E != 0 {
- fmt.Printf("import const %v %L = %v\n", s, t, val)
- }
+type exporter struct {
+ marked map[*types.Type]bool // types already seen by markType
}
-// importfunc declares symbol s as an imported function with type t.
-// ipkg is the package being imported
-func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
- n := importobj(ipkg, pos, s, ONAME, PFUNC, t)
- if n == nil {
- return
+// markObject visits a reachable object.
+func (p *exporter) markObject(n ir.Node) {
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ if n.Class == ir.PFUNC {
+ inline.Inline_Flood(n, typecheck.Export)
+ }
}
- n.Func = new(Func)
- t.SetNname(asTypesNode(n))
-
- if Debug.E != 0 {
- fmt.Printf("import func %v%S\n", s, t)
- }
+ p.markType(n.Type())
}
-// importvar declares symbol s as an imported variable with type t.
-// ipkg is the package being imported
-func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
- n := importobj(ipkg, pos, s, ONAME, PEXTERN, t)
- if n == nil {
+// markType recursively visits types reachable from t to identify
+// functions whose inline bodies may be needed.
+func (p *exporter) markType(t *types.Type) {
+ if p.marked[t] {
return
}
-
- if Debug.E != 0 {
- fmt.Printf("import var %v %L\n", s, t)
+ p.marked[t] = true
+
+ // If this is a named type, mark all of its associated
+ // methods. Skip interface types because t.Methods contains
+ // only their unexpanded method set (i.e., exclusive of
+ // interface embeddings), and the switch statement below
+ // handles their full method set.
+ if t.Sym() != nil && t.Kind() != types.TINTER {
+ for _, m := range t.Methods().Slice() {
+ if types.IsExported(m.Sym.Name) {
+ p.markObject(ir.AsNode(m.Nname))
+ }
+ }
}
-}
-// importalias declares symbol s as an imported type alias with type t.
-// ipkg is the package being imported
-func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
- n := importobj(ipkg, pos, s, OTYPE, PEXTERN, t)
- if n == nil {
- return
- }
+ // Recursively mark any types that can be produced given a
+ // value of type t: dereferencing a pointer; indexing or
+ // iterating over an array, slice, or map; receiving from a
+ // channel; accessing a struct field or interface method; or
+ // calling a function.
+ //
+ // Notably, we don't mark function parameter types, because
+ // the user already needs some way to construct values of
+ // those types.
+ switch t.Kind() {
+ case types.TPTR, types.TARRAY, types.TSLICE:
+ p.markType(t.Elem())
+
+ case types.TCHAN:
+ if t.ChanDir().CanRecv() {
+ p.markType(t.Elem())
+ }
- if Debug.E != 0 {
- fmt.Printf("import type %v = %L\n", s, t)
- }
-}
+ case types.TMAP:
+ p.markType(t.Key())
+ p.markType(t.Elem())
-func dumpasmhdr() {
- b, err := bio.Create(asmhdr)
- if err != nil {
- Fatalf("%v", err)
- }
- fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", localpkg.Name)
- for _, n := range asmlist {
- if n.Sym.IsBlank() {
- continue
- }
- switch n.Op {
- case OLITERAL:
- t := n.Val().Ctype()
- if t == CTFLT || t == CTCPLX {
- break
+ case types.TSTRUCT:
+ for _, f := range t.FieldSlice() {
+ if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
+ p.markType(f.Type)
}
- fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym.Name, n.Val())
+ }
- case OTYPE:
- t := n.Type
- if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
- break
- }
- fmt.Fprintf(b, "#define %s__size %d\n", n.Sym.Name, int(t.Width))
- for _, f := range t.Fields().Slice() {
- if !f.Sym.IsBlank() {
- fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, f.Sym.Name, int(f.Offset))
- }
+ case types.TFUNC:
+ for _, f := range t.Results().FieldSlice() {
+ p.markType(f.Type)
+ }
+
+ case types.TINTER:
+ for _, f := range t.FieldSlice() {
+ if types.IsExported(f.Sym.Name) {
+ p.markType(f.Type)
}
}
}
-
- b.Close()
}
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
deleted file mode 100644
index f92f5d0e884a7..0000000000000
--- a/src/cmd/compile/internal/gc/fmt.go
+++ /dev/null
@@ -1,1986 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
- "bytes"
- "cmd/compile/internal/types"
- "cmd/internal/src"
- "fmt"
- "io"
- "strconv"
- "strings"
- "sync"
- "unicode/utf8"
-)
-
-// A FmtFlag value is a set of flags (or 0).
-// They control how the Xconv functions format their values.
-// See the respective function's documentation for details.
-type FmtFlag int
-
-const ( // fmt.Format flag/prec or verb
- FmtLeft FmtFlag = 1 << iota // '-'
- FmtSharp // '#'
- FmtSign // '+'
- FmtUnsigned // internal use only (historic: u flag)
- FmtShort // verb == 'S' (historic: h flag)
- FmtLong // verb == 'L' (historic: l flag)
- FmtComma // '.' (== hasPrec) (historic: , flag)
- FmtByte // '0' (historic: hh flag)
-)
-
-// fmtFlag computes the (internal) FmtFlag
-// value given the fmt.State and format verb.
-func fmtFlag(s fmt.State, verb rune) FmtFlag {
- var flag FmtFlag
- if s.Flag('-') {
- flag |= FmtLeft
- }
- if s.Flag('#') {
- flag |= FmtSharp
- }
- if s.Flag('+') {
- flag |= FmtSign
- }
- if s.Flag(' ') {
- Fatalf("FmtUnsigned in format string")
- }
- if _, ok := s.Precision(); ok {
- flag |= FmtComma
- }
- if s.Flag('0') {
- flag |= FmtByte
- }
- switch verb {
- case 'S':
- flag |= FmtShort
- case 'L':
- flag |= FmtLong
- }
- return flag
-}
-
-// Format conversions:
-// TODO(gri) verify these; eliminate those not used anymore
-//
-// %v Op Node opcodes
-// Flags: #: print Go syntax (automatic unless mode == FDbg)
-//
-// %j *Node Node details
-// Flags: 0: suppresses things not relevant until walk
-//
-// %v *Val Constant values
-//
-// %v *types.Sym Symbols
-// %S unqualified identifier in any mode
-// Flags: +,- #: mode (see below)
-// 0: in export mode: unqualified identifier if exported, qualified if not
-//
-// %v *types.Type Types
-// %S omit "func" and receiver in function types
-// %L definition instead of name.
-// Flags: +,- #: mode (see below)
-// ' ' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
-//
-// %v *Node Nodes
-// %S (only in +/debug mode) suppress recursion
-// %L (only in Error mode) print "foo (type Bar)"
-// Flags: +,- #: mode (see below)
-//
-// %v Nodes Node lists
-// Flags: those of *Node
-// .: separate items with ',' instead of ';'
-
-// *types.Sym, *types.Type, and *Node types use the flags below to set the format mode
-const (
- FErr fmtMode = iota
- FDbg
- FTypeId
- FTypeIdName // same as FTypeId, but use package name instead of prefix
-)
-
-// The mode flags '+', '-', and '#' are sticky; they persist through
-// recursions of *Node, *types.Type, and *types.Sym values. The ' ' flag is
-// sticky only on *types.Type recursions and only used in %-/*types.Sym mode.
-//
-// Example: given a *types.Sym: %+v %#v %-v print an identifier properly qualified for debug/export/internal mode
-
-// Useful format combinations:
-// TODO(gri): verify these
-//
-// *Node, Nodes:
-// %+v multiline recursive debug dump of *Node/Nodes
-// %+S non-recursive debug dump
-//
-// *Node:
-// %#v Go format
-// %L "foo (type Bar)" for error messages
-//
-// *types.Type:
-// %#v Go format
-// %#L type definition instead of name
-// %#S omit "func" and receiver in function signature
-//
-// %-v type identifiers
-// %-S type identifiers without "func" and arg names in type signatures (methodsym)
-// %- v type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
-
-// update returns the results of applying f to mode.
-func (f FmtFlag) update(mode fmtMode) (FmtFlag, fmtMode) {
- switch {
- case f&FmtSign != 0:
- mode = FDbg
- case f&FmtSharp != 0:
- // ignore (textual export format no longer supported)
- case f&FmtUnsigned != 0:
- mode = FTypeIdName
- case f&FmtLeft != 0:
- mode = FTypeId
- }
-
- f &^= FmtSharp | FmtLeft | FmtSign
- return f, mode
-}
-
-var goopnames = []string{
- OADDR: "&",
- OADD: "+",
- OADDSTR: "+",
- OALIGNOF: "unsafe.Alignof",
- OANDAND: "&&",
- OANDNOT: "&^",
- OAND: "&",
- OAPPEND: "append",
- OAS: "=",
- OAS2: "=",
- OBREAK: "break",
- OCALL: "function call", // not actual syntax
- OCAP: "cap",
- OCASE: "case",
- OCLOSE: "close",
- OCOMPLEX: "complex",
- OBITNOT: "^",
- OCONTINUE: "continue",
- OCOPY: "copy",
- ODELETE: "delete",
- ODEFER: "defer",
- ODIV: "/",
- OEQ: "==",
- OFALL: "fallthrough",
- OFOR: "for",
- OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892
- OGE: ">=",
- OGOTO: "goto",
- OGT: ">",
- OIF: "if",
- OIMAG: "imag",
- OINLMARK: "inlmark",
- ODEREF: "*",
- OLEN: "len",
- OLE: "<=",
- OLSH: "<<",
- OLT: "<",
- OMAKE: "make",
- ONEG: "-",
- OMOD: "%",
- OMUL: "*",
- ONEW: "new",
- ONE: "!=",
- ONOT: "!",
- OOFFSETOF: "unsafe.Offsetof",
- OOROR: "||",
- OOR: "|",
- OPANIC: "panic",
- OPLUS: "+",
- OPRINTN: "println",
- OPRINT: "print",
- ORANGE: "range",
- OREAL: "real",
- ORECV: "<-",
- ORECOVER: "recover",
- ORETURN: "return",
- ORSH: ">>",
- OSELECT: "select",
- OSEND: "<-",
- OSIZEOF: "unsafe.Sizeof",
- OSUB: "-",
- OSWITCH: "switch",
- OXOR: "^",
-}
-
-func (o Op) GoString() string {
- return fmt.Sprintf("%#v", o)
-}
-
-func (o Op) format(s fmt.State, verb rune, mode fmtMode) {
- switch verb {
- case 'v':
- o.oconv(s, fmtFlag(s, verb), mode)
-
- default:
- fmt.Fprintf(s, "%%!%c(Op=%d)", verb, int(o))
- }
-}
-
-func (o Op) oconv(s fmt.State, flag FmtFlag, mode fmtMode) {
- if flag&FmtSharp != 0 || mode != FDbg {
- if int(o) < len(goopnames) && goopnames[o] != "" {
- fmt.Fprint(s, goopnames[o])
- return
- }
- }
-
- // 'o.String()' instead of just 'o' to avoid infinite recursion
- fmt.Fprint(s, o.String())
-}
-
-type (
- fmtMode int
-
- fmtNodeErr Node
- fmtNodeDbg Node
- fmtNodeTypeId Node
- fmtNodeTypeIdName Node
-
- fmtOpErr Op
- fmtOpDbg Op
- fmtOpTypeId Op
- fmtOpTypeIdName Op
-
- fmtTypeErr types.Type
- fmtTypeDbg types.Type
- fmtTypeTypeId types.Type
- fmtTypeTypeIdName types.Type
-
- fmtSymErr types.Sym
- fmtSymDbg types.Sym
- fmtSymTypeId types.Sym
- fmtSymTypeIdName types.Sym
-
- fmtNodesErr Nodes
- fmtNodesDbg Nodes
- fmtNodesTypeId Nodes
- fmtNodesTypeIdName Nodes
-)
-
-func (n *fmtNodeErr) Format(s fmt.State, verb rune) { (*Node)(n).format(s, verb, FErr) }
-func (n *fmtNodeDbg) Format(s fmt.State, verb rune) { (*Node)(n).format(s, verb, FDbg) }
-func (n *fmtNodeTypeId) Format(s fmt.State, verb rune) { (*Node)(n).format(s, verb, FTypeId) }
-func (n *fmtNodeTypeIdName) Format(s fmt.State, verb rune) { (*Node)(n).format(s, verb, FTypeIdName) }
-func (n *Node) Format(s fmt.State, verb rune) { n.format(s, verb, FErr) }
-
-func (o fmtOpErr) Format(s fmt.State, verb rune) { Op(o).format(s, verb, FErr) }
-func (o fmtOpDbg) Format(s fmt.State, verb rune) { Op(o).format(s, verb, FDbg) }
-func (o fmtOpTypeId) Format(s fmt.State, verb rune) { Op(o).format(s, verb, FTypeId) }
-func (o fmtOpTypeIdName) Format(s fmt.State, verb rune) { Op(o).format(s, verb, FTypeIdName) }
-func (o Op) Format(s fmt.State, verb rune) { o.format(s, verb, FErr) }
-
-func (t *fmtTypeErr) Format(s fmt.State, verb rune) { typeFormat((*types.Type)(t), s, verb, FErr) }
-func (t *fmtTypeDbg) Format(s fmt.State, verb rune) { typeFormat((*types.Type)(t), s, verb, FDbg) }
-func (t *fmtTypeTypeId) Format(s fmt.State, verb rune) {
- typeFormat((*types.Type)(t), s, verb, FTypeId)
-}
-func (t *fmtTypeTypeIdName) Format(s fmt.State, verb rune) {
- typeFormat((*types.Type)(t), s, verb, FTypeIdName)
-}
-
-// func (t *types.Type) Format(s fmt.State, verb rune) // in package types
-
-func (y *fmtSymErr) Format(s fmt.State, verb rune) { symFormat((*types.Sym)(y), s, verb, FErr) }
-func (y *fmtSymDbg) Format(s fmt.State, verb rune) { symFormat((*types.Sym)(y), s, verb, FDbg) }
-func (y *fmtSymTypeId) Format(s fmt.State, verb rune) { symFormat((*types.Sym)(y), s, verb, FTypeId) }
-func (y *fmtSymTypeIdName) Format(s fmt.State, verb rune) {
- symFormat((*types.Sym)(y), s, verb, FTypeIdName)
-}
-
-// func (y *types.Sym) Format(s fmt.State, verb rune) // in package types { y.format(s, verb, FErr) }
-
-func (n fmtNodesErr) Format(s fmt.State, verb rune) { (Nodes)(n).format(s, verb, FErr) }
-func (n fmtNodesDbg) Format(s fmt.State, verb rune) { (Nodes)(n).format(s, verb, FDbg) }
-func (n fmtNodesTypeId) Format(s fmt.State, verb rune) { (Nodes)(n).format(s, verb, FTypeId) }
-func (n fmtNodesTypeIdName) Format(s fmt.State, verb rune) { (Nodes)(n).format(s, verb, FTypeIdName) }
-func (n Nodes) Format(s fmt.State, verb rune) { n.format(s, verb, FErr) }
-
-func (m fmtMode) Fprintf(s fmt.State, format string, args ...interface{}) {
- m.prepareArgs(args)
- fmt.Fprintf(s, format, args...)
-}
-
-func (m fmtMode) Sprintf(format string, args ...interface{}) string {
- m.prepareArgs(args)
- return fmt.Sprintf(format, args...)
-}
-
-func (m fmtMode) Sprint(args ...interface{}) string {
- m.prepareArgs(args)
- return fmt.Sprint(args...)
-}
-
-func (m fmtMode) prepareArgs(args []interface{}) {
- switch m {
- case FErr:
- for i, arg := range args {
- switch arg := arg.(type) {
- case Op:
- args[i] = fmtOpErr(arg)
- case *Node:
- args[i] = (*fmtNodeErr)(arg)
- case *types.Type:
- args[i] = (*fmtTypeErr)(arg)
- case *types.Sym:
- args[i] = (*fmtSymErr)(arg)
- case Nodes:
- args[i] = fmtNodesErr(arg)
- case Val, int32, int64, string, types.EType:
- // OK: printing these types doesn't depend on mode
- default:
- Fatalf("mode.prepareArgs type %T", arg)
- }
- }
- case FDbg:
- for i, arg := range args {
- switch arg := arg.(type) {
- case Op:
- args[i] = fmtOpDbg(arg)
- case *Node:
- args[i] = (*fmtNodeDbg)(arg)
- case *types.Type:
- args[i] = (*fmtTypeDbg)(arg)
- case *types.Sym:
- args[i] = (*fmtSymDbg)(arg)
- case Nodes:
- args[i] = fmtNodesDbg(arg)
- case Val, int32, int64, string, types.EType:
- // OK: printing these types doesn't depend on mode
- default:
- Fatalf("mode.prepareArgs type %T", arg)
- }
- }
- case FTypeId:
- for i, arg := range args {
- switch arg := arg.(type) {
- case Op:
- args[i] = fmtOpTypeId(arg)
- case *Node:
- args[i] = (*fmtNodeTypeId)(arg)
- case *types.Type:
- args[i] = (*fmtTypeTypeId)(arg)
- case *types.Sym:
- args[i] = (*fmtSymTypeId)(arg)
- case Nodes:
- args[i] = fmtNodesTypeId(arg)
- case Val, int32, int64, string, types.EType:
- // OK: printing these types doesn't depend on mode
- default:
- Fatalf("mode.prepareArgs type %T", arg)
- }
- }
- case FTypeIdName:
- for i, arg := range args {
- switch arg := arg.(type) {
- case Op:
- args[i] = fmtOpTypeIdName(arg)
- case *Node:
- args[i] = (*fmtNodeTypeIdName)(arg)
- case *types.Type:
- args[i] = (*fmtTypeTypeIdName)(arg)
- case *types.Sym:
- args[i] = (*fmtSymTypeIdName)(arg)
- case Nodes:
- args[i] = fmtNodesTypeIdName(arg)
- case Val, int32, int64, string, types.EType:
- // OK: printing these types doesn't depend on mode
- default:
- Fatalf("mode.prepareArgs type %T", arg)
- }
- }
- default:
- Fatalf("mode.prepareArgs mode %d", m)
- }
-}
-
-func (n *Node) format(s fmt.State, verb rune, mode fmtMode) {
- switch verb {
- case 'v', 'S', 'L':
- n.nconv(s, fmtFlag(s, verb), mode)
-
- case 'j':
- n.jconv(s, fmtFlag(s, verb))
-
- default:
- fmt.Fprintf(s, "%%!%c(*Node=%p)", verb, n)
- }
-}
-
-// *Node details
-func (n *Node) jconv(s fmt.State, flag FmtFlag) {
- c := flag & FmtShort
-
- // Useful to see which nodes in a Node Dump/dumplist are actually identical
- if Debug_dumpptrs != 0 {
- fmt.Fprintf(s, " p(%p)", n)
- }
- if c == 0 && n.Name != nil && n.Name.Vargen != 0 {
- fmt.Fprintf(s, " g(%d)", n.Name.Vargen)
- }
-
- if Debug_dumpptrs != 0 && c == 0 && n.Name != nil && n.Name.Defn != nil {
- // Useful to see where Defn is set and what node it points to
- fmt.Fprintf(s, " defn(%p)", n.Name.Defn)
- }
-
- if n.Pos.IsKnown() {
- pfx := ""
- switch n.Pos.IsStmt() {
- case src.PosNotStmt:
- pfx = "_" // "-" would be confusing
- case src.PosIsStmt:
- pfx = "+"
- }
- fmt.Fprintf(s, " l(%s%d)", pfx, n.Pos.Line())
- }
-
- if c == 0 && n.Xoffset != BADWIDTH {
- fmt.Fprintf(s, " x(%d)", n.Xoffset)
- }
-
- if n.Class() != 0 {
- fmt.Fprintf(s, " class(%v)", n.Class())
- }
-
- if n.Colas() {
- fmt.Fprintf(s, " colas(%v)", n.Colas())
- }
-
- switch n.Esc {
- case EscUnknown:
- break
-
- case EscHeap:
- fmt.Fprint(s, " esc(h)")
-
- case EscNone:
- fmt.Fprint(s, " esc(no)")
-
- case EscNever:
- if c == 0 {
- fmt.Fprint(s, " esc(N)")
- }
-
- default:
- fmt.Fprintf(s, " esc(%d)", n.Esc)
- }
-
- if e, ok := n.Opt().(*EscLocation); ok && e.loopDepth != 0 {
- fmt.Fprintf(s, " ld(%d)", e.loopDepth)
- }
-
- if c == 0 && n.Typecheck() != 0 {
- fmt.Fprintf(s, " tc(%d)", n.Typecheck())
- }
-
- if n.IsDDD() {
- fmt.Fprintf(s, " isddd(%v)", n.IsDDD())
- }
-
- if n.Implicit() {
- fmt.Fprintf(s, " implicit(%v)", n.Implicit())
- }
-
- if n.Embedded() {
- fmt.Fprintf(s, " embedded")
- }
-
- if n.Op == ONAME {
- if n.Name.Addrtaken() {
- fmt.Fprint(s, " addrtaken")
- }
- if n.Name.Assigned() {
- fmt.Fprint(s, " assigned")
- }
- if n.Name.IsClosureVar() {
- fmt.Fprint(s, " closurevar")
- }
- if n.Name.Captured() {
- fmt.Fprint(s, " captured")
- }
- if n.Name.IsOutputParamHeapAddr() {
- fmt.Fprint(s, " outputparamheapaddr")
- }
- }
- if n.Bounded() {
- fmt.Fprint(s, " bounded")
- }
- if n.NonNil() {
- fmt.Fprint(s, " nonnil")
- }
-
- if c == 0 && n.HasCall() {
- fmt.Fprint(s, " hascall")
- }
-
- if c == 0 && n.Name != nil && n.Name.Used() {
- fmt.Fprint(s, " used")
- }
-}
-
-func (v Val) Format(s fmt.State, verb rune) {
- switch verb {
- case 'v':
- v.vconv(s, fmtFlag(s, verb))
-
- default:
- fmt.Fprintf(s, "%%!%c(Val=%T)", verb, v)
- }
-}
-
-func (v Val) vconv(s fmt.State, flag FmtFlag) {
- switch u := v.U.(type) {
- case *Mpint:
- if !u.Rune {
- if flag&FmtSharp != 0 {
- fmt.Fprint(s, u.String())
- return
- }
- fmt.Fprint(s, u.GoString())
- return
- }
-
- switch x := u.Int64(); {
- case ' ' <= x && x < utf8.RuneSelf && x != '\\' && x != '\'':
- fmt.Fprintf(s, "'%c'", int(x))
-
- case 0 <= x && x < 1<<16:
- fmt.Fprintf(s, "'\\u%04x'", uint(int(x)))
-
- case 0 <= x && x <= utf8.MaxRune:
- fmt.Fprintf(s, "'\\U%08x'", uint64(x))
-
- default:
- fmt.Fprintf(s, "('\\x00' + %v)", u)
- }
-
- case *Mpflt:
- if flag&FmtSharp != 0 {
- fmt.Fprint(s, u.String())
- return
- }
- fmt.Fprint(s, u.GoString())
- return
-
- case *Mpcplx:
- if flag&FmtSharp != 0 {
- fmt.Fprint(s, u.String())
- return
- }
- fmt.Fprint(s, u.GoString())
- return
-
- case string:
- fmt.Fprint(s, strconv.Quote(u))
-
- case bool:
- fmt.Fprint(s, u)
-
- case *NilVal:
- fmt.Fprint(s, "nil")
-
- default:
- fmt.Fprintf(s, "", v.Ctype())
- }
-}
-
-/*
-s%,%,\n%g
-s%\n+%\n%g
-s%^[ ]*T%%g
-s%,.*%%g
-s%.+% [T&] = "&",%g
-s%^ ........*\]%&~%g
-s%~ %%g
-*/
-
-func symfmt(b *bytes.Buffer, s *types.Sym, flag FmtFlag, mode fmtMode) {
- if flag&FmtShort == 0 {
- switch mode {
- case FErr: // This is for the user
- if s.Pkg == builtinpkg || s.Pkg == localpkg {
- b.WriteString(s.Name)
- return
- }
-
- // If the name was used by multiple packages, display the full path,
- if s.Pkg.Name != "" && numImport[s.Pkg.Name] > 1 {
- fmt.Fprintf(b, "%q.%s", s.Pkg.Path, s.Name)
- return
- }
- b.WriteString(s.Pkg.Name)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
-
- case FDbg:
- b.WriteString(s.Pkg.Name)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
-
- case FTypeIdName:
- // dcommontype, typehash
- b.WriteString(s.Pkg.Name)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
-
- case FTypeId:
- // (methodsym), typesym, weaksym
- b.WriteString(s.Pkg.Prefix)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
- }
- }
-
- if flag&FmtByte != 0 {
- // FmtByte (hh) implies FmtShort (h)
- // skip leading "type." in method name
- name := s.Name
- if i := strings.LastIndex(name, "."); i >= 0 {
- name = name[i+1:]
- }
-
- if mode == FDbg {
- fmt.Fprintf(b, "@%q.%s", s.Pkg.Path, name)
- return
- }
-
- b.WriteString(name)
- return
- }
-
- b.WriteString(s.Name)
-}
-
-var basicnames = []string{
- TINT: "int",
- TUINT: "uint",
- TINT8: "int8",
- TUINT8: "uint8",
- TINT16: "int16",
- TUINT16: "uint16",
- TINT32: "int32",
- TUINT32: "uint32",
- TINT64: "int64",
- TUINT64: "uint64",
- TUINTPTR: "uintptr",
- TFLOAT32: "float32",
- TFLOAT64: "float64",
- TCOMPLEX64: "complex64",
- TCOMPLEX128: "complex128",
- TBOOL: "bool",
- TANY: "any",
- TSTRING: "string",
- TNIL: "nil",
- TIDEAL: "untyped number",
- TBLANK: "blank",
-}
-
-var fmtBufferPool = sync.Pool{
- New: func() interface{} {
- return new(bytes.Buffer)
- },
-}
-
-func tconv(t *types.Type, flag FmtFlag, mode fmtMode) string {
- buf := fmtBufferPool.Get().(*bytes.Buffer)
- buf.Reset()
- defer fmtBufferPool.Put(buf)
-
- tconv2(buf, t, flag, mode, nil)
- return types.InternString(buf.Bytes())
-}
-
-// tconv2 writes a string representation of t to b.
-// flag and mode control exactly what is printed.
-// Any types x that are already in the visited map get printed as @%d where %d=visited[x].
-// See #16897 before changing the implementation of tconv.
-func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited map[*types.Type]int) {
- if off, ok := visited[t]; ok {
- // We've seen this type before, so we're trying to print it recursively.
- // Print a reference to it instead.
- fmt.Fprintf(b, "@%d", off)
- return
- }
- if t == nil {
- b.WriteString("")
- return
- }
- if t.Etype == types.TSSA {
- b.WriteString(t.Extra.(string))
- return
- }
- if t.Etype == types.TTUPLE {
- b.WriteString(t.FieldType(0).String())
- b.WriteByte(',')
- b.WriteString(t.FieldType(1).String())
- 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
- }
- if t == types.Bytetype || t == types.Runetype {
- // in %-T mode collapse rune and byte with their originals.
- switch mode {
- case FTypeIdName, FTypeId:
- t = types.Types[t.Etype]
- default:
- sconv2(b, t.Sym, FmtShort, mode)
- return
- }
- }
- if t == types.Errortype {
- b.WriteString("error")
- return
- }
-
- // Unless the 'L' flag was specified, if the type has a name, just print that name.
- if flag&FmtLong == 0 && t.Sym != nil && t != types.Types[t.Etype] {
- switch mode {
- case FTypeId, FTypeIdName:
- if flag&FmtShort != 0 {
- if t.Vargen != 0 {
- sconv2(b, t.Sym, FmtShort, mode)
- fmt.Fprintf(b, "·%d", t.Vargen)
- return
- }
- sconv2(b, t.Sym, FmtShort, mode)
- return
- }
-
- if mode == FTypeIdName {
- sconv2(b, t.Sym, FmtUnsigned, mode)
- return
- }
-
- if t.Sym.Pkg == localpkg && t.Vargen != 0 {
- b.WriteString(mode.Sprintf("%v·%d", t.Sym, t.Vargen))
- return
- }
- }
-
- sconv2(b, t.Sym, 0, mode)
- return
- }
-
- if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
- var name string
- switch t {
- case types.UntypedBool:
- name = "untyped bool"
- case types.UntypedString:
- name = "untyped string"
- case types.UntypedInt:
- name = "untyped int"
- case types.UntypedRune:
- name = "untyped rune"
- case types.UntypedFloat:
- name = "untyped float"
- case types.UntypedComplex:
- name = "untyped complex"
- default:
- name = basicnames[t.Etype]
- }
- b.WriteString(name)
- return
- }
-
- if mode == FDbg {
- b.WriteString(t.Etype.String())
- b.WriteByte('-')
- tconv2(b, t, flag, FErr, visited)
- return
- }
-
- // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't
- // try to print it recursively.
- // We record the offset in the result buffer where the type's text starts. This offset serves as a reference
- // point for any later references to the same type.
- // Note that we remove the type from the visited map as soon as the recursive call is done.
- // This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work,
- // but I'd like to use the @ notation only when strictly necessary.)
- if visited == nil {
- visited = map[*types.Type]int{}
- }
- visited[t] = b.Len()
- defer delete(visited, t)
-
- switch t.Etype {
- case TPTR:
- b.WriteByte('*')
- switch mode {
- case FTypeId, FTypeIdName:
- if flag&FmtShort != 0 {
- tconv2(b, t.Elem(), FmtShort, mode, visited)
- return
- }
- }
- tconv2(b, t.Elem(), 0, mode, visited)
-
- case TARRAY:
- b.WriteByte('[')
- b.WriteString(strconv.FormatInt(t.NumElem(), 10))
- b.WriteByte(']')
- tconv2(b, t.Elem(), 0, mode, visited)
-
- case TSLICE:
- b.WriteString("[]")
- tconv2(b, t.Elem(), 0, mode, visited)
-
- case TCHAN:
- switch t.ChanDir() {
- case types.Crecv:
- b.WriteString("<-chan ")
- tconv2(b, t.Elem(), 0, mode, visited)
- case types.Csend:
- b.WriteString("chan<- ")
- tconv2(b, t.Elem(), 0, mode, visited)
- default:
- b.WriteString("chan ")
- if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym == nil && t.Elem().ChanDir() == types.Crecv {
- b.WriteByte('(')
- tconv2(b, t.Elem(), 0, mode, visited)
- b.WriteByte(')')
- } else {
- tconv2(b, t.Elem(), 0, mode, visited)
- }
- }
-
- case TMAP:
- b.WriteString("map[")
- tconv2(b, t.Key(), 0, mode, visited)
- b.WriteByte(']')
- tconv2(b, t.Elem(), 0, mode, visited)
-
- case TINTER:
- if t.IsEmptyInterface() {
- b.WriteString("interface {}")
- break
- }
- b.WriteString("interface {")
- for i, f := range t.Fields().Slice() {
- if i != 0 {
- b.WriteByte(';')
- }
- b.WriteByte(' ')
- switch {
- case f.Sym == nil:
- // Check first that a symbol is defined for this type.
- // Wrong interface definitions may have types lacking a symbol.
- break
- case types.IsExported(f.Sym.Name):
- sconv2(b, f.Sym, FmtShort, mode)
- default:
- flag1 := FmtLeft
- if flag&FmtUnsigned != 0 {
- flag1 = FmtUnsigned
- }
- sconv2(b, f.Sym, flag1, mode)
- }
- tconv2(b, f.Type, FmtShort, mode, visited)
- }
- if t.NumFields() != 0 {
- b.WriteByte(' ')
- }
- b.WriteByte('}')
-
- case TFUNC:
- if flag&FmtShort != 0 {
- // no leading func
- } else {
- if t.Recv() != nil {
- b.WriteString("method")
- tconv2(b, t.Recvs(), 0, mode, visited)
- b.WriteByte(' ')
- }
- b.WriteString("func")
- }
- tconv2(b, t.Params(), 0, mode, visited)
-
- switch t.NumResults() {
- case 0:
- // nothing to do
-
- case 1:
- b.WriteByte(' ')
- tconv2(b, t.Results().Field(0).Type, 0, mode, visited) // struct->field->field's type
-
- default:
- b.WriteByte(' ')
- tconv2(b, t.Results(), 0, mode, visited)
- }
-
- case TSTRUCT:
- if m := t.StructType().Map; m != nil {
- mt := m.MapType()
- // Format the bucket struct for map[x]y as map.bucket[x]y.
- // This avoids a recursive print that generates very long names.
- switch t {
- case mt.Bucket:
- b.WriteString("map.bucket[")
- case mt.Hmap:
- b.WriteString("map.hdr[")
- case mt.Hiter:
- b.WriteString("map.iter[")
- default:
- Fatalf("unknown internal map type")
- }
- tconv2(b, m.Key(), 0, mode, visited)
- b.WriteByte(']')
- tconv2(b, m.Elem(), 0, mode, visited)
- break
- }
-
- if funarg := t.StructType().Funarg; funarg != types.FunargNone {
- b.WriteByte('(')
- var flag1 FmtFlag
- switch mode {
- case FTypeId, FTypeIdName, FErr:
- // no argument names on function signature, and no "noescape"/"nosplit" tags
- flag1 = FmtShort
- }
- for i, f := range t.Fields().Slice() {
- if i != 0 {
- b.WriteString(", ")
- }
- fldconv(b, f, flag1, mode, visited, funarg)
- }
- b.WriteByte(')')
- } else {
- b.WriteString("struct {")
- for i, f := range t.Fields().Slice() {
- if i != 0 {
- b.WriteByte(';')
- }
- b.WriteByte(' ')
- fldconv(b, f, FmtLong, mode, visited, funarg)
- }
- if t.NumFields() != 0 {
- b.WriteByte(' ')
- }
- b.WriteByte('}')
- }
-
- case TFORW:
- b.WriteString("undefined")
- if t.Sym != nil {
- b.WriteByte(' ')
- sconv2(b, t.Sym, 0, mode)
- }
-
- case TUNSAFEPTR:
- b.WriteString("unsafe.Pointer")
-
- case Txxx:
- b.WriteString("Txxx")
- default:
- // Don't know how to handle - fall back to detailed prints.
- b.WriteString(mode.Sprintf("%v <%v>", t.Etype, t.Sym))
- }
-}
-
-// Statements which may be rendered with a simplestmt as init.
-func stmtwithinit(op Op) bool {
- switch op {
- case OIF, OFOR, OFORUNTIL, OSWITCH:
- return true
- }
-
- return false
-}
-
-func (n *Node) stmtfmt(s fmt.State, mode fmtMode) {
- // some statements allow for an init, but at most one,
- // but we may have an arbitrary number added, eg by typecheck
- // and inlining. If it doesn't fit the syntax, emit an enclosing
- // block starting with the init statements.
-
- // if we can just say "for" n->ninit; ... then do so
- simpleinit := n.Ninit.Len() == 1 && n.Ninit.First().Ninit.Len() == 0 && stmtwithinit(n.Op)
-
- // otherwise, print the inits as separate statements
- complexinit := n.Ninit.Len() != 0 && !simpleinit && (mode != FErr)
-
- // but if it was for if/for/switch, put in an extra surrounding block to limit the scope
- extrablock := complexinit && stmtwithinit(n.Op)
-
- if extrablock {
- fmt.Fprint(s, "{")
- }
-
- if complexinit {
- mode.Fprintf(s, " %v; ", n.Ninit)
- }
-
- switch n.Op {
- case ODCL:
- mode.Fprintf(s, "var %v %v", n.Left.Sym, n.Left.Type)
-
- case ODCLFIELD:
- if n.Sym != nil {
- mode.Fprintf(s, "%v %v", n.Sym, n.Left)
- } else {
- mode.Fprintf(s, "%v", n.Left)
- }
-
- // Don't export "v = " initializing statements, hope they're always
- // preceded by the DCL which will be re-parsed and typechecked to reproduce
- // the "v = " again.
- case OAS:
- if n.Colas() && !complexinit {
- mode.Fprintf(s, "%v := %v", n.Left, n.Right)
- } else {
- mode.Fprintf(s, "%v = %v", n.Left, n.Right)
- }
-
- case OASOP:
- if n.Implicit() {
- if n.SubOp() == OADD {
- mode.Fprintf(s, "%v++", n.Left)
- } else {
- mode.Fprintf(s, "%v--", n.Left)
- }
- break
- }
-
- mode.Fprintf(s, "%v %#v= %v", n.Left, n.SubOp(), n.Right)
-
- case OAS2:
- if n.Colas() && !complexinit {
- mode.Fprintf(s, "%.v := %.v", n.List, n.Rlist)
- break
- }
- fallthrough
-
- case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- mode.Fprintf(s, "%.v = %v", n.List, n.Right)
-
- case ORETURN:
- mode.Fprintf(s, "return %.v", n.List)
-
- case ORETJMP:
- mode.Fprintf(s, "retjmp %v", n.Sym)
-
- case OINLMARK:
- mode.Fprintf(s, "inlmark %d", n.Xoffset)
-
- case OGO:
- mode.Fprintf(s, "go %v", n.Left)
-
- case ODEFER:
- mode.Fprintf(s, "defer %v", n.Left)
-
- case OIF:
- if simpleinit {
- mode.Fprintf(s, "if %v; %v { %v }", n.Ninit.First(), n.Left, n.Nbody)
- } else {
- mode.Fprintf(s, "if %v { %v }", n.Left, n.Nbody)
- }
- if n.Rlist.Len() != 0 {
- mode.Fprintf(s, " else { %v }", n.Rlist)
- }
-
- case OFOR, OFORUNTIL:
- opname := "for"
- if n.Op == OFORUNTIL {
- opname = "foruntil"
- }
- if mode == FErr { // TODO maybe only if FmtShort, same below
- fmt.Fprintf(s, "%s loop", opname)
- break
- }
-
- fmt.Fprint(s, opname)
- if simpleinit {
- mode.Fprintf(s, " %v;", n.Ninit.First())
- } else if n.Right != nil {
- fmt.Fprint(s, " ;")
- }
-
- if n.Left != nil {
- mode.Fprintf(s, " %v", n.Left)
- }
-
- if n.Right != nil {
- mode.Fprintf(s, "; %v", n.Right)
- } else if simpleinit {
- fmt.Fprint(s, ";")
- }
-
- if n.Op == OFORUNTIL && n.List.Len() != 0 {
- mode.Fprintf(s, "; %v", n.List)
- }
-
- mode.Fprintf(s, " { %v }", n.Nbody)
-
- case ORANGE:
- if mode == FErr {
- fmt.Fprint(s, "for loop")
- break
- }
-
- if n.List.Len() == 0 {
- mode.Fprintf(s, "for range %v { %v }", n.Right, n.Nbody)
- break
- }
-
- mode.Fprintf(s, "for %.v = range %v { %v }", n.List, n.Right, n.Nbody)
-
- case OSELECT, OSWITCH:
- if mode == FErr {
- mode.Fprintf(s, "%v statement", n.Op)
- break
- }
-
- mode.Fprintf(s, "%#v", n.Op)
- if simpleinit {
- mode.Fprintf(s, " %v;", n.Ninit.First())
- }
- if n.Left != nil {
- mode.Fprintf(s, " %v ", n.Left)
- }
-
- mode.Fprintf(s, " { %v }", n.List)
-
- case OCASE:
- if n.List.Len() != 0 {
- mode.Fprintf(s, "case %.v", n.List)
- } else {
- fmt.Fprint(s, "default")
- }
- mode.Fprintf(s, ": %v", n.Nbody)
-
- case OBREAK, OCONTINUE, OGOTO, OFALL:
- if n.Sym != nil {
- mode.Fprintf(s, "%#v %v", n.Op, n.Sym)
- } else {
- mode.Fprintf(s, "%#v", n.Op)
- }
-
- case OEMPTY:
- break
-
- case OLABEL:
- mode.Fprintf(s, "%v: ", n.Sym)
- }
-
- if extrablock {
- fmt.Fprint(s, "}")
- }
-}
-
-var opprec = []int{
- OALIGNOF: 8,
- OAPPEND: 8,
- OBYTES2STR: 8,
- OARRAYLIT: 8,
- OSLICELIT: 8,
- ORUNES2STR: 8,
- OCALLFUNC: 8,
- OCALLINTER: 8,
- OCALLMETH: 8,
- OCALL: 8,
- OCAP: 8,
- OCLOSE: 8,
- OCONVIFACE: 8,
- OCONVNOP: 8,
- OCONV: 8,
- OCOPY: 8,
- ODELETE: 8,
- OGETG: 8,
- OLEN: 8,
- OLITERAL: 8,
- OMAKESLICE: 8,
- OMAKESLICECOPY: 8,
- OMAKE: 8,
- OMAPLIT: 8,
- ONAME: 8,
- ONEW: 8,
- ONONAME: 8,
- OOFFSETOF: 8,
- OPACK: 8,
- OPANIC: 8,
- OPAREN: 8,
- OPRINTN: 8,
- OPRINT: 8,
- ORUNESTR: 8,
- OSIZEOF: 8,
- OSTR2BYTES: 8,
- OSTR2RUNES: 8,
- OSTRUCTLIT: 8,
- OTARRAY: 8,
- OTCHAN: 8,
- OTFUNC: 8,
- OTINTER: 8,
- OTMAP: 8,
- OTSTRUCT: 8,
- OINDEXMAP: 8,
- OINDEX: 8,
- OSLICE: 8,
- OSLICESTR: 8,
- OSLICEARR: 8,
- OSLICE3: 8,
- OSLICE3ARR: 8,
- OSLICEHEADER: 8,
- ODOTINTER: 8,
- ODOTMETH: 8,
- ODOTPTR: 8,
- ODOTTYPE2: 8,
- ODOTTYPE: 8,
- ODOT: 8,
- OXDOT: 8,
- OCALLPART: 8,
- OPLUS: 7,
- ONOT: 7,
- OBITNOT: 7,
- ONEG: 7,
- OADDR: 7,
- ODEREF: 7,
- ORECV: 7,
- OMUL: 6,
- ODIV: 6,
- OMOD: 6,
- OLSH: 6,
- ORSH: 6,
- OAND: 6,
- OANDNOT: 6,
- OADD: 5,
- OSUB: 5,
- OOR: 5,
- OXOR: 5,
- OEQ: 4,
- OLT: 4,
- OLE: 4,
- OGE: 4,
- OGT: 4,
- ONE: 4,
- OSEND: 3,
- OANDAND: 2,
- OOROR: 1,
-
- // Statements handled by stmtfmt
- OAS: -1,
- OAS2: -1,
- OAS2DOTTYPE: -1,
- OAS2FUNC: -1,
- OAS2MAPR: -1,
- OAS2RECV: -1,
- OASOP: -1,
- OBREAK: -1,
- OCASE: -1,
- OCONTINUE: -1,
- ODCL: -1,
- ODCLFIELD: -1,
- ODEFER: -1,
- OEMPTY: -1,
- OFALL: -1,
- OFOR: -1,
- OFORUNTIL: -1,
- OGOTO: -1,
- OIF: -1,
- OLABEL: -1,
- OGO: -1,
- ORANGE: -1,
- ORETURN: -1,
- OSELECT: -1,
- OSWITCH: -1,
-
- OEND: 0,
-}
-
-func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
- for n != nil && n.Implicit() && (n.Op == ODEREF || n.Op == OADDR) {
- n = n.Left
- }
-
- if n == nil {
- fmt.Fprint(s, "")
- return
- }
-
- nprec := opprec[n.Op]
- if n.Op == OTYPE && n.Sym != nil {
- nprec = 8
- }
-
- if prec > nprec {
- mode.Fprintf(s, "(%v)", n)
- return
- }
-
- switch n.Op {
- case OPAREN:
- mode.Fprintf(s, "(%v)", n.Left)
-
- case OLITERAL: // this is a bit of a mess
- if mode == FErr {
- if n.Orig != nil && n.Orig != n {
- n.Orig.exprfmt(s, prec, mode)
- return
- }
- if n.Sym != nil {
- fmt.Fprint(s, smodeString(n.Sym, mode))
- return
- }
- }
- if n.Val().Ctype() == CTNIL && n.Orig != nil && n.Orig != n {
- n.Orig.exprfmt(s, prec, mode)
- return
- }
- if n.Type != nil && !n.Type.IsUntyped() {
- // Need parens when type begins with what might
- // be misinterpreted as a unary operator: * or <-.
- if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == types.Crecv) {
- mode.Fprintf(s, "(%v)(%v)", n.Type, n.Val())
- return
- } else {
- mode.Fprintf(s, "%v(%v)", n.Type, n.Val())
- return
- }
- }
-
- mode.Fprintf(s, "%v", n.Val())
-
- // Special case: name used as local variable in export.
- // _ becomes ~b%d internally; print as _ for export
- case ONAME:
- if mode == FErr && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
- fmt.Fprint(s, "_")
- return
- }
- fallthrough
- case OPACK, ONONAME:
- fmt.Fprint(s, smodeString(n.Sym, mode))
-
- case OTYPE:
- if n.Type == nil && n.Sym != nil {
- fmt.Fprint(s, smodeString(n.Sym, mode))
- return
- }
- mode.Fprintf(s, "%v", n.Type)
-
- case OTARRAY:
- if n.Left != nil {
- mode.Fprintf(s, "[%v]%v", n.Left, n.Right)
- return
- }
- mode.Fprintf(s, "[]%v", n.Right) // happens before typecheck
-
- case OTMAP:
- mode.Fprintf(s, "map[%v]%v", n.Left, n.Right)
-
- case OTCHAN:
- switch n.TChanDir() {
- case types.Crecv:
- mode.Fprintf(s, "<-chan %v", n.Left)
-
- case types.Csend:
- mode.Fprintf(s, "chan<- %v", n.Left)
-
- default:
- if n.Left != nil && n.Left.Op == OTCHAN && n.Left.Sym == nil && n.Left.TChanDir() == types.Crecv {
- mode.Fprintf(s, "chan (%v)", n.Left)
- } else {
- mode.Fprintf(s, "chan %v", n.Left)
- }
- }
-
- case OTSTRUCT:
- fmt.Fprint(s, "")
-
- case OTINTER:
- fmt.Fprint(s, "")
-
- case OTFUNC:
- fmt.Fprint(s, "")
-
- case OCLOSURE:
- if mode == FErr {
- fmt.Fprint(s, "func literal")
- return
- }
- if n.Nbody.Len() != 0 {
- mode.Fprintf(s, "%v { %v }", n.Type, n.Nbody)
- return
- }
- mode.Fprintf(s, "%v { %v }", n.Type, n.Func.Closure.Nbody)
-
- case OCOMPLIT:
- if mode == FErr {
- if n.Implicit() {
- mode.Fprintf(s, "... argument")
- return
- }
- if n.Right != nil {
- mode.Fprintf(s, "%v{%s}", n.Right, ellipsisIf(n.List.Len() != 0))
- return
- }
-
- fmt.Fprint(s, "composite literal")
- return
- }
- mode.Fprintf(s, "(%v{ %.v })", n.Right, n.List)
-
- case OPTRLIT:
- mode.Fprintf(s, "&%v", n.Left)
-
- case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
- if mode == FErr {
- mode.Fprintf(s, "%v{%s}", n.Type, ellipsisIf(n.List.Len() != 0))
- return
- }
- mode.Fprintf(s, "(%v{ %.v })", n.Type, n.List)
-
- case OKEY:
- if n.Left != nil && n.Right != nil {
- mode.Fprintf(s, "%v:%v", n.Left, n.Right)
- return
- }
-
- if n.Left == nil && n.Right != nil {
- mode.Fprintf(s, ":%v", n.Right)
- return
- }
- if n.Left != nil && n.Right == nil {
- mode.Fprintf(s, "%v:", n.Left)
- return
- }
- fmt.Fprint(s, ":")
-
- case OSTRUCTKEY:
- mode.Fprintf(s, "%v:%v", n.Sym, n.Left)
-
- case OCALLPART:
- n.Left.exprfmt(s, nprec, mode)
- if n.Right == nil || n.Right.Sym == nil {
- fmt.Fprint(s, ".")
- return
- }
- mode.Fprintf(s, ".%0S", n.Right.Sym)
-
- case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
- n.Left.exprfmt(s, nprec, mode)
- if n.Sym == nil {
- fmt.Fprint(s, ".")
- return
- }
- mode.Fprintf(s, ".%0S", n.Sym)
-
- case ODOTTYPE, ODOTTYPE2:
- n.Left.exprfmt(s, nprec, mode)
- if n.Right != nil {
- mode.Fprintf(s, ".(%v)", n.Right)
- return
- }
- mode.Fprintf(s, ".(%v)", n.Type)
-
- case OINDEX, OINDEXMAP:
- n.Left.exprfmt(s, nprec, mode)
- mode.Fprintf(s, "[%v]", n.Right)
-
- case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
- n.Left.exprfmt(s, nprec, mode)
- fmt.Fprint(s, "[")
- low, high, max := n.SliceBounds()
- if low != nil {
- fmt.Fprint(s, low.modeString(mode))
- }
- fmt.Fprint(s, ":")
- if high != nil {
- fmt.Fprint(s, high.modeString(mode))
- }
- if n.Op.IsSlice3() {
- fmt.Fprint(s, ":")
- if max != nil {
- fmt.Fprint(s, max.modeString(mode))
- }
- }
- fmt.Fprint(s, "]")
-
- case OSLICEHEADER:
- if n.List.Len() != 2 {
- Fatalf("bad OSLICEHEADER list length %d", n.List.Len())
- }
- mode.Fprintf(s, "sliceheader{%v,%v,%v}", n.Left, n.List.First(), n.List.Second())
-
- case OCOMPLEX, OCOPY:
- if n.Left != nil {
- mode.Fprintf(s, "%#v(%v, %v)", n.Op, n.Left, n.Right)
- } else {
- mode.Fprintf(s, "%#v(%.v)", n.Op, n.List)
- }
-
- case OCONV,
- OCONVIFACE,
- OCONVNOP,
- OBYTES2STR,
- ORUNES2STR,
- OSTR2BYTES,
- OSTR2RUNES,
- ORUNESTR:
- if n.Type == nil || n.Type.Sym == nil {
- mode.Fprintf(s, "(%v)", n.Type)
- } else {
- mode.Fprintf(s, "%v", n.Type)
- }
- if n.Left != nil {
- mode.Fprintf(s, "(%v)", n.Left)
- } else {
- mode.Fprintf(s, "(%.v)", n.List)
- }
-
- case OREAL,
- OIMAG,
- OAPPEND,
- OCAP,
- OCLOSE,
- ODELETE,
- OLEN,
- OMAKE,
- ONEW,
- OPANIC,
- ORECOVER,
- OALIGNOF,
- OOFFSETOF,
- OSIZEOF,
- OPRINT,
- OPRINTN:
- if n.Left != nil {
- mode.Fprintf(s, "%#v(%v)", n.Op, n.Left)
- return
- }
- if n.IsDDD() {
- mode.Fprintf(s, "%#v(%.v...)", n.Op, n.List)
- return
- }
- mode.Fprintf(s, "%#v(%.v)", n.Op, n.List)
-
- case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG:
- n.Left.exprfmt(s, nprec, mode)
- if n.IsDDD() {
- mode.Fprintf(s, "(%.v...)", n.List)
- return
- }
- mode.Fprintf(s, "(%.v)", n.List)
-
- case OMAKEMAP, OMAKECHAN, OMAKESLICE:
- if n.List.Len() != 0 { // pre-typecheck
- mode.Fprintf(s, "make(%v, %.v)", n.Type, n.List)
- return
- }
- if n.Right != nil {
- mode.Fprintf(s, "make(%v, %v, %v)", n.Type, n.Left, n.Right)
- return
- }
- if n.Left != nil && (n.Op == OMAKESLICE || !n.Left.Type.IsUntyped()) {
- mode.Fprintf(s, "make(%v, %v)", n.Type, n.Left)
- return
- }
- mode.Fprintf(s, "make(%v)", n.Type)
-
- case OMAKESLICECOPY:
- mode.Fprintf(s, "makeslicecopy(%v, %v, %v)", n.Type, n.Left, n.Right)
-
- case OPLUS, ONEG, OADDR, OBITNOT, ODEREF, ONOT, ORECV:
- // Unary
- mode.Fprintf(s, "%#v", n.Op)
- if n.Left != nil && n.Left.Op == n.Op {
- fmt.Fprint(s, " ")
- }
- n.Left.exprfmt(s, nprec+1, mode)
-
- // Binary
- case OADD,
- OAND,
- OANDAND,
- OANDNOT,
- ODIV,
- OEQ,
- OGE,
- OGT,
- OLE,
- OLT,
- OLSH,
- OMOD,
- OMUL,
- ONE,
- OOR,
- OOROR,
- ORSH,
- OSEND,
- OSUB,
- OXOR:
- n.Left.exprfmt(s, nprec, mode)
- mode.Fprintf(s, " %#v ", n.Op)
- n.Right.exprfmt(s, nprec+1, mode)
-
- case OADDSTR:
- for i, n1 := range n.List.Slice() {
- if i != 0 {
- fmt.Fprint(s, " + ")
- }
- n1.exprfmt(s, nprec, mode)
- }
- case ODDD:
- mode.Fprintf(s, "...")
- default:
- mode.Fprintf(s, "", n.Op)
- }
-}
-
-func (n *Node) nodefmt(s fmt.State, flag FmtFlag, mode fmtMode) {
- t := n.Type
-
- // We almost always want the original.
- // TODO(gri) Why the special case for OLITERAL?
- if n.Op != OLITERAL && n.Orig != nil {
- n = n.Orig
- }
-
- if flag&FmtLong != 0 && t != nil {
- if t.Etype == TNIL {
- fmt.Fprint(s, "nil")
- } else if n.Op == ONAME && n.Name.AutoTemp() {
- mode.Fprintf(s, "%v value", t)
- } else {
- mode.Fprintf(s, "%v (type %v)", n, t)
- }
- return
- }
-
- // TODO inlining produces expressions with ninits. we can't print these yet.
-
- if opprec[n.Op] < 0 {
- n.stmtfmt(s, mode)
- return
- }
-
- n.exprfmt(s, 0, mode)
-}
-
-func (n *Node) nodedump(s fmt.State, flag FmtFlag, mode fmtMode) {
- recur := flag&FmtShort == 0
-
- if recur {
- indent(s)
- if dumpdepth > 40 {
- fmt.Fprint(s, "...")
- return
- }
-
- if n.Ninit.Len() != 0 {
- mode.Fprintf(s, "%v-init%v", n.Op, n.Ninit)
- indent(s)
- }
- }
-
- switch n.Op {
- default:
- mode.Fprintf(s, "%v%j", n.Op, n)
-
- case OLITERAL:
- mode.Fprintf(s, "%v-%v%j", n.Op, n.Val(), n)
-
- case ONAME, ONONAME:
- if n.Sym != nil {
- mode.Fprintf(s, "%v-%v%j", n.Op, n.Sym, n)
- } else {
- mode.Fprintf(s, "%v%j", n.Op, n)
- }
- if recur && n.Type == nil && n.Name != nil && n.Name.Param != nil && n.Name.Param.Ntype != nil {
- indent(s)
- mode.Fprintf(s, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
- }
-
- case OASOP:
- mode.Fprintf(s, "%v-%v%j", n.Op, n.SubOp(), n)
-
- case OTYPE:
- mode.Fprintf(s, "%v %v%j type=%v", n.Op, n.Sym, n, n.Type)
- if recur && n.Type == nil && n.Name != nil && n.Name.Param != nil && n.Name.Param.Ntype != nil {
- indent(s)
- mode.Fprintf(s, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
- }
- }
-
- if n.Op == OCLOSURE && n.Func.Closure != nil && n.Func.Closure.Func.Nname.Sym != nil {
- mode.Fprintf(s, " fnName %v", n.Func.Closure.Func.Nname.Sym)
- }
- if n.Sym != nil && n.Op != ONAME {
- mode.Fprintf(s, " %v", n.Sym)
- }
-
- if n.Type != nil {
- mode.Fprintf(s, " %v", n.Type)
- }
-
- if recur {
- if n.Left != nil {
- mode.Fprintf(s, "%v", n.Left)
- }
- if n.Right != nil {
- mode.Fprintf(s, "%v", n.Right)
- }
- if n.Func != nil && n.Func.Closure != nil && n.Func.Closure.Nbody.Len() != 0 {
- indent(s)
- // The function associated with a closure
- mode.Fprintf(s, "%v-clofunc%v", n.Op, n.Func.Closure)
- }
- if n.Func != nil && n.Func.Dcl != nil && len(n.Func.Dcl) != 0 {
- indent(s)
- // The dcls for a func or closure
- mode.Fprintf(s, "%v-dcl%v", n.Op, asNodes(n.Func.Dcl))
- }
- if n.List.Len() != 0 {
- indent(s)
- mode.Fprintf(s, "%v-list%v", n.Op, n.List)
- }
-
- if n.Rlist.Len() != 0 {
- indent(s)
- mode.Fprintf(s, "%v-rlist%v", n.Op, n.Rlist)
- }
-
- if n.Nbody.Len() != 0 {
- indent(s)
- mode.Fprintf(s, "%v-body%v", n.Op, n.Nbody)
- }
- }
-}
-
-// "%S" suppresses qualifying with package
-func symFormat(s *types.Sym, f fmt.State, verb rune, mode fmtMode) {
- switch verb {
- case 'v', 'S':
- fmt.Fprint(f, sconv(s, fmtFlag(f, verb), mode))
-
- default:
- fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s)
- }
-}
-
-func smodeString(s *types.Sym, mode fmtMode) string { return sconv(s, 0, mode) }
-
-// See #16897 before changing the implementation of sconv.
-func sconv(s *types.Sym, flag FmtFlag, mode fmtMode) string {
- if flag&FmtLong != 0 {
- panic("linksymfmt")
- }
-
- if s == nil {
- return ""
- }
-
- if s.Name == "_" {
- return "_"
- }
- buf := fmtBufferPool.Get().(*bytes.Buffer)
- buf.Reset()
- defer fmtBufferPool.Put(buf)
-
- flag, mode = flag.update(mode)
- symfmt(buf, s, flag, mode)
- return types.InternString(buf.Bytes())
-}
-
-func sconv2(b *bytes.Buffer, s *types.Sym, flag FmtFlag, mode fmtMode) {
- if flag&FmtLong != 0 {
- panic("linksymfmt")
- }
- if s == nil {
- b.WriteString("")
- return
- }
- if s.Name == "_" {
- b.WriteString("_")
- return
- }
-
- flag, mode = flag.update(mode)
- symfmt(b, s, flag, mode)
-}
-
-func fldconv(b *bytes.Buffer, f *types.Field, flag FmtFlag, mode fmtMode, visited map[*types.Type]int, funarg types.Funarg) {
- if f == nil {
- b.WriteString("")
- return
- }
- flag, mode = flag.update(mode)
- if mode == FTypeIdName {
- flag |= FmtUnsigned
- }
-
- var name string
- if flag&FmtShort == 0 {
- s := f.Sym
-
- // Take the name from the original.
- if mode == FErr {
- s = origSym(s)
- }
-
- if s != nil && f.Embedded == 0 {
- if funarg != types.FunargNone {
- name = asNode(f.Nname).modeString(mode)
- } else if flag&FmtLong != 0 {
- name = mode.Sprintf("%0S", s)
- if !types.IsExported(name) && flag&FmtUnsigned == 0 {
- name = smodeString(s, mode) // qualify non-exported names (used on structs, not on funarg)
- }
- } else {
- name = smodeString(s, mode)
- }
- }
- }
-
- if name != "" {
- b.WriteString(name)
- b.WriteString(" ")
- }
-
- if f.IsDDD() {
- var et *types.Type
- if f.Type != nil {
- et = f.Type.Elem()
- }
- b.WriteString("...")
- tconv2(b, et, 0, mode, visited)
- } else {
- tconv2(b, f.Type, 0, mode, visited)
- }
-
- if flag&FmtShort == 0 && funarg == types.FunargNone && f.Note != "" {
- b.WriteString(" ")
- b.WriteString(strconv.Quote(f.Note))
- }
-}
-
-// "%L" print definition, not name
-// "%S" omit 'func' and receiver from function types, short type names
-func typeFormat(t *types.Type, s fmt.State, verb rune, mode fmtMode) {
- switch verb {
- case 'v', 'S', 'L':
- fmt.Fprint(s, tconv(t, fmtFlag(s, verb), mode))
- default:
- fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t)
- }
-}
-
-func (n *Node) String() string { return fmt.Sprint(n) }
-func (n *Node) modeString(mode fmtMode) string { return mode.Sprint(n) }
-
-// "%L" suffix with "(type %T)" where possible
-// "%+S" in debug mode, don't recurse, no multiline output
-func (n *Node) nconv(s fmt.State, flag FmtFlag, mode fmtMode) {
- if n == nil {
- fmt.Fprint(s, "")
- return
- }
-
- flag, mode = flag.update(mode)
-
- switch mode {
- case FErr:
- n.nodefmt(s, flag, mode)
-
- case FDbg:
- dumpdepth++
- n.nodedump(s, flag, mode)
- dumpdepth--
-
- default:
- Fatalf("unhandled %%N mode: %d", mode)
- }
-}
-
-func (l Nodes) format(s fmt.State, verb rune, mode fmtMode) {
- switch verb {
- case 'v':
- l.hconv(s, fmtFlag(s, verb), mode)
-
- default:
- fmt.Fprintf(s, "%%!%c(Nodes)", verb)
- }
-}
-
-func (n Nodes) String() string {
- return fmt.Sprint(n)
-}
-
-// Flags: all those of %N plus '.': separate with comma's instead of semicolons.
-func (l Nodes) hconv(s fmt.State, flag FmtFlag, mode fmtMode) {
- if l.Len() == 0 && mode == FDbg {
- fmt.Fprint(s, "")
- return
- }
-
- flag, mode = flag.update(mode)
- sep := "; "
- if mode == FDbg {
- sep = "\n"
- } else if flag&FmtComma != 0 {
- sep = ", "
- }
-
- for i, n := range l.Slice() {
- fmt.Fprint(s, n.modeString(mode))
- if i+1 < l.Len() {
- fmt.Fprint(s, sep)
- }
- }
-}
-
-func dumplist(s string, l Nodes) {
- fmt.Printf("%s%+v\n", s, l)
-}
-
-func fdumplist(w io.Writer, s string, l Nodes) {
- fmt.Fprintf(w, "%s%+v\n", s, l)
-}
-
-func Dump(s string, n *Node) {
- fmt.Printf("%s [%p]%+v\n", s, n, n)
-}
-
-// TODO(gri) make variable local somehow
-var dumpdepth int
-
-// indent prints indentation to s.
-func indent(s fmt.State) {
- fmt.Fprint(s, "\n")
- for i := 0; i < dumpdepth; i++ {
- fmt.Fprint(s, ". ")
- }
-}
-
-func ellipsisIf(b bool) string {
- if b {
- return "..."
- }
- return ""
-}
diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
deleted file mode 100644
index 929653ebbd092..0000000000000
--- a/src/cmd/compile/internal/gc/gen.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/src"
- "strconv"
-)
-
-// sysfunc looks up Go function name in package runtime. This function
-// must follow the internal calling convention.
-func sysfunc(name string) *obj.LSym {
- s := Runtimepkg.Lookup(name)
- s.SetFunc(true)
- return s.Linksym()
-}
-
-// sysvar looks up a variable (or assembly function) name in package
-// runtime. If this is a function, it may have a special calling
-// convention.
-func sysvar(name string) *obj.LSym {
- return Runtimepkg.Lookup(name).Linksym()
-}
-
-// isParamStackCopy reports whether this is the on-stack copy of a
-// function parameter that moved to the heap.
-func (n *Node) isParamStackCopy() bool {
- return n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) && n.Name.Param.Heapaddr != nil
-}
-
-// isParamHeapCopy reports whether this is the on-heap copy of
-// a function parameter that moved to the heap.
-func (n *Node) isParamHeapCopy() bool {
- return n.Op == ONAME && n.Class() == PAUTOHEAP && n.Name.Param.Stackcopy != nil
-}
-
-// autotmpname returns the name for an autotmp variable numbered n.
-func autotmpname(n int) string {
- // Give each tmp a different name so that they can be registerized.
- // Add a preceding . to avoid clashing with legal names.
- const prefix = ".autotmp_"
- // Start with a buffer big enough to hold a large n.
- b := []byte(prefix + " ")[:len(prefix)]
- b = strconv.AppendInt(b, int64(n), 10)
- return types.InternString(b)
-}
-
-// make a new Node off the books
-func tempAt(pos src.XPos, curfn *Node, t *types.Type) *Node {
- if curfn == nil {
- Fatalf("no curfn for tempAt")
- }
- if curfn.Func.Closure != nil && curfn.Op == OCLOSURE {
- Dump("tempAt", curfn)
- Fatalf("adding tempAt to wrong closure function")
- }
- if t == nil {
- Fatalf("tempAt called with nil type")
- }
-
- s := &types.Sym{
- Name: autotmpname(len(curfn.Func.Dcl)),
- Pkg: localpkg,
- }
- n := newnamel(pos, s)
- s.Def = asTypesNode(n)
- n.Type = t
- n.SetClass(PAUTO)
- n.Esc = EscNever
- n.Name.Curfn = curfn
- n.Name.SetUsed(true)
- n.Name.SetAutoTemp(true)
- curfn.Func.Dcl = append(curfn.Func.Dcl, n)
-
- dowidth(t)
-
- return n.Orig
-}
-
-func temp(t *types.Type) *Node {
- return tempAt(lineno, Curfn, t)
-}
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
deleted file mode 100644
index 274930bd156f8..0000000000000
--- a/src/cmd/compile/internal/gc/go.go
+++ /dev/null
@@ -1,349 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/ssa"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/src"
- "sync"
-)
-
-const (
- BADWIDTH = types.BADWIDTH
-)
-
-var (
- // maximum size variable which we will allocate on the stack.
- // This limit is for explicit variable declarations like "var x T" or "x := ...".
- // Note: the flag smallframes can update this value.
- maxStackVarSize = int64(10 * 1024 * 1024)
-
- // maximum size of implicit variables that we will allocate on the stack.
- // p := new(T) allocating T on the stack
- // p := &T{} allocating T on the stack
- // s := make([]T, n) allocating [n]T on the stack
- // s := []byte("...") allocating [n]byte on the stack
- // Note: the flag smallframes can update this value.
- maxImplicitStackVarSize = int64(64 * 1024)
-
- // smallArrayBytes is the maximum size of an array which is considered small.
- // Small arrays will be initialized directly with a sequence of constant stores.
- // Large arrays will be initialized by copying from a static temp.
- // 256 bytes was chosen to minimize generated code + statictmp size.
- smallArrayBytes = int64(256)
-)
-
-// isRuntimePkg reports whether p is package runtime.
-func isRuntimePkg(p *types.Pkg) bool {
- if compiling_runtime && p == localpkg {
- return true
- }
- return p.Path == "runtime"
-}
-
-// isReflectPkg reports whether p is package reflect.
-func isReflectPkg(p *types.Pkg) bool {
- if p == localpkg {
- return myimportpath == "reflect"
- }
- return p.Path == "reflect"
-}
-
-// The Class of a variable/function describes the "storage class"
-// of a variable or function. During parsing, storage classes are
-// called declaration contexts.
-type Class uint8
-
-//go:generate stringer -type=Class
-const (
- Pxxx Class = iota // no class; used during ssa conversion to indicate pseudo-variables
- PEXTERN // global variables
- PAUTO // local variables
- PAUTOHEAP // local variables or parameters moved to heap
- PPARAM // input arguments
- PPARAMOUT // output results
- PFUNC // global functions
-
- // Careful: Class is stored in three bits in Node.flags.
- _ = uint((1 << 3) - iota) // static assert for iota <= (1 << 3)
-)
-
-// Slices in the runtime are represented by three components:
-//
-// type slice struct {
-// ptr unsafe.Pointer
-// len int
-// cap int
-// }
-//
-// Strings in the runtime are represented by two components:
-//
-// type string struct {
-// ptr unsafe.Pointer
-// len int
-// }
-//
-// These variables are the offsets of fields and sizes of these structs.
-var (
- slicePtrOffset int64
- sliceLenOffset int64
- sliceCapOffset int64
-
- sizeofSlice int64
- sizeofString int64
-)
-
-var pragcgobuf [][]string
-
-var outfile string
-var linkobj string
-
-// nerrors is the number of compiler errors reported
-// since the last call to saveerrors.
-var nerrors int
-
-// nsavederrors is the total number of compiler errors
-// reported before the last call to saveerrors.
-var nsavederrors int
-
-var nsyntaxerrors int
-
-var decldepth int32
-
-var nolocalimports bool
-
-// gc debug flags
-type DebugFlags struct {
- P, B, C, E,
- K, L, N, S,
- W, e, h, j,
- l, m, r, w int
-}
-
-var Debug DebugFlags
-
-var debugstr string
-
-var Debug_checknil int
-var Debug_typeassert int
-
-var localpkg *types.Pkg // package being compiled
-
-var inimport bool // set during import
-
-var itabpkg *types.Pkg // fake pkg for itab entries
-
-var itablinkpkg *types.Pkg // fake package for runtime itab entries
-
-var Runtimepkg *types.Pkg // fake package runtime
-
-var racepkg *types.Pkg // package runtime/race
-
-var msanpkg *types.Pkg // package runtime/msan
-
-var unsafepkg *types.Pkg // package unsafe
-
-var trackpkg *types.Pkg // fake package for field tracking
-
-var mappkg *types.Pkg // fake package for map zero value
-
-var gopkg *types.Pkg // pseudo-package for method symbols on anonymous receiver types
-
-var zerosize int64
-
-var myimportpath string
-
-var localimport string
-
-var asmhdr string
-
-var simtype [NTYPE]types.EType
-
-var (
- isInt [NTYPE]bool
- isFloat [NTYPE]bool
- isComplex [NTYPE]bool
- issimple [NTYPE]bool
-)
-
-var (
- okforeq [NTYPE]bool
- okforadd [NTYPE]bool
- okforand [NTYPE]bool
- okfornone [NTYPE]bool
- okforcmp [NTYPE]bool
- okforbool [NTYPE]bool
- okforcap [NTYPE]bool
- okforlen [NTYPE]bool
- okforarith [NTYPE]bool
- okforconst [NTYPE]bool
-)
-
-var (
- okfor [OEND][]bool
- iscmp [OEND]bool
-)
-
-var minintval [NTYPE]*Mpint
-
-var maxintval [NTYPE]*Mpint
-
-var minfltval [NTYPE]*Mpflt
-
-var maxfltval [NTYPE]*Mpflt
-
-var xtop []*Node
-
-var exportlist []*Node
-
-var importlist []*Node // imported functions and methods with inlinable bodies
-
-var (
- funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym)
- funcsyms []*types.Sym
-)
-
-var dclcontext Class // PEXTERN/PAUTO
-
-var Curfn *Node
-
-var Widthptr int
-
-var Widthreg int
-
-var nblank *Node
-
-var typecheckok bool
-
-var compiling_runtime bool
-
-// Compiling the standard library
-var compiling_std bool
-
-var use_writebarrier bool
-
-var pure_go bool
-
-var flag_installsuffix string
-
-var flag_race bool
-
-var flag_msan bool
-
-var flagDWARF bool
-
-// Whether we are adding any sort of code instrumentation, such as
-// when the race detector is enabled.
-var instrumenting bool
-
-// Whether we are tracking lexical scopes for DWARF.
-var trackScopes bool
-
-// Controls generation of DWARF inlined instance records. Zero
-// disables, 1 emits inlined routines but suppresses var info,
-// and 2 emits inlined routines with tracking of formals/locals.
-var genDwarfInline int
-
-var debuglive int
-
-var Ctxt *obj.Link
-
-var writearchive bool
-
-var nodfp *Node
-
-var disable_checknil int
-
-var autogeneratedPos src.XPos
-
-// interface to back end
-
-type Arch struct {
- LinkArch *obj.LinkArch
-
- REGSP int
- MAXWIDTH int64
- SoftFloat bool
-
- PadFrame func(int64) int64
-
- // ZeroRange zeroes a range of memory on stack. It is only inserted
- // at function entry, and it is ok to clobber registers.
- ZeroRange func(*Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog
-
- Ginsnop func(*Progs) *obj.Prog
- Ginsnopdefer func(*Progs) *obj.Prog // special ginsnop for deferreturn
-
- // SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
- SSAMarkMoves func(*SSAGenState, *ssa.Block)
-
- // SSAGenValue emits Prog(s) for the Value.
- SSAGenValue func(*SSAGenState, *ssa.Value)
-
- // SSAGenBlock emits end-of-block Progs. SSAGenValue should be called
- // for all values in the block before SSAGenBlock.
- SSAGenBlock func(s *SSAGenState, b, next *ssa.Block)
-}
-
-var thearch Arch
-
-var (
- staticuint64s,
- zerobase *Node
-
- assertE2I,
- assertE2I2,
- assertI2I,
- assertI2I2,
- deferproc,
- deferprocStack,
- Deferreturn,
- Duffcopy,
- Duffzero,
- gcWriteBarrier,
- goschedguarded,
- growslice,
- msanread,
- msanwrite,
- msanmove,
- newobject,
- newproc,
- panicdivide,
- panicshift,
- panicdottypeE,
- panicdottypeI,
- panicnildottype,
- panicoverflow,
- raceread,
- racereadrange,
- racewrite,
- racewriterange,
- x86HasPOPCNT,
- x86HasSSE41,
- x86HasFMA,
- armHasVFPv4,
- arm64HasATOMICS,
- typedmemclr,
- typedmemmove,
- Udiv,
- writeBarrier,
- zerobaseSym *obj.LSym
-
- BoundsCheckFunc [ssa.BoundsKindCount]*obj.LSym
- ExtendCheckFunc [ssa.BoundsKindCount]*obj.LSym
-
- // Wasm
- WasmMove,
- WasmZero,
- WasmDiv,
- WasmTruncS,
- WasmTruncU,
- SigPanic *obj.LSym
-)
-
-// GCWriteBarrierReg maps from registers to gcWriteBarrier implementation LSyms.
-var GCWriteBarrierReg map[int16]*obj.LSym
diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go
deleted file mode 100644
index d599a383e7b88..0000000000000
--- a/src/cmd/compile/internal/gc/gsubr.go
+++ /dev/null
@@ -1,333 +0,0 @@
-// Derived from Inferno utils/6c/txt.c
-// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6c/txt.c
-//
-// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
-// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
-// Portions Copyright © 1997-1999 Vita Nuova Limited
-// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-// Portions Copyright © 2004,2006 Bruce Ellis
-// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
-// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-// Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package gc
-
-import (
- "cmd/compile/internal/ssa"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
-)
-
-var sharedProgArray = new([10000]obj.Prog) // *T instead of T to work around issue 19839
-
-// Progs accumulates Progs for a function and converts them into machine code.
-type Progs struct {
- Text *obj.Prog // ATEXT Prog for this function
- next *obj.Prog // next Prog
- pc int64 // virtual PC; count of Progs
- pos src.XPos // position to use for new Progs
- curfn *Node // fn these Progs are for
- progcache []obj.Prog // local progcache
- cacheidx int // first free element of progcache
-
- nextLive LivenessIndex // liveness index for the next Prog
- prevLive LivenessIndex // last emitted liveness index
-}
-
-// newProgs returns a new Progs for fn.
-// worker indicates which of the backend workers will use the Progs.
-func newProgs(fn *Node, worker int) *Progs {
- pp := new(Progs)
- if Ctxt.CanReuseProgs() {
- sz := len(sharedProgArray) / nBackendWorkers
- pp.progcache = sharedProgArray[sz*worker : sz*(worker+1)]
- }
- pp.curfn = fn
-
- // prime the pump
- pp.next = pp.NewProg()
- pp.clearp(pp.next)
-
- pp.pos = fn.Pos
- pp.settext(fn)
- // PCDATA tables implicitly start with index -1.
- pp.prevLive = LivenessIndex{-1, false}
- pp.nextLive = pp.prevLive
- return pp
-}
-
-func (pp *Progs) NewProg() *obj.Prog {
- var p *obj.Prog
- if pp.cacheidx < len(pp.progcache) {
- p = &pp.progcache[pp.cacheidx]
- pp.cacheidx++
- } else {
- p = new(obj.Prog)
- }
- p.Ctxt = Ctxt
- return p
-}
-
-// Flush converts from pp to machine code.
-func (pp *Progs) Flush() {
- plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.curfn}
- obj.Flushplist(Ctxt, plist, pp.NewProg, myimportpath)
-}
-
-// Free clears pp and any associated resources.
-func (pp *Progs) Free() {
- if Ctxt.CanReuseProgs() {
- // Clear progs to enable GC and avoid abuse.
- s := pp.progcache[:pp.cacheidx]
- for i := range s {
- s[i] = obj.Prog{}
- }
- }
- // Clear pp to avoid abuse.
- *pp = Progs{}
-}
-
-// Prog adds a Prog with instruction As to pp.
-func (pp *Progs) Prog(as obj.As) *obj.Prog {
- if pp.nextLive.StackMapValid() && pp.nextLive.stackMapIndex != pp.prevLive.stackMapIndex {
- // Emit stack map index change.
- idx := pp.nextLive.stackMapIndex
- pp.prevLive.stackMapIndex = idx
- p := pp.Prog(obj.APCDATA)
- Addrconst(&p.From, objabi.PCDATA_StackMapIndex)
- Addrconst(&p.To, int64(idx))
- }
- if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint {
- // Emit unsafe-point marker.
- pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint
- p := pp.Prog(obj.APCDATA)
- Addrconst(&p.From, objabi.PCDATA_UnsafePoint)
- if pp.nextLive.isUnsafePoint {
- Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe)
- } else {
- Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe)
- }
- }
-
- p := pp.next
- pp.next = pp.NewProg()
- pp.clearp(pp.next)
- p.Link = pp.next
-
- if !pp.pos.IsKnown() && Debug.K != 0 {
- Warn("prog: unknown position (line 0)")
- }
-
- p.As = as
- p.Pos = pp.pos
- if pp.pos.IsStmt() == src.PosIsStmt {
- // Clear IsStmt for later Progs at this pos provided that as can be marked as a stmt
- if ssa.LosesStmtMark(as) {
- return p
- }
- pp.pos = pp.pos.WithNotStmt()
- }
- return p
-}
-
-func (pp *Progs) clearp(p *obj.Prog) {
- obj.Nopout(p)
- p.As = obj.AEND
- p.Pc = pp.pc
- pp.pc++
-}
-
-func (pp *Progs) Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
- q := pp.NewProg()
- pp.clearp(q)
- q.As = as
- q.Pos = p.Pos
- q.From.Type = ftype
- q.From.Reg = freg
- q.From.Offset = foffset
- q.To.Type = ttype
- q.To.Reg = treg
- q.To.Offset = toffset
- q.Link = p.Link
- p.Link = q
- return q
-}
-
-func (pp *Progs) settext(fn *Node) {
- if pp.Text != nil {
- Fatalf("Progs.settext called twice")
- }
- ptxt := pp.Prog(obj.ATEXT)
- pp.Text = ptxt
-
- fn.Func.lsym.Func().Text = ptxt
- ptxt.From.Type = obj.TYPE_MEM
- ptxt.From.Name = obj.NAME_EXTERN
- ptxt.From.Sym = fn.Func.lsym
-}
-
-// initLSym defines f's obj.LSym and initializes it based on the
-// properties of f. This includes setting the symbol flags and ABI and
-// creating and initializing related DWARF symbols.
-//
-// initLSym must be called exactly once per function and must be
-// called for both functions with bodies and functions without bodies.
-func (f *Func) initLSym(hasBody bool) {
- if f.lsym != nil {
- Fatalf("Func.initLSym called twice")
- }
-
- if nam := f.Nname; !nam.isBlank() {
- f.lsym = nam.Sym.Linksym()
- if f.Pragma&Systemstack != 0 {
- f.lsym.Set(obj.AttrCFunc, true)
- }
-
- var aliasABI obj.ABI
- needABIAlias := false
- defABI, hasDefABI := symabiDefs[f.lsym.Name]
- if hasDefABI && defABI == obj.ABI0 {
- // Symbol is defined as ABI0. Create an
- // Internal -> ABI0 wrapper.
- f.lsym.SetABI(obj.ABI0)
- needABIAlias, aliasABI = true, obj.ABIInternal
- } else {
- // No ABI override. Check that the symbol is
- // using the expected ABI.
- want := obj.ABIInternal
- if f.lsym.ABI() != want {
- Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.lsym.Name, f.lsym.ABI(), want)
- }
- }
-
- isLinknameExported := nam.Sym.Linkname != "" && (hasBody || hasDefABI)
- if abi, ok := symabiRefs[f.lsym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
- // Either 1) this symbol is definitely
- // referenced as ABI0 from this package; or 2)
- // this symbol is defined in this package but
- // given a linkname, indicating that it may be
- // referenced from another package. Create an
- // ABI0 -> Internal wrapper so it can be
- // called as ABI0. In case 2, it's important
- // that we know it's defined in this package
- // since other packages may "pull" symbols
- // using linkname and we don't want to create
- // duplicate ABI wrappers.
- if f.lsym.ABI() != obj.ABI0 {
- needABIAlias, aliasABI = true, obj.ABI0
- }
- }
-
- if needABIAlias {
- // These LSyms have the same name as the
- // native function, so we create them directly
- // rather than looking them up. The uniqueness
- // of f.lsym ensures uniqueness of asym.
- asym := &obj.LSym{
- Name: f.lsym.Name,
- Type: objabi.SABIALIAS,
- R: []obj.Reloc{{Sym: f.lsym}}, // 0 size, so "informational"
- }
- asym.SetABI(aliasABI)
- asym.Set(obj.AttrDuplicateOK, true)
- Ctxt.ABIAliases = append(Ctxt.ABIAliases, asym)
- }
- }
-
- if !hasBody {
- // For body-less functions, we only create the LSym.
- return
- }
-
- var flag int
- if f.Dupok() {
- flag |= obj.DUPOK
- }
- if f.Wrapper() {
- flag |= obj.WRAPPER
- }
- if f.Needctxt() {
- flag |= obj.NEEDCTXT
- }
- if f.Pragma&Nosplit != 0 {
- flag |= obj.NOSPLIT
- }
- if f.ReflectMethod() {
- flag |= obj.REFLECTMETHOD
- }
-
- // Clumsy but important.
- // See test/recover.go for test cases and src/reflect/value.go
- // for the actual functions being considered.
- if myimportpath == "reflect" {
- switch f.Nname.Sym.Name {
- case "callReflect", "callMethod":
- flag |= obj.WRAPPER
- }
- }
-
- Ctxt.InitTextSym(f.lsym, flag)
-}
-
-func ggloblnod(nam *Node) {
- s := nam.Sym.Linksym()
- s.Gotype = ngotype(nam).Linksym()
- flags := 0
- if nam.Name.Readonly() {
- flags = obj.RODATA
- }
- if nam.Type != nil && !nam.Type.HasPointers() {
- flags |= obj.NOPTR
- }
- Ctxt.Globl(s, nam.Type.Width, flags)
- if nam.Name.LibfuzzerExtraCounter() {
- s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
- }
- if nam.Sym.Linkname != "" {
- // Make sure linkname'd symbol is non-package. When a symbol is
- // both imported and linkname'd, s.Pkg may not set to "_" in
- // types.Sym.Linksym because LSym already exists. Set it here.
- s.Pkg = "_"
- }
-}
-
-func ggloblsym(s *obj.LSym, width int32, flags int16) {
- if flags&obj.LOCAL != 0 {
- s.Set(obj.AttrLocal, true)
- flags &^= obj.LOCAL
- }
- Ctxt.Globl(s, int64(width), int(flags))
-}
-
-func Addrconst(a *obj.Addr, v int64) {
- a.Sym = nil
- a.Type = obj.TYPE_CONST
- a.Offset = v
-}
-
-func Patch(p *obj.Prog, to *obj.Prog) {
- if p.To.Type != obj.TYPE_BRANCH {
- Fatalf("patch: not a branch")
- }
- p.To.SetTarget(to)
- p.To.Offset = to.Pc
-}
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
deleted file mode 100644
index c0114d0e53b16..0000000000000
--- a/src/cmd/compile/internal/gc/iimport.go
+++ /dev/null
@@ -1,1117 +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.
-
-// Indexed package import.
-// See iexport.go for the export data format.
-
-package gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/bio"
- "cmd/internal/goobj"
- "cmd/internal/obj"
- "cmd/internal/src"
- "encoding/binary"
- "fmt"
- "io"
- "math/big"
- "os"
- "strings"
-)
-
-// An iimporterAndOffset identifies an importer and an offset within
-// its data section.
-type iimporterAndOffset struct {
- p *iimporter
- off uint64
-}
-
-var (
- // declImporter maps from imported identifiers to an importer
- // and offset where that identifier's declaration can be read.
- declImporter = map[*types.Sym]iimporterAndOffset{}
-
- // inlineImporter is like declImporter, but for inline bodies
- // for function and method symbols.
- inlineImporter = map[*types.Sym]iimporterAndOffset{}
-)
-
-func expandDecl(n *Node) {
- if n.Op != ONONAME {
- return
- }
-
- r := importReaderFor(n, declImporter)
- if r == nil {
- // Can happen if user tries to reference an undeclared name.
- return
- }
-
- r.doDecl(n)
-}
-
-func expandInline(fn *Node) {
- if fn.Func.Inl.Body != nil {
- return
- }
-
- r := importReaderFor(fn, inlineImporter)
- if r == nil {
- Fatalf("missing import reader for %v", fn)
- }
-
- r.doInline(fn)
-}
-
-func importReaderFor(n *Node, importers map[*types.Sym]iimporterAndOffset) *importReader {
- x, ok := importers[n.Sym]
- if !ok {
- return nil
- }
-
- return x.p.newReader(x.off, n.Sym.Pkg)
-}
-
-type intReader struct {
- *bio.Reader
- pkg *types.Pkg
-}
-
-func (r *intReader) int64() int64 {
- i, err := binary.ReadVarint(r.Reader)
- if err != nil {
- yyerror("import %q: read error: %v", r.pkg.Path, err)
- errorexit()
- }
- return i
-}
-
-func (r *intReader) uint64() uint64 {
- i, err := binary.ReadUvarint(r.Reader)
- if err != nil {
- yyerror("import %q: read error: %v", r.pkg.Path, err)
- errorexit()
- }
- return i
-}
-
-func iimport(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintType) {
- ir := &intReader{in, pkg}
-
- version := ir.uint64()
- if version != iexportVersion {
- yyerror("import %q: unknown export format version %d", pkg.Path, version)
- errorexit()
- }
-
- sLen := ir.uint64()
- dLen := ir.uint64()
-
- // Map string (and data) section into memory as a single large
- // string. This reduces heap fragmentation and allows
- // returning individual substrings very efficiently.
- data, err := mapFile(in.File(), in.Offset(), int64(sLen+dLen))
- if err != nil {
- yyerror("import %q: mapping input: %v", pkg.Path, err)
- errorexit()
- }
- stringData := data[:sLen]
- declData := data[sLen:]
-
- in.MustSeek(int64(sLen+dLen), os.SEEK_CUR)
-
- p := &iimporter{
- ipkg: pkg,
-
- pkgCache: map[uint64]*types.Pkg{},
- posBaseCache: map[uint64]*src.PosBase{},
- typCache: map[uint64]*types.Type{},
-
- stringData: stringData,
- declData: declData,
- }
-
- for i, pt := range predeclared() {
- p.typCache[uint64(i)] = pt
- }
-
- // Declaration index.
- for nPkgs := ir.uint64(); nPkgs > 0; nPkgs-- {
- pkg := p.pkgAt(ir.uint64())
- pkgName := p.stringAt(ir.uint64())
- pkgHeight := int(ir.uint64())
- if pkg.Name == "" {
- pkg.Name = pkgName
- pkg.Height = pkgHeight
- numImport[pkgName]++
-
- // TODO(mdempsky): This belongs somewhere else.
- pkg.Lookup("_").Def = asTypesNode(nblank)
- } else {
- if pkg.Name != pkgName {
- Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path)
- }
- if pkg.Height != pkgHeight {
- Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path)
- }
- }
-
- for nSyms := ir.uint64(); nSyms > 0; nSyms-- {
- s := pkg.Lookup(p.stringAt(ir.uint64()))
- off := ir.uint64()
-
- if _, ok := declImporter[s]; ok {
- continue
- }
- declImporter[s] = iimporterAndOffset{p, off}
-
- // Create stub declaration. If used, this will
- // be overwritten by expandDecl.
- if s.Def != nil {
- Fatalf("unexpected definition for %v: %v", s, asNode(s.Def))
- }
- s.Def = asTypesNode(npos(src.NoXPos, dclname(s)))
- }
- }
-
- // Inline body index.
- for nPkgs := ir.uint64(); nPkgs > 0; nPkgs-- {
- pkg := p.pkgAt(ir.uint64())
-
- for nSyms := ir.uint64(); nSyms > 0; nSyms-- {
- s := pkg.Lookup(p.stringAt(ir.uint64()))
- off := ir.uint64()
-
- if _, ok := inlineImporter[s]; ok {
- continue
- }
- inlineImporter[s] = iimporterAndOffset{p, off}
- }
- }
-
- // Fingerprint.
- _, err = io.ReadFull(in, fingerprint[:])
- if err != nil {
- yyerror("import %s: error reading fingerprint", pkg.Path)
- errorexit()
- }
- return fingerprint
-}
-
-type iimporter struct {
- ipkg *types.Pkg
-
- pkgCache map[uint64]*types.Pkg
- posBaseCache map[uint64]*src.PosBase
- typCache map[uint64]*types.Type
-
- stringData string
- declData string
-}
-
-func (p *iimporter) stringAt(off uint64) string {
- var x [binary.MaxVarintLen64]byte
- n := copy(x[:], p.stringData[off:])
-
- slen, n := binary.Uvarint(x[:n])
- if n <= 0 {
- Fatalf("varint failed")
- }
- spos := off + uint64(n)
- return p.stringData[spos : spos+slen]
-}
-
-func (p *iimporter) posBaseAt(off uint64) *src.PosBase {
- if posBase, ok := p.posBaseCache[off]; ok {
- return posBase
- }
-
- file := p.stringAt(off)
- posBase := src.NewFileBase(file, file)
- p.posBaseCache[off] = posBase
- return posBase
-}
-
-func (p *iimporter) pkgAt(off uint64) *types.Pkg {
- if pkg, ok := p.pkgCache[off]; ok {
- return pkg
- }
-
- pkg := p.ipkg
- if pkgPath := p.stringAt(off); pkgPath != "" {
- pkg = types.NewPkg(pkgPath, "")
- }
- p.pkgCache[off] = pkg
- return pkg
-}
-
-// An importReader keeps state for reading an individual imported
-// object (declaration or inline body).
-type importReader struct {
- strings.Reader
- p *iimporter
-
- currPkg *types.Pkg
- prevBase *src.PosBase
- prevLine int64
- prevColumn int64
-}
-
-func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
- r := &importReader{
- p: p,
- currPkg: pkg,
- }
- // (*strings.Reader).Reset wasn't added until Go 1.7, and we
- // need to build with Go 1.4.
- r.Reader = *strings.NewReader(p.declData[off:])
- return r
-}
-
-func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
-func (r *importReader) posBase() *src.PosBase { return r.p.posBaseAt(r.uint64()) }
-func (r *importReader) pkg() *types.Pkg { return r.p.pkgAt(r.uint64()) }
-
-func (r *importReader) setPkg() {
- r.currPkg = r.pkg()
-}
-
-func (r *importReader) doDecl(n *Node) {
- if n.Op != ONONAME {
- Fatalf("doDecl: unexpected Op for %v: %v", n.Sym, n.Op)
- }
-
- tag := r.byte()
- pos := r.pos()
-
- switch tag {
- case 'A':
- typ := r.typ()
-
- importalias(r.p.ipkg, pos, n.Sym, typ)
-
- case 'C':
- typ, val := r.value()
-
- importconst(r.p.ipkg, pos, n.Sym, typ, val)
-
- case 'F':
- typ := r.signature(nil)
-
- importfunc(r.p.ipkg, pos, n.Sym, typ)
- r.funcExt(n)
-
- case 'T':
- // Types can be recursive. We need to setup a stub
- // declaration before recursing.
- t := importtype(r.p.ipkg, pos, n.Sym)
-
- // We also need to defer width calculations until
- // after the underlying type has been assigned.
- defercheckwidth()
- underlying := r.typ()
- setUnderlying(t, underlying)
- resumecheckwidth()
-
- if underlying.IsInterface() {
- r.typeExt(t)
- break
- }
-
- ms := make([]*types.Field, r.uint64())
- for i := range ms {
- mpos := r.pos()
- msym := r.ident()
- recv := r.param()
- mtyp := r.signature(recv)
-
- f := types.NewField()
- f.Pos = mpos
- f.Sym = msym
- f.Type = mtyp
- ms[i] = f
-
- m := newfuncnamel(mpos, methodSym(recv.Type, msym))
- m.Type = mtyp
- m.SetClass(PFUNC)
- // methodSym already marked m.Sym as a function.
-
- // (comment from parser.go)
- // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
- // (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled
- // out by typecheck's lookdot as this $$.ttype. So by providing
- // this back link here we avoid special casing there.
- mtyp.SetNname(asTypesNode(m))
- }
- t.Methods().Set(ms)
-
- r.typeExt(t)
- for _, m := range ms {
- r.methExt(m)
- }
-
- case 'V':
- typ := r.typ()
-
- importvar(r.p.ipkg, pos, n.Sym, typ)
- r.varExt(n)
-
- default:
- Fatalf("unexpected tag: %v", tag)
- }
-}
-
-func (p *importReader) value() (typ *types.Type, v Val) {
- typ = p.typ()
-
- switch constTypeOf(typ) {
- case CTNIL:
- v.U = &NilVal{}
- case CTBOOL:
- v.U = p.bool()
- case CTSTR:
- v.U = p.string()
- case CTINT:
- x := new(Mpint)
- x.Rune = typ == types.UntypedRune
- p.mpint(&x.Val, typ)
- v.U = x
- case CTFLT:
- x := newMpflt()
- p.float(x, typ)
- v.U = x
- case CTCPLX:
- x := newMpcmplx()
- p.float(&x.Real, typ)
- p.float(&x.Imag, typ)
- v.U = x
- }
- return
-}
-
-func (p *importReader) mpint(x *big.Int, typ *types.Type) {
- signed, maxBytes := intSize(typ)
-
- maxSmall := 256 - maxBytes
- if signed {
- maxSmall = 256 - 2*maxBytes
- }
- if maxBytes == 1 {
- maxSmall = 256
- }
-
- n, _ := p.ReadByte()
- if uint(n) < maxSmall {
- v := int64(n)
- if signed {
- v >>= 1
- if n&1 != 0 {
- v = ^v
- }
- }
- x.SetInt64(v)
- return
- }
-
- v := -n
- if signed {
- v = -(n &^ 1) >> 1
- }
- if v < 1 || uint(v) > maxBytes {
- Fatalf("weird decoding: %v, %v => %v", n, signed, v)
- }
- b := make([]byte, v)
- p.Read(b)
- x.SetBytes(b)
- if signed && n&1 != 0 {
- x.Neg(x)
- }
-}
-
-func (p *importReader) float(x *Mpflt, typ *types.Type) {
- var mant big.Int
- p.mpint(&mant, typ)
- m := x.Val.SetInt(&mant)
- if m.Sign() == 0 {
- return
- }
- m.SetMantExp(m, int(p.int64()))
-}
-
-func (r *importReader) ident() *types.Sym {
- name := r.string()
- if name == "" {
- return nil
- }
- pkg := r.currPkg
- if types.IsExported(name) {
- pkg = localpkg
- }
- return pkg.Lookup(name)
-}
-
-func (r *importReader) qualifiedIdent() *types.Sym {
- name := r.string()
- pkg := r.pkg()
- return pkg.Lookup(name)
-}
-
-func (r *importReader) pos() src.XPos {
- delta := r.int64()
- r.prevColumn += delta >> 1
- if delta&1 != 0 {
- delta = r.int64()
- r.prevLine += delta >> 1
- if delta&1 != 0 {
- r.prevBase = r.posBase()
- }
- }
-
- if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
- // TODO(mdempsky): Remove once we reliably write
- // position information for all nodes.
- return src.NoXPos
- }
-
- if r.prevBase == nil {
- Fatalf("missing posbase")
- }
- pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn))
- return Ctxt.PosTable.XPos(pos)
-}
-
-func (r *importReader) typ() *types.Type {
- return r.p.typAt(r.uint64())
-}
-
-func (p *iimporter) typAt(off uint64) *types.Type {
- t, ok := p.typCache[off]
- if !ok {
- if off < predeclReserved {
- Fatalf("predeclared type missing from cache: %d", off)
- }
- t = p.newReader(off-predeclReserved, nil).typ1()
- p.typCache[off] = t
- }
- return t
-}
-
-func (r *importReader) typ1() *types.Type {
- switch k := r.kind(); k {
- default:
- Fatalf("unexpected kind tag in %q: %v", r.p.ipkg.Path, k)
- return nil
-
- case definedType:
- // We might be called from within doInline, in which
- // case Sym.Def can point to declared parameters
- // instead of the top-level types. Also, we don't
- // support inlining functions with local defined
- // types. Therefore, this must be a package-scope
- // type.
- n := asNode(r.qualifiedIdent().PkgDef())
- if n.Op == ONONAME {
- expandDecl(n)
- }
- if n.Op != OTYPE {
- Fatalf("expected OTYPE, got %v: %v, %v", n.Op, n.Sym, n)
- }
- return n.Type
- case pointerType:
- return types.NewPtr(r.typ())
- case sliceType:
- return types.NewSlice(r.typ())
- case arrayType:
- n := r.uint64()
- return types.NewArray(r.typ(), int64(n))
- case chanType:
- dir := types.ChanDir(r.uint64())
- return types.NewChan(r.typ(), dir)
- case mapType:
- return types.NewMap(r.typ(), r.typ())
-
- case signatureType:
- r.setPkg()
- return r.signature(nil)
-
- case structType:
- r.setPkg()
-
- fs := make([]*types.Field, r.uint64())
- for i := range fs {
- pos := r.pos()
- sym := r.ident()
- typ := r.typ()
- emb := r.bool()
- note := r.string()
-
- f := types.NewField()
- f.Pos = pos
- f.Sym = sym
- f.Type = typ
- if emb {
- f.Embedded = 1
- }
- f.Note = note
- fs[i] = f
- }
-
- t := types.New(TSTRUCT)
- t.SetPkg(r.currPkg)
- t.SetFields(fs)
- return t
-
- case interfaceType:
- r.setPkg()
-
- embeddeds := make([]*types.Field, r.uint64())
- for i := range embeddeds {
- pos := r.pos()
- typ := r.typ()
-
- f := types.NewField()
- f.Pos = pos
- f.Type = typ
- embeddeds[i] = f
- }
-
- methods := make([]*types.Field, r.uint64())
- for i := range methods {
- pos := r.pos()
- sym := r.ident()
- typ := r.signature(fakeRecvField())
-
- f := types.NewField()
- f.Pos = pos
- f.Sym = sym
- f.Type = typ
- methods[i] = f
- }
-
- t := types.New(TINTER)
- t.SetPkg(r.currPkg)
- t.SetInterface(append(embeddeds, methods...))
-
- // Ensure we expand the interface in the frontend (#25055).
- checkwidth(t)
- return t
- }
-}
-
-func (r *importReader) kind() itag {
- return itag(r.uint64())
-}
-
-func (r *importReader) signature(recv *types.Field) *types.Type {
- params := r.paramList()
- results := r.paramList()
- if n := len(params); n > 0 {
- params[n-1].SetIsDDD(r.bool())
- }
- t := functypefield(recv, params, results)
- t.SetPkg(r.currPkg)
- return t
-}
-
-func (r *importReader) paramList() []*types.Field {
- fs := make([]*types.Field, r.uint64())
- for i := range fs {
- fs[i] = r.param()
- }
- return fs
-}
-
-func (r *importReader) param() *types.Field {
- f := types.NewField()
- f.Pos = r.pos()
- f.Sym = r.ident()
- f.Type = r.typ()
- return f
-}
-
-func (r *importReader) bool() bool {
- return r.uint64() != 0
-}
-
-func (r *importReader) int64() int64 {
- n, err := binary.ReadVarint(r)
- if err != nil {
- Fatalf("readVarint: %v", err)
- }
- return n
-}
-
-func (r *importReader) uint64() uint64 {
- n, err := binary.ReadUvarint(r)
- if err != nil {
- Fatalf("readVarint: %v", err)
- }
- return n
-}
-
-func (r *importReader) byte() byte {
- x, err := r.ReadByte()
- if err != nil {
- Fatalf("declReader.ReadByte: %v", err)
- }
- return x
-}
-
-// Compiler-specific extensions.
-
-func (r *importReader) varExt(n *Node) {
- r.linkname(n.Sym)
- r.symIdx(n.Sym)
-}
-
-func (r *importReader) funcExt(n *Node) {
- r.linkname(n.Sym)
- r.symIdx(n.Sym)
-
- // Escape analysis.
- for _, fs := range &types.RecvsParams {
- for _, f := range fs(n.Type).FieldSlice() {
- f.Note = r.string()
- }
- }
-
- // Inline body.
- if u := r.uint64(); u > 0 {
- n.Func.Inl = &Inline{
- Cost: int32(u - 1),
- }
- n.Func.Endlineno = r.pos()
- }
-}
-
-func (r *importReader) methExt(m *types.Field) {
- if r.bool() {
- m.SetNointerface(true)
- }
- r.funcExt(asNode(m.Type.Nname()))
-}
-
-func (r *importReader) linkname(s *types.Sym) {
- s.Linkname = r.string()
-}
-
-func (r *importReader) symIdx(s *types.Sym) {
- lsym := s.Linksym()
- idx := int32(r.int64())
- if idx != -1 {
- if s.Linkname != "" {
- Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
- }
- lsym.SymIdx = idx
- lsym.Set(obj.AttrIndexed, true)
- }
-}
-
-func (r *importReader) typeExt(t *types.Type) {
- t.SetNotInHeap(r.bool())
- i, pi := r.int64(), r.int64()
- if i != -1 && pi != -1 {
- typeSymIdx[t] = [2]int64{i, pi}
- }
-}
-
-// Map imported type T to the index of type descriptor symbols of T and *T,
-// so we can use index to reference the symbol.
-var typeSymIdx = make(map[*types.Type][2]int64)
-
-func (r *importReader) doInline(n *Node) {
- if len(n.Func.Inl.Body) != 0 {
- Fatalf("%v already has inline body", n)
- }
-
- funchdr(n)
- body := r.stmtList()
- funcbody()
- 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 = []*Node{}
- }
- n.Func.Inl.Body = body
-
- importlist = append(importlist, n)
-
- if Debug.E > 0 && Debug.m > 2 {
- if Debug.m > 3 {
- fmt.Printf("inl body for %v %#v: %+v\n", n, n.Type, asNodes(n.Func.Inl.Body))
- } else {
- fmt.Printf("inl body for %v %#v: %v\n", n, n.Type, asNodes(n.Func.Inl.Body))
- }
- }
-}
-
-// ----------------------------------------------------------------------------
-// Inlined function bodies
-
-// Approach: Read nodes and use them to create/declare the same data structures
-// as done originally by the (hidden) parser by closely following the parser's
-// original code. In other words, "parsing" the import data (which happens to
-// be encoded in binary rather textual form) is the best way at the moment to
-// re-establish the syntax tree's invariants. At some future point we might be
-// able to avoid this round-about way and create the rewritten nodes directly,
-// possibly avoiding a lot of duplicate work (name resolution, type checking).
-//
-// Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their
-// unrefined nodes (since this is what the importer uses). The respective case
-// entries are unreachable in the importer.
-
-func (r *importReader) stmtList() []*Node {
- var list []*Node
- for {
- n := r.node()
- if n == nil {
- break
- }
- // OBLOCK nodes may be created when importing ODCL nodes - unpack them
- if n.Op == OBLOCK {
- list = append(list, n.List.Slice()...)
- } else {
- list = append(list, n)
- }
-
- }
- return list
-}
-
-func (r *importReader) caseList(sw *Node) []*Node {
- namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
-
- cases := make([]*Node, r.uint64())
- for i := range cases {
- cas := nodl(r.pos(), OCASE, nil, nil)
- cas.List.Set(r.stmtList())
- if namedTypeSwitch {
- // Note: per-case variables will have distinct, dotted
- // names after import. That's okay: swt.go only needs
- // Sym for diagnostics anyway.
- caseVar := newnamel(cas.Pos, r.ident())
- declare(caseVar, dclcontext)
- cas.Rlist.Set1(caseVar)
- caseVar.Name.Defn = sw.Left
- }
- cas.Nbody.Set(r.stmtList())
- cases[i] = cas
- }
- return cases
-}
-
-func (r *importReader) exprList() []*Node {
- var list []*Node
- for {
- n := r.expr()
- if n == nil {
- break
- }
- list = append(list, n)
- }
- return list
-}
-
-func (r *importReader) expr() *Node {
- n := r.node()
- if n != nil && n.Op == OBLOCK {
- Fatalf("unexpected block node: %v", n)
- }
- return n
-}
-
-// TODO(gri) split into expr and stmt
-func (r *importReader) node() *Node {
- switch op := r.op(); op {
- // expressions
- // case OPAREN:
- // unreachable - unpacked by exporter
-
- case OLITERAL:
- pos := r.pos()
- typ, val := r.value()
-
- n := npos(pos, nodlit(val))
- n.Type = typ
- return n
-
- case ONONAME:
- return mkname(r.qualifiedIdent())
-
- case ONAME:
- return mkname(r.ident())
-
- // case OPACK, ONONAME:
- // unreachable - should have been resolved by typechecking
-
- case OTYPE:
- return typenod(r.typ())
-
- case OTYPESW:
- n := nodl(r.pos(), OTYPESW, nil, nil)
- if s := r.ident(); s != nil {
- n.Left = npos(n.Pos, newnoname(s))
- }
- n.Right, _ = r.exprsOrNil()
- return n
-
- // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
- // unreachable - should have been resolved by typechecking
-
- // case OCLOSURE:
- // unimplemented
-
- // case OPTRLIT:
- // unreachable - mapped to case OADDR below by exporter
-
- case OSTRUCTLIT:
- // TODO(mdempsky): Export position information for OSTRUCTKEY nodes.
- savedlineno := lineno
- lineno = r.pos()
- n := nodl(lineno, OCOMPLIT, nil, typenod(r.typ()))
- n.List.Set(r.elemList()) // special handling of field names
- lineno = savedlineno
- return n
-
- // case OARRAYLIT, OSLICELIT, OMAPLIT:
- // unreachable - mapped to case OCOMPLIT below by exporter
-
- case OCOMPLIT:
- n := nodl(r.pos(), OCOMPLIT, nil, typenod(r.typ()))
- n.List.Set(r.exprList())
- return n
-
- case OKEY:
- pos := r.pos()
- left, right := r.exprsOrNil()
- return nodl(pos, OKEY, left, right)
-
- // case OSTRUCTKEY:
- // unreachable - handled in case OSTRUCTLIT by elemList
-
- // case OCALLPART:
- // unreachable - mapped to case OXDOT below by exporter
-
- // case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
- // unreachable - mapped to case OXDOT below by exporter
-
- case OXDOT:
- // see parser.new_dotname
- return npos(r.pos(), nodSym(OXDOT, r.expr(), r.ident()))
-
- // case ODOTTYPE, ODOTTYPE2:
- // unreachable - mapped to case ODOTTYPE below by exporter
-
- case ODOTTYPE:
- n := nodl(r.pos(), ODOTTYPE, r.expr(), nil)
- n.Type = r.typ()
- return n
-
- // case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
- // unreachable - mapped to cases below by exporter
-
- case OINDEX:
- return nodl(r.pos(), op, r.expr(), r.expr())
-
- case OSLICE, OSLICE3:
- n := nodl(r.pos(), op, r.expr(), nil)
- low, high := r.exprsOrNil()
- var max *Node
- if n.Op.IsSlice3() {
- max = r.expr()
- }
- n.SetSliceBounds(low, high, max)
- return n
-
- // case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, ORUNES2STR, OSTR2BYTES, OSTR2RUNES, ORUNESTR:
- // unreachable - mapped to OCONV case below by exporter
-
- case OCONV:
- n := nodl(r.pos(), OCONV, r.expr(), nil)
- n.Type = r.typ()
- return n
-
- case OCOPY, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN:
- n := npos(r.pos(), builtinCall(op))
- n.List.Set(r.exprList())
- if op == OAPPEND {
- n.SetIsDDD(r.bool())
- }
- return n
-
- // case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
- // unreachable - mapped to OCALL case below by exporter
-
- case OCALL:
- n := nodl(r.pos(), OCALL, nil, nil)
- n.Ninit.Set(r.stmtList())
- n.Left = r.expr()
- n.List.Set(r.exprList())
- n.SetIsDDD(r.bool())
- return n
-
- case OMAKEMAP, OMAKECHAN, OMAKESLICE:
- n := npos(r.pos(), builtinCall(OMAKE))
- n.List.Append(typenod(r.typ()))
- n.List.Append(r.exprList()...)
- return n
-
- // unary expressions
- case OPLUS, ONEG, OADDR, OBITNOT, ODEREF, ONOT, ORECV:
- return nodl(r.pos(), op, r.expr(), nil)
-
- // binary expressions
- case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT,
- OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND, OSUB, OXOR:
- return nodl(r.pos(), op, r.expr(), r.expr())
-
- case OADDSTR:
- pos := r.pos()
- list := r.exprList()
- x := npos(pos, list[0])
- for _, y := range list[1:] {
- x = nodl(pos, OADD, x, y)
- }
- return x
-
- // --------------------------------------------------------------------
- // statements
- case ODCL:
- pos := r.pos()
- lhs := npos(pos, dclname(r.ident()))
- typ := typenod(r.typ())
- return npos(pos, liststmt(variter([]*Node{lhs}, typ, nil))) // TODO(gri) avoid list creation
-
- // case ODCLFIELD:
- // unimplemented
-
- // case OAS, OASWB:
- // unreachable - mapped to OAS case below by exporter
-
- case OAS:
- return nodl(r.pos(), OAS, r.expr(), r.expr())
-
- case OASOP:
- n := nodl(r.pos(), OASOP, nil, nil)
- n.SetSubOp(r.op())
- n.Left = r.expr()
- if !r.bool() {
- n.Right = nodintconst(1)
- n.SetImplicit(true)
- } else {
- n.Right = r.expr()
- }
- return n
-
- // case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- // unreachable - mapped to OAS2 case below by exporter
-
- case OAS2:
- n := nodl(r.pos(), OAS2, nil, nil)
- n.List.Set(r.exprList())
- n.Rlist.Set(r.exprList())
- return n
-
- case ORETURN:
- n := nodl(r.pos(), ORETURN, nil, nil)
- n.List.Set(r.exprList())
- return n
-
- // case ORETJMP:
- // unreachable - generated by compiler for trampolin routines (not exported)
-
- case OGO, ODEFER:
- return nodl(r.pos(), op, r.expr(), nil)
-
- case OIF:
- n := nodl(r.pos(), OIF, nil, nil)
- n.Ninit.Set(r.stmtList())
- n.Left = r.expr()
- n.Nbody.Set(r.stmtList())
- n.Rlist.Set(r.stmtList())
- return n
-
- case OFOR:
- n := nodl(r.pos(), OFOR, nil, nil)
- n.Ninit.Set(r.stmtList())
- n.Left, n.Right = r.exprsOrNil()
- n.Nbody.Set(r.stmtList())
- return n
-
- case ORANGE:
- n := nodl(r.pos(), ORANGE, nil, nil)
- n.List.Set(r.stmtList())
- n.Right = r.expr()
- n.Nbody.Set(r.stmtList())
- return n
-
- case OSELECT, OSWITCH:
- n := nodl(r.pos(), op, nil, nil)
- n.Ninit.Set(r.stmtList())
- n.Left, _ = r.exprsOrNil()
- n.List.Set(r.caseList(n))
- return n
-
- // case OCASE:
- // handled by caseList
-
- case OFALL:
- n := nodl(r.pos(), OFALL, nil, nil)
- return n
-
- case OBREAK, OCONTINUE:
- pos := r.pos()
- left, _ := r.exprsOrNil()
- if left != nil {
- left = newname(left.Sym)
- }
- return nodl(pos, op, left, nil)
-
- // case OEMPTY:
- // unreachable - not emitted by exporter
-
- case OGOTO, OLABEL:
- n := nodl(r.pos(), op, nil, nil)
- n.Sym = lookup(r.string())
- return n
-
- case OEND:
- return nil
-
- default:
- Fatalf("cannot import %v (%d) node\n"+
- "\t==> please file an issue and assign to gri@", op, int(op))
- panic("unreachable") // satisfy compiler
- }
-}
-
-func (r *importReader) op() Op {
- return Op(r.uint64())
-}
-
-func (r *importReader) elemList() []*Node {
- c := r.uint64()
- list := make([]*Node, c)
- for i := range list {
- s := r.ident()
- list[i] = nodSym(OSTRUCTKEY, r.expr(), s)
- }
- return list
-}
-
-func (r *importReader) exprsOrNil() (a, b *Node) {
- ab := r.uint64()
- if ab&1 != 0 {
- a = r.expr()
- }
- if ab&2 != 0 {
- b = r.node()
- }
- return
-}
diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
deleted file mode 100644
index ec9cc4bddc54b..0000000000000
--- a/src/cmd/compile/internal/gc/init.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/obj"
-)
-
-// A function named init is a special case.
-// It is called by the initialization before main is run.
-// To make it unique within a package and also uncallable,
-// the name, normally "pkg.init", is altered to "pkg.init.0".
-var renameinitgen int
-
-// Dummy function for autotmps generated during typechecking.
-var dummyInitFn = nod(ODCLFUNC, nil, nil)
-
-func renameinit() *types.Sym {
- s := lookupN("init.", renameinitgen)
- renameinitgen++
- return s
-}
-
-// fninit makes an initialization record for the package.
-// See runtime/proc.go:initTask for its layout.
-// The 3 tasks for initialization are:
-// 1) Initialize all of the packages the current package depends on.
-// 2) Initialize all the variables that have initializers.
-// 3) Run any init functions.
-func fninit(n []*Node) {
- nf := initOrder(n)
-
- var deps []*obj.LSym // initTask records for packages the current package depends on
- var fns []*obj.LSym // functions to call for package initialization
-
- // Find imported packages with init tasks.
- for _, s := range types.InitSyms {
- deps = append(deps, s.Linksym())
- }
-
- // Make a function that contains all the initialization statements.
- if len(nf) > 0 {
- lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt
- initializers := lookup("init")
- fn := dclfunc(initializers, nod(OTFUNC, nil, nil))
- for _, dcl := range dummyInitFn.Func.Dcl {
- dcl.Name.Curfn = fn
- }
- fn.Func.Dcl = append(fn.Func.Dcl, dummyInitFn.Func.Dcl...)
- dummyInitFn.Func.Dcl = nil
-
- fn.Nbody.Set(nf)
- funcbody()
-
- fn = typecheck(fn, ctxStmt)
- Curfn = fn
- typecheckslice(nf, ctxStmt)
- Curfn = nil
- xtop = append(xtop, fn)
- fns = append(fns, initializers.Linksym())
- }
- if dummyInitFn.Func.Dcl != nil {
- // We only generate temps using dummyInitFn if there
- // are package-scope initialization statements, so
- // 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.
- if fn.Nbody.Len() == 1 && fn.Nbody.First().Op == OEMPTY {
- continue
- }
- fns = append(fns, s.Linksym())
- }
-
- if len(deps) == 0 && len(fns) == 0 && localpkg.Name != "main" && localpkg.Name != "runtime" {
- return // nothing to initialize
- }
-
- // Make an .inittask structure.
- sym := lookup(".inittask")
- nn := newname(sym)
- nn.Type = types.Types[TUINT8] // dummy type
- nn.SetClass(PEXTERN)
- sym.Def = asTypesNode(nn)
- exportsym(nn)
- lsym := sym.Linksym()
- ot := 0
- ot = duintptr(lsym, ot, 0) // state: not initialized yet
- ot = duintptr(lsym, ot, uint64(len(deps)))
- ot = duintptr(lsym, ot, uint64(len(fns)))
- for _, d := range deps {
- ot = dsymptr(lsym, ot, d, 0)
- }
- for _, f := range fns {
- ot = dsymptr(lsym, ot, f, 0)
- }
- // An initTask has pointers, but none into the Go heap.
- // It's not quite read only, the state field must be modifiable.
- ggloblsym(lsym, int32(ot), obj.NOPTR)
-}
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
deleted file mode 100644
index 600d12b59b2c5..0000000000000
--- a/src/cmd/compile/internal/gc/inl.go
+++ /dev/null
@@ -1,1499 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-//
-// The inlining facility makes 2 passes: first caninl determines which
-// functions are suitable for inlining, and for those that are it
-// saves a copy of the body. Then inlcalls walks each function body to
-// expand calls to inlinable functions.
-//
-// The Debug.l flag controls the aggressiveness. Note that main() swaps level 0 and 1,
-// making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and
-// are not supported.
-// 0: disabled
-// 1: 80-nodes leaf functions, oneliners, panic, lazy typechecking (default)
-// 2: (unassigned)
-// 3: (unassigned)
-// 4: allow non-leaf functions
-//
-// At some point this may get another default and become switch-offable with -N.
-//
-// The -d typcheckinl flag enables early typechecking of all imported bodies,
-// which is useful to flush out bugs.
-//
-// The Debug.m flag enables diagnostic output. a single -m is useful for verifying
-// which calls get inlined or not, more is for debugging, and may go away at any point.
-
-package gc
-
-import (
- "cmd/compile/internal/logopt"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/src"
- "fmt"
- "strings"
-)
-
-// Inlining budget parameters, gathered in one place
-const (
- inlineMaxBudget = 80
- inlineExtraAppendCost = 0
- // default is to inline if there's at most one call. -l=4 overrides this by using 1 instead.
- inlineExtraCallCost = 57 // 57 was benchmarked to provided most benefit with no bad surprises; see https://github.com/golang/go/issues/19348#issuecomment-439370742
- inlineExtraPanicCost = 1 // do not penalize inlining panics.
- inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
-
- inlineBigFunctionNodes = 5000 // Functions with this many nodes are considered "big".
- inlineBigFunctionMaxCost = 20 // Max cost of inlinee when inlining into a "big" function.
-)
-
-// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
-// the ->sym can be re-used in the local package, so peel it off the receiver's type.
-func fnpkg(fn *Node) *types.Pkg {
- if fn.IsMethod() {
- // method
- rcvr := fn.Type.Recv().Type
-
- if rcvr.IsPtr() {
- rcvr = rcvr.Elem()
- }
- if rcvr.Sym == nil {
- Fatalf("receiver with no sym: [%v] %L (%v)", fn.Sym, fn, rcvr)
- }
- return rcvr.Sym.Pkg
- }
-
- // non-method
- return fn.Sym.Pkg
-}
-
-// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
-// because they're a copy of an already checked body.
-func typecheckinl(fn *Node) {
- lno := setlineno(fn)
-
- expandInline(fn)
-
- // typecheckinl is only for imported functions;
- // their bodies may refer to unsafe as long as the package
- // was marked safe during import (which was checked then).
- // the ->inl of a local function has been typechecked before caninl copied it.
- pkg := fnpkg(fn)
-
- if pkg == localpkg || pkg == nil {
- return // typecheckinl on local function
- }
-
- if Debug.m > 2 || Debug_export != 0 {
- fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, asNodes(fn.Func.Inl.Body))
- }
-
- savefn := Curfn
- Curfn = fn
- typecheckslice(fn.Func.Inl.Body, ctxStmt)
- Curfn = savefn
-
- // During expandInline (which imports fn.Func.Inl.Body),
- // declarations are added to fn.Func.Dcl by funcHdr(). Move them
- // to fn.Func.Inl.Dcl for consistency with how local functions
- // behave. (Append because typecheckinl may be called multiple
- // times.)
- fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...)
- fn.Func.Dcl = nil
-
- lineno = lno
-}
-
-// Caninl determines whether fn is inlineable.
-// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
-// fn and ->nbody will already have been typechecked.
-func caninl(fn *Node) {
- if fn.Op != ODCLFUNC {
- Fatalf("caninl %v", fn)
- }
- if fn.Func.Nname == nil {
- Fatalf("caninl no nname %+v", fn)
- }
-
- var reason string // reason, if any, that the function was not inlined
- if Debug.m > 1 || logopt.Enabled() {
- defer func() {
- if reason != "" {
- if Debug.m > 1 {
- fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
- }
- if logopt.Enabled() {
- logopt.LogOpt(fn.Pos, "cannotInlineFunction", "inline", fn.funcname(), reason)
- }
- }
- }()
- }
-
- // If marked "go:noinline", don't inline
- if fn.Func.Pragma&Noinline != 0 {
- reason = "marked go:noinline"
- return
- }
-
- // If marked "go:norace" and -race compilation, don't inline.
- if flag_race && fn.Func.Pragma&Norace != 0 {
- reason = "marked go:norace with -race compilation"
- return
- }
-
- // If marked "go:nocheckptr" and -d checkptr compilation, don't inline.
- if Debug_checkptr != 0 && fn.Func.Pragma&NoCheckPtr != 0 {
- reason = "marked go:nocheckptr"
- return
- }
-
- // If marked "go:cgo_unsafe_args", don't inline, since the
- // function makes assumptions about its argument frame layout.
- if fn.Func.Pragma&CgoUnsafeArgs != 0 {
- reason = "marked go:cgo_unsafe_args"
- return
- }
-
- // If marked as "go:uintptrescapes", don't inline, since the
- // escape information is lost during inlining.
- if fn.Func.Pragma&UintptrEscapes != 0 {
- reason = "marked as having an escaping uintptr argument"
- return
- }
-
- // The nowritebarrierrec checker currently works at function
- // granularity, so inlining yeswritebarrierrec functions can
- // confuse it (#22342). As a workaround, disallow inlining
- // them for now.
- if fn.Func.Pragma&Yeswritebarrierrec != 0 {
- reason = "marked go:yeswritebarrierrec"
- return
- }
-
- // If fn has no body (is defined outside of Go), cannot inline it.
- if fn.Nbody.Len() == 0 {
- reason = "no function body"
- return
- }
-
- if fn.Typecheck() == 0 {
- Fatalf("caninl on non-typechecked function %v", fn)
- }
-
- n := fn.Func.Nname
- if n.Func.InlinabilityChecked() {
- return
- }
- defer n.Func.SetInlinabilityChecked(true)
-
- cc := int32(inlineExtraCallCost)
- if Debug.l == 4 {
- cc = 1 // this appears to yield better performance than 0.
- }
-
- // At this point in the game the function we're looking at may
- // have "stale" autos, vars that still appear in the Dcl list, but
- // which no longer have any uses in the function body (due to
- // elimination by deadcode). We'd like to exclude these dead vars
- // when creating the "Inline.Dcl" field below; to accomplish this,
- // the hairyVisitor below builds up a map of used/referenced
- // locals, and we use this map to produce a pruned Inline.Dcl
- // list. See issue 25249 for more context.
-
- visitor := hairyVisitor{
- budget: inlineMaxBudget,
- extraCallCost: cc,
- usedLocals: make(map[*Node]bool),
- }
- if visitor.visitList(fn.Nbody) {
- reason = visitor.reason
- return
- }
- if visitor.budget < 0 {
- reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-visitor.budget, inlineMaxBudget)
- return
- }
-
- n.Func.Inl = &Inline{
- Cost: inlineMaxBudget - visitor.budget,
- Dcl: inlcopylist(pruneUnusedAutos(n.Name.Defn.Func.Dcl, &visitor)),
- Body: inlcopylist(fn.Nbody.Slice()),
- }
-
- // hack, TODO, check for better way to link method nodes back to the thing with the ->inl
- // this is so export can find the body of a method
- fn.Type.FuncType().Nname = asTypesNode(n)
-
- if Debug.m > 1 {
- fmt.Printf("%v: can inline %#v with cost %d as: %#v { %#v }\n", fn.Line(), n, inlineMaxBudget-visitor.budget, fn.Type, asNodes(n.Func.Inl.Body))
- } else if Debug.m != 0 {
- fmt.Printf("%v: can inline %v\n", fn.Line(), n)
- }
- if logopt.Enabled() {
- logopt.LogOpt(fn.Pos, "canInlineFunction", "inline", fn.funcname(), fmt.Sprintf("cost: %d", inlineMaxBudget-visitor.budget))
- }
-}
-
-// inlFlood marks n's inline body for export and recursively ensures
-// all called functions are marked too.
-func inlFlood(n *Node) {
- if n == nil {
- return
- }
- if n.Op != ONAME || n.Class() != PFUNC {
- Fatalf("inlFlood: unexpected %v, %v, %v", n, n.Op, n.Class())
- }
- if n.Func == nil {
- Fatalf("inlFlood: missing Func on %v", n)
- }
- if n.Func.Inl == nil {
- return
- }
-
- if n.Func.ExportInline() {
- return
- }
- n.Func.SetExportInline(true)
-
- typecheckinl(n)
-
- // Recursively identify all referenced functions for
- // reexport. We want to include even non-called functions,
- // because after inlining they might be callable.
- inspectList(asNodes(n.Func.Inl.Body), func(n *Node) bool {
- switch n.Op {
- case ONAME:
- switch n.Class() {
- case PFUNC:
- if n.isMethodExpression() {
- inlFlood(asNode(n.Type.Nname()))
- } else {
- inlFlood(n)
- exportsym(n)
- }
- case PEXTERN:
- exportsym(n)
- }
-
- case ODOTMETH:
- fn := asNode(n.Type.Nname())
- inlFlood(fn)
-
- case OCALLPART:
- // Okay, because we don't yet inline indirect
- // calls to method values.
- case OCLOSURE:
- // If the closure is inlinable, we'll need to
- // flood it too. But today we don't support
- // inlining functions that contain closures.
- //
- // When we do, we'll probably want:
- // inlFlood(n.Func.Closure.Func.Nname)
- Fatalf("unexpected closure in inlinable function")
- }
- return true
- })
-}
-
-// hairyVisitor visits a function body to determine its inlining
-// hairiness and whether or not it can be inlined.
-type hairyVisitor struct {
- budget int32
- reason string
- extraCallCost int32
- usedLocals map[*Node]bool
-}
-
-// Look for anything we want to punt on.
-func (v *hairyVisitor) visitList(ll Nodes) bool {
- for _, n := range ll.Slice() {
- if v.visit(n) {
- return true
- }
- }
- return false
-}
-
-func (v *hairyVisitor) visit(n *Node) bool {
- if n == nil {
- return false
- }
-
- switch n.Op {
- // Call is okay if inlinable and we have the budget for the body.
- case OCALLFUNC:
- // Functions that call runtime.getcaller{pc,sp} can not be inlined
- // because getcaller{pc,sp} expect a pointer to the caller's first argument.
- //
- // runtime.throw is a "cheap call" like panic in normal code.
- if n.Left.Op == ONAME && n.Left.Class() == PFUNC && isRuntimePkg(n.Left.Sym.Pkg) {
- fn := n.Left.Sym.Name
- if fn == "getcallerpc" || fn == "getcallersp" {
- v.reason = "call to " + fn
- return true
- }
- if fn == "throw" {
- v.budget -= inlineExtraThrowCost
- break
- }
- }
-
- if isIntrinsicCall(n) {
- // Treat like any other node.
- break
- }
-
- if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil {
- v.budget -= fn.Func.Inl.Cost
- break
- }
-
- // Call cost for non-leaf inlining.
- v.budget -= v.extraCallCost
-
- // Call is okay if inlinable and we have the budget for the body.
- case OCALLMETH:
- t := n.Left.Type
- if t == nil {
- Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
- }
- if t.Nname() == nil {
- Fatalf("no function definition for [%p] %+v\n", t, t)
- }
- if isRuntimePkg(n.Left.Sym.Pkg) {
- fn := n.Left.Sym.Name
- if fn == "heapBits.nextArena" {
- // Special case: explicitly allow
- // mid-stack inlining of
- // runtime.heapBits.next even though
- // it calls slow-path
- // runtime.heapBits.nextArena.
- break
- }
- }
- if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl != nil {
- v.budget -= inlfn.Inl.Cost
- break
- }
- // Call cost for non-leaf inlining.
- v.budget -= v.extraCallCost
-
- // Things that are too hairy, irrespective of the budget
- case OCALL, OCALLINTER:
- // Call cost for non-leaf inlining.
- v.budget -= v.extraCallCost
-
- case OPANIC:
- v.budget -= inlineExtraPanicCost
-
- case ORECOVER:
- // recover matches the argument frame pointer to find
- // the right panic value, so it needs an argument frame.
- v.reason = "call to recover"
- return true
-
- case OCLOSURE,
- ORANGE,
- OSELECT,
- OGO,
- ODEFER,
- ODCLTYPE, // can't print yet
- ORETJMP:
- v.reason = "unhandled op " + n.Op.String()
- return true
-
- case OAPPEND:
- v.budget -= inlineExtraAppendCost
-
- case ODCLCONST, OEMPTY, OFALL:
- // These nodes don't produce code; omit from inlining budget.
- return false
-
- case OLABEL:
- // TODO(mdempsky): Add support for inlining labeled control statements.
- if n.labeledControl() != nil {
- v.reason = "labeled control"
- return true
- }
-
- case OBREAK, OCONTINUE:
- if n.Sym != nil {
- // Should have short-circuited due to labeledControl above.
- Fatalf("unexpected labeled break/continue: %v", n)
- }
-
- case OIF:
- if Isconst(n.Left, CTBOOL) {
- // This if and the condition cost nothing.
- return v.visitList(n.Ninit) || v.visitList(n.Nbody) ||
- v.visitList(n.Rlist)
- }
-
- case ONAME:
- if n.Class() == PAUTO {
- v.usedLocals[n] = true
- }
-
- }
-
- v.budget--
-
- // When debugging, don't stop early, to get full cost of inlining this function
- if v.budget < 0 && Debug.m < 2 && !logopt.Enabled() {
- return true
- }
-
- return v.visit(n.Left) || v.visit(n.Right) ||
- v.visitList(n.List) || v.visitList(n.Rlist) ||
- v.visitList(n.Ninit) || v.visitList(n.Nbody)
-}
-
-// inlcopylist (together with inlcopy) recursively copies a list of nodes, except
-// that it keeps the same ONAME, OTYPE, and OLITERAL nodes. It is used for copying
-// the body and dcls of an inlineable function.
-func inlcopylist(ll []*Node) []*Node {
- s := make([]*Node, 0, len(ll))
- for _, n := range ll {
- s = append(s, inlcopy(n))
- }
- return s
-}
-
-func inlcopy(n *Node) *Node {
- if n == nil {
- return nil
- }
-
- switch n.Op {
- case ONAME, OTYPE, OLITERAL:
- return n
- }
-
- m := n.copy()
- if n.Op != OCALLPART && m.Func != nil {
- Fatalf("unexpected Func: %v", m)
- }
- m.Left = inlcopy(n.Left)
- m.Right = inlcopy(n.Right)
- m.List.Set(inlcopylist(n.List.Slice()))
- m.Rlist.Set(inlcopylist(n.Rlist.Slice()))
- m.Ninit.Set(inlcopylist(n.Ninit.Slice()))
- m.Nbody.Set(inlcopylist(n.Nbody.Slice()))
-
- return m
-}
-
-func countNodes(n *Node) int {
- if n == nil {
- return 0
- }
- cnt := 1
- cnt += countNodes(n.Left)
- cnt += countNodes(n.Right)
- for _, n1 := range n.Ninit.Slice() {
- cnt += countNodes(n1)
- }
- for _, n1 := range n.Nbody.Slice() {
- cnt += countNodes(n1)
- }
- for _, n1 := range n.List.Slice() {
- cnt += countNodes(n1)
- }
- for _, n1 := range n.Rlist.Slice() {
- cnt += countNodes(n1)
- }
- return cnt
-}
-
-// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
-// calls made to inlineable functions. This is the external entry point.
-func inlcalls(fn *Node) {
- savefn := Curfn
- Curfn = fn
- maxCost := int32(inlineMaxBudget)
- if countNodes(fn) >= inlineBigFunctionNodes {
- maxCost = inlineBigFunctionMaxCost
- }
- // Map to keep track of functions that have been inlined at a particular
- // call site, in order to stop inlining when we reach the beginning of a
- // recursion cycle again. We don't inline immediately recursive functions,
- // but allow inlining if there is a recursion cycle of many functions.
- // Most likely, the inlining will stop before we even hit the beginning of
- // the cycle again, but the map catches the unusual case.
- inlMap := make(map[*Node]bool)
- fn = inlnode(fn, maxCost, inlMap)
- if fn != Curfn {
- Fatalf("inlnode replaced curfn")
- }
- Curfn = savefn
-}
-
-// Turn an OINLCALL into a statement.
-func inlconv2stmt(n *Node) {
- n.Op = OBLOCK
-
- // n->ninit stays
- n.List.Set(n.Nbody.Slice())
-
- n.Nbody.Set(nil)
- n.Rlist.Set(nil)
-}
-
-// Turn an OINLCALL into a single valued expression.
-// The result of inlconv2expr MUST be assigned back to n, e.g.
-// n.Left = inlconv2expr(n.Left)
-func inlconv2expr(n *Node) *Node {
- r := n.Rlist.First()
- return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...))
-}
-
-// Turn the rlist (with the return values) of the OINLCALL in
-// n into an expression list lumping the ninit and body
-// containing the inlined statements on the first list element so
-// order will be preserved Used in return, oas2func and call
-// statements.
-func inlconv2list(n *Node) []*Node {
- if n.Op != OINLCALL || n.Rlist.Len() == 0 {
- Fatalf("inlconv2list %+v\n", n)
- }
-
- s := n.Rlist.Slice()
- s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...))
- return s
-}
-
-func inlnodelist(l Nodes, maxCost int32, inlMap map[*Node]bool) {
- s := l.Slice()
- for i := range s {
- s[i] = inlnode(s[i], maxCost, inlMap)
- }
-}
-
-// inlnode recurses over the tree to find inlineable calls, which will
-// be turned into OINLCALLs by mkinlcall. When the recursion comes
-// back up will examine left, right, list, rlist, ninit, ntest, nincr,
-// nbody and nelse and use one of the 4 inlconv/glue functions above
-// to turn the OINLCALL into an expression, a statement, or patch it
-// in to this nodes list or rlist as appropriate.
-// NOTE it makes no sense to pass the glue functions down the
-// recursion to the level where the OINLCALL gets created because they
-// have to edit /this/ n, so you'd have to push that one down as well,
-// but then you may as well do it here. so this is cleaner and
-// shorter and less complicated.
-// The result of inlnode MUST be assigned back to n, e.g.
-// n.Left = inlnode(n.Left)
-func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node {
- if n == nil {
- return n
- }
-
- switch n.Op {
- case ODEFER, OGO:
- switch n.Left.Op {
- case OCALLFUNC, OCALLMETH:
- n.Left.SetNoInline(true)
- }
-
- // TODO do them here (or earlier),
- // so escape analysis can avoid more heapmoves.
- case OCLOSURE:
- return n
- case OCALLMETH:
- // Prevent inlining some reflect.Value methods when using checkptr,
- // even when package reflect was compiled without it (#35073).
- if s := n.Left.Sym; Debug_checkptr != 0 && isReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
- return n
- }
- }
-
- lno := setlineno(n)
-
- inlnodelist(n.Ninit, maxCost, inlMap)
- for _, n1 := range n.Ninit.Slice() {
- if n1.Op == OINLCALL {
- inlconv2stmt(n1)
- }
- }
-
- n.Left = inlnode(n.Left, maxCost, inlMap)
- if n.Left != nil && n.Left.Op == OINLCALL {
- n.Left = inlconv2expr(n.Left)
- }
-
- n.Right = inlnode(n.Right, maxCost, inlMap)
- if n.Right != nil && n.Right.Op == OINLCALL {
- if n.Op == OFOR || n.Op == OFORUNTIL {
- inlconv2stmt(n.Right)
- } else if n.Op == OAS2FUNC {
- n.Rlist.Set(inlconv2list(n.Right))
- n.Right = nil
- n.Op = OAS2
- n.SetTypecheck(0)
- n = typecheck(n, ctxStmt)
- } else {
- n.Right = inlconv2expr(n.Right)
- }
- }
-
- inlnodelist(n.List, maxCost, inlMap)
- if n.Op == OBLOCK {
- for _, n2 := range n.List.Slice() {
- if n2.Op == OINLCALL {
- inlconv2stmt(n2)
- }
- }
- } else {
- s := n.List.Slice()
- for i1, n1 := range s {
- if n1 != nil && n1.Op == OINLCALL {
- s[i1] = inlconv2expr(s[i1])
- }
- }
- }
-
- inlnodelist(n.Rlist, maxCost, inlMap)
- s := n.Rlist.Slice()
- for i1, n1 := range s {
- if n1.Op == OINLCALL {
- if n.Op == OIF {
- inlconv2stmt(n1)
- } else {
- s[i1] = inlconv2expr(s[i1])
- }
- }
- }
-
- inlnodelist(n.Nbody, maxCost, inlMap)
- for _, n := range n.Nbody.Slice() {
- if n.Op == OINLCALL {
- inlconv2stmt(n)
- }
- }
-
- // with all the branches out of the way, it is now time to
- // transmogrify this node itself unless inhibited by the
- // switch at the top of this function.
- switch n.Op {
- case OCALLFUNC, OCALLMETH:
- if n.NoInline() {
- return n
- }
- }
-
- switch n.Op {
- case OCALLFUNC:
- if Debug.m > 3 {
- fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
- }
- if isIntrinsicCall(n) {
- break
- }
- if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil {
- n = mkinlcall(n, fn, maxCost, inlMap)
- }
-
- case OCALLMETH:
- if Debug.m > 3 {
- fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
- }
-
- // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
- if n.Left.Type == nil {
- Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
- }
-
- if n.Left.Type.Nname() == nil {
- Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type)
- }
-
- n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), maxCost, inlMap)
- }
-
- lineno = lno
- return n
-}
-
-// inlCallee takes a function-typed expression and returns the underlying function ONAME
-// that it refers to if statically known. Otherwise, it returns nil.
-func inlCallee(fn *Node) *Node {
- fn = staticValue(fn)
- switch {
- case fn.Op == ONAME && fn.Class() == PFUNC:
- if fn.isMethodExpression() {
- n := asNode(fn.Type.Nname())
- // Check that receiver type matches fn.Left.
- // TODO(mdempsky): Handle implicit dereference
- // of pointer receiver argument?
- if n == nil || !types.Identical(n.Type.Recv().Type, fn.Left.Type) {
- return nil
- }
- return n
- }
- return fn
- case fn.Op == OCLOSURE:
- c := fn.Func.Closure
- caninl(c)
- return c.Func.Nname
- }
- return nil
-}
-
-func staticValue(n *Node) *Node {
- for {
- if n.Op == OCONVNOP {
- n = n.Left
- continue
- }
-
- n1 := staticValue1(n)
- if n1 == nil {
- return n
- }
- n = n1
- }
-}
-
-// staticValue1 implements a simple SSA-like optimization. If n is a local variable
-// that is initialized and never reassigned, staticValue1 returns the initializer
-// expression. Otherwise, it returns nil.
-func staticValue1(n *Node) *Node {
- if n.Op != ONAME || n.Class() != PAUTO || n.Name.Addrtaken() {
- return nil
- }
-
- defn := n.Name.Defn
- if defn == nil {
- return nil
- }
-
- var rhs *Node
-FindRHS:
- switch defn.Op {
- case OAS:
- rhs = defn.Right
- case OAS2:
- for i, lhs := range defn.List.Slice() {
- if lhs == n {
- rhs = defn.Rlist.Index(i)
- break FindRHS
- }
- }
- Fatalf("%v missing from LHS of %v", n, defn)
- default:
- return nil
- }
- if rhs == nil {
- Fatalf("RHS is nil: %v", defn)
- }
-
- unsafe, _ := reassigned(n)
- if unsafe {
- return nil
- }
-
- return rhs
-}
-
-// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
-// indicating whether the name has any assignments other than its declaration.
-// The second return value is the first such assignment encountered in the walk, if any. It is mostly
-// useful for -m output documenting the reason for inhibited optimizations.
-// NB: global variables are always considered to be re-assigned.
-// TODO: handle initial declaration not including an assignment and followed by a single assignment?
-func reassigned(n *Node) (bool, *Node) {
- if n.Op != ONAME {
- Fatalf("reassigned %v", n)
- }
- // no way to reliably check for no-reassignment of globals, assume it can be
- if n.Name.Curfn == nil {
- return true, nil
- }
- f := n.Name.Curfn
- // There just might be a good reason for this although this can be pretty surprising:
- // local variables inside a closure have Curfn pointing to the OCLOSURE node instead
- // of the corresponding ODCLFUNC.
- // We need to walk the function body to check for reassignments so we follow the
- // linkage to the ODCLFUNC node as that is where body is held.
- if f.Op == OCLOSURE {
- f = f.Func.Closure
- }
- v := reassignVisitor{name: n}
- a := v.visitList(f.Nbody)
- return a != nil, a
-}
-
-type reassignVisitor struct {
- name *Node
-}
-
-func (v *reassignVisitor) visit(n *Node) *Node {
- if n == nil {
- return nil
- }
- switch n.Op {
- case OAS, OSELRECV:
- if n.Left == v.name && n != v.name.Name.Defn {
- return n
- }
- case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV:
- for _, p := range n.List.Slice() {
- if p == v.name && n != v.name.Name.Defn {
- return n
- }
- }
- case OSELRECV2:
- if (n.Left == v.name || n.List.First() == v.name) && n != v.name.Name.Defn {
- return n
- }
- }
- if a := v.visit(n.Left); a != nil {
- return a
- }
- if a := v.visit(n.Right); a != nil {
- return a
- }
- if a := v.visitList(n.List); a != nil {
- return a
- }
- if a := v.visitList(n.Rlist); a != nil {
- return a
- }
- if a := v.visitList(n.Ninit); a != nil {
- return a
- }
- if a := v.visitList(n.Nbody); a != nil {
- return a
- }
- return nil
-}
-
-func (v *reassignVisitor) visitList(l Nodes) *Node {
- for _, n := range l.Slice() {
- if a := v.visit(n); a != nil {
- return a
- }
- }
- return nil
-}
-
-func inlParam(t *types.Field, as *Node, inlvars map[*Node]*Node) *Node {
- n := asNode(t.Nname)
- if n == nil || n.isBlank() {
- return nblank
- }
-
- inlvar := inlvars[n]
- if inlvar == nil {
- Fatalf("missing inlvar for %v", n)
- }
- as.Ninit.Append(nod(ODCL, inlvar, nil))
- inlvar.Name.Defn = as
- return inlvar
-}
-
-var inlgen int
-
-// If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a
-// function with an inlinable body, return an OINLCALL node that can replace n.
-// The returned node's Ninit has the parameter assignments, the Nbody is the
-// inlined function body, and (List, Rlist) contain the (input, output)
-// parameters.
-// The result of mkinlcall MUST be assigned back to n, e.g.
-// n.Left = mkinlcall(n.Left, fn, isddd)
-func mkinlcall(n, fn *Node, maxCost int32, inlMap map[*Node]bool) *Node {
- if fn.Func.Inl == nil {
- if logopt.Enabled() {
- logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
- fmt.Sprintf("%s cannot be inlined", fn.pkgFuncName()))
- }
- return n
- }
- if fn.Func.Inl.Cost > maxCost {
- // The inlined function body is too big. Typically we use this check to restrict
- // inlining into very big functions. See issue 26546 and 17566.
- if logopt.Enabled() {
- logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
- fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Func.Inl.Cost, fn.pkgFuncName(), maxCost))
- }
- return n
- }
-
- if fn == Curfn || fn.Name.Defn == Curfn {
- // Can't recursively inline a function into itself.
- if logopt.Enabled() {
- logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", Curfn.funcname()))
- }
- return n
- }
-
- if instrumenting && isRuntimePkg(fn.Sym.Pkg) {
- // Runtime package must not be instrumented.
- // Instrument skips runtime package. However, some runtime code can be
- // inlined into other packages and instrumented there. To avoid this,
- // we disable inlining of runtime functions when instrumenting.
- // The example that we observed is inlining of LockOSThread,
- // which lead to false race reports on m contents.
- return n
- }
-
- if inlMap[fn] {
- if Debug.m > 1 {
- fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", n.Line(), fn, Curfn.funcname())
- }
- return n
- }
- inlMap[fn] = true
- defer func() {
- inlMap[fn] = false
- }()
- if Debug_typecheckinl == 0 {
- typecheckinl(fn)
- }
-
- // We have a function node, and it has an inlineable body.
- if Debug.m > 1 {
- fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, asNodes(fn.Func.Inl.Body))
- } else if Debug.m != 0 {
- fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
- }
- if Debug.m > 2 {
- fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
- }
-
- if ssaDump != "" && ssaDump == Curfn.funcname() {
- ssaDumpInlined = append(ssaDumpInlined, fn)
- }
-
- ninit := n.Ninit
-
- // For normal function calls, the function callee expression
- // may contain side effects (e.g., added by addinit during
- // inlconv2expr or inlconv2list). Make sure to preserve these,
- // if necessary (#42703).
- if n.Op == OCALLFUNC {
- callee := n.Left
- for callee.Op == OCONVNOP {
- ninit.AppendNodes(&callee.Ninit)
- callee = callee.Left
- }
- if callee.Op != ONAME && callee.Op != OCLOSURE {
- Fatalf("unexpected callee expression: %v", callee)
- }
- }
-
- // Make temp names to use instead of the originals.
- inlvars := make(map[*Node]*Node)
-
- // record formals/locals for later post-processing
- var inlfvars []*Node
-
- // Handle captured variables when inlining closures.
- if fn.Name.Defn != nil {
- if c := fn.Name.Defn.Func.Closure; c != nil {
- for _, v := range c.Func.Closure.Func.Cvars.Slice() {
- if v.Op == OXXX {
- continue
- }
-
- o := v.Name.Param.Outer
- // make sure the outer param matches the inlining location
- // NB: if we enabled inlining of functions containing OCLOSURE or refined
- // the reassigned check via some sort of copy propagation this would most
- // likely need to be changed to a loop to walk up to the correct Param
- if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.Closure != Curfn) {
- Fatalf("%v: unresolvable capture %v %v\n", n.Line(), fn, v)
- }
-
- if v.Name.Byval() {
- iv := typecheck(inlvar(v), ctxExpr)
- ninit.Append(nod(ODCL, iv, nil))
- ninit.Append(typecheck(nod(OAS, iv, o), ctxStmt))
- inlvars[v] = iv
- } else {
- addr := newname(lookup("&" + v.Sym.Name))
- addr.Type = types.NewPtr(v.Type)
- ia := typecheck(inlvar(addr), ctxExpr)
- ninit.Append(nod(ODCL, ia, nil))
- ninit.Append(typecheck(nod(OAS, ia, nod(OADDR, o, nil)), ctxStmt))
- inlvars[addr] = ia
-
- // When capturing by reference, all occurrence of the captured var
- // must be substituted with dereference of the temporary address
- inlvars[v] = typecheck(nod(ODEREF, ia, nil), ctxExpr)
- }
- }
- }
- }
-
- for _, ln := range fn.Func.Inl.Dcl {
- if ln.Op != ONAME {
- continue
- }
- if ln.Class() == PPARAMOUT { // return values handled below.
- continue
- }
- if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
- // TODO(mdempsky): Remove once I'm confident
- // this never actually happens. We currently
- // perform inlining before escape analysis, so
- // nothing should have moved to the heap yet.
- Fatalf("impossible: %v", ln)
- }
- inlf := typecheck(inlvar(ln), ctxExpr)
- inlvars[ln] = inlf
- if genDwarfInline > 0 {
- if ln.Class() == PPARAM {
- inlf.Name.SetInlFormal(true)
- } else {
- inlf.Name.SetInlLocal(true)
- }
- inlf.Pos = ln.Pos
- inlfvars = append(inlfvars, inlf)
- }
- }
-
- nreturns := 0
- inspectList(asNodes(fn.Func.Inl.Body), func(n *Node) bool {
- if n != nil && n.Op == ORETURN {
- nreturns++
- }
- return true
- })
-
- // We can delay declaring+initializing result parameters if:
- // (1) there's only one "return" statement in the inlined
- // function, and (2) the result parameters aren't named.
- delayretvars := nreturns == 1
-
- // temporaries for return values.
- var retvars []*Node
- for i, t := range fn.Type.Results().Fields().Slice() {
- var m *Node
- if n := asNode(t.Nname); n != nil && !n.isBlank() && !strings.HasPrefix(n.Sym.Name, "~r") {
- m = inlvar(n)
- m = typecheck(m, ctxExpr)
- inlvars[n] = m
- delayretvars = false // found a named result parameter
- } else {
- // anonymous return values, synthesize names for use in assignment that replaces return
- m = retvar(t, i)
- }
-
- if genDwarfInline > 0 {
- // Don't update the src.Pos on a return variable if it
- // was manufactured by the inliner (e.g. "~R2"); such vars
- // were not part of the original callee.
- if !strings.HasPrefix(m.Sym.Name, "~R") {
- m.Name.SetInlFormal(true)
- m.Pos = t.Pos
- inlfvars = append(inlfvars, m)
- }
- }
-
- retvars = append(retvars, m)
- }
-
- // Assign arguments to the parameters' temp names.
- as := nod(OAS2, nil, nil)
- as.SetColas(true)
- if n.Op == OCALLMETH {
- if n.Left.Left == nil {
- Fatalf("method call without receiver: %+v", n)
- }
- as.Rlist.Append(n.Left.Left)
- }
- as.Rlist.Append(n.List.Slice()...)
-
- // For non-dotted calls to variadic functions, we assign the
- // variadic parameter's temp name separately.
- var vas *Node
-
- if recv := fn.Type.Recv(); recv != nil {
- as.List.Append(inlParam(recv, as, inlvars))
- }
- for _, param := range fn.Type.Params().Fields().Slice() {
- // For ordinary parameters or variadic parameters in
- // dotted calls, just add the variable to the
- // assignment list, and we're done.
- if !param.IsDDD() || n.IsDDD() {
- as.List.Append(inlParam(param, as, inlvars))
- continue
- }
-
- // Otherwise, we need to collect the remaining values
- // to pass as a slice.
-
- x := as.List.Len()
- for as.List.Len() < as.Rlist.Len() {
- as.List.Append(argvar(param.Type, as.List.Len()))
- }
- varargs := as.List.Slice()[x:]
-
- vas = nod(OAS, nil, nil)
- vas.Left = inlParam(param, vas, inlvars)
- if len(varargs) == 0 {
- vas.Right = nodnil()
- vas.Right.Type = param.Type
- } else {
- vas.Right = nod(OCOMPLIT, nil, typenod(param.Type))
- vas.Right.List.Set(varargs)
- }
- }
-
- if as.Rlist.Len() != 0 {
- as = typecheck(as, ctxStmt)
- ninit.Append(as)
- }
-
- if vas != nil {
- vas = typecheck(vas, ctxStmt)
- ninit.Append(vas)
- }
-
- if !delayretvars {
- // Zero the return parameters.
- for _, n := range retvars {
- ninit.Append(nod(ODCL, n, nil))
- ras := nod(OAS, n, nil)
- ras = typecheck(ras, ctxStmt)
- ninit.Append(ras)
- }
- }
-
- retlabel := autolabel(".i")
-
- inlgen++
-
- parent := -1
- if b := Ctxt.PosTable.Pos(n.Pos).Base(); b != nil {
- parent = b.InliningIndex()
- }
- newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym())
-
- // Add an inline mark just before the inlined body.
- // This mark is inline in the code so that it's a reasonable spot
- // to put a breakpoint. Not sure if that's really necessary or not
- // (in which case it could go at the end of the function instead).
- // Note issue 28603.
- inlMark := nod(OINLMARK, nil, nil)
- inlMark.Pos = n.Pos.WithIsStmt()
- inlMark.Xoffset = int64(newIndex)
- ninit.Append(inlMark)
-
- if genDwarfInline > 0 {
- if !fn.Sym.Linksym().WasInlined() {
- Ctxt.DwFixups.SetPrecursorFunc(fn.Sym.Linksym(), fn)
- fn.Sym.Linksym().Set(obj.AttrWasInlined, true)
- }
- }
-
- subst := inlsubst{
- retlabel: retlabel,
- retvars: retvars,
- delayretvars: delayretvars,
- inlvars: inlvars,
- bases: make(map[*src.PosBase]*src.PosBase),
- newInlIndex: newIndex,
- }
-
- body := subst.list(asNodes(fn.Func.Inl.Body))
-
- lab := nodSym(OLABEL, nil, retlabel)
- body = append(body, lab)
-
- typecheckslice(body, ctxStmt)
-
- if genDwarfInline > 0 {
- for _, v := range inlfvars {
- v.Pos = subst.updatedPos(v.Pos)
- }
- }
-
- //dumplist("ninit post", ninit);
-
- call := nod(OINLCALL, nil, nil)
- call.Ninit.Set(ninit.Slice())
- call.Nbody.Set(body)
- call.Rlist.Set(retvars)
- call.Type = n.Type
- call.SetTypecheck(1)
-
- // transitive inlining
- // might be nice to do this before exporting the body,
- // but can't emit the body with inlining expanded.
- // instead we emit the things that the body needs
- // and each use must redo the inlining.
- // luckily these are small.
- inlnodelist(call.Nbody, maxCost, inlMap)
- for _, n := range call.Nbody.Slice() {
- if n.Op == OINLCALL {
- inlconv2stmt(n)
- }
- }
-
- if Debug.m > 2 {
- fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call)
- }
-
- return call
-}
-
-// Every time we expand a function we generate a new set of tmpnames,
-// PAUTO's in the calling functions, and link them off of the
-// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
-func inlvar(var_ *Node) *Node {
- if Debug.m > 3 {
- fmt.Printf("inlvar %+v\n", var_)
- }
-
- n := newname(var_.Sym)
- n.Type = var_.Type
- n.SetClass(PAUTO)
- n.Name.SetUsed(true)
- n.Name.Curfn = Curfn // the calling function, not the called one
- n.Name.SetAddrtaken(var_.Name.Addrtaken())
-
- Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
- return n
-}
-
-// Synthesize a variable to store the inlined function's results in.
-func retvar(t *types.Field, i int) *Node {
- n := newname(lookupN("~R", i))
- n.Type = t.Type
- n.SetClass(PAUTO)
- n.Name.SetUsed(true)
- n.Name.Curfn = Curfn // the calling function, not the called one
- Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
- return n
-}
-
-// Synthesize a variable to store the inlined function's arguments
-// when they come from a multiple return call.
-func argvar(t *types.Type, i int) *Node {
- n := newname(lookupN("~arg", i))
- n.Type = t.Elem()
- n.SetClass(PAUTO)
- n.Name.SetUsed(true)
- n.Name.Curfn = Curfn // the calling function, not the called one
- Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
- return n
-}
-
-// The inlsubst type implements the actual inlining of a single
-// function call.
-type inlsubst struct {
- // Target of the goto substituted in place of a return.
- retlabel *types.Sym
-
- // Temporary result variables.
- retvars []*Node
-
- // Whether result variables should be initialized at the
- // "return" statement.
- delayretvars bool
-
- inlvars map[*Node]*Node
-
- // bases maps from original PosBase to PosBase with an extra
- // inlined call frame.
- bases map[*src.PosBase]*src.PosBase
-
- // newInlIndex is the index of the inlined call frame to
- // insert for inlined nodes.
- newInlIndex int
-}
-
-// list inlines a list of nodes.
-func (subst *inlsubst) list(ll Nodes) []*Node {
- s := make([]*Node, 0, ll.Len())
- for _, n := range ll.Slice() {
- s = append(s, subst.node(n))
- }
- return s
-}
-
-// node recursively copies a node from the saved pristine body of the
-// inlined function, substituting references to input/output
-// parameters with ones to the tmpnames, and substituting returns with
-// assignments to the output.
-func (subst *inlsubst) node(n *Node) *Node {
- if n == nil {
- return nil
- }
-
- switch n.Op {
- case ONAME:
- if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
- if Debug.m > 2 {
- fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
- }
- return inlvar
- }
-
- if Debug.m > 2 {
- fmt.Printf("not substituting name %+v\n", n)
- }
- return n
-
- case OLITERAL, OTYPE:
- // If n is a named constant or type, we can continue
- // using it in the inline copy. Otherwise, make a copy
- // so we can update the line number.
- if n.Sym != nil {
- return n
- }
-
- // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
-
- // dump("Return before substitution", n);
- case ORETURN:
- m := nodSym(OGOTO, nil, subst.retlabel)
- m.Ninit.Set(subst.list(n.Ninit))
-
- if len(subst.retvars) != 0 && n.List.Len() != 0 {
- as := nod(OAS2, nil, nil)
-
- // Make a shallow copy of retvars.
- // Otherwise OINLCALL.Rlist will be the same list,
- // and later walk and typecheck may clobber it.
- for _, n := range subst.retvars {
- as.List.Append(n)
- }
- as.Rlist.Set(subst.list(n.List))
-
- if subst.delayretvars {
- for _, n := range as.List.Slice() {
- as.Ninit.Append(nod(ODCL, n, nil))
- n.Name.Defn = as
- }
- }
-
- as = typecheck(as, ctxStmt)
- m.Ninit.Append(as)
- }
-
- typecheckslice(m.Ninit.Slice(), ctxStmt)
- m = typecheck(m, ctxStmt)
-
- // dump("Return after substitution", m);
- return m
-
- case OGOTO, OLABEL:
- m := n.copy()
- m.Pos = subst.updatedPos(m.Pos)
- m.Ninit.Set(nil)
- p := fmt.Sprintf("%s·%d", n.Sym.Name, inlgen)
- m.Sym = lookup(p)
-
- return m
- }
-
- m := n.copy()
- m.Pos = subst.updatedPos(m.Pos)
- m.Ninit.Set(nil)
-
- if n.Op == OCLOSURE {
- Fatalf("cannot inline function containing closure: %+v", n)
- }
-
- m.Left = subst.node(n.Left)
- m.Right = subst.node(n.Right)
- m.List.Set(subst.list(n.List))
- m.Rlist.Set(subst.list(n.Rlist))
- m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...))
- m.Nbody.Set(subst.list(n.Nbody))
-
- return m
-}
-
-func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
- pos := Ctxt.PosTable.Pos(xpos)
- oldbase := pos.Base() // can be nil
- newbase := subst.bases[oldbase]
- if newbase == nil {
- newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
- subst.bases[oldbase] = newbase
- }
- pos.SetBase(newbase)
- return Ctxt.PosTable.XPos(pos)
-}
-
-func pruneUnusedAutos(ll []*Node, vis *hairyVisitor) []*Node {
- s := make([]*Node, 0, len(ll))
- for _, n := range ll {
- if n.Class() == PAUTO {
- if _, found := vis.usedLocals[n]; !found {
- continue
- }
- }
- s = append(s, n)
- }
- return s
-}
-
-// devirtualize replaces interface method calls within fn with direct
-// concrete-type method calls where applicable.
-func devirtualize(fn *Node) {
- Curfn = fn
- inspectList(fn.Nbody, func(n *Node) bool {
- if n.Op == OCALLINTER {
- devirtualizeCall(n)
- }
- return true
- })
-}
-
-func devirtualizeCall(call *Node) {
- recv := staticValue(call.Left.Left)
- if recv.Op != OCONVIFACE {
- return
- }
-
- typ := recv.Left.Type
- if typ.IsInterface() {
- return
- }
-
- x := nodl(call.Left.Pos, ODOTTYPE, call.Left.Left, nil)
- x.Type = typ
- x = nodlSym(call.Left.Pos, OXDOT, x, call.Left.Sym)
- x = typecheck(x, ctxExpr|ctxCallee)
- switch x.Op {
- case ODOTMETH:
- if Debug.m != 0 {
- Warnl(call.Pos, "devirtualizing %v to %v", call.Left, typ)
- }
- call.Op = OCALLMETH
- call.Left = x
- case ODOTINTER:
- // Promoted method from embedded interface-typed field (#42279).
- if Debug.m != 0 {
- Warnl(call.Pos, "partially devirtualizing %v to %v", call.Left, typ)
- }
- call.Op = OCALLINTER
- call.Left = x
- default:
- // TODO(mdempsky): Turn back into Fatalf after more testing.
- if Debug.m != 0 {
- Warnl(call.Pos, "failed to devirtualize %v (%v)", x, x.Op)
- }
- return
- }
-
- // Duplicated logic from typecheck for function call return
- // value types.
- //
- // Receiver parameter size may have changed; need to update
- // call.Type to get correct stack offsets for result
- // parameters.
- checkwidth(x.Type)
- switch ft := x.Type; ft.NumResults() {
- case 0:
- case 1:
- call.Type = ft.Results().Field(0).Type
- default:
- call.Type = ft.Results()
- }
-}
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index a6963a3d66e62..726a0685d57ff 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -2,716 +2,238 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:generate go run mkbuiltin.go
-
package gc
import (
"bufio"
"bytes"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/deadcode"
+ "cmd/compile/internal/devirtualize"
+ "cmd/compile/internal/dwarfgen"
+ "cmd/compile/internal/escape"
+ "cmd/compile/internal/inline"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
+ "cmd/compile/internal/noder"
+ "cmd/compile/internal/pkginit"
+ "cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
- "cmd/internal/bio"
"cmd/internal/dwarf"
- "cmd/internal/goobj"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
- "cmd/internal/sys"
"flag"
"fmt"
- "internal/goversion"
- "io"
- "io/ioutil"
"log"
"os"
- "path"
- "regexp"
"runtime"
- "sort"
- "strconv"
- "strings"
-)
-
-var (
- buildid string
- spectre string
- spectreIndex bool
)
-var (
- Debug_append int
- Debug_checkptr int
- Debug_closure int
- Debug_compilelater int
- debug_dclstack int
- Debug_dumpptrs int
- Debug_libfuzzer int
- Debug_panic int
- Debug_slice int
- Debug_vlog bool
- Debug_wb int
- Debug_pctab string
- Debug_locationlist int
- Debug_typecheckinl int
- Debug_gendwarfinl int
- Debug_softfloat int
- Debug_defer int
-)
-
-// Debug arguments.
-// These can be specified with the -d flag, as in "-d nil"
-// to set the debug_checknil variable.
-// Multiple options can be comma-separated.
-// Each option accepts an optional argument, as in "gcprog=2"
-var debugtab = []struct {
- name string
- help string
- val interface{} // must be *int or *string
-}{
- {"append", "print information about append compilation", &Debug_append},
- {"checkptr", "instrument unsafe pointer conversions", &Debug_checkptr},
- {"closure", "print information about closure compilation", &Debug_closure},
- {"compilelater", "compile functions as late as possible", &Debug_compilelater},
- {"disablenil", "disable nil checks", &disable_checknil},
- {"dclstack", "run internal dclstack check", &debug_dclstack},
- {"dumpptrs", "show Node pointer values in Dump/dumplist output", &Debug_dumpptrs},
- {"gcprog", "print dump of GC programs", &Debug_gcprog},
- {"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer},
- {"nil", "print information about nil checks", &Debug_checknil},
- {"panic", "do not hide any compiler panic", &Debug_panic},
- {"slice", "print information about slice compilation", &Debug_slice},
- {"typeassert", "print information about type assertion inlining", &Debug_typeassert},
- {"wb", "print information about write barriers", &Debug_wb},
- {"export", "print export data", &Debug_export},
- {"pctab", "print named pc-value table", &Debug_pctab},
- {"locationlists", "print information about DWARF location list creation", &Debug_locationlist},
- {"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl},
- {"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
- {"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
- {"defer", "print information about defer compilation", &Debug_defer},
- {"fieldtrack", "enable fieldtracking", &objabi.Fieldtrack_enabled},
-}
-
-const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=]
-
- is one of:
-
-`
-
-const debugHelpFooter = `
- is key-specific.
-
-Key "checkptr" supports values:
- "0": instrumentation disabled
- "1": conversions involving unsafe.Pointer are instrumented
- "2": conversions to unsafe.Pointer force heap allocation
-
-Key "pctab" supports values:
- "pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata"
-`
-
-func usage() {
- fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
- objabi.Flagprint(os.Stderr)
- Exit(2)
-}
-
func hidePanic() {
- if Debug_panic == 0 && nsavederrors+nerrors > 0 {
+ if base.Debug.Panic == 0 && base.Errors() > 0 {
// If we've already complained about things
// in the program, don't bother complaining
// about a panic too; let the user clean up
// the code and try again.
if err := recover(); err != nil {
- errorexit()
+ if err == "-h" {
+ panic(err)
+ }
+ base.ErrorExit()
}
}
}
-// supportsDynlink reports whether or not the code generator for the given
-// architecture supports the -shared and -dynlink flags.
-func supportsDynlink(arch *sys.Arch) bool {
- return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X)
-}
-
-// timing data for compiler phases
-var timings Timings
-var benchfile string
-
-var nowritebarrierrecCheck *nowritebarrierrecChecker
-
// Main parses flags and Go source files specified in the command-line
// arguments, type-checks the parsed Go package, compiles functions to machine
// code, and finally writes the compiled package definition to disk.
-func Main(archInit func(*Arch)) {
- timings.Start("fe", "init")
+func Main(archInit func(*ssagen.ArchInfo)) {
+ base.Timer.Start("fe", "init")
defer hidePanic()
- archInit(&thearch)
+ archInit(&ssagen.Arch)
- Ctxt = obj.Linknew(thearch.LinkArch)
- Ctxt.DiagFunc = yyerror
- Ctxt.DiagFlush = flusherrors
- Ctxt.Bso = bufio.NewWriter(os.Stdout)
+ base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch)
+ base.Ctxt.DiagFunc = base.Errorf
+ base.Ctxt.DiagFlush = base.FlushErrors
+ base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
// UseBASEntries is preferred because it shaves about 2% off build time, but LLDB, dsymutil, and dwarfdump
// on Darwin don't support it properly, especially since macOS 10.14 (Mojave). This is exposed as a flag
// to allow testing with LLVM tools on Linux, and to help with reporting this bug to the LLVM project.
// See bugs 31188 and 21945 (CLs 170638, 98075, 72371).
- Ctxt.UseBASEntries = Ctxt.Headtype != objabi.Hdarwin
+ base.Ctxt.UseBASEntries = base.Ctxt.Headtype != objabi.Hdarwin
- localpkg = types.NewPkg("", "")
- localpkg.Prefix = "\"\""
+ types.LocalPkg = types.NewPkg("", "")
+ types.LocalPkg.Prefix = "\"\""
// We won't know localpkg's height until after import
// processing. In the mean time, set to MaxPkgHeight to ensure
// height comparisons at least work until then.
- localpkg.Height = types.MaxPkgHeight
+ types.LocalPkg.Height = types.MaxPkgHeight
// pseudo-package, for scoping
- builtinpkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
- builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin
+ types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
+ types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin
// pseudo-package, accessed by import "unsafe"
- unsafepkg = types.NewPkg("unsafe", "unsafe")
+ ir.Pkgs.Unsafe = types.NewPkg("unsafe", "unsafe")
// Pseudo-package that contains the compiler's builtin
// declarations for package runtime. These are declared in a
// separate package to avoid conflicts with package runtime's
// actual declarations, which may differ intentionally but
// insignificantly.
- Runtimepkg = types.NewPkg("go.runtime", "runtime")
- Runtimepkg.Prefix = "runtime"
+ ir.Pkgs.Runtime = types.NewPkg("go.runtime", "runtime")
+ ir.Pkgs.Runtime.Prefix = "runtime"
// pseudo-packages used in symbol tables
- itabpkg = types.NewPkg("go.itab", "go.itab")
- itabpkg.Prefix = "go.itab" // not go%2eitab
-
- itablinkpkg = types.NewPkg("go.itablink", "go.itablink")
- itablinkpkg.Prefix = "go.itablink" // not go%2eitablink
-
- trackpkg = types.NewPkg("go.track", "go.track")
- trackpkg.Prefix = "go.track" // not go%2etrack
-
- // pseudo-package used for map zero values
- mappkg = types.NewPkg("go.map", "go.map")
- mappkg.Prefix = "go.map"
+ ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab")
+ ir.Pkgs.Itab.Prefix = "go.itab" // not go%2eitab
// pseudo-package used for methods with anonymous receivers
- gopkg = types.NewPkg("go", "")
+ ir.Pkgs.Go = types.NewPkg("go", "")
- Wasm := objabi.GOARCH == "wasm"
-
- // Whether the limit for stack-allocated objects is much smaller than normal.
- // This can be helpful for diagnosing certain causes of GC latency. See #27732.
- smallFrames := false
- jsonLogOpt := ""
-
- flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
- flag.BoolVar(&compiling_std, "std", false, "compiling standard library")
- flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
-
- objabi.Flagcount("%", "debug non-static initializers", &Debug.P)
- objabi.Flagcount("B", "disable bounds checking", &Debug.B)
- objabi.Flagcount("C", "disable printing of columns in error messages", &Debug.C)
- objabi.Flagcount("E", "debug symbol export", &Debug.E)
- objabi.Flagcount("K", "debug missing line numbers", &Debug.K)
- objabi.Flagcount("L", "show full file names in error messages", &Debug.L)
- objabi.Flagcount("N", "disable optimizations", &Debug.N)
- objabi.Flagcount("S", "print assembly listing", &Debug.S)
- objabi.Flagcount("W", "debug parse tree after type checking", &Debug.W)
- objabi.Flagcount("e", "no limit on number of errors reported", &Debug.e)
- objabi.Flagcount("h", "halt on error", &Debug.h)
- objabi.Flagcount("j", "debug runtime-initialized variables", &Debug.j)
- objabi.Flagcount("l", "disable inlining", &Debug.l)
- objabi.Flagcount("m", "print optimization decisions", &Debug.m)
- objabi.Flagcount("r", "debug generated wrappers", &Debug.r)
- objabi.Flagcount("w", "debug type checking", &Debug.w)
-
- objabi.Flagfn1("I", "add `directory` to import search path", addidir)
- objabi.AddVersionFlag() // -V
- flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`")
- flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata")
- flag.IntVar(&nBackendWorkers, "c", 1, "concurrency during compilation, 1 means no concurrency")
- flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
- flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`; try -d help")
- flag.BoolVar(&flagDWARF, "dwarf", !Wasm, "generate DWARF symbols")
- flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", true, "add location lists to DWARF in optimized mode")
- flag.IntVar(&genDwarfInline, "gendwarfinl", 2, "generate DWARF inline info records")
- objabi.Flagfn1("embedcfg", "read go:embed configuration from `file`", readEmbedCfg)
- objabi.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
- objabi.Flagfn1("importcfg", "read import configuration from `file`", readImportCfg)
- flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
- flag.StringVar(&flag_lang, "lang", "", "release to compile for")
- flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`")
- objabi.Flagcount("live", "debug liveness analysis", &debuglive)
- if sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
- flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
- }
- flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
- flag.StringVar(&outfile, "o", "", "write output to `file`")
- flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
- flag.BoolVar(&writearchive, "pack", false, "write to file.a instead of file.o")
- if sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) {
- flag.BoolVar(&flag_race, "race", false, "enable race detector")
- }
- flag.StringVar(&spectre, "spectre", spectre, "enable spectre mitigations in `list` (all, index, ret)")
- if enableTrace {
- flag.BoolVar(&trace, "t", false, "trace type-checking")
- }
- flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
- flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity")
- flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
- var flag_shared bool
- var flag_dynlink bool
- if supportsDynlink(thearch.LinkArch.Arch) {
- flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
- flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
- flag.BoolVar(&Ctxt.Flag_linkshared, "linkshared", false, "generate code that will be linked against Go shared libraries")
- }
- flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
- flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
- flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
- var goversion string
- flag.StringVar(&goversion, "goversion", "", "required version of the runtime")
- var symabisPath string
- flag.StringVar(&symabisPath, "symabis", "", "read symbol ABIs from `file`")
- flag.StringVar(&traceprofile, "traceprofile", "", "write an execution trace to `file`")
- flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to `file`")
- flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`")
- flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
- flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
- flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
- flag.StringVar(&jsonLogOpt, "json", "", "version,destination for JSON compiler/optimizer logging")
-
- objabi.Flagparse(usage)
-
- Ctxt.Pkgpath = myimportpath
-
- for _, f := range strings.Split(spectre, ",") {
- f = strings.TrimSpace(f)
- switch f {
- default:
- log.Fatalf("unknown setting -spectre=%s", f)
- case "":
- // nothing
- case "all":
- spectreIndex = true
- Ctxt.Retpoline = true
- case "index":
- spectreIndex = true
- case "ret":
- Ctxt.Retpoline = true
- }
- }
-
- if spectreIndex {
- switch objabi.GOARCH {
- case "amd64":
- // ok
- default:
- log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH)
- }
- }
+ base.DebugSSA = ssa.PhaseOption
+ base.ParseFlags()
// Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious
// changes in the binary.)
- recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
-
- if smallFrames {
- maxStackVarSize = 128 * 1024
- maxImplicitStackVarSize = 16 * 1024
- }
-
- Ctxt.Flag_shared = flag_dynlink || flag_shared
- Ctxt.Flag_dynlink = flag_dynlink
- Ctxt.Flag_optimize = Debug.N == 0
-
- Ctxt.Debugasm = Debug.S
- Ctxt.Debugvlog = Debug_vlog
- if flagDWARF {
- Ctxt.DebugInfo = debuginfo
- Ctxt.GenAbstractFunc = genAbstractFunc
- Ctxt.DwFixups = obj.NewDwarfFixupTable(Ctxt)
- } else {
- // turn off inline generation if no dwarf at all
- genDwarfInline = 0
- Ctxt.Flag_locationlists = false
- }
-
- if flag.NArg() < 1 && debugstr != "help" && debugstr != "ssa/help" {
- usage()
- }
+ dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
- if goversion != "" && goversion != runtime.Version() {
- fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), goversion)
- Exit(2)
+ if !base.EnableTrace && base.Flag.LowerT {
+ log.Fatalf("compiler not built with support for -t")
}
- checkLang()
-
- if symabisPath != "" {
- readSymABIs(symabisPath, myimportpath)
+ // Enable inlining (after RecordFlags, to avoid recording the rewritten -l). For now:
+ // default: inlining on. (Flag.LowerL == 1)
+ // -l: inlining off (Flag.LowerL == 0)
+ // -l=2, -l=3: inlining on again, with extra debugging (Flag.LowerL > 1)
+ if base.Flag.LowerL <= 1 {
+ base.Flag.LowerL = 1 - base.Flag.LowerL
}
- thearch.LinkArch.Init(Ctxt)
-
- if outfile == "" {
- p := flag.Arg(0)
- if i := strings.LastIndex(p, "/"); i >= 0 {
- p = p[i+1:]
- }
- if runtime.GOOS == "windows" {
- if i := strings.LastIndex(p, `\`); i >= 0 {
- p = p[i+1:]
- }
- }
- if i := strings.LastIndex(p, "."); i >= 0 {
- p = p[:i]
- }
- suffix := ".o"
- if writearchive {
- suffix = ".a"
- }
- outfile = p + suffix
+ if base.Flag.SmallFrames {
+ ir.MaxStackVarSize = 128 * 1024
+ ir.MaxImplicitStackVarSize = 16 * 1024
}
- startProfile()
-
- if flag_race && flag_msan {
- log.Fatal("cannot use both -race and -msan")
- }
- if flag_race || flag_msan {
- // -race and -msan imply -d=checkptr for now.
- Debug_checkptr = 1
- }
- if ispkgin(omit_pkgs) {
- flag_race = false
- flag_msan = false
- }
- if flag_race {
- racepkg = types.NewPkg("runtime/race", "")
- }
- if flag_msan {
- msanpkg = types.NewPkg("runtime/msan", "")
- }
- if flag_race || flag_msan {
- instrumenting = true
- }
-
- if compiling_runtime && Debug.N != 0 {
- log.Fatal("cannot disable optimizations while compiling runtime")
- }
- if nBackendWorkers < 1 {
- log.Fatalf("-c must be at least 1, got %d", nBackendWorkers)
- }
- if nBackendWorkers > 1 && !concurrentBackendAllowed() {
- log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
- }
- if Ctxt.Flag_locationlists && len(Ctxt.Arch.DWARFRegisters) == 0 {
- log.Fatalf("location lists requested but register mapping not available on %v", Ctxt.Arch.Name)
+ if base.Flag.Dwarf {
+ base.Ctxt.DebugInfo = dwarfgen.Info
+ base.Ctxt.GenAbstractFunc = dwarfgen.AbstractFunc
+ base.Ctxt.DwFixups = obj.NewDwarfFixupTable(base.Ctxt)
+ } else {
+ // turn off inline generation if no dwarf at all
+ base.Flag.GenDwarfInl = 0
+ base.Ctxt.Flag_locationlists = false
}
-
- // parse -d argument
- if debugstr != "" {
- Split:
- for _, name := range strings.Split(debugstr, ",") {
- if name == "" {
- continue
- }
- // display help about the -d option itself and quit
- if name == "help" {
- fmt.Print(debugHelpHeader)
- maxLen := len("ssa/help")
- for _, t := range debugtab {
- if len(t.name) > maxLen {
- maxLen = len(t.name)
- }
- }
- for _, t := range debugtab {
- fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help)
- }
- // ssa options have their own help
- fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
- fmt.Print(debugHelpFooter)
- os.Exit(0)
- }
- val, valstring, haveInt := 1, "", true
- if i := strings.IndexAny(name, "=:"); i >= 0 {
- var err error
- name, valstring = name[:i], name[i+1:]
- val, err = strconv.Atoi(valstring)
- if err != nil {
- val, haveInt = 1, false
- }
- }
- for _, t := range debugtab {
- if t.name != name {
- continue
- }
- switch vp := t.val.(type) {
- case nil:
- // Ignore
- case *string:
- *vp = valstring
- case *int:
- if !haveInt {
- log.Fatalf("invalid debug value %v", name)
- }
- *vp = val
- default:
- panic("bad debugtab type")
- }
- continue Split
- }
- // special case for ssa for now
- if strings.HasPrefix(name, "ssa/") {
- // expect form ssa/phase/flag
- // e.g. -d=ssa/generic_cse/time
- // _ in phase name also matches space
- phase := name[4:]
- flag := "debug" // default flag is debug
- if i := strings.Index(phase, "/"); i >= 0 {
- flag = phase[i+1:]
- phase = phase[:i]
- }
- err := ssa.PhaseOption(phase, flag, val, valstring)
- if err != "" {
- log.Fatalf(err)
- }
- continue Split
- }
- log.Fatalf("unknown debug key -d %s\n", name)
- }
+ if base.Ctxt.Flag_locationlists && len(base.Ctxt.Arch.DWARFRegisters) == 0 {
+ log.Fatalf("location lists requested but register mapping not available on %v", base.Ctxt.Arch.Name)
}
- if compiling_runtime {
- // Runtime can't use -d=checkptr, at least not yet.
- Debug_checkptr = 0
+ types.ParseLangFlag()
- // Fuzzing the runtime isn't interesting either.
- Debug_libfuzzer = 0
+ if base.Flag.SymABIs != "" {
+ ssagen.ReadSymABIs(base.Flag.SymABIs, base.Ctxt.Pkgpath)
}
- // set via a -d flag
- Ctxt.Debugpcln = Debug_pctab
- if flagDWARF {
- dwarf.EnableLogging(Debug_gendwarfinl != 0)
+ if base.Compiling(base.NoInstrumentPkgs) {
+ base.Flag.Race = false
+ base.Flag.MSan = false
}
- if Debug_softfloat != 0 {
- thearch.SoftFloat = true
- }
-
- // enable inlining. for now:
- // default: inlining on. (Debug.l == 1)
- // -l: inlining off (Debug.l == 0)
- // -l=2, -l=3: inlining on again, with extra debugging (Debug.l > 1)
- if Debug.l <= 1 {
- Debug.l = 1 - Debug.l
+ ssagen.Arch.LinkArch.Init(base.Ctxt)
+ startProfile()
+ if base.Flag.Race || base.Flag.MSan {
+ base.Flag.Cfg.Instrumenting = true
}
-
- if jsonLogOpt != "" { // parse version,destination from json logging optimization.
- logopt.LogJsonOption(jsonLogOpt)
+ if base.Flag.Dwarf {
+ dwarf.EnableLogging(base.Debug.DwarfInl != 0)
}
-
- ssaDump = os.Getenv("GOSSAFUNC")
- ssaDir = os.Getenv("GOSSADIR")
- if ssaDump != "" {
- if strings.HasSuffix(ssaDump, "+") {
- ssaDump = ssaDump[:len(ssaDump)-1]
- ssaDumpStdout = true
- }
- spl := strings.Split(ssaDump, ":")
- if len(spl) > 1 {
- ssaDump = spl[0]
- ssaDumpCFG = spl[1]
- }
+ if base.Debug.SoftFloat != 0 {
+ ssagen.Arch.SoftFloat = true
}
- trackScopes = flagDWARF
-
- Widthptr = thearch.LinkArch.PtrSize
- Widthreg = thearch.LinkArch.RegSize
-
- // initialize types package
- // (we need to do this to break dependencies that otherwise
- // would lead to import cycles)
- types.Widthptr = Widthptr
- types.Dowidth = dowidth
- types.Fatalf = Fatalf
- types.Sconv = func(s *types.Sym, flag, mode int) string {
- return sconv(s, FmtFlag(flag), fmtMode(mode))
+ if base.Flag.JSON != "" { // parse version,destination from json logging optimization.
+ logopt.LogJsonOption(base.Flag.JSON)
}
- types.Tconv = func(t *types.Type, flag, mode int) string {
- return tconv(t, FmtFlag(flag), fmtMode(mode))
- }
- types.FormatSym = func(sym *types.Sym, s fmt.State, verb rune, mode int) {
- symFormat(sym, s, verb, fmtMode(mode))
- }
- types.FormatType = func(t *types.Type, s fmt.State, verb rune, mode int) {
- typeFormat(t, s, verb, fmtMode(mode))
- }
- types.TypeLinkSym = func(t *types.Type) *obj.LSym {
- return typenamesym(t).Linksym()
- }
- types.FmtLeft = int(FmtLeft)
- types.FmtUnsigned = int(FmtUnsigned)
- types.FErr = int(FErr)
- types.Ctxt = Ctxt
-
- initUniverse()
- dclcontext = PEXTERN
- nerrors = 0
+ ir.EscFmt = escape.Fmt
+ ir.IsIntrinsicCall = ssagen.IsIntrinsicCall
+ inline.SSADumpInline = ssagen.DumpInline
+ ssagen.InitEnv()
+ ssagen.InitTables()
- autogeneratedPos = makePos(src.NewFileBase("", ""), 1, 0)
+ types.PtrSize = ssagen.Arch.LinkArch.PtrSize
+ types.RegSize = ssagen.Arch.LinkArch.RegSize
+ types.MaxWidth = ssagen.Arch.MAXWIDTH
- timings.Start("fe", "loadsys")
- loadsys()
+ typecheck.Target = new(ir.Package)
- timings.Start("fe", "parse")
- lines := parseFiles(flag.Args())
- timings.Stop()
- timings.AddEvent(int64(lines), "lines")
+ typecheck.NeedITab = func(t, iface *types.Type) { reflectdata.ITabAddr(t, iface) }
+ typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): TypeSym for lock?
- finishUniverse()
+ base.AutogeneratedPos = makePos(src.NewFileBase("", ""), 1, 0)
- recordPackageName()
+ typecheck.InitUniverse()
- typecheckok = true
+ // Parse and typecheck input.
+ noder.LoadPackage(flag.Args())
- // Process top-level declarations in phases.
+ dwarfgen.RecordPackageName()
+ ssagen.CgoSymABIs()
- // Phase 1: const, type, and names and types of funcs.
- // This will gather all the information about types
- // and methods but doesn't depend on any of it.
- //
- // We also defer type alias declarations until phase 2
- // to avoid cycles like #18640.
- // TODO(gri) Remove this again once we have a fix for #25838.
-
- // Don't use range--typecheck can add closures to xtop.
- timings.Start("fe", "typecheck", "top1")
- for i := 0; i < len(xtop); i++ {
- n := xtop[i]
- if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias()) {
- xtop[i] = typecheck(n, ctxStmt)
- }
- }
-
- // Phase 2: Variable assignments.
- // To check interface assignments, depends on phase 1.
-
- // Don't use range--typecheck can add closures to xtop.
- timings.Start("fe", "typecheck", "top2")
- for i := 0; i < len(xtop); i++ {
- n := xtop[i]
- if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias() {
- xtop[i] = typecheck(n, ctxStmt)
- }
+ // Build init task.
+ if initTask := pkginit.Task(); initTask != nil {
+ typecheck.Export(initTask)
}
- // Phase 3: Type check function bodies.
- // Don't use range--typecheck can add closures to xtop.
- timings.Start("fe", "typecheck", "func")
- var fcount int64
- for i := 0; i < len(xtop); i++ {
- n := xtop[i]
- if n.Op == ODCLFUNC {
- Curfn = n
- decldepth = 1
- saveerrors()
- typecheckslice(Curfn.Nbody.Slice(), ctxStmt)
- checkreturn(Curfn)
- if nerrors != 0 {
- Curfn.Nbody.Set(nil) // type errors; do not compile
- }
- // Now that we've checked whether n terminates,
- // we can eliminate some obviously dead code.
- deadcode(Curfn)
- fcount++
+ // Eliminate some obviously dead code.
+ // Must happen after typechecking.
+ for _, n := range typecheck.Target.Decls {
+ if n.Op() == ir.ODCLFUNC {
+ deadcode.Func(n.(*ir.Func))
}
}
- // With all types checked, it's now safe to verify map keys. One single
- // check past phase 9 isn't sufficient, as we may exit with other errors
- // before then, thus skipping map key errors.
- checkMapKeys()
- timings.AddEvent(fcount, "funcs")
-
- if nsavederrors+nerrors != 0 {
- 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.
- timings.Start("fe", "capturevars")
- for _, n := range xtop {
- if n.Op == ODCLFUNC && n.Func.Closure != nil {
- Curfn = n
- capturevars(n)
- }
+ // Compute Addrtaken for names.
+ // We need to wait until typechecking is done so that when we see &x[i]
+ // we know that x has its address taken if x is an array, but not if x is a slice.
+ // We compute Addrtaken in bulk here.
+ // After this phase, we maintain Addrtaken incrementally.
+ if typecheck.DirtyAddrtaken {
+ typecheck.ComputeAddrtaken(typecheck.Target.Decls)
+ typecheck.DirtyAddrtaken = false
}
- capturevarscomplete = true
+ typecheck.IncrementalAddrtaken = true
- Curfn = nil
-
- if nsavederrors+nerrors != 0 {
- errorexit()
- }
-
- // Phase 5: Inlining
- timings.Start("fe", "inlining")
- if Debug_typecheckinl != 0 {
+ if base.Debug.TypecheckInl != 0 {
// Typecheck imported function bodies if Debug.l > 1,
// otherwise lazily when used or re-exported.
- for _, n := range importlist {
- if n.Func.Inl != nil {
- saveerrors()
- typecheckinl(n)
- }
- }
-
- if nsavederrors+nerrors != 0 {
- errorexit()
- }
+ typecheck.AllImportedBodies()
}
- if Debug.l != 0 {
- // Find functions that can be inlined and clone them before walk expands them.
- visitBottomUp(xtop, func(list []*Node, recursive bool) {
- numfns := numNonClosures(list)
- for _, n := range list {
- if !recursive || numfns > 1 {
- // We allow inlining if there is no
- // recursion, or the recursion cycle is
- // across more than one function.
- caninl(n)
- } else {
- if Debug.m > 1 {
- fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
- }
- }
- inlcalls(n)
- }
- })
+ // Inlining
+ base.Timer.Start("fe", "inlining")
+ if base.Flag.LowerL != 0 {
+ inline.InlinePackage()
}
- for _, n := range xtop {
- if n.Op == ODCLFUNC {
- devirtualize(n)
+ // Devirtualize.
+ for _, n := range typecheck.Target.Decls {
+ if n.Op() == ir.ODCLFUNC {
+ devirtualize.Func(n.(*ir.Func))
}
}
- Curfn = nil
+ ir.CurFunc = nil
- // Phase 6: Escape analysis.
+ // Escape analysis.
// Required for moving heap allocations onto stack,
// which in turn is required by the closure implementation,
// which stores the addresses of stack variables into the closure.
@@ -719,140 +241,86 @@ func Main(archInit func(*Arch)) {
// or else the stack copier will not update it.
// Large values are also moved off stack in escape analysis;
// because large values may contain pointers, it must happen early.
- timings.Start("fe", "escapes")
- escapes(xtop)
+ base.Timer.Start("fe", "escapes")
+ escape.Funcs(typecheck.Target.Decls)
// Collect information for go:nowritebarrierrec
- // checking. This must happen before transformclosure.
+ // checking. This must happen before transforming closures during Walk
// We'll do the final check after write barriers are
// inserted.
- if compiling_runtime {
- nowritebarrierrecCheck = newNowritebarrierrecChecker()
- }
-
- // Phase 7: 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.
- timings.Start("fe", "xclosures")
- for _, n := range xtop {
- if n.Op == ODCLFUNC && n.Func.Closure != nil {
- Curfn = n
- transformclosure(n)
- }
+ if base.Flag.CompilingRuntime {
+ ssagen.EnableNoWriteBarrierRecCheck()
}
// Prepare for SSA compilation.
- // This must be before peekitabs, because peekitabs
+ // This must be before CompileITabs, because CompileITabs
// can trigger function compilation.
- initssaconfig()
+ typecheck.InitRuntime()
+ ssagen.InitConfig()
// Just before compilation, compile itabs found on
// the right side of OCONVIFACE so that methods
// can be de-virtualized during compilation.
- Curfn = nil
- peekitabs()
-
- // Phase 8: Compile top level functions.
- // Don't use range--walk can add functions to xtop.
- timings.Start("be", "compilefuncs")
- fcount = 0
- for i := 0; i < len(xtop); i++ {
- n := xtop[i]
- if n.Op == ODCLFUNC {
- funccompile(n)
+ ir.CurFunc = nil
+ reflectdata.CompileITabs()
+
+ // Compile top level functions.
+ // Don't use range--walk can add functions to Target.Decls.
+ base.Timer.Start("be", "compilefuncs")
+ fcount := int64(0)
+ for i := 0; i < len(typecheck.Target.Decls); i++ {
+ if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
+ enqueueFunc(fn)
fcount++
}
}
- timings.AddEvent(fcount, "funcs")
+ base.Timer.AddEvent(fcount, "funcs")
compileFunctions()
- if nowritebarrierrecCheck != nil {
- // Write barriers are now known. Check the
- // call graph.
- nowritebarrierrecCheck.check()
- nowritebarrierrecCheck = nil
+ if base.Flag.CompilingRuntime {
+ // Write barriers are now known. Check the call graph.
+ ssagen.NoWriteBarrierRecCheck()
}
// Finalize DWARF inline routine DIEs, then explicitly turn off
// DWARF inlining gen so as to avoid problems with generated
// method wrappers.
- if Ctxt.DwFixups != nil {
- Ctxt.DwFixups.Finalize(myimportpath, Debug_gendwarfinl != 0)
- Ctxt.DwFixups = nil
- genDwarfInline = 0
- }
-
- // Phase 9: Check external declarations.
- timings.Start("be", "externaldcls")
- for i, n := range externdcl {
- if n.Op == ONAME {
- externdcl[i] = typecheck(externdcl[i], ctxExpr)
- }
- }
- // Check the map keys again, since we typechecked the external
- // declarations.
- checkMapKeys()
-
- if nerrors+nsavederrors != 0 {
- errorexit()
+ if base.Ctxt.DwFixups != nil {
+ base.Ctxt.DwFixups.Finalize(base.Ctxt.Pkgpath, base.Debug.DwarfInl != 0)
+ base.Ctxt.DwFixups = nil
+ base.Flag.GenDwarfInl = 0
}
// Write object data to disk.
- timings.Start("be", "dumpobj")
+ base.Timer.Start("be", "dumpobj")
dumpdata()
- Ctxt.NumberSyms()
+ base.Ctxt.NumberSyms()
dumpobj()
- if asmhdr != "" {
+ if base.Flag.AsmHdr != "" {
dumpasmhdr()
}
- // Check whether any of the functions we have compiled have gigantic stack frames.
- sort.Slice(largeStackFrames, func(i, j int) bool {
- return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
- })
- for _, large := range largeStackFrames {
- if large.callee != 0 {
- yyerrorl(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
- } else {
- yyerrorl(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
- }
- }
+ ssagen.CheckLargeStacks()
+ typecheck.CheckFuncStack()
- if len(funcStack) != 0 {
- Fatalf("funcStack is non-empty: %v", len(funcStack))
- }
if len(compilequeue) != 0 {
- Fatalf("%d uncompiled functions", len(compilequeue))
+ base.Fatalf("%d uncompiled functions", len(compilequeue))
}
- logopt.FlushLoggedOpts(Ctxt, myimportpath)
-
- if nerrors+nsavederrors != 0 {
- errorexit()
- }
+ logopt.FlushLoggedOpts(base.Ctxt, base.Ctxt.Pkgpath)
+ base.ExitIfErrors()
- flusherrors()
- timings.Stop()
+ base.FlushErrors()
+ base.Timer.Stop()
- if benchfile != "" {
- if err := writebench(benchfile); err != nil {
+ if base.Flag.Bench != "" {
+ if err := writebench(base.Flag.Bench); err != nil {
log.Fatalf("cannot write benchmark data: %v", err)
}
}
}
-// numNonClosures returns the number of functions in list which are not closures.
-func numNonClosures(list []*Node) int {
- count := 0
- for _, n := range list {
- if n.Func.Closure == nil {
- count++
- }
- }
- return count
-}
-
func writebench(filename string) error {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
@@ -863,7 +331,7 @@ func writebench(filename string) error {
fmt.Fprintln(&buf, "commit:", objabi.Version)
fmt.Fprintln(&buf, "goos:", runtime.GOOS)
fmt.Fprintln(&buf, "goarch:", runtime.GOARCH)
- timings.Write(&buf, "BenchmarkCompile:"+myimportpath+":")
+ base.Timer.Write(&buf, "BenchmarkCompile:"+base.Ctxt.Pkgpath+":")
n, err := f.Write(buf.Bytes())
if err != nil {
@@ -876,735 +344,6 @@ func writebench(filename string) error {
return f.Close()
}
-var (
- importMap = map[string]string{}
- packageFile map[string]string // nil means not in use
-)
-
-func addImportMap(s string) {
- if strings.Count(s, "=") != 1 {
- log.Fatal("-importmap argument must be of the form source=actual")
- }
- i := strings.Index(s, "=")
- source, actual := s[:i], s[i+1:]
- if source == "" || actual == "" {
- log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
- }
- importMap[source] = actual
-}
-
-func readImportCfg(file string) {
- packageFile = map[string]string{}
- data, err := ioutil.ReadFile(file)
- if err != nil {
- log.Fatalf("-importcfg: %v", err)
- }
-
- for lineNum, line := range strings.Split(string(data), "\n") {
- lineNum++ // 1-based
- line = strings.TrimSpace(line)
- if line == "" || strings.HasPrefix(line, "#") {
- continue
- }
-
- var verb, args string
- if i := strings.Index(line, " "); i < 0 {
- verb = line
- } else {
- verb, args = line[:i], strings.TrimSpace(line[i+1:])
- }
- var before, after string
- if i := strings.Index(args, "="); i >= 0 {
- before, after = args[:i], args[i+1:]
- }
- switch verb {
- default:
- log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
- case "importmap":
- if before == "" || after == "" {
- log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
- }
- importMap[before] = after
- case "packagefile":
- if before == "" || after == "" {
- log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
- }
- packageFile[before] = after
- }
- }
-}
-
-// symabiDefs and symabiRefs record the defined and referenced ABIs of
-// symbols required by non-Go code. These are keyed by link symbol
-// name, where the local package prefix is always `"".`
-var symabiDefs, symabiRefs map[string]obj.ABI
-
-// readSymABIs reads a symabis file that specifies definitions and
-// references of text symbols by ABI.
-//
-// The symabis format is a set of lines, where each line is a sequence
-// of whitespace-separated fields. The first field is a verb and is
-// either "def" for defining a symbol ABI or "ref" for referencing a
-// symbol using an ABI. For both "def" and "ref", the second field is
-// the symbol name and the third field is the ABI name, as one of the
-// named cmd/internal/obj.ABI constants.
-func readSymABIs(file, myimportpath string) {
- data, err := ioutil.ReadFile(file)
- if err != nil {
- log.Fatalf("-symabis: %v", err)
- }
-
- symabiDefs = make(map[string]obj.ABI)
- symabiRefs = make(map[string]obj.ABI)
-
- localPrefix := ""
- if myimportpath != "" {
- // Symbols in this package may be written either as
- // "".X or with the package's import path already in
- // the symbol.
- localPrefix = objabi.PathToPrefix(myimportpath) + "."
- }
-
- for lineNum, line := range strings.Split(string(data), "\n") {
- lineNum++ // 1-based
- line = strings.TrimSpace(line)
- if line == "" || strings.HasPrefix(line, "#") {
- continue
- }
-
- parts := strings.Fields(line)
- switch parts[0] {
- case "def", "ref":
- // Parse line.
- if len(parts) != 3 {
- log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
- }
- sym, abistr := parts[1], parts[2]
- abi, valid := obj.ParseABI(abistr)
- if !valid {
- log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
- }
-
- // If the symbol is already prefixed with
- // myimportpath, rewrite it to start with ""
- // so it matches the compiler's internal
- // symbol names.
- if localPrefix != "" && strings.HasPrefix(sym, localPrefix) {
- sym = `"".` + sym[len(localPrefix):]
- }
-
- // Record for later.
- if parts[0] == "def" {
- symabiDefs[sym] = abi
- } else {
- symabiRefs[sym] = abi
- }
- default:
- log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
- }
- }
-}
-
-func saveerrors() {
- nsavederrors += nerrors
- nerrors = 0
-}
-
-func arsize(b *bufio.Reader, name string) int {
- var buf [ArhdrSize]byte
- if _, err := io.ReadFull(b, buf[:]); err != nil {
- return -1
- }
- aname := strings.Trim(string(buf[0:16]), " ")
- if !strings.HasPrefix(aname, name) {
- return -1
- }
- asize := strings.Trim(string(buf[48:58]), " ")
- i, _ := strconv.Atoi(asize)
- return i
-}
-
-var idirs []string
-
-func addidir(dir string) {
- if dir != "" {
- idirs = append(idirs, dir)
- }
-}
-
-func isDriveLetter(b byte) bool {
- return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
-}
-
-// is this path a local name? begins with ./ or ../ or /
-func islocalname(name string) bool {
- return strings.HasPrefix(name, "/") ||
- runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
- strings.HasPrefix(name, "./") || name == "." ||
- strings.HasPrefix(name, "../") || name == ".."
-}
-
-func findpkg(name string) (file string, ok bool) {
- if islocalname(name) {
- if nolocalimports {
- return "", false
- }
-
- if packageFile != nil {
- file, ok = packageFile[name]
- return file, ok
- }
-
- // try .a before .6. important for building libraries:
- // if there is an array.6 in the array.a library,
- // want to find all of array.a, not just array.6.
- file = fmt.Sprintf("%s.a", name)
- if _, err := os.Stat(file); err == nil {
- return file, true
- }
- file = fmt.Sprintf("%s.o", name)
- if _, err := os.Stat(file); err == nil {
- return file, true
- }
- return "", false
- }
-
- // local imports should be canonicalized already.
- // don't want to see "encoding/../encoding/base64"
- // as different from "encoding/base64".
- if q := path.Clean(name); q != name {
- yyerror("non-canonical import path %q (should be %q)", name, q)
- return "", false
- }
-
- if packageFile != nil {
- file, ok = packageFile[name]
- return file, ok
- }
-
- for _, dir := range idirs {
- file = fmt.Sprintf("%s/%s.a", dir, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
- }
- file = fmt.Sprintf("%s/%s.o", dir, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
- }
- }
-
- if objabi.GOROOT != "" {
- suffix := ""
- suffixsep := ""
- if flag_installsuffix != "" {
- suffixsep = "_"
- suffix = flag_installsuffix
- } else if flag_race {
- suffixsep = "_"
- suffix = "race"
- } else if flag_msan {
- suffixsep = "_"
- suffix = "msan"
- }
-
- file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
- }
- file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
- }
- }
-
- return "", false
-}
-
-// loadsys loads the definitions for the low-level runtime functions,
-// so that the compiler can generate calls to them,
-// but does not make them visible to user code.
-func loadsys() {
- types.Block = 1
-
- inimport = true
- typecheckok = true
-
- typs := runtimeTypes()
- for _, d := range &runtimeDecls {
- sym := Runtimepkg.Lookup(d.name)
- typ := typs[d.typ]
- switch d.tag {
- case funcTag:
- importfunc(Runtimepkg, src.NoXPos, sym, typ)
- case varTag:
- importvar(Runtimepkg, src.NoXPos, sym, typ)
- default:
- Fatalf("unhandled declaration tag %v", d.tag)
- }
- }
-
- typecheckok = false
- inimport = false
-}
-
-// myheight tracks the local package's height based on packages
-// imported so far.
-var myheight int
-
-func importfile(f *Val) *types.Pkg {
- path_, ok := f.U.(string)
- if !ok {
- yyerror("import path must be a string")
- return nil
- }
-
- if len(path_) == 0 {
- yyerror("import path is empty")
- return nil
- }
-
- if isbadimport(path_, false) {
- return nil
- }
-
- // The package name main is no longer reserved,
- // but we reserve the import path "main" to identify
- // the main package, just as we reserve the import
- // path "math" to identify the standard math package.
- if path_ == "main" {
- yyerror("cannot import \"main\"")
- errorexit()
- }
-
- if myimportpath != "" && path_ == myimportpath {
- yyerror("import %q while compiling that package (import cycle)", path_)
- errorexit()
- }
-
- if mapped, ok := importMap[path_]; ok {
- path_ = mapped
- }
-
- if path_ == "unsafe" {
- return unsafepkg
- }
-
- if islocalname(path_) {
- if path_[0] == '/' {
- yyerror("import path cannot be absolute path")
- return nil
- }
-
- prefix := Ctxt.Pathname
- if localimport != "" {
- prefix = localimport
- }
- path_ = path.Join(prefix, path_)
-
- if isbadimport(path_, true) {
- return nil
- }
- }
-
- file, found := findpkg(path_)
- if !found {
- yyerror("can't find import: %q", path_)
- errorexit()
- }
-
- importpkg := types.NewPkg(path_, "")
- if importpkg.Imported {
- return importpkg
- }
-
- importpkg.Imported = true
-
- imp, err := bio.Open(file)
- if err != nil {
- yyerror("can't open import: %q: %v", path_, err)
- errorexit()
- }
- defer imp.Close()
-
- // check object header
- p, err := imp.ReadString('\n')
- if err != nil {
- yyerror("import %s: reading input: %v", file, err)
- errorexit()
- }
-
- if p == "!\n" { // package archive
- // package export block should be first
- sz := arsize(imp.Reader, "__.PKGDEF")
- if sz <= 0 {
- yyerror("import %s: not a package file", file)
- errorexit()
- }
- p, err = imp.ReadString('\n')
- if err != nil {
- yyerror("import %s: reading input: %v", file, err)
- errorexit()
- }
- }
-
- if !strings.HasPrefix(p, "go object ") {
- yyerror("import %s: not a go object file: %s", file, p)
- errorexit()
- }
- q := fmt.Sprintf("%s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring())
- if p[10:] != q {
- yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q)
- errorexit()
- }
-
- // process header lines
- for {
- p, err = imp.ReadString('\n')
- if err != nil {
- yyerror("import %s: reading input: %v", file, err)
- errorexit()
- }
- if p == "\n" {
- break // header ends with blank line
- }
- }
-
- // In the importfile, if we find:
- // $$\n (textual format): not supported anymore
- // $$B\n (binary format) : import directly, then feed the lexer a dummy statement
-
- // look for $$
- var c byte
- for {
- c, err = imp.ReadByte()
- if err != nil {
- break
- }
- if c == '$' {
- c, err = imp.ReadByte()
- if c == '$' || err != nil {
- break
- }
- }
- }
-
- // get character after $$
- if err == nil {
- c, _ = imp.ReadByte()
- }
-
- var fingerprint goobj.FingerprintType
- switch c {
- case '\n':
- yyerror("cannot import %s: old export format no longer supported (recompile library)", path_)
- return nil
-
- case 'B':
- if Debug_export != 0 {
- fmt.Printf("importing %s (%s)\n", path_, file)
- }
- imp.ReadByte() // skip \n after $$B
-
- c, err = imp.ReadByte()
- if err != nil {
- yyerror("import %s: reading input: %v", file, err)
- errorexit()
- }
-
- // Indexed format is distinguished by an 'i' byte,
- // whereas previous export formats started with 'c', 'd', or 'v'.
- if c != 'i' {
- yyerror("import %s: unexpected package format byte: %v", file, c)
- errorexit()
- }
- fingerprint = iimport(importpkg, imp)
-
- default:
- yyerror("no import in %q", path_)
- errorexit()
- }
-
- // assume files move (get installed) so don't record the full path
- if packageFile != nil {
- // If using a packageFile map, assume path_ can be recorded directly.
- Ctxt.AddImport(path_, fingerprint)
- } else {
- // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
- Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):], fingerprint)
- }
-
- if importpkg.Height >= myheight {
- myheight = importpkg.Height + 1
- }
-
- return importpkg
-}
-
-func pkgnotused(lineno src.XPos, path string, name string) {
- // If the package was imported with a name other than the final
- // import path element, show it explicitly in the error message.
- // Note that this handles both renamed imports and imports of
- // packages containing unconventional package declarations.
- // Note that this uses / always, even on Windows, because Go import
- // paths always use forward slashes.
- elem := path
- if i := strings.LastIndex(elem, "/"); i >= 0 {
- elem = elem[i+1:]
- }
- if name == "" || elem == name {
- yyerrorl(lineno, "imported and not used: %q", path)
- } else {
- yyerrorl(lineno, "imported and not used: %q as %s", path, name)
- }
-}
-
-func mkpackage(pkgname string) {
- if localpkg.Name == "" {
- if pkgname == "_" {
- yyerror("invalid package name _")
- }
- localpkg.Name = pkgname
- } else {
- if pkgname != localpkg.Name {
- yyerror("package %s; expected %s", pkgname, localpkg.Name)
- }
- }
-}
-
-func clearImports() {
- type importedPkg struct {
- pos src.XPos
- path string
- name string
- }
- var unused []importedPkg
-
- for _, s := range localpkg.Syms {
- n := asNode(s.Def)
- if n == nil {
- continue
- }
- if n.Op == OPACK {
- // throw away top-level package name left over
- // from previous file.
- // leave s->block set to cause redeclaration
- // errors if a conflicting top-level name is
- // introduced by a different file.
- if !n.Name.Used() && nsyntaxerrors == 0 {
- unused = append(unused, importedPkg{n.Pos, n.Name.Pkg.Path, s.Name})
- }
- s.Def = nil
- continue
- }
- if IsAlias(s) {
- // throw away top-level name left over
- // from previous import . "x"
- if n.Name != nil && n.Name.Pack != nil && !n.Name.Pack.Name.Used() && nsyntaxerrors == 0 {
- unused = append(unused, importedPkg{n.Name.Pack.Pos, n.Name.Pack.Name.Pkg.Path, ""})
- n.Name.Pack.Name.SetUsed(true)
- }
- s.Def = nil
- continue
- }
- }
-
- sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) })
- for _, pkg := range unused {
- pkgnotused(pkg.pos, pkg.path, pkg.name)
- }
-}
-
-func IsAlias(sym *types.Sym) bool {
- return sym.Def != nil && asNode(sym.Def).Sym != sym
-}
-
-// By default, assume any debug flags are incompatible with concurrent
-// compilation. A few are safe and potentially in common use for
-// normal compiles, though; return true for those.
-func concurrentFlagOk() bool {
- // Report whether any debug flag that would prevent concurrent
- // compilation is set, by zeroing out the allowed ones and then
- // checking if the resulting struct is zero.
- d := Debug
- d.B = 0 // disable bounds checking
- d.C = 0 // disable printing of columns in error messages
- d.e = 0 // no limit on errors; errors all come from non-concurrent code
- d.N = 0 // disable optimizations
- d.l = 0 // disable inlining
- d.w = 0 // all printing happens before compilation
- d.W = 0 // all printing happens before compilation
- d.S = 0 // printing disassembly happens at the end (but see concurrentBackendAllowed below)
-
- return d == DebugFlags{}
-}
-
-func concurrentBackendAllowed() bool {
- if !concurrentFlagOk() {
- return false
- }
-
- // Debug.S by itself is ok, because all printing occurs
- // while writing the object file, and that is non-concurrent.
- // Adding Debug_vlog, however, causes Debug.S to also print
- // while flushing the plist, which happens concurrently.
- if Debug_vlog || debugstr != "" || debuglive > 0 {
- return false
- }
- // TODO: Test and delete this condition.
- if objabi.Fieldtrack_enabled != 0 {
- return false
- }
- // TODO: fix races and enable the following flags
- if Ctxt.Flag_shared || Ctxt.Flag_dynlink || flag_race {
- return false
- }
- return true
-}
-
-// recordFlags records the specified command-line flags to be placed
-// in the DWARF info.
-func recordFlags(flags ...string) {
- if myimportpath == "" {
- // We can't record the flags if we don't know what the
- // package name is.
- return
- }
-
- type BoolFlag interface {
- IsBoolFlag() bool
- }
- type CountFlag interface {
- IsCountFlag() bool
- }
- var cmd bytes.Buffer
- for _, name := range flags {
- f := flag.Lookup(name)
- if f == nil {
- continue
- }
- getter := f.Value.(flag.Getter)
- if getter.String() == f.DefValue {
- // Flag has default value, so omit it.
- continue
- }
- if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
- val, ok := getter.Get().(bool)
- if ok && val {
- fmt.Fprintf(&cmd, " -%s", f.Name)
- continue
- }
- }
- if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
- val, ok := getter.Get().(int)
- if ok && val == 1 {
- fmt.Fprintf(&cmd, " -%s", f.Name)
- continue
- }
- }
- fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
- }
-
- if cmd.Len() == 0 {
- return
- }
- s := Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + myimportpath)
- s.Type = objabi.SDWARFCUINFO
- // Sometimes (for example when building tests) we can link
- // together two package main archives. So allow dups.
- s.Set(obj.AttrDuplicateOK, true)
- Ctxt.Data = append(Ctxt.Data, s)
- s.P = cmd.Bytes()[1:]
-}
-
-// recordPackageName records the name of the package being
-// compiled, so that the linker can save it in the compile unit's DIE.
-func recordPackageName() {
- s := Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + myimportpath)
- s.Type = objabi.SDWARFCUINFO
- // Sometimes (for example when building tests) we can link
- // together two package main archives. So allow dups.
- s.Set(obj.AttrDuplicateOK, true)
- Ctxt.Data = append(Ctxt.Data, s)
- s.P = []byte(localpkg.Name)
-}
-
-// flag_lang is the language version we are compiling for, set by the -lang flag.
-var flag_lang string
-
-// currentLang returns the current language version.
-func currentLang() string {
- return fmt.Sprintf("go1.%d", goversion.Version)
-}
-
-// goVersionRE is a regular expression that matches the valid
-// arguments to the -lang flag.
-var goVersionRE = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
-
-// A lang is a language version broken into major and minor numbers.
-type lang struct {
- major, minor int
-}
-
-// langWant is the desired language version set by the -lang flag.
-// If the -lang flag is not set, this is the zero value, meaning that
-// any language version is supported.
-var langWant lang
-
-// langSupported reports whether language version major.minor is
-// supported in a particular package.
-func langSupported(major, minor int, pkg *types.Pkg) bool {
- if pkg == nil {
- // TODO(mdempsky): Set Pkg for local types earlier.
- pkg = localpkg
- }
- if pkg != localpkg {
- // Assume imported packages passed type-checking.
- return true
- }
-
- if langWant.major == 0 && langWant.minor == 0 {
- return true
- }
- return langWant.major > major || (langWant.major == major && langWant.minor >= minor)
-}
-
-// checkLang verifies that the -lang flag holds a valid value, and
-// exits if not. It initializes data used by langSupported.
-func checkLang() {
- if flag_lang == "" {
- return
- }
-
- var err error
- langWant, err = parseLang(flag_lang)
- if err != nil {
- log.Fatalf("invalid value %q for -lang: %v", flag_lang, err)
- }
-
- if def := currentLang(); flag_lang != def {
- defVers, err := parseLang(def)
- if err != nil {
- log.Fatalf("internal error parsing default lang %q: %v", def, err)
- }
- if langWant.major > defVers.major || (langWant.major == defVers.major && langWant.minor > defVers.minor) {
- log.Fatalf("invalid value %q for -lang: max known version is %q", flag_lang, def)
- }
- }
-}
-
-// parseLang parses a -lang option into a langVer.
-func parseLang(s string) (lang, error) {
- matches := goVersionRE.FindStringSubmatch(s)
- if matches == nil {
- return lang{}, fmt.Errorf(`should be something like "go1.12"`)
- }
- major, err := strconv.Atoi(matches[1])
- if err != nil {
- return lang{}, err
- }
- minor, err := strconv.Atoi(matches[2])
- if err != nil {
- return lang{}, err
- }
- return lang{major: major, minor: minor}, nil
+func makePos(b *src.PosBase, line, col uint) src.XPos {
+ return base.Ctxt.PosTable.XPos(src.MakePos(b, line, col))
}
diff --git a/src/cmd/compile/internal/gc/mpfloat.go b/src/cmd/compile/internal/gc/mpfloat.go
deleted file mode 100644
index 401aef319de7c..0000000000000
--- a/src/cmd/compile/internal/gc/mpfloat.go
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "fmt"
- "math"
- "math/big"
-)
-
-// implements float arithmetic
-
-const (
- // Maximum size in bits for Mpints before signalling
- // overflow and also mantissa precision for Mpflts.
- Mpprec = 512
- // Turn on for constant arithmetic debugging output.
- Mpdebug = false
-)
-
-// Mpflt represents a floating-point constant.
-type Mpflt struct {
- Val big.Float
-}
-
-// Mpcplx represents a complex constant.
-type Mpcplx struct {
- Real Mpflt
- Imag Mpflt
-}
-
-// Use newMpflt (not new(Mpflt)!) to get the correct default precision.
-func newMpflt() *Mpflt {
- var a Mpflt
- a.Val.SetPrec(Mpprec)
- return &a
-}
-
-// Use newMpcmplx (not new(Mpcplx)!) to get the correct default precision.
-func newMpcmplx() *Mpcplx {
- var a Mpcplx
- a.Real = *newMpflt()
- a.Imag = *newMpflt()
- return &a
-}
-
-func (a *Mpflt) SetInt(b *Mpint) {
- if b.checkOverflow(0) {
- // sign doesn't really matter but copy anyway
- a.Val.SetInf(b.Val.Sign() < 0)
- return
- }
- a.Val.SetInt(&b.Val)
-}
-
-func (a *Mpflt) Set(b *Mpflt) {
- a.Val.Set(&b.Val)
-}
-
-func (a *Mpflt) Add(b *Mpflt) {
- if Mpdebug {
- fmt.Printf("\n%v + %v", a, b)
- }
-
- a.Val.Add(&a.Val, &b.Val)
-
- if Mpdebug {
- fmt.Printf(" = %v\n\n", a)
- }
-}
-
-func (a *Mpflt) AddFloat64(c float64) {
- var b Mpflt
-
- b.SetFloat64(c)
- a.Add(&b)
-}
-
-func (a *Mpflt) Sub(b *Mpflt) {
- if Mpdebug {
- fmt.Printf("\n%v - %v", a, b)
- }
-
- a.Val.Sub(&a.Val, &b.Val)
-
- if Mpdebug {
- fmt.Printf(" = %v\n\n", a)
- }
-}
-
-func (a *Mpflt) Mul(b *Mpflt) {
- if Mpdebug {
- fmt.Printf("%v\n * %v\n", a, b)
- }
-
- a.Val.Mul(&a.Val, &b.Val)
-
- if Mpdebug {
- fmt.Printf(" = %v\n\n", a)
- }
-}
-
-func (a *Mpflt) MulFloat64(c float64) {
- var b Mpflt
-
- b.SetFloat64(c)
- a.Mul(&b)
-}
-
-func (a *Mpflt) Quo(b *Mpflt) {
- if Mpdebug {
- fmt.Printf("%v\n / %v\n", a, b)
- }
-
- a.Val.Quo(&a.Val, &b.Val)
-
- if Mpdebug {
- fmt.Printf(" = %v\n\n", a)
- }
-}
-
-func (a *Mpflt) Cmp(b *Mpflt) int {
- return a.Val.Cmp(&b.Val)
-}
-
-func (a *Mpflt) CmpFloat64(c float64) int {
- if c == 0 {
- return a.Val.Sign() // common case shortcut
- }
- return a.Val.Cmp(big.NewFloat(c))
-}
-
-func (a *Mpflt) Float64() float64 {
- x, _ := a.Val.Float64()
-
- // check for overflow
- if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpflt Float64")
- }
-
- return x + 0 // avoid -0 (should not be needed, but be conservative)
-}
-
-func (a *Mpflt) Float32() float64 {
- x32, _ := a.Val.Float32()
- x := float64(x32)
-
- // check for overflow
- if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpflt Float32")
- }
-
- return x + 0 // avoid -0 (should not be needed, but be conservative)
-}
-
-func (a *Mpflt) SetFloat64(c float64) {
- if Mpdebug {
- fmt.Printf("\nconst %g", c)
- }
-
- // convert -0 to 0
- if c == 0 {
- c = 0
- }
- a.Val.SetFloat64(c)
-
- if Mpdebug {
- fmt.Printf(" = %v\n", a)
- }
-}
-
-func (a *Mpflt) Neg() {
- // avoid -0
- if a.Val.Sign() != 0 {
- a.Val.Neg(&a.Val)
- }
-}
-
-func (a *Mpflt) SetString(as string) {
- f, _, err := a.Val.Parse(as, 0)
- if err != nil {
- yyerror("malformed constant: %s (%v)", as, err)
- a.Val.SetFloat64(0)
- return
- }
-
- if f.IsInf() {
- yyerror("constant too large: %s", as)
- a.Val.SetFloat64(0)
- return
- }
-
- // -0 becomes 0
- if f.Sign() == 0 && f.Signbit() {
- a.Val.SetFloat64(0)
- }
-}
-
-func (f *Mpflt) String() string {
- return f.Val.Text('b', 0)
-}
-
-func (fvp *Mpflt) GoString() string {
- // determine sign
- sign := ""
- f := &fvp.Val
- if f.Sign() < 0 {
- sign = "-"
- f = new(big.Float).Abs(f)
- }
-
- // Don't try to convert infinities (will not terminate).
- if f.IsInf() {
- return sign + "Inf"
- }
-
- // Use exact fmt formatting if in float64 range (common case):
- // proceed if f doesn't underflow to 0 or overflow to inf.
- if x, _ := f.Float64(); f.Sign() == 0 == (x == 0) && !math.IsInf(x, 0) {
- return fmt.Sprintf("%s%.6g", sign, x)
- }
-
- // Out of float64 range. Do approximate manual to decimal
- // conversion to avoid precise but possibly slow Float
- // formatting.
- // f = mant * 2**exp
- var mant big.Float
- exp := f.MantExp(&mant) // 0.5 <= mant < 1.0
-
- // approximate float64 mantissa m and decimal exponent d
- // f ~ m * 10**d
- m, _ := mant.Float64() // 0.5 <= m < 1.0
- d := float64(exp) * (math.Ln2 / math.Ln10) // log_10(2)
-
- // adjust m for truncated (integer) decimal exponent e
- e := int64(d)
- m *= math.Pow(10, d-float64(e))
-
- // ensure 1 <= m < 10
- switch {
- case m < 1-0.5e-6:
- // The %.6g format below rounds m to 5 digits after the
- // decimal point. Make sure that m*10 < 10 even after
- // rounding up: m*10 + 0.5e-5 < 10 => m < 1 - 0.5e6.
- m *= 10
- e--
- case m >= 10:
- m /= 10
- e++
- }
-
- return fmt.Sprintf("%s%.6ge%+d", sign, m, e)
-}
-
-// complex multiply v *= rv
-// (a, b) * (c, d) = (a*c - b*d, b*c + a*d)
-func (v *Mpcplx) Mul(rv *Mpcplx) {
- var ac, ad, bc, bd Mpflt
-
- ac.Set(&v.Real)
- ac.Mul(&rv.Real) // ac
-
- bd.Set(&v.Imag)
- bd.Mul(&rv.Imag) // bd
-
- bc.Set(&v.Imag)
- bc.Mul(&rv.Real) // bc
-
- ad.Set(&v.Real)
- ad.Mul(&rv.Imag) // ad
-
- v.Real.Set(&ac)
- v.Real.Sub(&bd) // ac-bd
-
- v.Imag.Set(&bc)
- v.Imag.Add(&ad) // bc+ad
-}
-
-// complex divide v /= rv
-// (a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d)
-func (v *Mpcplx) Div(rv *Mpcplx) bool {
- if rv.Real.CmpFloat64(0) == 0 && rv.Imag.CmpFloat64(0) == 0 {
- return false
- }
-
- var ac, ad, bc, bd, cc_plus_dd Mpflt
-
- cc_plus_dd.Set(&rv.Real)
- cc_plus_dd.Mul(&rv.Real) // cc
-
- ac.Set(&rv.Imag)
- ac.Mul(&rv.Imag) // dd
- cc_plus_dd.Add(&ac) // cc+dd
-
- // We already checked that c and d are not both zero, but we can't
- // assume that c²+d² != 0 follows, because for tiny values of c
- // and/or d c²+d² can underflow to zero. Check that c²+d² is
- // nonzero, return if it's not.
- if cc_plus_dd.CmpFloat64(0) == 0 {
- return false
- }
-
- ac.Set(&v.Real)
- ac.Mul(&rv.Real) // ac
-
- bd.Set(&v.Imag)
- bd.Mul(&rv.Imag) // bd
-
- bc.Set(&v.Imag)
- bc.Mul(&rv.Real) // bc
-
- ad.Set(&v.Real)
- ad.Mul(&rv.Imag) // ad
-
- v.Real.Set(&ac)
- v.Real.Add(&bd) // ac+bd
- v.Real.Quo(&cc_plus_dd) // (ac+bd)/(cc+dd)
-
- v.Imag.Set(&bc)
- v.Imag.Sub(&ad) // bc-ad
- v.Imag.Quo(&cc_plus_dd) // (bc+ad)/(cc+dd)
-
- return true
-}
-
-func (v *Mpcplx) String() string {
- return fmt.Sprintf("(%s+%si)", v.Real.String(), v.Imag.String())
-}
-
-func (v *Mpcplx) GoString() string {
- var re string
- sre := v.Real.CmpFloat64(0)
- if sre != 0 {
- re = v.Real.GoString()
- }
-
- var im string
- sim := v.Imag.CmpFloat64(0)
- if sim != 0 {
- im = v.Imag.GoString()
- }
-
- switch {
- case sre == 0 && sim == 0:
- return "0"
- case sre == 0:
- return im + "i"
- case sim == 0:
- return re
- case sim < 0:
- return fmt.Sprintf("(%s%si)", re, im)
- default:
- return fmt.Sprintf("(%s+%si)", re, im)
- }
-}
diff --git a/src/cmd/compile/internal/gc/mpint.go b/src/cmd/compile/internal/gc/mpint.go
deleted file mode 100644
index 340350bca7b2b..0000000000000
--- a/src/cmd/compile/internal/gc/mpint.go
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "fmt"
- "math/big"
-)
-
-// implements integer arithmetic
-
-// Mpint represents an integer constant.
-type Mpint struct {
- Val big.Int
- Ovf bool // set if Val overflowed compiler limit (sticky)
- Rune bool // set if syntax indicates default type rune
-}
-
-func (a *Mpint) SetOverflow() {
- a.Val.SetUint64(1) // avoid spurious div-zero errors
- a.Ovf = true
-}
-
-func (a *Mpint) checkOverflow(extra int) bool {
- // We don't need to be precise here, any reasonable upper limit would do.
- // For now, use existing limit so we pass all the tests unchanged.
- if a.Val.BitLen()+extra > Mpprec {
- a.SetOverflow()
- }
- return a.Ovf
-}
-
-func (a *Mpint) Set(b *Mpint) {
- a.Val.Set(&b.Val)
-}
-
-func (a *Mpint) SetFloat(b *Mpflt) bool {
- // avoid converting huge floating-point numbers to integers
- // (2*Mpprec is large enough to permit all tests to pass)
- if b.Val.MantExp(nil) > 2*Mpprec {
- a.SetOverflow()
- return false
- }
-
- if _, acc := b.Val.Int(&a.Val); acc == big.Exact {
- return true
- }
-
- const delta = 16 // a reasonably small number of bits > 0
- var t big.Float
- t.SetPrec(Mpprec - delta)
-
- // try rounding down a little
- t.SetMode(big.ToZero)
- t.Set(&b.Val)
- if _, acc := t.Int(&a.Val); acc == big.Exact {
- return true
- }
-
- // try rounding up a little
- t.SetMode(big.AwayFromZero)
- t.Set(&b.Val)
- if _, acc := t.Int(&a.Val); acc == big.Exact {
- return true
- }
-
- a.Ovf = false
- return false
-}
-
-func (a *Mpint) Add(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Add")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.Add(&a.Val, &b.Val)
-
- if a.checkOverflow(0) {
- yyerror("constant addition overflow")
- }
-}
-
-func (a *Mpint) Sub(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Sub")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.Sub(&a.Val, &b.Val)
-
- if a.checkOverflow(0) {
- yyerror("constant subtraction overflow")
- }
-}
-
-func (a *Mpint) Mul(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Mul")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.Mul(&a.Val, &b.Val)
-
- if a.checkOverflow(0) {
- yyerror("constant multiplication overflow")
- }
-}
-
-func (a *Mpint) Quo(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Quo")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.Quo(&a.Val, &b.Val)
-
- if a.checkOverflow(0) {
- // can only happen for div-0 which should be checked elsewhere
- yyerror("constant division overflow")
- }
-}
-
-func (a *Mpint) Rem(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Rem")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.Rem(&a.Val, &b.Val)
-
- if a.checkOverflow(0) {
- // should never happen
- yyerror("constant modulo overflow")
- }
-}
-
-func (a *Mpint) Or(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Or")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.Or(&a.Val, &b.Val)
-}
-
-func (a *Mpint) And(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint And")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.And(&a.Val, &b.Val)
-}
-
-func (a *Mpint) AndNot(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint AndNot")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.AndNot(&a.Val, &b.Val)
-}
-
-func (a *Mpint) Xor(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Xor")
- }
- a.SetOverflow()
- return
- }
-
- a.Val.Xor(&a.Val, &b.Val)
-}
-
-func (a *Mpint) Lsh(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Lsh")
- }
- a.SetOverflow()
- return
- }
-
- s := b.Int64()
- if s < 0 || s >= Mpprec {
- msg := "shift count too large"
- if s < 0 {
- msg = "invalid negative shift count"
- }
- yyerror("%s: %d", msg, s)
- a.SetInt64(0)
- return
- }
-
- if a.checkOverflow(int(s)) {
- yyerror("constant shift overflow")
- return
- }
- a.Val.Lsh(&a.Val, uint(s))
-}
-
-func (a *Mpint) Rsh(b *Mpint) {
- if a.Ovf || b.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("ovf in Mpint Rsh")
- }
- a.SetOverflow()
- return
- }
-
- s := b.Int64()
- if s < 0 {
- yyerror("invalid negative shift count: %d", s)
- if a.Val.Sign() < 0 {
- a.SetInt64(-1)
- } else {
- a.SetInt64(0)
- }
- return
- }
-
- a.Val.Rsh(&a.Val, uint(s))
-}
-
-func (a *Mpint) Cmp(b *Mpint) int {
- return a.Val.Cmp(&b.Val)
-}
-
-func (a *Mpint) CmpInt64(c int64) int {
- if c == 0 {
- return a.Val.Sign() // common case shortcut
- }
- return a.Val.Cmp(big.NewInt(c))
-}
-
-func (a *Mpint) Neg() {
- a.Val.Neg(&a.Val)
-}
-
-func (a *Mpint) Int64() int64 {
- if a.Ovf {
- if nsavederrors+nerrors == 0 {
- Fatalf("constant overflow")
- }
- return 0
- }
-
- return a.Val.Int64()
-}
-
-func (a *Mpint) SetInt64(c int64) {
- a.Val.SetInt64(c)
-}
-
-func (a *Mpint) SetString(as string) {
- _, ok := a.Val.SetString(as, 0)
- if !ok {
- // The lexer checks for correct syntax of the literal
- // and reports detailed errors. Thus SetString should
- // never fail (in theory it might run out of memory,
- // but that wouldn't be reported as an error here).
- Fatalf("malformed integer constant: %s", as)
- return
- }
- if a.checkOverflow(0) {
- yyerror("constant too large: %s", as)
- }
-}
-
-func (a *Mpint) GoString() string {
- return a.Val.String()
-}
-
-func (a *Mpint) String() string {
- return fmt.Sprintf("%#x", &a.Val)
-}
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
deleted file mode 100644
index 7494c3ef6b1b7..0000000000000
--- a/src/cmd/compile/internal/gc/noder.go
+++ /dev/null
@@ -1,1756 +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.
-
-package gc
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "runtime"
- "strconv"
- "strings"
- "unicode"
- "unicode/utf8"
-
- "cmd/compile/internal/syntax"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
-)
-
-// parseFiles concurrently parses files into *syntax.File structures.
-// Each declaration in every *syntax.File is converted to a syntax tree
-// and its root represented by *Node is appended to xtop.
-// Returns the total count of parsed lines.
-func parseFiles(filenames []string) uint {
- noders := make([]*noder, 0, len(filenames))
- // Limit the number of simultaneously open files.
- sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
-
- for _, filename := range filenames {
- p := &noder{
- basemap: make(map[*syntax.PosBase]*src.PosBase),
- err: make(chan syntax.Error),
- }
- noders = append(noders, p)
-
- go func(filename string) {
- sem <- struct{}{}
- defer func() { <-sem }()
- defer close(p.err)
- base := syntax.NewFileBase(filename)
-
- f, err := os.Open(filename)
- if err != nil {
- p.error(syntax.Error{Msg: err.Error()})
- return
- }
- defer f.Close()
-
- p.file, _ = syntax.Parse(base, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error
- }(filename)
- }
-
- var lines uint
- for _, p := range noders {
- for e := range p.err {
- p.yyerrorpos(e.Pos, "%s", e.Msg)
- }
-
- p.node()
- lines += p.file.Lines
- p.file = nil // release memory
-
- if nsyntaxerrors != 0 {
- errorexit()
- }
- // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
- testdclstack()
- }
-
- localpkg.Height = myheight
-
- return lines
-}
-
-// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
-func (p *noder) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
- // fast path: most likely PosBase hasn't changed
- if p.basecache.last == b0 {
- return p.basecache.base
- }
-
- b1, ok := p.basemap[b0]
- if !ok {
- fn := b0.Filename()
- if b0.IsFileBase() {
- b1 = src.NewFileBase(fn, absFilename(fn))
- } else {
- // line directive base
- p0 := b0.Pos()
- p0b := p0.Base()
- if p0b == b0 {
- panic("infinite recursion in makeSrcPosBase")
- }
- p1 := src.MakePos(p.makeSrcPosBase(p0b), p0.Line(), p0.Col())
- b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col())
- }
- p.basemap[b0] = b1
- }
-
- // update cache
- p.basecache.last = b0
- p.basecache.base = b1
-
- return b1
-}
-
-func (p *noder) makeXPos(pos syntax.Pos) (_ src.XPos) {
- return Ctxt.PosTable.XPos(src.MakePos(p.makeSrcPosBase(pos.Base()), pos.Line(), pos.Col()))
-}
-
-func (p *noder) yyerrorpos(pos syntax.Pos, format string, args ...interface{}) {
- yyerrorl(p.makeXPos(pos), format, args...)
-}
-
-var pathPrefix string
-
-// TODO(gri) Can we eliminate fileh in favor of absFilename?
-func fileh(name string) string {
- return objabi.AbsFile("", name, pathPrefix)
-}
-
-func absFilename(name string) string {
- return objabi.AbsFile(Ctxt.Pathname, name, pathPrefix)
-}
-
-// noder transforms package syntax's AST into a Node tree.
-type noder struct {
- basemap map[*syntax.PosBase]*src.PosBase
- basecache struct {
- last *syntax.PosBase
- base *src.PosBase
- }
-
- file *syntax.File
- linknames []linkname
- pragcgobuf [][]string
- err chan syntax.Error
- scope ScopeID
- importedUnsafe bool
- importedEmbed bool
-
- // scopeVars is a stack tracking the number of variables declared in the
- // current function at the moment each open scope was opened.
- scopeVars []int
-
- lastCloseScopePos syntax.Pos
-}
-
-func (p *noder) funcBody(fn *Node, block *syntax.BlockStmt) {
- oldScope := p.scope
- p.scope = 0
- funchdr(fn)
-
- if block != nil {
- body := p.stmts(block.List)
- if body == nil {
- body = []*Node{nod(OEMPTY, nil, nil)}
- }
- fn.Nbody.Set(body)
-
- lineno = p.makeXPos(block.Rbrace)
- fn.Func.Endlineno = lineno
- }
-
- funcbody()
- p.scope = oldScope
-}
-
-func (p *noder) openScope(pos syntax.Pos) {
- types.Markdcl()
-
- if trackScopes {
- Curfn.Func.Parents = append(Curfn.Func.Parents, p.scope)
- p.scopeVars = append(p.scopeVars, len(Curfn.Func.Dcl))
- p.scope = ScopeID(len(Curfn.Func.Parents))
-
- p.markScope(pos)
- }
-}
-
-func (p *noder) closeScope(pos syntax.Pos) {
- p.lastCloseScopePos = pos
- types.Popdcl()
-
- if trackScopes {
- scopeVars := p.scopeVars[len(p.scopeVars)-1]
- p.scopeVars = p.scopeVars[:len(p.scopeVars)-1]
- if scopeVars == len(Curfn.Func.Dcl) {
- // no variables were declared in this scope, so we can retract it.
-
- if int(p.scope) != len(Curfn.Func.Parents) {
- Fatalf("scope tracking inconsistency, no variables declared but scopes were not retracted")
- }
-
- p.scope = Curfn.Func.Parents[p.scope-1]
- Curfn.Func.Parents = Curfn.Func.Parents[:len(Curfn.Func.Parents)-1]
-
- nmarks := len(Curfn.Func.Marks)
- Curfn.Func.Marks[nmarks-1].Scope = p.scope
- prevScope := ScopeID(0)
- if nmarks >= 2 {
- prevScope = Curfn.Func.Marks[nmarks-2].Scope
- }
- if Curfn.Func.Marks[nmarks-1].Scope == prevScope {
- Curfn.Func.Marks = Curfn.Func.Marks[:nmarks-1]
- }
- return
- }
-
- p.scope = Curfn.Func.Parents[p.scope-1]
-
- p.markScope(pos)
- }
-}
-
-func (p *noder) markScope(pos syntax.Pos) {
- xpos := p.makeXPos(pos)
- if i := len(Curfn.Func.Marks); i > 0 && Curfn.Func.Marks[i-1].Pos == xpos {
- Curfn.Func.Marks[i-1].Scope = p.scope
- } else {
- Curfn.Func.Marks = append(Curfn.Func.Marks, Mark{xpos, p.scope})
- }
-}
-
-// closeAnotherScope is like closeScope, but it reuses the same mark
-// position as the last closeScope call. This is useful for "for" and
-// "if" statements, as their implicit blocks always end at the same
-// position as an explicit block.
-func (p *noder) closeAnotherScope() {
- p.closeScope(p.lastCloseScopePos)
-}
-
-// linkname records a //go:linkname directive.
-type linkname struct {
- pos syntax.Pos
- local string
- remote string
-}
-
-func (p *noder) node() {
- types.Block = 1
- p.importedUnsafe = false
- p.importedEmbed = false
-
- p.setlineno(p.file.PkgName)
- mkpackage(p.file.PkgName.Value)
-
- if pragma, ok := p.file.Pragma.(*Pragma); ok {
- pragma.Flag &^= GoBuildPragma
- p.checkUnused(pragma)
- }
-
- xtop = append(xtop, p.decls(p.file.DeclList)...)
-
- for _, n := range p.linknames {
- if !p.importedUnsafe {
- p.yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
- continue
- }
- s := lookup(n.local)
- if n.remote != "" {
- s.Linkname = n.remote
- } else {
- // Use the default object symbol name if the
- // user didn't provide one.
- if myimportpath == "" {
- p.yyerrorpos(n.pos, "//go:linkname requires linkname argument or -p compiler flag")
- } else {
- s.Linkname = objabi.PathToPrefix(myimportpath) + "." + n.local
- }
- }
- }
-
- // The linker expects an ABI0 wrapper for all cgo-exported
- // functions.
- for _, prag := range p.pragcgobuf {
- switch prag[0] {
- case "cgo_export_static", "cgo_export_dynamic":
- if symabiRefs == nil {
- symabiRefs = make(map[string]obj.ABI)
- }
- symabiRefs[prag[1]] = obj.ABI0
- }
- }
-
- pragcgobuf = append(pragcgobuf, p.pragcgobuf...)
- lineno = src.NoXPos
- clearImports()
-}
-
-func (p *noder) decls(decls []syntax.Decl) (l []*Node) {
- var cs constState
-
- for _, decl := range decls {
- p.setlineno(decl)
- switch decl := decl.(type) {
- case *syntax.ImportDecl:
- p.importDecl(decl)
-
- case *syntax.VarDecl:
- l = append(l, p.varDecl(decl)...)
-
- case *syntax.ConstDecl:
- l = append(l, p.constDecl(decl, &cs)...)
-
- case *syntax.TypeDecl:
- l = append(l, p.typeDecl(decl))
-
- case *syntax.FuncDecl:
- l = append(l, p.funcDecl(decl))
-
- default:
- panic("unhandled Decl")
- }
- }
-
- return
-}
-
-func (p *noder) importDecl(imp *syntax.ImportDecl) {
- if imp.Path.Bad {
- return // avoid follow-on errors if there was a syntax error
- }
-
- if pragma, ok := imp.Pragma.(*Pragma); ok {
- p.checkUnused(pragma)
- }
-
- val := p.basicLit(imp.Path)
- ipkg := importfile(&val)
- if ipkg == nil {
- if nerrors == 0 {
- Fatalf("phase error in import")
- }
- return
- }
-
- if ipkg == unsafepkg {
- p.importedUnsafe = true
- }
- if ipkg.Path == "embed" {
- p.importedEmbed = true
- }
-
- ipkg.Direct = true
-
- var my *types.Sym
- if imp.LocalPkgName != nil {
- my = p.name(imp.LocalPkgName)
- } else {
- my = lookup(ipkg.Name)
- }
-
- pack := p.nod(imp, OPACK, nil, nil)
- pack.Sym = my
- pack.Name.Pkg = ipkg
-
- switch my.Name {
- case ".":
- importdot(ipkg, pack)
- return
- case "init":
- yyerrorl(pack.Pos, "cannot import package as init - init must be a func")
- return
- case "_":
- return
- }
- if my.Def != nil {
- redeclare(pack.Pos, my, "as imported package name")
- }
- my.Def = asTypesNode(pack)
- my.Lastlineno = pack.Pos
- my.Block = 1 // at top level
-}
-
-func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
- names := p.declNames(decl.NameList)
- typ := p.typeExprOrNil(decl.Type)
-
- var exprs []*Node
- if decl.Values != nil {
- exprs = p.exprList(decl.Values)
- }
-
- if pragma, ok := decl.Pragma.(*Pragma); ok {
- if len(pragma.Embeds) > 0 {
- if !p.importedEmbed {
- // This check can't be done when building the list pragma.Embeds
- // because that list is created before the noder starts walking over the file,
- // so at that point it hasn't seen the imports.
- // We're left to check now, just before applying the //go:embed lines.
- for _, e := range pragma.Embeds {
- p.yyerrorpos(e.Pos, "//go:embed only allowed in Go files that import \"embed\"")
- }
- } else {
- varEmbed(p, names, typ, exprs, pragma.Embeds)
- }
- pragma.Embeds = nil
- }
- p.checkUnused(pragma)
- }
-
- p.setlineno(decl)
- return variter(names, typ, exprs)
-}
-
-// constState tracks state between constant specifiers within a
-// declaration group. This state is kept separate from noder so nested
-// constant declarations are handled correctly (e.g., issue 15550).
-type constState struct {
- group *syntax.Group
- typ *Node
- values []*Node
- iota int64
-}
-
-func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []*Node {
- if decl.Group == nil || decl.Group != cs.group {
- *cs = constState{
- group: decl.Group,
- }
- }
-
- if pragma, ok := decl.Pragma.(*Pragma); ok {
- p.checkUnused(pragma)
- }
-
- names := p.declNames(decl.NameList)
- typ := p.typeExprOrNil(decl.Type)
-
- var values []*Node
- if decl.Values != nil {
- values = p.exprList(decl.Values)
- cs.typ, cs.values = typ, values
- } else {
- if typ != nil {
- yyerror("const declaration cannot have type without expression")
- }
- typ, values = cs.typ, cs.values
- }
-
- nn := make([]*Node, 0, len(names))
- for i, n := range names {
- if i >= len(values) {
- yyerror("missing value in const declaration")
- break
- }
- v := values[i]
- if decl.Values == nil {
- v = treecopy(v, n.Pos)
- }
-
- n.Op = OLITERAL
- declare(n, dclcontext)
-
- n.Name.Param.Ntype = typ
- n.Name.Defn = v
- n.SetIota(cs.iota)
-
- nn = append(nn, p.nod(decl, ODCLCONST, n, nil))
- }
-
- if len(values) > len(names) {
- yyerror("extra expression in const declaration")
- }
-
- cs.iota++
-
- return nn
-}
-
-func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
- n := p.declName(decl.Name)
- n.Op = OTYPE
- declare(n, dclcontext)
-
- // decl.Type may be nil but in that case we got a syntax error during parsing
- typ := p.typeExprOrNil(decl.Type)
-
- param := n.Name.Param
- param.Ntype = typ
- param.SetAlias(decl.Alias)
- if pragma, ok := decl.Pragma.(*Pragma); ok {
- if !decl.Alias {
- param.SetPragma(pragma.Flag & TypePragmas)
- pragma.Flag &^= TypePragmas
- }
- p.checkUnused(pragma)
- }
-
- nod := p.nod(decl, ODCLTYPE, n, nil)
- if param.Alias() && !langSupported(1, 9, localpkg) {
- yyerrorl(nod.Pos, "type aliases only supported as of -lang=go1.9")
- }
- return nod
-}
-
-func (p *noder) declNames(names []*syntax.Name) []*Node {
- nodes := make([]*Node, 0, len(names))
- for _, name := range names {
- nodes = append(nodes, p.declName(name))
- }
- return nodes
-}
-
-func (p *noder) declName(name *syntax.Name) *Node {
- n := dclname(p.name(name))
- n.Pos = p.pos(name)
- return n
-}
-
-func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
- name := p.name(fun.Name)
- t := p.signature(fun.Recv, fun.Type)
- f := p.nod(fun, ODCLFUNC, nil, nil)
-
- if fun.Recv == nil {
- if name.Name == "init" {
- name = renameinit()
- if t.List.Len() > 0 || t.Rlist.Len() > 0 {
- yyerrorl(f.Pos, "func init must have no arguments and no return values")
- }
- }
-
- if localpkg.Name == "main" && name.Name == "main" {
- if t.List.Len() > 0 || t.Rlist.Len() > 0 {
- yyerrorl(f.Pos, "func main must have no arguments and no return values")
- }
- }
- } else {
- f.Func.Shortname = name
- name = nblank.Sym // filled in by typecheckfunc
- }
-
- f.Func.Nname = newfuncnamel(p.pos(fun.Name), name)
- f.Func.Nname.Name.Defn = f
- f.Func.Nname.Name.Param.Ntype = t
-
- if pragma, ok := fun.Pragma.(*Pragma); ok {
- f.Func.Pragma = pragma.Flag & FuncPragmas
- if pragma.Flag&Systemstack != 0 && pragma.Flag&Nosplit != 0 {
- yyerrorl(f.Pos, "go:nosplit and go:systemstack cannot be combined")
- }
- pragma.Flag &^= FuncPragmas
- p.checkUnused(pragma)
- }
-
- if fun.Recv == nil {
- declare(f.Func.Nname, PFUNC)
- }
-
- p.funcBody(f, fun.Body)
-
- if fun.Body != nil {
- if f.Func.Pragma&Noescape != 0 {
- yyerrorl(f.Pos, "can only use //go:noescape with external func implementations")
- }
- } else {
- if pure_go || strings.HasPrefix(f.funcname(), "init.") {
- // Linknamed functions are allowed to have no body. Hopefully
- // the linkname target has a body. See issue 23311.
- isLinknamed := false
- for _, n := range p.linknames {
- if f.funcname() == n.local {
- isLinknamed = true
- break
- }
- }
- if !isLinknamed {
- yyerrorl(f.Pos, "missing function body")
- }
- }
- }
-
- return f
-}
-
-func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *Node {
- n := p.nod(typ, OTFUNC, nil, nil)
- if recv != nil {
- n.Left = p.param(recv, false, false)
- }
- n.List.Set(p.params(typ.ParamList, true))
- n.Rlist.Set(p.params(typ.ResultList, false))
- return n
-}
-
-func (p *noder) params(params []*syntax.Field, dddOk bool) []*Node {
- nodes := make([]*Node, 0, len(params))
- for i, param := range params {
- p.setlineno(param)
- nodes = append(nodes, p.param(param, dddOk, i+1 == len(params)))
- }
- return nodes
-}
-
-func (p *noder) param(param *syntax.Field, dddOk, final bool) *Node {
- var name *types.Sym
- if param.Name != nil {
- name = p.name(param.Name)
- }
-
- typ := p.typeExpr(param.Type)
- n := p.nodSym(param, ODCLFIELD, typ, name)
-
- // rewrite ...T parameter
- if typ.Op == ODDD {
- if !dddOk {
- // We mark these as syntax errors to get automatic elimination
- // of multiple such errors per line (see yyerrorl in subr.go).
- yyerror("syntax error: cannot use ... in receiver or result parameter list")
- } else if !final {
- if param.Name == nil {
- yyerror("syntax error: cannot use ... with non-final parameter")
- } else {
- p.yyerrorpos(param.Name.Pos(), "syntax error: cannot use ... with non-final parameter %s", param.Name.Value)
- }
- }
- typ.Op = OTARRAY
- typ.Right = typ.Left
- typ.Left = nil
- n.SetIsDDD(true)
- if n.Left != nil {
- n.Left.SetIsDDD(true)
- }
- }
-
- return n
-}
-
-func (p *noder) exprList(expr syntax.Expr) []*Node {
- if list, ok := expr.(*syntax.ListExpr); ok {
- return p.exprs(list.ElemList)
- }
- return []*Node{p.expr(expr)}
-}
-
-func (p *noder) exprs(exprs []syntax.Expr) []*Node {
- nodes := make([]*Node, 0, len(exprs))
- for _, expr := range exprs {
- nodes = append(nodes, p.expr(expr))
- }
- return nodes
-}
-
-func (p *noder) expr(expr syntax.Expr) *Node {
- p.setlineno(expr)
- switch expr := expr.(type) {
- case nil, *syntax.BadExpr:
- return nil
- case *syntax.Name:
- return p.mkname(expr)
- case *syntax.BasicLit:
- n := nodlit(p.basicLit(expr))
- n.SetDiag(expr.Bad) // avoid follow-on errors if there was a syntax error
- return n
- case *syntax.CompositeLit:
- n := p.nod(expr, OCOMPLIT, nil, nil)
- if expr.Type != nil {
- n.Right = p.expr(expr.Type)
- }
- l := p.exprs(expr.ElemList)
- for i, e := range l {
- l[i] = p.wrapname(expr.ElemList[i], e)
- }
- n.List.Set(l)
- lineno = p.makeXPos(expr.Rbrace)
- return n
- case *syntax.KeyValueExpr:
- // use position of expr.Key rather than of expr (which has position of ':')
- return p.nod(expr.Key, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
- case *syntax.FuncLit:
- return p.funcLit(expr)
- case *syntax.ParenExpr:
- return p.nod(expr, OPAREN, p.expr(expr.X), nil)
- case *syntax.SelectorExpr:
- // parser.new_dotname
- obj := p.expr(expr.X)
- if obj.Op == OPACK {
- obj.Name.SetUsed(true)
- return importName(obj.Name.Pkg.Lookup(expr.Sel.Value))
- }
- n := nodSym(OXDOT, obj, p.name(expr.Sel))
- n.Pos = p.pos(expr) // lineno may have been changed by p.expr(expr.X)
- return n
- case *syntax.IndexExpr:
- return p.nod(expr, OINDEX, p.expr(expr.X), p.expr(expr.Index))
- case *syntax.SliceExpr:
- op := OSLICE
- if expr.Full {
- op = OSLICE3
- }
- n := p.nod(expr, op, p.expr(expr.X), nil)
- var index [3]*Node
- for i, x := range &expr.Index {
- if x != nil {
- index[i] = p.expr(x)
- }
- }
- n.SetSliceBounds(index[0], index[1], index[2])
- return n
- case *syntax.AssertExpr:
- return p.nod(expr, ODOTTYPE, p.expr(expr.X), p.typeExpr(expr.Type))
- case *syntax.Operation:
- if expr.Op == syntax.Add && expr.Y != nil {
- return p.sum(expr)
- }
- x := p.expr(expr.X)
- if expr.Y == nil {
- return p.nod(expr, p.unOp(expr.Op), x, nil)
- }
- return p.nod(expr, p.binOp(expr.Op), x, p.expr(expr.Y))
- case *syntax.CallExpr:
- n := p.nod(expr, OCALL, p.expr(expr.Fun), nil)
- n.List.Set(p.exprs(expr.ArgList))
- n.SetIsDDD(expr.HasDots)
- return n
-
- case *syntax.ArrayType:
- var len *Node
- if expr.Len != nil {
- len = p.expr(expr.Len)
- } else {
- len = p.nod(expr, ODDD, nil, nil)
- }
- return p.nod(expr, OTARRAY, len, p.typeExpr(expr.Elem))
- case *syntax.SliceType:
- return p.nod(expr, OTARRAY, nil, p.typeExpr(expr.Elem))
- case *syntax.DotsType:
- return p.nod(expr, ODDD, p.typeExpr(expr.Elem), nil)
- case *syntax.StructType:
- return p.structType(expr)
- case *syntax.InterfaceType:
- return p.interfaceType(expr)
- case *syntax.FuncType:
- return p.signature(nil, expr)
- case *syntax.MapType:
- return p.nod(expr, OTMAP, p.typeExpr(expr.Key), p.typeExpr(expr.Value))
- case *syntax.ChanType:
- n := p.nod(expr, OTCHAN, p.typeExpr(expr.Elem), nil)
- n.SetTChanDir(p.chanDir(expr.Dir))
- return n
-
- case *syntax.TypeSwitchGuard:
- n := p.nod(expr, OTYPESW, nil, p.expr(expr.X))
- if expr.Lhs != nil {
- n.Left = p.declName(expr.Lhs)
- if n.Left.isBlank() {
- yyerror("invalid variable name %v in type switch", n.Left)
- }
- }
- return n
- }
- panic("unhandled Expr")
-}
-
-// sum efficiently handles very large summation expressions (such as
-// in issue #16394). In particular, it avoids left recursion and
-// collapses string literals.
-func (p *noder) sum(x syntax.Expr) *Node {
- // While we need to handle long sums with asymptotic
- // efficiency, the vast majority of sums are very small: ~95%
- // have only 2 or 3 operands, and ~99% of string literals are
- // never concatenated.
-
- adds := make([]*syntax.Operation, 0, 2)
- for {
- add, ok := x.(*syntax.Operation)
- if !ok || add.Op != syntax.Add || add.Y == nil {
- break
- }
- adds = append(adds, add)
- x = add.X
- }
-
- // nstr is the current rightmost string literal in the
- // summation (if any), and chunks holds its accumulated
- // substrings.
- //
- // Consider the expression x + "a" + "b" + "c" + y. When we
- // reach the string literal "a", we assign nstr to point to
- // its corresponding Node and initialize chunks to {"a"}.
- // Visiting the subsequent string literals "b" and "c", we
- // simply append their values to chunks. Finally, when we
- // reach the non-constant operand y, we'll join chunks to form
- // "abc" and reassign the "a" string literal's value.
- //
- // N.B., we need to be careful about named string constants
- // (indicated by Sym != nil) because 1) we can't modify their
- // value, as doing so would affect other uses of the string
- // constant, and 2) they may have types, which we need to
- // handle correctly. For now, we avoid these problems by
- // treating named string constants the same as non-constant
- // operands.
- var nstr *Node
- chunks := make([]string, 0, 1)
-
- n := p.expr(x)
- if Isconst(n, CTSTR) && n.Sym == nil {
- nstr = n
- chunks = append(chunks, nstr.StringVal())
- }
-
- for i := len(adds) - 1; i >= 0; i-- {
- add := adds[i]
-
- r := p.expr(add.Y)
- if Isconst(r, CTSTR) && r.Sym == nil {
- if nstr != nil {
- // Collapse r into nstr instead of adding to n.
- chunks = append(chunks, r.StringVal())
- continue
- }
-
- nstr = r
- chunks = append(chunks, nstr.StringVal())
- } else {
- if len(chunks) > 1 {
- nstr.SetVal(Val{U: strings.Join(chunks, "")})
- }
- nstr = nil
- chunks = chunks[:0]
- }
- n = p.nod(add, OADD, n, r)
- }
- if len(chunks) > 1 {
- nstr.SetVal(Val{U: strings.Join(chunks, "")})
- }
-
- return n
-}
-
-func (p *noder) typeExpr(typ syntax.Expr) *Node {
- // TODO(mdempsky): Be stricter? typecheck should handle errors anyway.
- return p.expr(typ)
-}
-
-func (p *noder) typeExprOrNil(typ syntax.Expr) *Node {
- if typ != nil {
- return p.expr(typ)
- }
- return nil
-}
-
-func (p *noder) chanDir(dir syntax.ChanDir) types.ChanDir {
- switch dir {
- case 0:
- return types.Cboth
- case syntax.SendOnly:
- return types.Csend
- case syntax.RecvOnly:
- return types.Crecv
- }
- panic("unhandled ChanDir")
-}
-
-func (p *noder) structType(expr *syntax.StructType) *Node {
- l := make([]*Node, 0, len(expr.FieldList))
- for i, field := range expr.FieldList {
- p.setlineno(field)
- var n *Node
- if field.Name == nil {
- n = p.embedded(field.Type)
- } else {
- n = p.nodSym(field, ODCLFIELD, p.typeExpr(field.Type), p.name(field.Name))
- }
- if i < len(expr.TagList) && expr.TagList[i] != nil {
- n.SetVal(p.basicLit(expr.TagList[i]))
- }
- l = append(l, n)
- }
-
- p.setlineno(expr)
- n := p.nod(expr, OTSTRUCT, nil, nil)
- n.List.Set(l)
- return n
-}
-
-func (p *noder) interfaceType(expr *syntax.InterfaceType) *Node {
- l := make([]*Node, 0, len(expr.MethodList))
- for _, method := range expr.MethodList {
- p.setlineno(method)
- var n *Node
- if method.Name == nil {
- n = p.nodSym(method, ODCLFIELD, importName(p.packname(method.Type)), nil)
- } else {
- mname := p.name(method.Name)
- sig := p.typeExpr(method.Type)
- sig.Left = fakeRecv()
- n = p.nodSym(method, ODCLFIELD, sig, mname)
- ifacedcl(n)
- }
- l = append(l, n)
- }
-
- n := p.nod(expr, OTINTER, nil, nil)
- n.List.Set(l)
- return n
-}
-
-func (p *noder) packname(expr syntax.Expr) *types.Sym {
- switch expr := expr.(type) {
- case *syntax.Name:
- name := p.name(expr)
- if n := oldname(name); n.Name != nil && n.Name.Pack != nil {
- n.Name.Pack.Name.SetUsed(true)
- }
- return name
- case *syntax.SelectorExpr:
- name := p.name(expr.X.(*syntax.Name))
- def := asNode(name.Def)
- if def == nil {
- yyerror("undefined: %v", name)
- return name
- }
- var pkg *types.Pkg
- if def.Op != OPACK {
- yyerror("%v is not a package", name)
- pkg = localpkg
- } else {
- def.Name.SetUsed(true)
- pkg = def.Name.Pkg
- }
- return pkg.Lookup(expr.Sel.Value)
- }
- panic(fmt.Sprintf("unexpected packname: %#v", expr))
-}
-
-func (p *noder) embedded(typ syntax.Expr) *Node {
- op, isStar := typ.(*syntax.Operation)
- if isStar {
- if op.Op != syntax.Mul || op.Y != nil {
- panic("unexpected Operation")
- }
- typ = op.X
- }
-
- sym := p.packname(typ)
- n := p.nodSym(typ, ODCLFIELD, importName(sym), lookup(sym.Name))
- n.SetEmbedded(true)
-
- if isStar {
- n.Left = p.nod(op, ODEREF, n.Left, nil)
- }
- return n
-}
-
-func (p *noder) stmts(stmts []syntax.Stmt) []*Node {
- return p.stmtsFall(stmts, false)
-}
-
-func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []*Node {
- var nodes []*Node
- for i, stmt := range stmts {
- s := p.stmtFall(stmt, fallOK && i+1 == len(stmts))
- if s == nil {
- } else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
- nodes = append(nodes, s.List.Slice()...)
- } else {
- nodes = append(nodes, s)
- }
- }
- return nodes
-}
-
-func (p *noder) stmt(stmt syntax.Stmt) *Node {
- return p.stmtFall(stmt, false)
-}
-
-func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) *Node {
- p.setlineno(stmt)
- switch stmt := stmt.(type) {
- case *syntax.EmptyStmt:
- return nil
- case *syntax.LabeledStmt:
- return p.labeledStmt(stmt, fallOK)
- case *syntax.BlockStmt:
- l := p.blockStmt(stmt)
- if len(l) == 0 {
- // TODO(mdempsky): Line number?
- return nod(OEMPTY, nil, nil)
- }
- return liststmt(l)
- case *syntax.ExprStmt:
- return p.wrapname(stmt, p.expr(stmt.X))
- case *syntax.SendStmt:
- return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value))
- case *syntax.DeclStmt:
- return liststmt(p.decls(stmt.DeclList))
- case *syntax.AssignStmt:
- if stmt.Op != 0 && stmt.Op != syntax.Def {
- n := p.nod(stmt, OASOP, p.expr(stmt.Lhs), p.expr(stmt.Rhs))
- n.SetImplicit(stmt.Rhs == syntax.ImplicitOne)
- n.SetSubOp(p.binOp(stmt.Op))
- return n
- }
-
- n := p.nod(stmt, OAS, nil, nil) // assume common case
-
- rhs := p.exprList(stmt.Rhs)
- lhs := p.assignList(stmt.Lhs, n, stmt.Op == syntax.Def)
-
- if len(lhs) == 1 && len(rhs) == 1 {
- // common case
- n.Left = lhs[0]
- n.Right = rhs[0]
- } else {
- n.Op = OAS2
- n.List.Set(lhs)
- n.Rlist.Set(rhs)
- }
- return n
-
- case *syntax.BranchStmt:
- var op Op
- switch stmt.Tok {
- case syntax.Break:
- op = OBREAK
- case syntax.Continue:
- op = OCONTINUE
- case syntax.Fallthrough:
- if !fallOK {
- yyerror("fallthrough statement out of place")
- }
- op = OFALL
- case syntax.Goto:
- op = OGOTO
- default:
- panic("unhandled BranchStmt")
- }
- n := p.nod(stmt, op, nil, nil)
- if stmt.Label != nil {
- n.Sym = p.name(stmt.Label)
- }
- return n
- case *syntax.CallStmt:
- var op Op
- switch stmt.Tok {
- case syntax.Defer:
- op = ODEFER
- case syntax.Go:
- op = OGO
- default:
- panic("unhandled CallStmt")
- }
- return p.nod(stmt, op, p.expr(stmt.Call), nil)
- case *syntax.ReturnStmt:
- var results []*Node
- if stmt.Results != nil {
- results = p.exprList(stmt.Results)
- }
- n := p.nod(stmt, ORETURN, nil, nil)
- n.List.Set(results)
- if n.List.Len() == 0 && Curfn != nil {
- for _, ln := range Curfn.Func.Dcl {
- if ln.Class() == PPARAM {
- continue
- }
- if ln.Class() != PPARAMOUT {
- break
- }
- if asNode(ln.Sym.Def) != ln {
- yyerror("%s is shadowed during return", ln.Sym.Name)
- }
- }
- }
- return n
- case *syntax.IfStmt:
- return p.ifStmt(stmt)
- case *syntax.ForStmt:
- return p.forStmt(stmt)
- case *syntax.SwitchStmt:
- return p.switchStmt(stmt)
- case *syntax.SelectStmt:
- return p.selectStmt(stmt)
- }
- panic("unhandled Stmt")
-}
-
-func (p *noder) assignList(expr syntax.Expr, defn *Node, colas bool) []*Node {
- if !colas {
- return p.exprList(expr)
- }
-
- defn.SetColas(true)
-
- var exprs []syntax.Expr
- if list, ok := expr.(*syntax.ListExpr); ok {
- exprs = list.ElemList
- } else {
- exprs = []syntax.Expr{expr}
- }
-
- res := make([]*Node, len(exprs))
- seen := make(map[*types.Sym]bool, len(exprs))
-
- newOrErr := false
- for i, expr := range exprs {
- p.setlineno(expr)
- res[i] = nblank
-
- name, ok := expr.(*syntax.Name)
- if !ok {
- p.yyerrorpos(expr.Pos(), "non-name %v on left side of :=", p.expr(expr))
- newOrErr = true
- continue
- }
-
- sym := p.name(name)
- if sym.IsBlank() {
- continue
- }
-
- if seen[sym] {
- p.yyerrorpos(expr.Pos(), "%v repeated on left side of :=", sym)
- newOrErr = true
- continue
- }
- seen[sym] = true
-
- if sym.Block == types.Block {
- res[i] = oldname(sym)
- continue
- }
-
- newOrErr = true
- n := newname(sym)
- declare(n, dclcontext)
- n.Name.Defn = defn
- defn.Ninit.Append(nod(ODCL, n, nil))
- res[i] = n
- }
-
- if !newOrErr {
- yyerrorl(defn.Pos, "no new variables on left side of :=")
- }
- return res
-}
-
-func (p *noder) blockStmt(stmt *syntax.BlockStmt) []*Node {
- p.openScope(stmt.Pos())
- nodes := p.stmts(stmt.List)
- p.closeScope(stmt.Rbrace)
- return nodes
-}
-
-func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
- p.openScope(stmt.Pos())
- n := p.nod(stmt, OIF, nil, nil)
- if stmt.Init != nil {
- n.Ninit.Set1(p.stmt(stmt.Init))
- }
- if stmt.Cond != nil {
- n.Left = p.expr(stmt.Cond)
- }
- n.Nbody.Set(p.blockStmt(stmt.Then))
- if stmt.Else != nil {
- e := p.stmt(stmt.Else)
- if e.Op == OBLOCK && e.Ninit.Len() == 0 {
- n.Rlist.Set(e.List.Slice())
- } else {
- n.Rlist.Set1(e)
- }
- }
- p.closeAnotherScope()
- return n
-}
-
-func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
- p.openScope(stmt.Pos())
- var n *Node
- if r, ok := stmt.Init.(*syntax.RangeClause); ok {
- if stmt.Cond != nil || stmt.Post != nil {
- panic("unexpected RangeClause")
- }
-
- n = p.nod(r, ORANGE, nil, p.expr(r.X))
- if r.Lhs != nil {
- n.List.Set(p.assignList(r.Lhs, n, r.Def))
- }
- } else {
- n = p.nod(stmt, OFOR, nil, nil)
- if stmt.Init != nil {
- n.Ninit.Set1(p.stmt(stmt.Init))
- }
- if stmt.Cond != nil {
- n.Left = p.expr(stmt.Cond)
- }
- if stmt.Post != nil {
- n.Right = p.stmt(stmt.Post)
- }
- }
- n.Nbody.Set(p.blockStmt(stmt.Body))
- p.closeAnotherScope()
- return n
-}
-
-func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
- p.openScope(stmt.Pos())
- n := p.nod(stmt, OSWITCH, nil, nil)
- if stmt.Init != nil {
- n.Ninit.Set1(p.stmt(stmt.Init))
- }
- if stmt.Tag != nil {
- n.Left = p.expr(stmt.Tag)
- }
-
- tswitch := n.Left
- if tswitch != nil && tswitch.Op != OTYPESW {
- tswitch = nil
- }
- n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace))
-
- p.closeScope(stmt.Rbrace)
- return n
-}
-
-func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node, rbrace syntax.Pos) []*Node {
- nodes := make([]*Node, 0, len(clauses))
- for i, clause := range clauses {
- p.setlineno(clause)
- if i > 0 {
- p.closeScope(clause.Pos())
- }
- p.openScope(clause.Pos())
-
- n := p.nod(clause, OCASE, nil, nil)
- if clause.Cases != nil {
- n.List.Set(p.exprList(clause.Cases))
- }
- if tswitch != nil && tswitch.Left != nil {
- nn := newname(tswitch.Left.Sym)
- declare(nn, dclcontext)
- n.Rlist.Set1(nn)
- // keep track of the instances for reporting unused
- nn.Name.Defn = tswitch
- }
-
- // Trim trailing empty statements. We omit them from
- // the Node AST anyway, and it's easier to identify
- // out-of-place fallthrough statements without them.
- body := clause.Body
- for len(body) > 0 {
- if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok {
- break
- }
- body = body[:len(body)-1]
- }
-
- n.Nbody.Set(p.stmtsFall(body, true))
- if l := n.Nbody.Len(); l > 0 && n.Nbody.Index(l-1).Op == OFALL {
- if tswitch != nil {
- yyerror("cannot fallthrough in type switch")
- }
- if i+1 == len(clauses) {
- yyerror("cannot fallthrough final case in switch")
- }
- }
-
- nodes = append(nodes, n)
- }
- if len(clauses) > 0 {
- p.closeScope(rbrace)
- }
- return nodes
-}
-
-func (p *noder) selectStmt(stmt *syntax.SelectStmt) *Node {
- n := p.nod(stmt, OSELECT, nil, nil)
- n.List.Set(p.commClauses(stmt.Body, stmt.Rbrace))
- return n
-}
-
-func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*Node {
- nodes := make([]*Node, 0, len(clauses))
- for i, clause := range clauses {
- p.setlineno(clause)
- if i > 0 {
- p.closeScope(clause.Pos())
- }
- p.openScope(clause.Pos())
-
- n := p.nod(clause, OCASE, nil, nil)
- if clause.Comm != nil {
- n.List.Set1(p.stmt(clause.Comm))
- }
- n.Nbody.Set(p.stmts(clause.Body))
- nodes = append(nodes, n)
- }
- if len(clauses) > 0 {
- p.closeScope(rbrace)
- }
- return nodes
-}
-
-func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) *Node {
- lhs := p.nodSym(label, OLABEL, nil, p.name(label.Label))
-
- var ls *Node
- if label.Stmt != nil { // TODO(mdempsky): Should always be present.
- ls = p.stmtFall(label.Stmt, fallOK)
- }
-
- lhs.Name.Defn = ls
- l := []*Node{lhs}
- if ls != nil {
- if ls.Op == OBLOCK && ls.Ninit.Len() == 0 {
- l = append(l, ls.List.Slice()...)
- } else {
- l = append(l, ls)
- }
- }
- return liststmt(l)
-}
-
-var unOps = [...]Op{
- syntax.Recv: ORECV,
- syntax.Mul: ODEREF,
- syntax.And: OADDR,
-
- syntax.Not: ONOT,
- syntax.Xor: OBITNOT,
- syntax.Add: OPLUS,
- syntax.Sub: ONEG,
-}
-
-func (p *noder) unOp(op syntax.Operator) Op {
- if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 {
- panic("invalid Operator")
- }
- return unOps[op]
-}
-
-var binOps = [...]Op{
- syntax.OrOr: OOROR,
- syntax.AndAnd: OANDAND,
-
- syntax.Eql: OEQ,
- syntax.Neq: ONE,
- syntax.Lss: OLT,
- syntax.Leq: OLE,
- syntax.Gtr: OGT,
- syntax.Geq: OGE,
-
- syntax.Add: OADD,
- syntax.Sub: OSUB,
- syntax.Or: OOR,
- syntax.Xor: OXOR,
-
- syntax.Mul: OMUL,
- syntax.Div: ODIV,
- syntax.Rem: OMOD,
- syntax.And: OAND,
- syntax.AndNot: OANDNOT,
- syntax.Shl: OLSH,
- syntax.Shr: ORSH,
-}
-
-func (p *noder) binOp(op syntax.Operator) Op {
- if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 {
- panic("invalid Operator")
- }
- return binOps[op]
-}
-
-// checkLangCompat reports an error if the representation of a numeric
-// literal is not compatible with the current language version.
-func checkLangCompat(lit *syntax.BasicLit) {
- s := lit.Value
- if len(s) <= 2 || langSupported(1, 13, localpkg) {
- return
- }
- // len(s) > 2
- if strings.Contains(s, "_") {
- yyerrorv("go1.13", "underscores in numeric literals")
- return
- }
- if s[0] != '0' {
- return
- }
- base := s[1]
- if base == 'b' || base == 'B' {
- yyerrorv("go1.13", "binary literals")
- return
- }
- if base == 'o' || base == 'O' {
- yyerrorv("go1.13", "0o/0O-style octal literals")
- return
- }
- if lit.Kind != syntax.IntLit && (base == 'x' || base == 'X') {
- yyerrorv("go1.13", "hexadecimal floating-point literals")
- }
-}
-
-func (p *noder) basicLit(lit *syntax.BasicLit) Val {
- // We don't use the errors of the conversion routines to determine
- // if a literal string is valid because the conversion routines may
- // accept a wider syntax than the language permits. Rely on lit.Bad
- // instead.
- switch s := lit.Value; lit.Kind {
- case syntax.IntLit:
- checkLangCompat(lit)
- x := new(Mpint)
- if !lit.Bad {
- x.SetString(s)
- }
- return Val{U: x}
-
- case syntax.FloatLit:
- checkLangCompat(lit)
- x := newMpflt()
- if !lit.Bad {
- x.SetString(s)
- }
- return Val{U: x}
-
- case syntax.ImagLit:
- checkLangCompat(lit)
- x := newMpcmplx()
- if !lit.Bad {
- x.Imag.SetString(strings.TrimSuffix(s, "i"))
- }
- return Val{U: x}
-
- case syntax.RuneLit:
- x := new(Mpint)
- x.Rune = true
- if !lit.Bad {
- u, _ := strconv.Unquote(s)
- var r rune
- if len(u) == 1 {
- r = rune(u[0])
- } else {
- r, _ = utf8.DecodeRuneInString(u)
- }
- x.SetInt64(int64(r))
- }
- return Val{U: x}
-
- case syntax.StringLit:
- var x string
- if !lit.Bad {
- if len(s) > 0 && s[0] == '`' {
- // strip carriage returns from raw string
- s = strings.Replace(s, "\r", "", -1)
- }
- x, _ = strconv.Unquote(s)
- }
- return Val{U: x}
-
- default:
- panic("unhandled BasicLit kind")
- }
-}
-
-func (p *noder) name(name *syntax.Name) *types.Sym {
- return lookup(name.Value)
-}
-
-func (p *noder) mkname(name *syntax.Name) *Node {
- // TODO(mdempsky): Set line number?
- return mkname(p.name(name))
-}
-
-func (p *noder) wrapname(n syntax.Node, x *Node) *Node {
- // These nodes do not carry line numbers.
- // Introduce a wrapper node to give them the correct line.
- switch x.Op {
- case OTYPE, OLITERAL:
- if x.Sym == nil {
- break
- }
- fallthrough
- case ONAME, ONONAME, OPACK:
- x = p.nod(n, OPAREN, x, nil)
- x.SetImplicit(true)
- }
- return x
-}
-
-func (p *noder) nod(orig syntax.Node, op Op, left, right *Node) *Node {
- return nodl(p.pos(orig), op, left, right)
-}
-
-func (p *noder) nodSym(orig syntax.Node, op Op, left *Node, sym *types.Sym) *Node {
- n := nodSym(op, left, sym)
- n.Pos = p.pos(orig)
- return n
-}
-
-func (p *noder) pos(n syntax.Node) src.XPos {
- // TODO(gri): orig.Pos() should always be known - fix package syntax
- xpos := lineno
- if pos := n.Pos(); pos.IsKnown() {
- xpos = p.makeXPos(pos)
- }
- return xpos
-}
-
-func (p *noder) setlineno(n syntax.Node) {
- if n != nil {
- lineno = p.pos(n)
- }
-}
-
-// error is called concurrently if files are parsed concurrently.
-func (p *noder) error(err error) {
- p.err <- err.(syntax.Error)
-}
-
-// pragmas that are allowed in the std lib, but don't have
-// a syntax.Pragma value (see lex.go) associated with them.
-var allowedStdPragmas = map[string]bool{
- "go:cgo_export_static": true,
- "go:cgo_export_dynamic": true,
- "go:cgo_import_static": true,
- "go:cgo_import_dynamic": true,
- "go:cgo_ldflag": true,
- "go:cgo_dynamic_linker": true,
- "go:embed": true,
- "go:generate": true,
-}
-
-// *Pragma is the value stored in a syntax.Pragma during parsing.
-type Pragma struct {
- Flag PragmaFlag // collected bits
- Pos []PragmaPos // position of each individual flag
- Embeds []PragmaEmbed
-}
-
-type PragmaPos struct {
- Flag PragmaFlag
- Pos syntax.Pos
-}
-
-type PragmaEmbed struct {
- Pos syntax.Pos
- Patterns []string
-}
-
-func (p *noder) checkUnused(pragma *Pragma) {
- for _, pos := range pragma.Pos {
- if pos.Flag&pragma.Flag != 0 {
- p.yyerrorpos(pos.Pos, "misplaced compiler directive")
- }
- }
- if len(pragma.Embeds) > 0 {
- for _, e := range pragma.Embeds {
- p.yyerrorpos(e.Pos, "misplaced go:embed directive")
- }
- }
-}
-
-func (p *noder) checkUnusedDuringParse(pragma *Pragma) {
- for _, pos := range pragma.Pos {
- if pos.Flag&pragma.Flag != 0 {
- p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"})
- }
- }
- if len(pragma.Embeds) > 0 {
- for _, e := range pragma.Embeds {
- p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"})
- }
- }
-}
-
-// pragma is called concurrently if files are parsed concurrently.
-func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma {
- pragma, _ := old.(*Pragma)
- if pragma == nil {
- pragma = new(Pragma)
- }
-
- if text == "" {
- // unused pragma; only called with old != nil.
- p.checkUnusedDuringParse(pragma)
- return nil
- }
-
- if strings.HasPrefix(text, "line ") {
- // line directives are handled by syntax package
- panic("unreachable")
- }
-
- if !blankLine {
- // directive must be on line by itself
- p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"})
- return pragma
- }
-
- switch {
- case strings.HasPrefix(text, "go:linkname "):
- f := strings.Fields(text)
- if !(2 <= len(f) && len(f) <= 3) {
- p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"})
- break
- }
- // The second argument is optional. If omitted, we use
- // the default object symbol name for this and
- // linkname only serves to mark this symbol as
- // something that may be referenced via the object
- // symbol name from another package.
- var target string
- if len(f) == 3 {
- target = f[2]
- }
- p.linknames = append(p.linknames, linkname{pos, f[1], target})
-
- case text == "go:embed", strings.HasPrefix(text, "go:embed "):
- args, err := parseGoEmbed(text[len("go:embed"):])
- if err != nil {
- p.error(syntax.Error{Pos: pos, Msg: err.Error()})
- }
- if len(args) == 0 {
- p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."})
- break
- }
- pragma.Embeds = append(pragma.Embeds, PragmaEmbed{pos, args})
-
- case strings.HasPrefix(text, "go:cgo_import_dynamic "):
- // This is permitted for general use because Solaris
- // code relies on it in golang.org/x/sys/unix and others.
- fields := pragmaFields(text)
- if len(fields) >= 4 {
- lib := strings.Trim(fields[3], `"`)
- if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) {
- p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)})
- }
- p.pragcgo(pos, text)
- pragma.Flag |= pragmaFlag("go:cgo_import_dynamic")
- break
- }
- fallthrough
- case strings.HasPrefix(text, "go:cgo_"):
- // For security, we disallow //go:cgo_* directives other
- // than cgo_import_dynamic outside cgo-generated files.
- // Exception: they are allowed in the standard library, for runtime and syscall.
- if !isCgoGeneratedFile(pos) && !compiling_std {
- p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)})
- }
- p.pragcgo(pos, text)
- fallthrough // because of //go:cgo_unsafe_args
- default:
- verb := text
- if i := strings.Index(text, " "); i >= 0 {
- verb = verb[:i]
- }
- flag := pragmaFlag(verb)
- const runtimePragmas = Systemstack | Nowritebarrier | Nowritebarrierrec | Yeswritebarrierrec
- if !compiling_runtime && flag&runtimePragmas != 0 {
- p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)})
- }
- if flag == 0 && !allowedStdPragmas[verb] && compiling_std {
- p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)})
- }
- pragma.Flag |= flag
- pragma.Pos = append(pragma.Pos, PragmaPos{flag, pos})
- }
-
- return pragma
-}
-
-// isCgoGeneratedFile reports whether pos is in a file
-// generated by cgo, which is to say a file with name
-// beginning with "_cgo_". Such files are allowed to
-// contain cgo directives, and for security reasons
-// (primarily misuse of linker flags), other files are not.
-// See golang.org/issue/23672.
-func isCgoGeneratedFile(pos syntax.Pos) bool {
- return strings.HasPrefix(filepath.Base(filepath.Clean(fileh(pos.Base().Filename()))), "_cgo_")
-}
-
-// safeArg reports whether arg is a "safe" command-line argument,
-// meaning that when it appears in a command-line, it probably
-// doesn't have some special meaning other than its own name.
-// This is copied from SafeArg in cmd/go/internal/load/pkg.go.
-func safeArg(name string) bool {
- if name == "" {
- return false
- }
- c := name[0]
- return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
-}
-
-func mkname(sym *types.Sym) *Node {
- n := oldname(sym)
- if n.Name != nil && n.Name.Pack != nil {
- n.Name.Pack.Name.SetUsed(true)
- }
- return n
-}
-
-// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
-// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
-// go/build/read.go also processes these strings and contains similar logic.
-func parseGoEmbed(args string) ([]string, error) {
- var list []string
- for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
- var path string
- Switch:
- switch args[0] {
- default:
- i := len(args)
- for j, c := range args {
- if unicode.IsSpace(c) {
- i = j
- break
- }
- }
- path = args[:i]
- args = args[i:]
-
- case '`':
- i := strings.Index(args[1:], "`")
- if i < 0 {
- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
- }
- path = args[1 : 1+i]
- args = args[1+i+1:]
-
- case '"':
- i := 1
- for ; i < len(args); i++ {
- if args[i] == '\\' {
- i++
- continue
- }
- if args[i] == '"' {
- q, err := strconv.Unquote(args[:i+1])
- if err != nil {
- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
- }
- path = q
- args = args[i+1:]
- break Switch
- }
- }
- if i >= len(args) {
- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
- }
- }
-
- if args != "" {
- r, _ := utf8.DecodeRuneInString(args)
- if !unicode.IsSpace(r) {
- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
- }
- }
- list = append(list, path)
- }
- return list, nil
-}
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 32aa7c5bb1ae0..0472af74419e5 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -5,28 +5,21 @@
package gc
import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/staticdata"
+ "cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/internal/archive"
"cmd/internal/bio"
"cmd/internal/obj"
"cmd/internal/objabi"
- "cmd/internal/src"
- "crypto/sha256"
"encoding/json"
"fmt"
- "io"
- "io/ioutil"
- "os"
- "sort"
- "strconv"
)
-// architecture-independent object file output
-const ArhdrSize = 60
-
-func formathdr(arhdr []byte, name string, size int64) {
- copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
-}
-
// These modes say which kind of object file to generate.
// The default use of the toolchain is to set both bits,
// generating a combined compiler+linker object, one that
@@ -46,20 +39,20 @@ const (
)
func dumpobj() {
- if linkobj == "" {
- dumpobj1(outfile, modeCompilerObj|modeLinkerObj)
+ if base.Flag.LinkObj == "" {
+ dumpobj1(base.Flag.LowerO, modeCompilerObj|modeLinkerObj)
return
}
- dumpobj1(outfile, modeCompilerObj)
- dumpobj1(linkobj, modeLinkerObj)
+ dumpobj1(base.Flag.LowerO, modeCompilerObj)
+ dumpobj1(base.Flag.LinkObj, modeLinkerObj)
}
func dumpobj1(outfile string, mode int) {
bout, err := bio.Create(outfile)
if err != nil {
- flusherrors()
+ base.FlushErrors()
fmt.Printf("can't create %s: %v\n", outfile, err)
- errorexit()
+ base.ErrorExit()
}
defer bout.Close()
bout.WriteString("!\n")
@@ -78,17 +71,17 @@ func dumpobj1(outfile string, mode int) {
func printObjHeader(bout *bio.Writer) {
fmt.Fprintf(bout, "go object %s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring())
- if buildid != "" {
- fmt.Fprintf(bout, "build id %q\n", buildid)
+ if base.Flag.BuildID != "" {
+ fmt.Fprintf(bout, "build id %q\n", base.Flag.BuildID)
}
- if localpkg.Name == "main" {
+ if types.LocalPkg.Name == "main" {
fmt.Fprintf(bout, "main\n")
}
fmt.Fprintf(bout, "\n") // header ends with blank line
}
func startArchiveEntry(bout *bio.Writer) int64 {
- var arhdr [ArhdrSize]byte
+ var arhdr [archive.HeaderSize]byte
bout.Write(arhdr[:])
return bout.Offset()
}
@@ -99,10 +92,10 @@ func finishArchiveEntry(bout *bio.Writer, start int64, name string) {
if size&1 != 0 {
bout.WriteByte(0)
}
- bout.MustSeek(start-ArhdrSize, 0)
+ bout.MustSeek(start-archive.HeaderSize, 0)
- var arhdr [ArhdrSize]byte
- formathdr(arhdr[:], name, size)
+ var arhdr [archive.HeaderSize]byte
+ archive.FormatHeader(arhdr[:], name, size)
bout.Write(arhdr[:])
bout.Flush()
bout.MustSeek(start+size+(size&1), 0)
@@ -114,22 +107,21 @@ 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()
+ numExterns := len(typecheck.Target.Externs)
+ numDecls := len(typecheck.Target.Decls)
+
+ dumpglobls(typecheck.Target.Externs)
+ reflectdata.CollectPTabs()
+ numExports := len(typecheck.Target.Exports)
+ addsignats(typecheck.Target.Externs)
+ reflectdata.WriteRuntimeTypes()
+ reflectdata.WriteTabs()
+ numPTabs, numITabs := reflectdata.CountTabs()
+ reflectdata.WriteImportStrings()
+ reflectdata.WriteBasicTypes()
dumpembeds()
- // Calls to dumpsignats can generate functions,
+ // Calls to WriteRuntimeTypes can generate functions,
// like method wrappers and hash and equality routines.
// Compile any generated functions, process any new resulting types, repeat.
// This can't loop forever, because there is no way to generate an infinite
@@ -137,169 +129,108 @@ func dumpdata() {
// 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 {
- for i := xtops; i < len(xtop); i++ {
- n := xtop[i]
- if n.Op == ODCLFUNC {
- funccompile(n)
+ for i := numDecls; i < len(typecheck.Target.Decls); i++ {
+ if n, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
+ enqueueFunc(n)
}
}
- xtops = len(xtop)
+ numDecls = len(typecheck.Target.Decls)
compileFunctions()
- dumpsignats()
- if xtops == len(xtop) {
+ reflectdata.WriteRuntimeTypes()
+ if numDecls == len(typecheck.Target.Decls) {
break
}
}
// Dump extra globals.
- tmp := externdcl
+ dumpglobls(typecheck.Target.Externs[numExterns:])
- if externdcl != nil {
- externdcl = externdcl[externs:]
- }
- dumpglobls()
- externdcl = tmp
-
- if zerosize > 0 {
- zero := mappkg.Lookup("zero")
- ggloblsym(zero.Linksym(), int32(zerosize), obj.DUPOK|obj.RODATA)
+ if reflectdata.ZeroSize > 0 {
+ zero := base.PkgLinksym("go.map", "zero", obj.ABI0)
+ objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA)
}
+ staticdata.WriteFuncSyms()
addGCLocals()
- if exportlistLen != len(exportlist) {
- Fatalf("exportlist changed after compile functions loop")
+ if numExports != len(typecheck.Target.Exports) {
+ base.Fatalf("Target.Exports changed after compile functions loop")
}
- if ptabsLen != len(ptabs) {
- Fatalf("ptabs changed after compile functions loop")
+ newNumPTabs, newNumITabs := reflectdata.CountTabs()
+ if newNumPTabs != numPTabs {
+ base.Fatalf("ptabs changed after compile functions loop")
}
- if itabsLen != len(itabs) {
- Fatalf("itabs changed after compile functions loop")
+ if newNumITabs != numITabs {
+ base.Fatalf("itabs changed after compile functions loop")
}
}
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
- if len(pragcgobuf) != 0 {
+ if len(typecheck.Target.CgoPragmas) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
- if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
- Fatalf("serializing pragcgobuf: %v", err)
+ if err := json.NewEncoder(bout).Encode(typecheck.Target.CgoPragmas); err != nil {
+ base.Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
}
fmt.Fprintf(bout, "\n!\n")
- obj.WriteObjFile(Ctxt, bout)
-}
-
-func addptabs() {
- if !Ctxt.Flag_dynlink || localpkg.Name != "main" {
- return
- }
- for _, exportn := range exportlist {
- s := exportn.Sym
- n := asNode(s.Def)
- if n == nil {
- continue
- }
- if n.Op != ONAME {
- continue
- }
- if !types.IsExported(s.Name) {
- continue
- }
- if s.Pkg.Name != "main" {
- continue
- }
- if n.Type.Etype == TFUNC && n.Class() == PFUNC {
- // function
- ptabs = append(ptabs, ptabEntry{s: s, t: asNode(s.Def).Type})
- } else {
- // variable
- ptabs = append(ptabs, ptabEntry{s: s, t: types.NewPtr(asNode(s.Def).Type)})
- }
- }
+ obj.WriteObjFile(base.Ctxt, bout)
}
-func dumpGlobal(n *Node) {
- if n.Type == nil {
- Fatalf("external %v nil type\n", n)
+func dumpGlobal(n *ir.Name) {
+ if n.Type() == nil {
+ base.Fatalf("external %v nil type\n", n)
}
- if n.Class() == PFUNC {
+ if n.Class == ir.PFUNC {
return
}
- if n.Sym.Pkg != localpkg {
+ if n.Sym().Pkg != types.LocalPkg {
return
}
- dowidth(n.Type)
+ types.CalcSize(n.Type())
ggloblnod(n)
}
-func dumpGlobalConst(n *Node) {
+func dumpGlobalConst(n ir.Node) {
// only export typed constants
- t := n.Type
+ t := n.Type()
if t == nil {
return
}
- if n.Sym.Pkg != localpkg {
+ if n.Sym().Pkg != types.LocalPkg {
return
}
// only export integer constants for now
- switch t.Etype {
- case TINT8:
- case TINT16:
- case TINT32:
- case TINT64:
- case TINT:
- case TUINT8:
- case TUINT16:
- case TUINT32:
- case TUINT64:
- case TUINT:
- case TUINTPTR:
- // ok
- case TIDEAL:
- if !Isconst(n, CTINT) {
- return
- }
- x := n.Val().U.(*Mpint)
- if x.Cmp(minintval[TINT]) < 0 || x.Cmp(maxintval[TINT]) > 0 {
+ if !t.IsInteger() {
+ return
+ }
+ v := n.Val()
+ if t.IsUntyped() {
+ // Export untyped integers as int (if they fit).
+ t = types.Types[types.TINT]
+ if ir.ConstOverflow(v, t) {
return
}
- // Ideal integers we export as int (if they fit).
- t = types.Types[TINT]
- default:
- return
}
- Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64Val())
+ base.Ctxt.DwarfIntConst(base.Ctxt.Pkgpath, n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v))
}
-func dumpglobls() {
+func dumpglobls(externs []ir.Node) {
// add globals
- for _, n := range externdcl {
- switch n.Op {
- case ONAME:
- dumpGlobal(n)
- case OLITERAL:
+ for _, n := range externs {
+ switch n.Op() {
+ case ir.ONAME:
+ dumpGlobal(n.(*ir.Name))
+ case ir.OLITERAL:
dumpGlobalConst(n)
}
}
-
- sort.Slice(funcsyms, func(i, j int) bool {
- return funcsyms[i].LinksymName() < funcsyms[j].LinksymName()
- })
- for _, s := range funcsyms {
- sf := s.Pkg.Lookup(funcsymname(s)).Linksym()
- dsymptr(sf, 0, s.Linksym(), 0)
- ggloblsym(sf, int32(Widthptr), obj.DUPOK|obj.RODATA)
- }
-
- // Do not reprocess funcsyms on next dumpglobls call.
- funcsyms = nil
}
// addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data.
@@ -307,332 +238,60 @@ func dumpglobls() {
// This is done during the sequential phase after compilation, since
// global symbols can't be declared during parallel compilation.
func addGCLocals() {
- for _, s := range Ctxt.Text {
+ for _, s := range base.Ctxt.Text {
fn := s.Func()
if fn == nil {
continue
}
for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} {
if gcsym != nil && !gcsym.OnList() {
- ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
+ objw.Global(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
}
}
if x := fn.StackObjects; x != nil {
attr := int16(obj.RODATA)
- ggloblsym(x, int32(len(x.P)), attr)
+ objw.Global(x, int32(len(x.P)), attr)
x.Set(obj.AttrStatic, true)
}
if x := fn.OpenCodedDeferInfo; x != nil {
- ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
+ objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
}
}
}
-func duintxx(s *obj.LSym, off int, v uint64, wid int) int {
- if off&(wid-1) != 0 {
- Fatalf("duintxxLSym: misaligned: v=%d wid=%d off=%d", v, wid, off)
+func ggloblnod(nam *ir.Name) {
+ s := nam.Linksym()
+ s.Gotype = reflectdata.TypeLinksym(nam.Type())
+ flags := 0
+ if nam.Readonly() {
+ flags = obj.RODATA
}
- s.WriteInt(Ctxt, int64(off), wid, int64(v))
- return off + wid
-}
-
-func duint8(s *obj.LSym, off int, v uint8) int {
- return duintxx(s, off, uint64(v), 1)
-}
-
-func duint16(s *obj.LSym, off int, v uint16) int {
- return duintxx(s, off, uint64(v), 2)
-}
-
-func duint32(s *obj.LSym, off int, v uint32) int {
- return duintxx(s, off, uint64(v), 4)
-}
-
-func duintptr(s *obj.LSym, off int, v uint64) int {
- return duintxx(s, off, v, Widthptr)
-}
-
-func dbvec(s *obj.LSym, off int, bv bvec) int {
- // Runtime reads the bitmaps as byte arrays. Oblige.
- for j := 0; int32(j) < bv.n; j += 8 {
- word := bv.b[j/32]
- off = duint8(s, off, uint8(word>>(uint(j)%32)))
+ if nam.Type() != nil && !nam.Type().HasPointers() {
+ flags |= obj.NOPTR
}
- return off
-}
-
-const (
- stringSymPrefix = "go.string."
- stringSymPattern = ".gostring.%d.%x"
-)
-
-// stringsym returns a symbol containing the string s.
-// The symbol contains the string data, not a string header.
-func stringsym(pos src.XPos, s string) (data *obj.LSym) {
- var symname string
- if len(s) > 100 {
- // Huge strings are hashed to avoid long names in object files.
- // Indulge in some paranoia by writing the length of s, too,
- // as protection against length extension attacks.
- // Same pattern is known to fileStringSym below.
- h := sha256.New()
- io.WriteString(h, s)
- symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil))
- } else {
- // Small strings get named directly by their contents.
- symname = strconv.Quote(s)
+ base.Ctxt.Globl(s, nam.Type().Width, flags)
+ if nam.LibfuzzerExtraCounter() {
+ s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
}
-
- symdata := Ctxt.Lookup(stringSymPrefix + symname)
- if !symdata.OnList() {
- off := dstringdata(symdata, 0, s, pos, "string")
- ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
- symdata.Set(obj.AttrContentAddressable, true)
+ if nam.Sym().Linkname != "" {
+ // Make sure linkname'd symbol is non-package. When a symbol is
+ // both imported and linkname'd, s.Pkg may not set to "_" in
+ // types.Sym.Linksym because LSym already exists. Set it here.
+ s.Pkg = "_"
}
-
- return symdata
}
-// fileStringSym returns a symbol for the contents and the size of file.
-// If readonly is true, the symbol shares storage with any literal string
-// or other file with the same content and is placed in a read-only section.
-// If readonly is false, the symbol is a read-write copy separate from any other,
-// for use as the backing store of a []byte.
-// The content hash of file is copied into hash. (If hash is nil, nothing is copied.)
-// The returned symbol contains the data itself, not a string header.
-func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) {
- f, err := os.Open(file)
- if err != nil {
- return nil, 0, err
+func dumpembeds() {
+ for _, v := range typecheck.Target.Embeds {
+ staticdata.WriteEmbed(v)
}
- defer f.Close()
- info, err := f.Stat()
- if err != nil {
- return nil, 0, err
- }
- if !info.Mode().IsRegular() {
- return nil, 0, fmt.Errorf("not a regular file")
- }
- size := info.Size()
- if size <= 1*1024 {
- data, err := ioutil.ReadAll(f)
- if err != nil {
- return nil, 0, err
- }
- if int64(len(data)) != size {
- return nil, 0, fmt.Errorf("file changed between reads")
- }
- var sym *obj.LSym
- if readonly {
- sym = stringsym(pos, string(data))
- } else {
- sym = slicedata(pos, string(data)).Sym.Linksym()
- }
- if len(hash) > 0 {
- sum := sha256.Sum256(data)
- copy(hash, sum[:])
- }
- return sym, size, nil
- }
- if size > 2e9 {
- // ggloblsym takes an int32,
- // and probably the rest of the toolchain
- // can't handle such big symbols either.
- // See golang.org/issue/9862.
- return nil, 0, fmt.Errorf("file too large")
- }
-
- // File is too big to read and keep in memory.
- // Compute hash if needed for read-only content hashing or if the caller wants it.
- var sum []byte
- if readonly || len(hash) > 0 {
- h := sha256.New()
- n, err := io.Copy(h, f)
- if err != nil {
- return nil, 0, err
- }
- if n != size {
- return nil, 0, fmt.Errorf("file changed between reads")
- }
- sum = h.Sum(nil)
- copy(hash, sum)
- }
-
- var symdata *obj.LSym
- if readonly {
- symname := fmt.Sprintf(stringSymPattern, size, sum)
- symdata = Ctxt.Lookup(stringSymPrefix + symname)
- if !symdata.OnList() {
- info := symdata.NewFileInfo()
- info.Name = file
- info.Size = size
- ggloblsym(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL)
- // Note: AttrContentAddressable cannot be set here,
- // because the content-addressable-handling code
- // does not know about file symbols.
- }
- } else {
- // Emit a zero-length data symbol
- // and then fix up length and content to use file.
- symdata = slicedata(pos, "").Sym.Linksym()
- symdata.Size = size
- symdata.Type = objabi.SNOPTRDATA
- info := symdata.NewFileInfo()
- info.Name = file
- info.Size = size
- }
-
- return symdata, size, nil
-}
-
-var slicedataGen int
-
-func slicedata(pos src.XPos, s string) *Node {
- slicedataGen++
- symname := fmt.Sprintf(".gobytes.%d", slicedataGen)
- sym := localpkg.Lookup(symname)
- symnode := newname(sym)
- sym.Def = asTypesNode(symnode)
-
- lsym := sym.Linksym()
- off := dstringdata(lsym, 0, s, pos, "slice")
- ggloblsym(lsym, int32(off), obj.NOPTR|obj.LOCAL)
-
- return symnode
}
-func slicebytes(nam *Node, s string) {
- if nam.Op != ONAME {
- Fatalf("slicebytes %v", nam)
- }
- slicesym(nam, slicedata(nam.Pos, s), int64(len(s)))
-}
-
-func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
- // Objects that are too large will cause the data section to overflow right away,
- // causing a cryptic error message by the linker. Check for oversize objects here
- // and provide a useful error message instead.
- if int64(len(t)) > 2e9 {
- yyerrorl(pos, "%v with length %v is too big", what, len(t))
- return 0
- }
-
- s.WriteString(Ctxt, int64(off), len(t), t)
- return off + len(t)
-}
-
-func dsymptr(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
- off = int(Rnd(int64(off), int64(Widthptr)))
- s.WriteAddr(Ctxt, int64(off), Widthptr, x, int64(xoff))
- off += Widthptr
- return off
-}
-
-func dsymptrOff(s *obj.LSym, off int, x *obj.LSym) int {
- s.WriteOff(Ctxt, int64(off), x, 0)
- off += 4
- return off
-}
-
-func dsymptrWeakOff(s *obj.LSym, off int, x *obj.LSym) int {
- s.WriteWeakOff(Ctxt, int64(off), x, 0)
- off += 4
- return off
-}
-
-// slicesym writes a static slice symbol {&arr, lencap, lencap} to n.
-// arr must be an ONAME. slicesym does not modify n.
-func slicesym(n, arr *Node, lencap int64) {
- s := n.Sym.Linksym()
- base := n.Xoffset
- if arr.Op != ONAME {
- Fatalf("slicesym non-name arr %v", arr)
- }
- s.WriteAddr(Ctxt, base, Widthptr, arr.Sym.Linksym(), arr.Xoffset)
- s.WriteInt(Ctxt, base+sliceLenOffset, Widthptr, lencap)
- s.WriteInt(Ctxt, base+sliceCapOffset, Widthptr, lencap)
-}
-
-// addrsym writes the static address of a to n. a must be an ONAME.
-// Neither n nor a is modified.
-func addrsym(n, a *Node) {
- if n.Op != ONAME {
- Fatalf("addrsym n op %v", n.Op)
- }
- if n.Sym == nil {
- Fatalf("addrsym nil n sym")
- }
- if a.Op != ONAME {
- Fatalf("addrsym a op %v", a.Op)
- }
- s := n.Sym.Linksym()
- s.WriteAddr(Ctxt, n.Xoffset, Widthptr, a.Sym.Linksym(), a.Xoffset)
-}
-
-// pfuncsym writes the static address of f to n. f must be a global function.
-// Neither n nor f is modified.
-func pfuncsym(n, f *Node) {
- if n.Op != ONAME {
- Fatalf("pfuncsym n op %v", n.Op)
- }
- if n.Sym == nil {
- Fatalf("pfuncsym nil n sym")
- }
- if f.Class() != PFUNC {
- Fatalf("pfuncsym class not PFUNC %d", f.Class())
- }
- s := n.Sym.Linksym()
- s.WriteAddr(Ctxt, n.Xoffset, Widthptr, funcsym(f.Sym).Linksym(), f.Xoffset)
-}
-
-// litsym writes the static literal c to n.
-// Neither n nor c is modified.
-func litsym(n, c *Node, wid int) {
- if n.Op != ONAME {
- Fatalf("litsym n op %v", n.Op)
- }
- if c.Op != OLITERAL {
- Fatalf("litsym c op %v", c.Op)
- }
- if n.Sym == nil {
- Fatalf("litsym nil n sym")
- }
- s := n.Sym.Linksym()
- switch u := c.Val().U.(type) {
- case bool:
- i := int64(obj.Bool2int(u))
- s.WriteInt(Ctxt, n.Xoffset, wid, i)
-
- case *Mpint:
- s.WriteInt(Ctxt, n.Xoffset, wid, u.Int64())
-
- case *Mpflt:
- f := u.Float64()
- switch n.Type.Etype {
- case TFLOAT32:
- s.WriteFloat32(Ctxt, n.Xoffset, float32(f))
- case TFLOAT64:
- s.WriteFloat64(Ctxt, n.Xoffset, f)
- }
-
- case *Mpcplx:
- r := u.Real.Float64()
- i := u.Imag.Float64()
- switch n.Type.Etype {
- case TCOMPLEX64:
- s.WriteFloat32(Ctxt, n.Xoffset, float32(r))
- s.WriteFloat32(Ctxt, n.Xoffset+4, float32(i))
- case TCOMPLEX128:
- s.WriteFloat64(Ctxt, n.Xoffset, r)
- s.WriteFloat64(Ctxt, n.Xoffset+8, i)
+func addsignats(dcls []ir.Node) {
+ // copy types from dcl list to signatset
+ for _, n := range dcls {
+ if n.Op() == ir.OTYPE {
+ reflectdata.NeedRuntimeType(n.Type())
}
-
- case string:
- symdata := stringsym(n.Pos, u)
- s.WriteAddr(Ctxt, n.Xoffset, Widthptr, symdata, 0)
- s.WriteInt(Ctxt, n.Xoffset+int64(Widthptr), Widthptr, int64(len(u)))
-
- default:
- Fatalf("litsym unhandled OLITERAL %v", c)
}
}
diff --git a/src/cmd/compile/internal/gc/op_string.go b/src/cmd/compile/internal/gc/op_string.go
deleted file mode 100644
index 41d588309c01e..0000000000000
--- a/src/cmd/compile/internal/gc/op_string.go
+++ /dev/null
@@ -1,175 +0,0 @@
-// Code generated by "stringer -type=Op -trimprefix=O"; DO NOT EDIT.
-
-package gc
-
-import "strconv"
-
-func _() {
- // An "invalid array index" compiler error signifies that the constant values have changed.
- // Re-run the stringer command to generate them again.
- var x [1]struct{}
- _ = x[OXXX-0]
- _ = x[ONAME-1]
- _ = x[ONONAME-2]
- _ = x[OTYPE-3]
- _ = x[OPACK-4]
- _ = x[OLITERAL-5]
- _ = x[OADD-6]
- _ = x[OSUB-7]
- _ = x[OOR-8]
- _ = x[OXOR-9]
- _ = x[OADDSTR-10]
- _ = x[OADDR-11]
- _ = x[OANDAND-12]
- _ = x[OAPPEND-13]
- _ = x[OBYTES2STR-14]
- _ = x[OBYTES2STRTMP-15]
- _ = x[ORUNES2STR-16]
- _ = x[OSTR2BYTES-17]
- _ = x[OSTR2BYTESTMP-18]
- _ = x[OSTR2RUNES-19]
- _ = x[OAS-20]
- _ = x[OAS2-21]
- _ = x[OAS2DOTTYPE-22]
- _ = x[OAS2FUNC-23]
- _ = x[OAS2MAPR-24]
- _ = x[OAS2RECV-25]
- _ = x[OASOP-26]
- _ = x[OCALL-27]
- _ = x[OCALLFUNC-28]
- _ = x[OCALLMETH-29]
- _ = x[OCALLINTER-30]
- _ = x[OCALLPART-31]
- _ = x[OCAP-32]
- _ = x[OCLOSE-33]
- _ = x[OCLOSURE-34]
- _ = x[OCOMPLIT-35]
- _ = x[OMAPLIT-36]
- _ = x[OSTRUCTLIT-37]
- _ = x[OARRAYLIT-38]
- _ = x[OSLICELIT-39]
- _ = x[OPTRLIT-40]
- _ = x[OCONV-41]
- _ = x[OCONVIFACE-42]
- _ = x[OCONVNOP-43]
- _ = x[OCOPY-44]
- _ = x[ODCL-45]
- _ = x[ODCLFUNC-46]
- _ = x[ODCLFIELD-47]
- _ = x[ODCLCONST-48]
- _ = x[ODCLTYPE-49]
- _ = x[ODELETE-50]
- _ = x[ODOT-51]
- _ = x[ODOTPTR-52]
- _ = x[ODOTMETH-53]
- _ = x[ODOTINTER-54]
- _ = x[OXDOT-55]
- _ = x[ODOTTYPE-56]
- _ = x[ODOTTYPE2-57]
- _ = x[OEQ-58]
- _ = x[ONE-59]
- _ = x[OLT-60]
- _ = x[OLE-61]
- _ = x[OGE-62]
- _ = x[OGT-63]
- _ = x[ODEREF-64]
- _ = x[OINDEX-65]
- _ = x[OINDEXMAP-66]
- _ = x[OKEY-67]
- _ = x[OSTRUCTKEY-68]
- _ = x[OLEN-69]
- _ = x[OMAKE-70]
- _ = x[OMAKECHAN-71]
- _ = x[OMAKEMAP-72]
- _ = x[OMAKESLICE-73]
- _ = x[OMAKESLICECOPY-74]
- _ = x[OMUL-75]
- _ = x[ODIV-76]
- _ = x[OMOD-77]
- _ = x[OLSH-78]
- _ = x[ORSH-79]
- _ = x[OAND-80]
- _ = x[OANDNOT-81]
- _ = x[ONEW-82]
- _ = x[ONEWOBJ-83]
- _ = x[ONOT-84]
- _ = x[OBITNOT-85]
- _ = x[OPLUS-86]
- _ = x[ONEG-87]
- _ = x[OOROR-88]
- _ = x[OPANIC-89]
- _ = x[OPRINT-90]
- _ = x[OPRINTN-91]
- _ = x[OPAREN-92]
- _ = x[OSEND-93]
- _ = x[OSLICE-94]
- _ = x[OSLICEARR-95]
- _ = x[OSLICESTR-96]
- _ = x[OSLICE3-97]
- _ = x[OSLICE3ARR-98]
- _ = x[OSLICEHEADER-99]
- _ = x[ORECOVER-100]
- _ = x[ORECV-101]
- _ = x[ORUNESTR-102]
- _ = x[OSELRECV-103]
- _ = x[OSELRECV2-104]
- _ = x[OIOTA-105]
- _ = x[OREAL-106]
- _ = x[OIMAG-107]
- _ = x[OCOMPLEX-108]
- _ = x[OALIGNOF-109]
- _ = x[OOFFSETOF-110]
- _ = x[OSIZEOF-111]
- _ = x[OBLOCK-112]
- _ = x[OBREAK-113]
- _ = x[OCASE-114]
- _ = x[OCONTINUE-115]
- _ = x[ODEFER-116]
- _ = x[OEMPTY-117]
- _ = x[OFALL-118]
- _ = x[OFOR-119]
- _ = x[OFORUNTIL-120]
- _ = x[OGOTO-121]
- _ = x[OIF-122]
- _ = x[OLABEL-123]
- _ = x[OGO-124]
- _ = x[ORANGE-125]
- _ = x[ORETURN-126]
- _ = x[OSELECT-127]
- _ = x[OSWITCH-128]
- _ = x[OTYPESW-129]
- _ = x[OTCHAN-130]
- _ = x[OTMAP-131]
- _ = x[OTSTRUCT-132]
- _ = x[OTINTER-133]
- _ = x[OTFUNC-134]
- _ = x[OTARRAY-135]
- _ = x[ODDD-136]
- _ = x[OINLCALL-137]
- _ = x[OEFACE-138]
- _ = x[OITAB-139]
- _ = x[OIDATA-140]
- _ = x[OSPTR-141]
- _ = x[OCLOSUREVAR-142]
- _ = x[OCFUNC-143]
- _ = x[OCHECKNIL-144]
- _ = x[OVARDEF-145]
- _ = x[OVARKILL-146]
- _ = x[OVARLIVE-147]
- _ = x[ORESULT-148]
- _ = x[OINLMARK-149]
- _ = x[ORETJMP-150]
- _ = x[OGETG-151]
- _ = x[OEND-152]
-}
-
-const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND"
-
-var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 136, 143, 150, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 449, 452, 455, 458, 461, 464, 467, 473, 476, 482, 485, 491, 495, 498, 502, 507, 512, 518, 523, 527, 532, 540, 548, 554, 563, 574, 581, 585, 592, 599, 607, 611, 615, 619, 626, 633, 641, 647, 652, 657, 661, 669, 674, 679, 683, 686, 694, 698, 700, 705, 707, 712, 718, 724, 730, 736, 741, 745, 752, 758, 763, 769, 772, 779, 784, 788, 793, 797, 807, 812, 820, 826, 833, 840, 846, 853, 859, 863, 866}
-
-func (i Op) String() string {
- if i >= Op(len(_Op_index)-1) {
- return "Op(" + strconv.FormatInt(int64(i), 10) + ")"
- }
- return _Op_name[_Op_index[i]:_Op_index[i+1]]
-}
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
deleted file mode 100644
index 30e1535c09c43..0000000000000
--- a/src/cmd/compile/internal/gc/order.go
+++ /dev/null
@@ -1,1441 +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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/src"
- "fmt"
-)
-
-// Rewrite tree to use separate statements to enforce
-// order of evaluation. Makes walk easier, because it
-// can (after this runs) reorder at will within an expression.
-//
-// Rewrite m[k] op= r into m[k] = m[k] op r if op is / or %.
-//
-// Introduce temporaries as needed by runtime routines.
-// For example, the map runtime routines take the map key
-// by reference, so make sure all map keys are addressable
-// by copying them to temporaries as needed.
-// The same is true for channel operations.
-//
-// Arrange that map index expressions only appear in direct
-// assignments x = m[k] or m[k] = x, never in larger expressions.
-//
-// Arrange that receive expressions only appear in direct assignments
-// x = <-c or as standalone statements <-c, never in larger expressions.
-
-// TODO(rsc): The temporary introduction during multiple assignments
-// should be moved into this file, so that the temporaries can be cleaned
-// and so that conversions implicit in the OAS2FUNC and OAS2RECV
-// nodes can be made explicit and then have their temporaries cleaned.
-
-// TODO(rsc): Goto and multilevel break/continue can jump over
-// inserted VARKILL annotations. Work out a way to handle these.
-// The current implementation is safe, in that it will execute correctly.
-// But it won't reuse temporaries as aggressively as it might, and
-// it can result in unnecessary zeroing of those variables in the function
-// prologue.
-
-// Order holds state during the ordering process.
-type Order struct {
- out []*Node // list of generated statements
- temp []*Node // stack of temporary variables
- free map[string][]*Node // free list of unused temporaries, by type.LongString().
-}
-
-// Order rewrites fn.Nbody to apply the ordering constraints
-// described in the comment at the top of the file.
-func order(fn *Node) {
- if Debug.W > 1 {
- s := fmt.Sprintf("\nbefore order %v", fn.Func.Nname.Sym)
- dumplist(s, fn.Nbody)
- }
-
- orderBlock(&fn.Nbody, map[string][]*Node{})
-}
-
-// newTemp allocates a new temporary with the given type,
-// pushes it onto the temp stack, and returns it.
-// If clear is true, newTemp emits code to zero the temporary.
-func (o *Order) newTemp(t *types.Type, clear bool) *Node {
- var v *Node
- // Note: LongString is close to the type equality we want,
- // but not exactly. We still need to double-check with types.Identical.
- key := t.LongString()
- a := o.free[key]
- for i, n := range a {
- if types.Identical(t, n.Type) {
- v = a[i]
- a[i] = a[len(a)-1]
- a = a[:len(a)-1]
- o.free[key] = a
- break
- }
- }
- if v == nil {
- v = temp(t)
- }
- if clear {
- a := nod(OAS, v, nil)
- a = typecheck(a, ctxStmt)
- o.out = append(o.out, a)
- }
-
- o.temp = append(o.temp, v)
- return v
-}
-
-// copyExpr behaves like newTemp but also emits
-// code to initialize the temporary to the value n.
-//
-// The clear argument is provided for use when the evaluation
-// of tmp = n turns into a function call that is passed a pointer
-// to the temporary as the output space. If the call blocks before
-// tmp has been written, the garbage collector will still treat the
-// temporary as live, so we must zero it before entering that call.
-// Today, this only happens for channel receive operations.
-// (The other candidate would be map access, but map access
-// returns a pointer to the result data instead of taking a pointer
-// to be filled in.)
-func (o *Order) copyExpr(n *Node, t *types.Type, clear bool) *Node {
- v := o.newTemp(t, clear)
- a := nod(OAS, v, n)
- a = typecheck(a, ctxStmt)
- o.out = append(o.out, a)
- return v
-}
-
-// cheapExpr returns a cheap version of n.
-// The definition of cheap is that n is a variable or constant.
-// If not, cheapExpr allocates a new tmp, emits tmp = n,
-// and then returns tmp.
-func (o *Order) cheapExpr(n *Node) *Node {
- if n == nil {
- return nil
- }
-
- switch n.Op {
- case ONAME, OLITERAL:
- return n
- case OLEN, OCAP:
- l := o.cheapExpr(n.Left)
- if l == n.Left {
- return n
- }
- a := n.sepcopy()
- a.Left = l
- return typecheck(a, ctxExpr)
- }
-
- return o.copyExpr(n, n.Type, false)
-}
-
-// safeExpr returns a safe version of n.
-// The definition of safe is that n can appear multiple times
-// without violating the semantics of the original program,
-// and that assigning to the safe version has the same effect
-// as assigning to the original n.
-//
-// The intended use is to apply to x when rewriting x += y into x = x + y.
-func (o *Order) safeExpr(n *Node) *Node {
- switch n.Op {
- case ONAME, OLITERAL:
- return n
-
- case ODOT, OLEN, OCAP:
- l := o.safeExpr(n.Left)
- if l == n.Left {
- return n
- }
- a := n.sepcopy()
- a.Left = l
- return typecheck(a, ctxExpr)
-
- case ODOTPTR, ODEREF:
- l := o.cheapExpr(n.Left)
- if l == n.Left {
- return n
- }
- a := n.sepcopy()
- a.Left = l
- return typecheck(a, ctxExpr)
-
- case OINDEX, OINDEXMAP:
- var l *Node
- if n.Left.Type.IsArray() {
- l = o.safeExpr(n.Left)
- } else {
- l = o.cheapExpr(n.Left)
- }
- r := o.cheapExpr(n.Right)
- if l == n.Left && r == n.Right {
- return n
- }
- a := n.sepcopy()
- a.Left = l
- a.Right = r
- return typecheck(a, ctxExpr)
-
- default:
- Fatalf("order.safeExpr %v", n.Op)
- return nil // not reached
- }
-}
-
-// isaddrokay reports whether it is okay to pass n's address to runtime routines.
-// Taking the address of a variable makes the liveness and optimization analyses
-// lose track of where the variable's lifetime ends. To avoid hurting the analyses
-// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
-// because we emit explicit VARKILL instructions marking the end of those
-// temporaries' lifetimes.
-func isaddrokay(n *Node) bool {
- return islvalue(n) && (n.Op != ONAME || n.Class() == PEXTERN || n.IsAutoTmp())
-}
-
-// addrTemp ensures that n is okay to pass by address to runtime routines.
-// If the original argument n is not okay, addrTemp creates a tmp, emits
-// tmp = n, and then returns tmp.
-// The result of addrTemp MUST be assigned back to n, e.g.
-// n.Left = o.addrTemp(n.Left)
-func (o *Order) addrTemp(n *Node) *Node {
- if consttype(n) != CTxxx {
- // TODO: expand this to all static composite literal nodes?
- n = defaultlit(n, nil)
- dowidth(n.Type)
- vstat := readonlystaticname(n.Type)
- var s InitSchedule
- s.staticassign(vstat, n)
- if s.out != nil {
- Fatalf("staticassign of const generated code: %+v", n)
- }
- vstat = typecheck(vstat, ctxExpr)
- return vstat
- }
- if isaddrokay(n) {
- return n
- }
- return o.copyExpr(n, n.Type, false)
-}
-
-// mapKeyTemp prepares n to be a key in a map runtime call and returns n.
-// It should only be used for map runtime calls which have *_fast* versions.
-func (o *Order) mapKeyTemp(t *types.Type, n *Node) *Node {
- // Most map calls need to take the address of the key.
- // Exception: map*_fast* calls. See golang.org/issue/19015.
- if mapfast(t) == mapslow {
- return o.addrTemp(n)
- }
- return n
-}
-
-// mapKeyReplaceStrConv replaces OBYTES2STR by OBYTES2STRTMP
-// in n to avoid string allocations for keys in map lookups.
-// Returns a bool that signals if a modification was made.
-//
-// For:
-// x = m[string(k)]
-// x = m[T1{... Tn{..., string(k), ...}]
-// where k is []byte, T1 to Tn is a nesting of struct and array literals,
-// the allocation of backing bytes for the string can be avoided
-// by reusing the []byte backing array. These are special cases
-// for avoiding allocations when converting byte slices to strings.
-// It would be nice to handle these generally, but because
-// []byte keys are not allowed in maps, the use of string(k)
-// comes up in important cases in practice. See issue 3512.
-func mapKeyReplaceStrConv(n *Node) bool {
- var replaced bool
- switch n.Op {
- case OBYTES2STR:
- n.Op = OBYTES2STRTMP
- replaced = true
- case OSTRUCTLIT:
- for _, elem := range n.List.Slice() {
- if mapKeyReplaceStrConv(elem.Left) {
- replaced = true
- }
- }
- case OARRAYLIT:
- for _, elem := range n.List.Slice() {
- if elem.Op == OKEY {
- elem = elem.Right
- }
- if mapKeyReplaceStrConv(elem) {
- replaced = true
- }
- }
- }
- return replaced
-}
-
-type ordermarker int
-
-// markTemp returns the top of the temporary variable stack.
-func (o *Order) markTemp() ordermarker {
- return ordermarker(len(o.temp))
-}
-
-// popTemp pops temporaries off the stack until reaching the mark,
-// which must have been returned by markTemp.
-func (o *Order) popTemp(mark ordermarker) {
- for _, n := range o.temp[mark:] {
- key := n.Type.LongString()
- o.free[key] = append(o.free[key], n)
- }
- o.temp = o.temp[:mark]
-}
-
-// 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]
- kill := nod(OVARKILL, n, nil)
- kill = typecheck(kill, ctxStmt)
- out = append(out, kill)
- }
- return out
-}
-
-// cleanTemp emits VARKILL instructions for each temporary above the
-// mark on the temporary stack and removes them from the stack.
-func (o *Order) cleanTemp(top ordermarker) {
- o.out = append(o.out, o.cleanTempNoPop(top)...)
- o.popTemp(top)
-}
-
-// stmtList orders each of the statements in the list.
-func (o *Order) stmtList(l Nodes) {
- s := l.Slice()
- for i := range s {
- orderMakeSliceCopy(s[i:])
- o.stmt(s[i])
- }
-}
-
-// orderMakeSliceCopy matches the pattern:
-// m = OMAKESLICE([]T, x); OCOPY(m, s)
-// and rewrites it to:
-// m = OMAKESLICECOPY([]T, x, s); nil
-func orderMakeSliceCopy(s []*Node) {
- if Debug.N != 0 || instrumenting {
- return
- }
-
- if len(s) < 2 {
- return
- }
-
- asn := s[0]
- copyn := s[1]
-
- if asn == nil || asn.Op != OAS {
- return
- }
- if asn.Left.Op != ONAME {
- return
- }
- if asn.Left.isBlank() {
- return
- }
- maken := asn.Right
- if maken == nil || maken.Op != OMAKESLICE {
- return
- }
- if maken.Esc == EscNone {
- return
- }
- if maken.Left == nil || maken.Right != nil {
- return
- }
- if copyn.Op != OCOPY {
- return
- }
- if copyn.Left.Op != ONAME {
- return
- }
- if asn.Left.Sym != copyn.Left.Sym {
- return
- }
- if copyn.Right.Op != ONAME {
- return
- }
-
- if copyn.Left.Sym == copyn.Right.Sym {
- return
- }
-
- maken.Op = OMAKESLICECOPY
- maken.Right = copyn.Right
- // Set bounded when m = OMAKESLICE([]T, len(s)); OCOPY(m, s)
- maken.SetBounded(maken.Left.Op == OLEN && samesafeexpr(maken.Left.Left, copyn.Right))
-
- maken = typecheck(maken, ctxExpr)
-
- s[1] = nil // remove separate copy call
-
- return
-}
-
-// edge inserts coverage instrumentation for libfuzzer.
-func (o *Order) edge() {
- if Debug_libfuzzer == 0 {
- return
- }
-
- // Create a new uint8 counter to be allocated in section
- // __libfuzzer_extra_counters.
- counter := staticname(types.Types[TUINT8])
- counter.Name.SetLibfuzzerExtraCounter(true)
-
- // counter += 1
- incr := nod(OASOP, counter, nodintconst(1))
- incr.SetSubOp(OADD)
- incr = typecheck(incr, ctxStmt)
-
- o.out = append(o.out, incr)
-}
-
-// orderBlock orders the block of statements in n into a new slice,
-// and then replaces the old slice in n with the new slice.
-// free is a map that can be used to obtain temporary variables by type.
-func orderBlock(n *Nodes, free map[string][]*Node) {
- var order Order
- order.free = free
- mark := order.markTemp()
- order.edge()
- order.stmtList(*n)
- order.cleanTemp(mark)
- n.Set(order.out)
-}
-
-// exprInPlace orders the side effects in *np and
-// leaves them as the init list of the final *np.
-// The result of exprInPlace MUST be assigned back to n, e.g.
-// n.Left = o.exprInPlace(n.Left)
-func (o *Order) exprInPlace(n *Node) *Node {
- var order Order
- order.free = o.free
- n = order.expr(n, nil)
- n = addinit(n, order.out)
-
- // insert new temporaries from order
- // at head of outer list.
- o.temp = append(o.temp, order.temp...)
- return n
-}
-
-// orderStmtInPlace orders the side effects of the single statement *np
-// and replaces it with the resulting statement list.
-// The result of orderStmtInPlace MUST be assigned back to n, e.g.
-// n.Left = orderStmtInPlace(n.Left)
-// free is a map that can be used to obtain temporary variables by type.
-func orderStmtInPlace(n *Node, free map[string][]*Node) *Node {
- var order Order
- order.free = free
- mark := order.markTemp()
- order.stmt(n)
- order.cleanTemp(mark)
- return liststmt(order.out)
-}
-
-// init moves n's init list to o.out.
-func (o *Order) init(n *Node) {
- if n.mayBeShared() {
- // For concurrency safety, don't mutate potentially shared nodes.
- // First, ensure that no work is required here.
- if n.Ninit.Len() > 0 {
- Fatalf("order.init shared node with ninit")
- }
- return
- }
- o.stmtList(n.Ninit)
- n.Ninit.Set(nil)
-}
-
-// call orders the call expression n.
-// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
-func (o *Order) call(n *Node) {
- if n.Ninit.Len() > 0 {
- // Caller should have already called o.init(n).
- Fatalf("%v with unexpected ninit", n.Op)
- }
-
- // Builtin functions.
- if n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER {
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
- o.exprList(n.List)
- return
- }
-
- fixVariadicCall(n)
- n.Left = o.expr(n.Left, nil)
- o.exprList(n.List)
-
- if n.Op == OCALLINTER {
- return
- }
- keepAlive := func(arg *Node) {
- // If the argument is really a pointer being converted to uintptr,
- // arrange for the pointer to be kept alive until the call returns,
- // by copying it into a temp and marking that temp
- // 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)
- arg.Left = x
- x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable
- n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt))
- }
- }
-
- // Check for "unsafe-uintptr" tag provided by escape analysis.
- for i, param := range n.Left.Type.Params().FieldSlice() {
- if param.Note == unsafeUintptrTag || param.Note == uintptrEscapesTag {
- if arg := n.List.Index(i); arg.Op == OSLICELIT {
- for _, elt := range arg.List.Slice() {
- keepAlive(elt)
- }
- } else {
- keepAlive(arg)
- }
- }
- }
-}
-
-// mapAssign appends n to o.out, introducing temporaries
-// to make sure that all map assignments have the form m[k] = x.
-// (Note: expr has already been called on n, so we know k is addressable.)
-//
-// If n is the multiple assignment form ..., m[k], ... = ..., x, ..., the rewrite is
-// t1 = m
-// t2 = k
-// ...., t3, ... = ..., x, ...
-// t1[t2] = t3
-//
-// The temporaries t1, t2 are needed in case the ... being assigned
-// contain m or k. They are usually unnecessary, but in the unnecessary
-// cases they are also typically registerizable, so not much harm done.
-// And this only applies to the multiple-assignment form.
-// We could do a more precise analysis if needed, like in walk.go.
-func (o *Order) mapAssign(n *Node) {
- switch n.Op {
- default:
- Fatalf("order.mapAssign %v", n.Op)
-
- case OAS, OASOP:
- if n.Left.Op == OINDEXMAP {
- // Make sure we evaluate the RHS before starting the map insert.
- // We need to make sure the RHS won't panic. See issue 22881.
- if n.Right.Op == OAPPEND {
- s := n.Right.List.Slice()[1:]
- for i, n := range s {
- s[i] = o.cheapExpr(n)
- }
- } else {
- n.Right = o.cheapExpr(n.Right)
- }
- }
- o.out = append(o.out, n)
-
- case OAS2, OAS2DOTTYPE, OAS2MAPR, OAS2FUNC:
- var post []*Node
- for i, m := range n.List.Slice() {
- switch {
- case m.Op == OINDEXMAP:
- if !m.Left.IsAutoTmp() {
- m.Left = o.copyExpr(m.Left, m.Left.Type, false)
- }
- if !m.Right.IsAutoTmp() {
- m.Right = o.copyExpr(m.Right, m.Right.Type, false)
- }
- fallthrough
- case instrumenting && n.Op == OAS2FUNC && !m.isBlank():
- t := o.newTemp(m.Type, false)
- n.List.SetIndex(i, t)
- a := nod(OAS, m, t)
- a = typecheck(a, ctxStmt)
- post = append(post, a)
- }
- }
-
- o.out = append(o.out, n)
- o.out = append(o.out, post...)
- }
-}
-
-// stmt orders the statement n, appending to o.out.
-// Temporaries created during the statement are cleaned
-// up using VARKILL instructions as possible.
-func (o *Order) stmt(n *Node) {
- if n == nil {
- return
- }
-
- lno := setlineno(n)
- o.init(n)
-
- switch n.Op {
- default:
- Fatalf("order.stmt %v", n.Op)
-
- case OVARKILL, OVARLIVE, OINLMARK:
- o.out = append(o.out, n)
-
- case OAS:
- t := o.markTemp()
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, n.Left)
- o.mapAssign(n)
- o.cleanTemp(t)
-
- case OASOP:
- t := o.markTemp()
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
-
- if instrumenting || n.Left.Op == OINDEXMAP && (n.SubOp() == ODIV || n.SubOp() == OMOD) {
- // Rewrite m[k] op= r into m[k] = m[k] op r so
- // that we can ensure that if op panics
- // because r is zero, the panic happens before
- // the map assignment.
-
- n.Left = o.safeExpr(n.Left)
-
- l := treecopy(n.Left, src.NoXPos)
- if l.Op == OINDEXMAP {
- l.SetIndexMapLValue(false)
- }
- l = o.copyExpr(l, n.Left.Type, false)
- n.Right = nod(n.SubOp(), l, n.Right)
- n.Right = typecheck(n.Right, ctxExpr)
- n.Right = o.expr(n.Right, nil)
-
- n.Op = OAS
- n.ResetAux()
- }
-
- o.mapAssign(n)
- o.cleanTemp(t)
-
- case OAS2:
- t := o.markTemp()
- o.exprList(n.List)
- o.exprList(n.Rlist)
- o.mapAssign(n)
- o.cleanTemp(t)
-
- // Special: avoid copy of func call n.Right
- case OAS2FUNC:
- t := o.markTemp()
- o.exprList(n.List)
- o.init(n.Right)
- o.call(n.Right)
- o.as2(n)
- o.cleanTemp(t)
-
- // Special: use temporary variables to hold result,
- // so that runtime can take address of temporary.
- // No temporary for blank assignment.
- //
- // OAS2MAPR: make sure key is addressable if needed,
- // and make sure OINDEXMAP is not copied out.
- case OAS2DOTTYPE, OAS2RECV, OAS2MAPR:
- t := o.markTemp()
- o.exprList(n.List)
-
- switch r := n.Right; r.Op {
- case ODOTTYPE2, ORECV:
- r.Left = o.expr(r.Left, nil)
- case OINDEXMAP:
- r.Left = o.expr(r.Left, nil)
- r.Right = o.expr(r.Right, nil)
- // See similar conversion for OINDEXMAP below.
- _ = mapKeyReplaceStrConv(r.Right)
- r.Right = o.mapKeyTemp(r.Left.Type, r.Right)
- default:
- Fatalf("order.stmt: %v", r.Op)
- }
-
- o.okAs2(n)
- o.cleanTemp(t)
-
- // Special: does not save n onto out.
- case OBLOCK, OEMPTY:
- o.stmtList(n.List)
-
- // Special: n->left is not an expression; save as is.
- case OBREAK,
- OCONTINUE,
- ODCL,
- ODCLCONST,
- ODCLTYPE,
- OFALL,
- OGOTO,
- OLABEL,
- ORETJMP:
- o.out = append(o.out, n)
-
- // Special: handle call arguments.
- case OCALLFUNC, OCALLINTER, OCALLMETH:
- t := o.markTemp()
- o.call(n)
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- case OCLOSE,
- OCOPY,
- OPRINT,
- OPRINTN,
- ORECOVER,
- ORECV:
- t := o.markTemp()
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
- o.exprList(n.List)
- o.exprList(n.Rlist)
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- // Special: order arguments to inner call but not call itself.
- case ODEFER, OGO:
- t := o.markTemp()
- o.init(n.Left)
- o.call(n.Left)
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- case ODELETE:
- t := o.markTemp()
- n.List.SetFirst(o.expr(n.List.First(), nil))
- n.List.SetSecond(o.expr(n.List.Second(), nil))
- n.List.SetSecond(o.mapKeyTemp(n.List.First().Type, n.List.Second()))
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- // Clean temporaries from condition evaluation at
- // beginning of loop body and after for statement.
- case OFOR:
- t := o.markTemp()
- n.Left = o.exprInPlace(n.Left)
- n.Nbody.Prepend(o.cleanTempNoPop(t)...)
- orderBlock(&n.Nbody, o.free)
- n.Right = orderStmtInPlace(n.Right, o.free)
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- // Clean temporaries from condition at
- // beginning of both branches.
- case OIF:
- t := o.markTemp()
- n.Left = o.exprInPlace(n.Left)
- n.Nbody.Prepend(o.cleanTempNoPop(t)...)
- n.Rlist.Prepend(o.cleanTempNoPop(t)...)
- o.popTemp(t)
- orderBlock(&n.Nbody, o.free)
- orderBlock(&n.Rlist, o.free)
- o.out = append(o.out, n)
-
- // Special: argument will be converted to interface using convT2E
- // so make sure it is an addressable temporary.
- case OPANIC:
- t := o.markTemp()
- n.Left = o.expr(n.Left, nil)
- if !n.Left.Type.IsInterface() {
- n.Left = o.addrTemp(n.Left)
- }
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- case ORANGE:
- // n.Right is the expression being ranged over.
- // order it, and then make a copy if we need one.
- // We almost always do, to ensure that we don't
- // see any value changes made during the loop.
- // Usually the copy is cheap (e.g., array pointer,
- // chan, slice, string are all tiny).
- // The exception is ranging over an array value
- // (not a slice, not a pointer to array),
- // which must make a copy to avoid seeing updates made during
- // the range body. Ranging over an array value is uncommon though.
-
- // Mark []byte(str) range expression to reuse string backing storage.
- // It is safe because the storage cannot be mutated.
- if n.Right.Op == OSTR2BYTES {
- n.Right.Op = OSTR2BYTESTMP
- }
-
- t := o.markTemp()
- n.Right = o.expr(n.Right, nil)
-
- orderBody := true
- switch n.Type.Etype {
- default:
- Fatalf("order.stmt range %v", n.Type)
-
- case TARRAY, TSLICE:
- if n.List.Len() < 2 || n.List.Second().isBlank() {
- // for i := range x will only use x once, to compute len(x).
- // No need to copy it.
- break
- }
- fallthrough
-
- case TCHAN, TSTRING:
- // chan, string, slice, array ranges use value multiple times.
- // make copy.
- r := n.Right
-
- if r.Type.IsString() && r.Type != types.Types[TSTRING] {
- r = nod(OCONV, r, nil)
- r.Type = types.Types[TSTRING]
- r = typecheck(r, ctxExpr)
- }
-
- n.Right = o.copyExpr(r, r.Type, false)
-
- case TMAP:
- if isMapClear(n) {
- // Preserve the body of the map clear pattern so it can
- // be detected during walk. The loop body will not be used
- // when optimizing away the range loop to a runtime call.
- orderBody = false
- break
- }
-
- // copy the map value in case it is a map literal.
- // TODO(rsc): Make tmp = literal expressions reuse tmp.
- // For maps tmp is just one word so it hardly matters.
- r := n.Right
- n.Right = o.copyExpr(r, r.Type, false)
-
- // prealloc[n] is the temp for the iterator.
- // hiter contains pointers and needs to be zeroed.
- prealloc[n] = o.newTemp(hiter(n.Type), true)
- }
- o.exprListInPlace(n.List)
- if orderBody {
- orderBlock(&n.Nbody, o.free)
- }
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- case ORETURN:
- o.exprList(n.List)
- o.out = append(o.out, n)
-
- // Special: clean case temporaries in each block entry.
- // Select must enter one of its blocks, so there is no
- // need for a cleaning at the end.
- // Doubly special: evaluation order for select is stricter
- // than ordinary expressions. Even something like p.c
- // has to be hoisted into a temporary, so that it cannot be
- // reordered after the channel evaluation for a different
- // case (if p were nil, then the timing of the fault would
- // give this away).
- case OSELECT:
- t := o.markTemp()
-
- for _, n2 := range n.List.Slice() {
- if n2.Op != OCASE {
- Fatalf("order select case %v", n2.Op)
- }
- r := n2.Left
- setlineno(n2)
-
- // Append any new body prologue to ninit.
- // The next loop will insert ninit into nbody.
- if n2.Ninit.Len() != 0 {
- Fatalf("order select ninit")
- }
- if r == nil {
- continue
- }
- switch r.Op {
- default:
- Dump("select case", r)
- Fatalf("unknown op in select %v", r.Op)
-
- // If this is case x := <-ch or case x, y := <-ch, the case has
- // the ODCL nodes to declare x and y. We want to delay that
- // declaration (and possible allocation) until inside the case body.
- // Delete the ODCL nodes here and recreate them inside the body below.
- case OSELRECV, OSELRECV2:
- if r.Colas() {
- i := 0
- if r.Ninit.Len() != 0 && r.Ninit.First().Op == ODCL && r.Ninit.First().Left == r.Left {
- i++
- }
- if i < r.Ninit.Len() && r.Ninit.Index(i).Op == ODCL && r.List.Len() != 0 && r.Ninit.Index(i).Left == r.List.First() {
- i++
- }
- if i >= r.Ninit.Len() {
- r.Ninit.Set(nil)
- }
- }
-
- if r.Ninit.Len() != 0 {
- dumplist("ninit", r.Ninit)
- Fatalf("ninit on select recv")
- }
-
- // case x = <-c
- // case x, ok = <-c
- // r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c.
- // r->left == N means 'case <-c'.
- // c is always evaluated; x and ok are only evaluated when assigned.
- r.Right.Left = o.expr(r.Right.Left, nil)
-
- if !r.Right.Left.IsAutoTmp() {
- r.Right.Left = o.copyExpr(r.Right.Left, r.Right.Left.Type, false)
- }
-
- // Introduce temporary for receive and move actual copy into case body.
- // avoids problems with target being addressed, as usual.
- // NOTE: If we wanted to be clever, we could arrange for just one
- // temporary per distinct type, sharing the temp among all receives
- // with that temp. Similarly one ok bool could be shared among all
- // the x,ok receives. Not worth doing until there's a clear need.
- if r.Left != nil && r.Left.isBlank() {
- r.Left = nil
- }
- if r.Left != nil {
- // use channel element type for temporary to avoid conversions,
- // such as in case interfacevalue = <-intchan.
- // the conversion happens in the OAS instead.
- tmp1 := r.Left
-
- if r.Colas() {
- tmp2 := nod(ODCL, tmp1, nil)
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.Ninit.Append(tmp2)
- }
-
- r.Left = o.newTemp(r.Right.Left.Type.Elem(), r.Right.Left.Type.Elem().HasPointers())
- tmp2 := nod(OAS, tmp1, r.Left)
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.Ninit.Append(tmp2)
- }
-
- if r.List.Len() != 0 && r.List.First().isBlank() {
- r.List.Set(nil)
- }
- if r.List.Len() != 0 {
- tmp1 := r.List.First()
- if r.Colas() {
- tmp2 := nod(ODCL, tmp1, nil)
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.Ninit.Append(tmp2)
- }
-
- r.List.Set1(o.newTemp(types.Types[TBOOL], false))
- tmp2 := okas(tmp1, r.List.First())
- tmp2 = typecheck(tmp2, ctxStmt)
- n2.Ninit.Append(tmp2)
- }
- orderBlock(&n2.Ninit, o.free)
-
- case OSEND:
- if r.Ninit.Len() != 0 {
- dumplist("ninit", r.Ninit)
- Fatalf("ninit on select send")
- }
-
- // case c <- x
- // r->left is c, r->right is x, both are always evaluated.
- r.Left = o.expr(r.Left, nil)
-
- if !r.Left.IsAutoTmp() {
- r.Left = o.copyExpr(r.Left, r.Left.Type, false)
- }
- r.Right = o.expr(r.Right, nil)
- if !r.Right.IsAutoTmp() {
- r.Right = o.copyExpr(r.Right, r.Right.Type, false)
- }
- }
- }
- // Now that we have accumulated all the temporaries, clean them.
- // Also insert any ninit queued during the previous loop.
- // (The temporary cleaning must follow that ninit work.)
- for _, n3 := range n.List.Slice() {
- orderBlock(&n3.Nbody, o.free)
- n3.Nbody.Prepend(o.cleanTempNoPop(t)...)
-
- // TODO(mdempsky): Is this actually necessary?
- // walkselect appears to walk Ninit.
- n3.Nbody.Prepend(n3.Ninit.Slice()...)
- n3.Ninit.Set(nil)
- }
-
- o.out = append(o.out, n)
- o.popTemp(t)
-
- // Special: value being sent is passed as a pointer; make it addressable.
- case OSEND:
- t := o.markTemp()
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
- if instrumenting {
- // Force copying to the stack so that (chan T)(nil) <- x
- // is still instrumented as a read of x.
- n.Right = o.copyExpr(n.Right, n.Right.Type, false)
- } else {
- n.Right = o.addrTemp(n.Right)
- }
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
- // TODO(rsc): Clean temporaries more aggressively.
- // Note that because walkswitch will rewrite some of the
- // switch into a binary search, this is not as easy as it looks.
- // (If we ran that code here we could invoke order.stmt on
- // the if-else chain instead.)
- // For now just clean all the temporaries at the end.
- // In practice that's fine.
- case OSWITCH:
- if Debug_libfuzzer != 0 && !hasDefaultCase(n) {
- // Add empty "default:" case for instrumentation.
- n.List.Append(nod(OCASE, nil, nil))
- }
-
- t := o.markTemp()
- n.Left = o.expr(n.Left, nil)
- for _, ncas := range n.List.Slice() {
- if ncas.Op != OCASE {
- Fatalf("order switch case %v", ncas.Op)
- }
- o.exprListInPlace(ncas.List)
- orderBlock(&ncas.Nbody, o.free)
- }
-
- o.out = append(o.out, n)
- o.cleanTemp(t)
- }
-
- lineno = lno
-}
-
-func hasDefaultCase(n *Node) bool {
- for _, ncas := range n.List.Slice() {
- if ncas.Op != OCASE {
- Fatalf("expected case, found %v", ncas.Op)
- }
- if ncas.List.Len() == 0 {
- return true
- }
- }
- return false
-}
-
-// exprList orders the expression list l into o.
-func (o *Order) exprList(l Nodes) {
- s := l.Slice()
- for i := range s {
- s[i] = o.expr(s[i], nil)
- }
-}
-
-// exprListInPlace orders the expression list l but saves
-// the side effects on the individual expression ninit lists.
-func (o *Order) exprListInPlace(l Nodes) {
- s := l.Slice()
- for i := range s {
- s[i] = o.exprInPlace(s[i])
- }
-}
-
-// prealloc[x] records the allocation to use for x.
-var prealloc = map[*Node]*Node{}
-
-// expr orders a single expression, appending side
-// effects to o.out as needed.
-// If this is part of an assignment lhs = *np, lhs is given.
-// Otherwise lhs == nil. (When lhs != nil it may be possible
-// to avoid copying the result of the expression to a temporary.)
-// The result of expr MUST be assigned back to n, e.g.
-// n.Left = o.expr(n.Left, lhs)
-func (o *Order) expr(n, lhs *Node) *Node {
- if n == nil {
- return n
- }
-
- lno := setlineno(n)
- o.init(n)
-
- switch n.Op {
- default:
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
- o.exprList(n.List)
- o.exprList(n.Rlist)
-
- // Addition of strings turns into a function call.
- // Allocate a temporary to hold the strings.
- // Fewer than 5 strings use direct runtime helpers.
- case OADDSTR:
- o.exprList(n.List)
-
- if n.List.Len() > 5 {
- t := types.NewArray(types.Types[TSTRING], int64(n.List.Len()))
- prealloc[n] = o.newTemp(t, false)
- }
-
- // Mark string(byteSlice) arguments to reuse byteSlice backing
- // buffer during conversion. String concatenation does not
- // memorize the strings for later use, so it is safe.
- // However, we can do it only if there is at least one non-empty string literal.
- // Otherwise if all other arguments are empty strings,
- // concatstrings will return the reference to the temp string
- // to the caller.
- hasbyte := false
-
- haslit := false
- for _, n1 := range n.List.Slice() {
- hasbyte = hasbyte || n1.Op == OBYTES2STR
- haslit = haslit || n1.Op == OLITERAL && len(n1.StringVal()) != 0
- }
-
- if haslit && hasbyte {
- for _, n2 := range n.List.Slice() {
- if n2.Op == OBYTES2STR {
- n2.Op = OBYTES2STRTMP
- }
- }
- }
-
- case OINDEXMAP:
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
- needCopy := false
-
- if !n.IndexMapLValue() {
- // Enforce that any []byte slices we are not copying
- // can not be changed before the map index by forcing
- // the map index to happen immediately following the
- // conversions. See copyExpr a few lines below.
- needCopy = mapKeyReplaceStrConv(n.Right)
-
- if instrumenting {
- // Race detector needs the copy so it can
- // call treecopy on the result.
- needCopy = true
- }
- }
-
- // key must be addressable
- n.Right = o.mapKeyTemp(n.Left.Type, n.Right)
- if needCopy {
- n = o.copyExpr(n, n.Type, false)
- }
-
- // concrete type (not interface) argument might need an addressable
- // temporary to pass to the runtime conversion routine.
- case OCONVIFACE:
- n.Left = o.expr(n.Left, nil)
- if n.Left.Type.IsInterface() {
- break
- }
- if _, needsaddr := convFuncName(n.Left.Type, n.Type); needsaddr || isStaticCompositeLiteral(n.Left) {
- // Need a temp if we need to pass the address to the conversion function.
- // We also process static composite literal node here, making a named static global
- // whose address we can put directly in an interface (see OCONVIFACE case in walk).
- n.Left = o.addrTemp(n.Left)
- }
-
- case OCONVNOP:
- if n.Type.IsKind(TUNSAFEPTR) && n.Left.Type.IsKind(TUINTPTR) && (n.Left.Op == OCALLFUNC || n.Left.Op == OCALLINTER || n.Left.Op == OCALLMETH) {
- // When reordering unsafe.Pointer(f()) into a separate
- // statement, the conversion and function call must stay
- // together. See golang.org/issue/15329.
- o.init(n.Left)
- o.call(n.Left)
- if lhs == nil || lhs.Op != ONAME || instrumenting {
- n = o.copyExpr(n, n.Type, false)
- }
- } else {
- n.Left = o.expr(n.Left, nil)
- }
-
- case OANDAND, OOROR:
- // ... = LHS && RHS
- //
- // var r bool
- // r = LHS
- // if r { // or !r, for OROR
- // r = RHS
- // }
- // ... = r
-
- r := o.newTemp(n.Type, false)
-
- // Evaluate left-hand side.
- lhs := o.expr(n.Left, nil)
- o.out = append(o.out, typecheck(nod(OAS, r, lhs), ctxStmt))
-
- // Evaluate right-hand side, save generated code.
- saveout := o.out
- o.out = nil
- t := o.markTemp()
- o.edge()
- rhs := o.expr(n.Right, nil)
- o.out = append(o.out, typecheck(nod(OAS, r, rhs), ctxStmt))
- o.cleanTemp(t)
- gen := o.out
- o.out = saveout
-
- // If left-hand side doesn't cause a short-circuit, issue right-hand side.
- nif := nod(OIF, r, nil)
- if n.Op == OANDAND {
- nif.Nbody.Set(gen)
- } else {
- nif.Rlist.Set(gen)
- }
- o.out = append(o.out, nif)
- n = r
-
- case OCALLFUNC,
- OCALLINTER,
- OCALLMETH,
- OCAP,
- OCOMPLEX,
- OCOPY,
- OIMAG,
- OLEN,
- OMAKECHAN,
- OMAKEMAP,
- OMAKESLICE,
- OMAKESLICECOPY,
- ONEW,
- OREAL,
- ORECOVER,
- OSTR2BYTES,
- OSTR2BYTESTMP,
- OSTR2RUNES:
-
- if isRuneCount(n) {
- // len([]rune(s)) is rewritten to runtime.countrunes(s) later.
- n.Left.Left = o.expr(n.Left.Left, nil)
- } else {
- o.call(n)
- }
-
- if lhs == nil || lhs.Op != ONAME || instrumenting {
- n = o.copyExpr(n, n.Type, false)
- }
-
- case OAPPEND:
- // Check for append(x, make([]T, y)...) .
- if isAppendOfMake(n) {
- n.List.SetFirst(o.expr(n.List.First(), nil)) // order x
- n.List.Second().Left = o.expr(n.List.Second().Left, nil) // order y
- } else {
- o.exprList(n.List)
- }
-
- if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.First()) {
- n = o.copyExpr(n, n.Type, false)
- }
-
- case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
- n.Left = o.expr(n.Left, nil)
- low, high, max := n.SliceBounds()
- low = o.expr(low, nil)
- low = o.cheapExpr(low)
- high = o.expr(high, nil)
- high = o.cheapExpr(high)
- max = o.expr(max, nil)
- max = o.cheapExpr(max)
- n.SetSliceBounds(low, high, max)
- if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
- n = o.copyExpr(n, n.Type, false)
- }
-
- case OCLOSURE:
- if n.Transient() && n.Func.Closure.Func.Cvars.Len() > 0 {
- prealloc[n] = o.newTemp(closureType(n), false)
- }
-
- case OSLICELIT, OCALLPART:
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
- o.exprList(n.List)
- o.exprList(n.Rlist)
- if n.Transient() {
- var t *types.Type
- switch n.Op {
- case OSLICELIT:
- t = types.NewArray(n.Type.Elem(), n.Right.Int64Val())
- case OCALLPART:
- t = partialCallType(n)
- }
- prealloc[n] = o.newTemp(t, false)
- }
-
- case ODOTTYPE, ODOTTYPE2:
- n.Left = o.expr(n.Left, nil)
- if !isdirectiface(n.Type) || instrumenting {
- n = o.copyExpr(n, n.Type, true)
- }
-
- case ORECV:
- n.Left = o.expr(n.Left, nil)
- n = o.copyExpr(n, n.Type, true)
-
- case OEQ, ONE, OLT, OLE, OGT, OGE:
- n.Left = o.expr(n.Left, nil)
- n.Right = o.expr(n.Right, nil)
-
- t := n.Left.Type
- switch {
- case t.IsString():
- // Mark string(byteSlice) arguments to reuse byteSlice backing
- // buffer during conversion. String comparison does not
- // memorize the strings for later use, so it is safe.
- if n.Left.Op == OBYTES2STR {
- n.Left.Op = OBYTES2STRTMP
- }
- if n.Right.Op == OBYTES2STR {
- n.Right.Op = OBYTES2STRTMP
- }
-
- case t.IsStruct() || t.IsArray():
- // for complex comparisons, we need both args to be
- // addressable so we can pass them to the runtime.
- n.Left = o.addrTemp(n.Left)
- n.Right = o.addrTemp(n.Right)
- }
- case OMAPLIT:
- // Order map by converting:
- // map[int]int{
- // a(): b(),
- // c(): d(),
- // e(): f(),
- // }
- // to
- // m := map[int]int{}
- // m[a()] = b()
- // m[c()] = d()
- // m[e()] = f()
- // Then order the result.
- // Without this special case, order would otherwise compute all
- // the keys and values before storing any of them to the map.
- // See issue 26552.
- entries := n.List.Slice()
- statics := entries[:0]
- var dynamics []*Node
- for _, r := range entries {
- if r.Op != OKEY {
- Fatalf("OMAPLIT entry not OKEY: %v\n", r)
- }
-
- if !isStaticCompositeLiteral(r.Left) || !isStaticCompositeLiteral(r.Right) {
- dynamics = append(dynamics, r)
- continue
- }
-
- // Recursively ordering some static entries can change them to dynamic;
- // e.g., OCONVIFACE nodes. See #31777.
- r = o.expr(r, nil)
- if !isStaticCompositeLiteral(r.Left) || !isStaticCompositeLiteral(r.Right) {
- dynamics = append(dynamics, r)
- continue
- }
-
- statics = append(statics, r)
- }
- n.List.Set(statics)
-
- if len(dynamics) == 0 {
- break
- }
-
- // Emit the creation of the map (with all its static entries).
- m := o.newTemp(n.Type, false)
- as := nod(OAS, m, n)
- typecheck(as, ctxStmt)
- o.stmt(as)
- n = m
-
- // Emit eval+insert of dynamic entries, one at a time.
- for _, r := range dynamics {
- as := nod(OAS, nod(OINDEX, n, r.Left), r.Right)
- typecheck(as, ctxStmt) // Note: this converts the OINDEX to an OINDEXMAP
- o.stmt(as)
- }
- }
-
- lineno = lno
- return n
-}
-
-// okas creates and returns an assignment of val to ok,
-// including an explicit conversion if necessary.
-func okas(ok, val *Node) *Node {
- if !ok.isBlank() {
- val = conv(val, ok.Type)
- }
- return nod(OAS, ok, val)
-}
-
-// as2 orders OAS2XXXX nodes. It creates temporaries to ensure left-to-right assignment.
-// The caller should order the right-hand side of the assignment before calling order.as2.
-// It rewrites,
-// a, b, a = ...
-// as
-// tmp1, tmp2, tmp3 = ...
-// a, b, a = tmp1, tmp2, tmp3
-// This is necessary to ensure left to right assignment order.
-func (o *Order) as2(n *Node) {
- tmplist := []*Node{}
- left := []*Node{}
- for ni, l := range n.List.Slice() {
- if !l.isBlank() {
- tmp := o.newTemp(l.Type, l.Type.HasPointers())
- n.List.SetIndex(ni, tmp)
- tmplist = append(tmplist, tmp)
- left = append(left, l)
- }
- }
-
- o.out = append(o.out, n)
-
- as := nod(OAS2, nil, nil)
- as.List.Set(left)
- as.Rlist.Set(tmplist)
- as = typecheck(as, ctxStmt)
- o.stmt(as)
-}
-
-// okAs2 orders OAS2XXX with ok.
-// Just like as2, this also adds temporaries to ensure left-to-right assignment.
-func (o *Order) okAs2(n *Node) {
- var tmp1, tmp2 *Node
- if !n.List.First().isBlank() {
- typ := n.Right.Type
- tmp1 = o.newTemp(typ, typ.HasPointers())
- }
-
- if !n.List.Second().isBlank() {
- tmp2 = o.newTemp(types.Types[TBOOL], false)
- }
-
- o.out = append(o.out, n)
-
- if tmp1 != nil {
- r := nod(OAS, n.List.First(), tmp1)
- r = typecheck(r, ctxStmt)
- o.mapAssign(r)
- n.List.SetFirst(tmp1)
- }
- if tmp2 != nil {
- r := okas(n.List.Second(), tmp2)
- r = typecheck(r, ctxStmt)
- o.mapAssign(r)
- n.List.SetSecond(tmp2)
- }
-}
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
deleted file mode 100644
index 353f4b08c9dcb..0000000000000
--- a/src/cmd/compile/internal/gc/pgen.go
+++ /dev/null
@@ -1,798 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
- "cmd/compile/internal/ssa"
- "cmd/compile/internal/types"
- "cmd/internal/dwarf"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
- "cmd/internal/sys"
- "internal/race"
- "math/rand"
- "sort"
- "sync"
- "time"
-)
-
-// "Portable" code generation.
-
-var (
- nBackendWorkers int // number of concurrent backend workers, set by a compiler flag
- compilequeue []*Node // functions waiting to be compiled
-)
-
-func emitptrargsmap(fn *Node) {
- if fn.funcname() == "_" || fn.Func.Nname.Sym.Linkname != "" {
- return
- }
- lsym := Ctxt.Lookup(fn.Func.lsym.Name + ".args_stackmap")
-
- nptr := int(fn.Type.ArgWidth() / int64(Widthptr))
- bv := bvalloc(int32(nptr) * 2)
- nbitmap := 1
- if fn.Type.NumResults() > 0 {
- nbitmap = 2
- }
- off := duint32(lsym, 0, uint32(nbitmap))
- off = duint32(lsym, off, uint32(bv.n))
-
- if fn.IsMethod() {
- onebitwalktype1(fn.Type.Recvs(), 0, bv)
- }
- if fn.Type.NumParams() > 0 {
- onebitwalktype1(fn.Type.Params(), 0, bv)
- }
- off = dbvec(lsym, off, bv)
-
- if fn.Type.NumResults() > 0 {
- onebitwalktype1(fn.Type.Results(), 0, bv)
- off = dbvec(lsym, off, bv)
- }
-
- ggloblsym(lsym, int32(off), obj.RODATA|obj.LOCAL)
-}
-
-// cmpstackvarlt reports whether the stack variable a sorts before b.
-//
-// Sort the list of stack variables. Autos after anything else,
-// within autos, unused after used, within used, things with
-// pointers first, zeroed things first, and then decreasing size.
-// Because autos are laid out in decreasing addresses
-// on the stack, pointers first, zeroed things first and decreasing size
-// really means, in memory, things with pointers needing zeroing at
-// the top of the stack and increasing in size.
-// Non-autos sort on offset.
-func cmpstackvarlt(a, b *Node) bool {
- if (a.Class() == PAUTO) != (b.Class() == PAUTO) {
- return b.Class() == PAUTO
- }
-
- if a.Class() != PAUTO {
- return a.Xoffset < b.Xoffset
- }
-
- if a.Name.Used() != b.Name.Used() {
- return a.Name.Used()
- }
-
- ap := a.Type.HasPointers()
- bp := b.Type.HasPointers()
- if ap != bp {
- return ap
- }
-
- ap = a.Name.Needzero()
- bp = b.Name.Needzero()
- if ap != bp {
- return ap
- }
-
- if a.Type.Width != b.Type.Width {
- return a.Type.Width > b.Type.Width
- }
-
- return a.Sym.Name < b.Sym.Name
-}
-
-// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
-type byStackVar []*Node
-
-func (s byStackVar) Len() int { return len(s) }
-func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
-func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-func (s *ssafn) AllocFrame(f *ssa.Func) {
- s.stksize = 0
- s.stkptrsize = 0
- fn := s.curfn.Func
-
- // Mark the PAUTO's unused.
- for _, ln := range fn.Dcl {
- if ln.Class() == PAUTO {
- ln.Name.SetUsed(false)
- }
- }
-
- for _, l := range f.RegAlloc {
- if ls, ok := l.(ssa.LocalSlot); ok {
- ls.N.(*Node).Name.SetUsed(true)
- }
- }
-
- scratchUsed := false
- for _, b := range f.Blocks {
- for _, v := range b.Values {
- if n, ok := v.Aux.(*Node); ok {
- switch n.Class() {
- case PPARAM, PPARAMOUT:
- // Don't modify nodfp; it is a global.
- if n != nodfp {
- n.Name.SetUsed(true)
- }
- case PAUTO:
- n.Name.SetUsed(true)
- }
- }
- if !scratchUsed {
- scratchUsed = v.Op.UsesScratch()
- }
-
- }
- }
-
- if f.Config.NeedsFpScratch && scratchUsed {
- s.scratchFpMem = tempAt(src.NoXPos, s.curfn, types.Types[TUINT64])
- }
-
- sort.Sort(byStackVar(fn.Dcl))
-
- // Reassign stack offsets of the locals that are used.
- lastHasPtr := false
- for i, n := range fn.Dcl {
- if n.Op != ONAME || n.Class() != PAUTO {
- continue
- }
- if !n.Name.Used() {
- fn.Dcl = fn.Dcl[:i]
- break
- }
-
- dowidth(n.Type)
- w := n.Type.Width
- if w >= thearch.MAXWIDTH || w < 0 {
- Fatalf("bad width")
- }
- if w == 0 && lastHasPtr {
- // Pad between a pointer-containing object and a zero-sized object.
- // This prevents a pointer to the zero-sized object from being interpreted
- // as a pointer to the pointer-containing object (and causing it
- // to be scanned when it shouldn't be). See issue 24993.
- w = 1
- }
- s.stksize += w
- s.stksize = Rnd(s.stksize, int64(n.Type.Align))
- if n.Type.HasPointers() {
- s.stkptrsize = s.stksize
- lastHasPtr = true
- } else {
- lastHasPtr = false
- }
- if thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
- s.stksize = Rnd(s.stksize, int64(Widthptr))
- }
- n.Xoffset = -s.stksize
- }
-
- s.stksize = Rnd(s.stksize, int64(Widthreg))
- s.stkptrsize = Rnd(s.stkptrsize, int64(Widthreg))
-}
-
-func funccompile(fn *Node) {
- if Curfn != nil {
- Fatalf("funccompile %v inside %v", fn.Func.Nname.Sym, Curfn.Func.Nname.Sym)
- }
-
- if fn.Type == nil {
- if nerrors == 0 {
- Fatalf("funccompile missing type")
- }
- return
- }
-
- // assign parameter offsets
- dowidth(fn.Type)
-
- if fn.Nbody.Len() == 0 {
- // Initialize ABI wrappers if necessary.
- fn.Func.initLSym(false)
- emitptrargsmap(fn)
- return
- }
-
- dclcontext = PAUTO
- Curfn = fn
-
- compile(fn)
-
- Curfn = nil
- dclcontext = PEXTERN
-}
-
-func compile(fn *Node) {
- saveerrors()
-
- order(fn)
- if nerrors != 0 {
- return
- }
-
- // 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).
- fn.Func.initLSym(true)
-
- walk(fn)
- if nerrors != 0 {
- return
- }
- if instrumenting {
- instrument(fn)
- }
-
- // From this point, there should be no uses of Curfn. Enforce that.
- Curfn = nil
-
- if fn.funcname() == "_" {
- // 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
- }
-
- // Make sure type syms are declared for all types that might
- // be types of stack objects. We need to do this here
- // because symbols must be allocated before the parallel
- // phase of the compiler.
- for _, n := range fn.Func.Dcl {
- switch n.Class() {
- case PPARAM, PPARAMOUT, PAUTO:
- if livenessShouldTrack(n) && n.Name.Addrtaken() {
- dtypesym(n.Type)
- // Also make sure we allocate a linker symbol
- // for the stack object data, for the same reason.
- if fn.Func.lsym.Func().StackObjects == nil {
- fn.Func.lsym.Func().StackObjects = Ctxt.Lookup(fn.Func.lsym.Name + ".stkobj")
- }
- }
- }
- }
-
- if compilenow(fn) {
- compileSSA(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 *Node) 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 xtop list.
- if fn.IsMethod() && isInlinableButNotInlined(fn) {
- return false
- }
- return nBackendWorkers == 1 && Debug_compilelater == 0
-}
-
-// 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 *Node) bool {
- if fn.Func.Nname.Func.Inl == nil {
- return false
- }
- if fn.Sym == nil {
- return true
- }
- return !fn.Sym.Linksym().WasInlined()
-}
-
-const maxStackSize = 1 << 30
-
-// compileSSA builds an SSA backend function,
-// uses it to generate a plist,
-// and flushes that plist to machine code.
-// worker indicates which of the backend workers is doing the processing.
-func compileSSA(fn *Node, worker int) {
- f := buildssa(fn, worker)
- // Note: check arg size to fix issue 25507.
- if f.Frontend().(*ssafn).stksize >= maxStackSize || fn.Type.ArgWidth() >= maxStackSize {
- largeStackFramesMu.Lock()
- largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: fn.Type.ArgWidth(), pos: fn.Pos})
- largeStackFramesMu.Unlock()
- return
- }
- pp := newProgs(fn, worker)
- defer pp.Free()
- genssa(f, pp)
- // Check frame size again.
- // The check above included only the space needed for local variables.
- // After genssa, the space needed includes local variables and the callee arg region.
- // We must do this check prior to calling pp.Flush.
- // If there are any oversized stack frames,
- // the assembler may emit inscrutable complaints about invalid instructions.
- if pp.Text.To.Offset >= maxStackSize {
- largeStackFramesMu.Lock()
- locals := f.Frontend().(*ssafn).stksize
- largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: fn.Type.ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos})
- largeStackFramesMu.Unlock()
- return
- }
-
- pp.Flush() // assemble, fill in boilerplate, etc.
- // fieldtrack must be called after pp.Flush. See issue 20014.
- fieldtrack(pp.Text.From.Sym, fn.Func.FieldTrack)
-}
-
-func init() {
- if race.Enabled {
- rand.Seed(time.Now().UnixNano())
- }
-}
-
-// 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 {
- sizeCalculationDisabled = true // not safe to calculate sizes concurrently
- if race.Enabled {
- // Randomize compilation order to try to shake out races.
- tmp := make([]*Node, 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 compilequeue[i].Nbody.Len() > compilequeue[j].Nbody.Len()
- })
- }
- var wg sync.WaitGroup
- Ctxt.InParallel = true
- c := make(chan *Node, nBackendWorkers)
- for i := 0; i < nBackendWorkers; i++ {
- wg.Add(1)
- go func(worker int) {
- for fn := range c {
- compileSSA(fn, worker)
- }
- wg.Done()
- }(i)
- }
- for _, fn := range compilequeue {
- c <- fn
- }
- close(c)
- compilequeue = nil
- wg.Wait()
- Ctxt.InParallel = false
- sizeCalculationDisabled = false
- }
-}
-
-func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) {
- fn := curfn.(*Node)
- if fn.Func.Nname != nil {
- if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect {
- Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
- }
- }
-
- var apdecls []*Node
- // Populate decls for fn.
- for _, n := range fn.Func.Dcl {
- if n.Op != ONAME { // might be OTYPE or OLITERAL
- continue
- }
- switch n.Class() {
- case PAUTO:
- if !n.Name.Used() {
- // Text == nil -> generating abstract function
- if fnsym.Func().Text != nil {
- Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
- }
- continue
- }
- case PPARAM, PPARAMOUT:
- default:
- continue
- }
- apdecls = append(apdecls, n)
- fnsym.Func().RecordAutoType(ngotype(n).Linksym())
- }
-
- decls, dwarfVars := createDwarfVars(fnsym, fn.Func, apdecls)
-
- // For each type referenced by the functions auto vars but not
- // already referenced by a dwarf var, attach a dummy relocation to
- // the function symbol to insure that the type included in DWARF
- // processing during linking.
- typesyms := []*obj.LSym{}
- for t, _ := range fnsym.Func().Autot {
- typesyms = append(typesyms, t)
- }
- sort.Sort(obj.BySymName(typesyms))
- for _, sym := range typesyms {
- r := obj.Addrel(infosym)
- r.Sym = sym
- r.Type = objabi.R_USETYPE
- }
- fnsym.Func().Autot = nil
-
- var varScopes []ScopeID
- for _, decl := range decls {
- pos := declPos(decl)
- varScopes = append(varScopes, findScope(fn.Func.Marks, pos))
- }
-
- scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes)
- var inlcalls dwarf.InlCalls
- if genDwarfInline > 0 {
- inlcalls = assembleInlines(fnsym, dwarfVars)
- }
- return scopes, inlcalls
-}
-
-func declPos(decl *Node) src.XPos {
- if decl.Name.Defn != nil && (decl.Name.Captured() || decl.Name.Byval()) {
- // It's not clear which position is correct for captured variables here:
- // * decl.Pos is the wrong position for captured variables, in the inner
- // function, but it is the right position in the outer function.
- // * decl.Name.Defn is nil for captured variables that were arguments
- // on the outer function, however the decl.Pos for those seems to be
- // correct.
- // * decl.Name.Defn is the "wrong" thing for variables declared in the
- // header of a type switch, it's their position in the header, rather
- // than the position of the case statement. In principle this is the
- // right thing, but here we prefer the latter because it makes each
- // instance of the header variable local to the lexical block of its
- // case statement.
- // This code is probably wrong for type switch variables that are also
- // captured.
- return decl.Name.Defn.Pos
- }
- return decl.Pos
-}
-
-// createSimpleVars creates a DWARF entry for every variable declared in the
-// function, claiming that they are permanently on the stack.
-func createSimpleVars(fnsym *obj.LSym, apDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) {
- var vars []*dwarf.Var
- var decls []*Node
- selected := make(map[*Node]bool)
- for _, n := range apDecls {
- if n.IsAutoTmp() {
- continue
- }
-
- decls = append(decls, n)
- vars = append(vars, createSimpleVar(fnsym, n))
- selected[n] = true
- }
- return decls, vars, selected
-}
-
-func createSimpleVar(fnsym *obj.LSym, n *Node) *dwarf.Var {
- var abbrev int
- offs := n.Xoffset
-
- switch n.Class() {
- case PAUTO:
- abbrev = dwarf.DW_ABRV_AUTO
- if Ctxt.FixedFrameSize() == 0 {
- offs -= int64(Widthptr)
- }
- if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
- // There is a word space for FP on ARM64 even if the frame pointer is disabled
- offs -= int64(Widthptr)
- }
-
- case PPARAM, PPARAMOUT:
- abbrev = dwarf.DW_ABRV_PARAM
- offs += Ctxt.FixedFrameSize()
- default:
- Fatalf("createSimpleVar unexpected class %v for node %v", n.Class(), n)
- }
-
- typename := dwarf.InfoPrefix + typesymname(n.Type)
- delete(fnsym.Func().Autot, ngotype(n).Linksym())
- inlIndex := 0
- if genDwarfInline > 1 {
- if n.Name.InlFormal() || n.Name.InlLocal() {
- inlIndex = posInlIndex(n.Pos) + 1
- if n.Name.InlFormal() {
- abbrev = dwarf.DW_ABRV_PARAM
- }
- }
- }
- declpos := Ctxt.InnermostPos(declPos(n))
- return &dwarf.Var{
- Name: n.Sym.Name,
- IsReturnValue: n.Class() == PPARAMOUT,
- IsInlFormal: n.Name.InlFormal(),
- Abbrev: abbrev,
- StackOffset: int32(offs),
- Type: Ctxt.Lookup(typename),
- DeclFile: declpos.RelFilename(),
- DeclLine: declpos.RelLine(),
- DeclCol: declpos.Col(),
- InlIndex: int32(inlIndex),
- ChildIndex: -1,
- }
-}
-
-// createComplexVars creates recomposed DWARF vars with location lists,
-// suitable for describing optimized code.
-func createComplexVars(fnsym *obj.LSym, fn *Func) ([]*Node, []*dwarf.Var, map[*Node]bool) {
- debugInfo := fn.DebugInfo
-
- // Produce a DWARF variable entry for each user variable.
- var decls []*Node
- var vars []*dwarf.Var
- ssaVars := make(map[*Node]bool)
-
- for varID, dvar := range debugInfo.Vars {
- n := dvar.(*Node)
- ssaVars[n] = true
- for _, slot := range debugInfo.VarSlots[varID] {
- ssaVars[debugInfo.Slots[slot].N.(*Node)] = true
- }
-
- if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil {
- decls = append(decls, n)
- vars = append(vars, dvar)
- }
- }
-
- return decls, vars, ssaVars
-}
-
-// createDwarfVars process fn, returning a list of DWARF variables and the
-// Nodes they represent.
-func createDwarfVars(fnsym *obj.LSym, fn *Func, apDecls []*Node) ([]*Node, []*dwarf.Var) {
- // Collect a raw list of DWARF vars.
- var vars []*dwarf.Var
- var decls []*Node
- var selected map[*Node]bool
- if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil {
- decls, vars, selected = createComplexVars(fnsym, fn)
- } else {
- decls, vars, selected = createSimpleVars(fnsym, apDecls)
- }
-
- dcl := apDecls
- if fnsym.WasInlined() {
- dcl = preInliningDcls(fnsym)
- }
-
- // If optimization is enabled, the list above will typically be
- // missing some of the original pre-optimization variables in the
- // function (they may have been promoted to registers, folded into
- // constants, dead-coded away, etc). Input arguments not eligible
- // for SSA optimization are also missing. Here we add back in entries
- // for selected missing vars. Note that the recipe below creates a
- // conservative location. The idea here is that we want to
- // communicate to the user that "yes, there is a variable named X
- // in this function, but no, I don't have enough information to
- // reliably report its contents."
- // For non-SSA-able arguments, however, the correct information
- // is known -- they have a single home on the stack.
- for _, n := range dcl {
- if _, found := selected[n]; found {
- continue
- }
- c := n.Sym.Name[0]
- if c == '.' || n.Type.IsUntyped() {
- continue
- }
- if n.Class() == PPARAM && !canSSAType(n.Type) {
- // SSA-able args get location lists, and may move in and
- // out of registers, so those are handled elsewhere.
- // Autos and named output params seem to get handled
- // with VARDEF, which creates location lists.
- // Args not of SSA-able type are treated here; they
- // are homed on the stack in a single place for the
- // entire call.
- vars = append(vars, createSimpleVar(fnsym, n))
- decls = append(decls, n)
- continue
- }
- typename := dwarf.InfoPrefix + typesymname(n.Type)
- decls = append(decls, n)
- abbrev := dwarf.DW_ABRV_AUTO_LOCLIST
- isReturnValue := (n.Class() == PPARAMOUT)
- if n.Class() == PPARAM || n.Class() == PPARAMOUT {
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
- } else if n.Class() == PAUTOHEAP {
- // If dcl in question has been promoted to heap, do a bit
- // of extra work to recover original class (auto or param);
- // see issue 30908. This insures that we get the proper
- // signature in the abstract function DIE, but leaves a
- // misleading location for the param (we want pointer-to-heap
- // and not stack).
- // TODO(thanm): generate a better location expression
- stackcopy := n.Name.Param.Stackcopy
- if stackcopy != nil && (stackcopy.Class() == PPARAM || stackcopy.Class() == PPARAMOUT) {
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
- isReturnValue = (stackcopy.Class() == PPARAMOUT)
- }
- }
- inlIndex := 0
- if genDwarfInline > 1 {
- if n.Name.InlFormal() || n.Name.InlLocal() {
- inlIndex = posInlIndex(n.Pos) + 1
- if n.Name.InlFormal() {
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
- }
- }
- }
- declpos := Ctxt.InnermostPos(n.Pos)
- vars = append(vars, &dwarf.Var{
- Name: n.Sym.Name,
- IsReturnValue: isReturnValue,
- Abbrev: abbrev,
- StackOffset: int32(n.Xoffset),
- Type: Ctxt.Lookup(typename),
- DeclFile: declpos.RelFilename(),
- DeclLine: declpos.RelLine(),
- DeclCol: declpos.Col(),
- InlIndex: int32(inlIndex),
- ChildIndex: -1,
- })
- // Record go type of to insure that it gets emitted by the linker.
- fnsym.Func().RecordAutoType(ngotype(n).Linksym())
- }
-
- return decls, vars
-}
-
-// Given a function that was inlined at some point during the
-// compilation, return a sorted list of nodes corresponding to the
-// autos/locals in that function prior to inlining. If this is a
-// function that is not local to the package being compiled, then the
-// names of the variables may have been "versioned" to avoid conflicts
-// with local vars; disregard this versioning when sorting.
-func preInliningDcls(fnsym *obj.LSym) []*Node {
- fn := Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*Node)
- var rdcl []*Node
- for _, n := range fn.Func.Inl.Dcl {
- c := n.Sym.Name[0]
- // Avoid reporting "_" parameters, since if there are more than
- // one, it can result in a collision later on, as in #23179.
- if unversion(n.Sym.Name) == "_" || c == '.' || n.Type.IsUntyped() {
- continue
- }
- rdcl = append(rdcl, n)
- }
- return rdcl
-}
-
-// stackOffset returns the stack location of a LocalSlot relative to the
-// stack pointer, suitable for use in a DWARF location entry. This has nothing
-// to do with its offset in the user variable.
-func stackOffset(slot ssa.LocalSlot) int32 {
- n := slot.N.(*Node)
- var base int64
- switch n.Class() {
- case PAUTO:
- if Ctxt.FixedFrameSize() == 0 {
- base -= int64(Widthptr)
- }
- if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
- // There is a word space for FP on ARM64 even if the frame pointer is disabled
- base -= int64(Widthptr)
- }
- case PPARAM, PPARAMOUT:
- base += Ctxt.FixedFrameSize()
- }
- return int32(base + n.Xoffset + slot.Off)
-}
-
-// createComplexVar builds a single DWARF variable entry and location list.
-func createComplexVar(fnsym *obj.LSym, fn *Func, varID ssa.VarID) *dwarf.Var {
- debug := fn.DebugInfo
- n := debug.Vars[varID].(*Node)
-
- var abbrev int
- switch n.Class() {
- case PAUTO:
- abbrev = dwarf.DW_ABRV_AUTO_LOCLIST
- case PPARAM, PPARAMOUT:
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
- default:
- return nil
- }
-
- gotype := ngotype(n).Linksym()
- delete(fnsym.Func().Autot, gotype)
- typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
- inlIndex := 0
- if genDwarfInline > 1 {
- if n.Name.InlFormal() || n.Name.InlLocal() {
- inlIndex = posInlIndex(n.Pos) + 1
- if n.Name.InlFormal() {
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
- }
- }
- }
- declpos := Ctxt.InnermostPos(n.Pos)
- dvar := &dwarf.Var{
- Name: n.Sym.Name,
- IsReturnValue: n.Class() == PPARAMOUT,
- IsInlFormal: n.Name.InlFormal(),
- Abbrev: abbrev,
- Type: Ctxt.Lookup(typename),
- // The stack offset is used as a sorting key, so for decomposed
- // variables just give it the first one. It's not used otherwise.
- // This won't work well if the first slot hasn't been assigned a stack
- // location, but it's not obvious how to do better.
- StackOffset: stackOffset(debug.Slots[debug.VarSlots[varID][0]]),
- DeclFile: declpos.RelFilename(),
- DeclLine: declpos.RelLine(),
- DeclCol: declpos.Col(),
- InlIndex: int32(inlIndex),
- ChildIndex: -1,
- }
- list := debug.LocationLists[varID]
- if len(list) != 0 {
- dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
- debug.PutLocationList(list, Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
- }
- }
- return dvar
-}
-
-// fieldtrack adds R_USEFIELD relocations to fnsym to record any
-// struct fields that it used.
-func fieldtrack(fnsym *obj.LSym, tracked map[*types.Sym]struct{}) {
- if fnsym == nil {
- return
- }
- if objabi.Fieldtrack_enabled == 0 || len(tracked) == 0 {
- return
- }
-
- trackSyms := make([]*types.Sym, 0, len(tracked))
- for sym := range tracked {
- trackSyms = append(trackSyms, sym)
- }
- sort.Sort(symByName(trackSyms))
- for _, sym := range trackSyms {
- r := obj.Addrel(fnsym)
- r.Sym = sym.Linksym()
- r.Type = objabi.R_USEFIELD
- }
-}
-
-type symByName []*types.Sym
-
-func (a symByName) Len() int { return len(a) }
-func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
-func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/src/cmd/compile/internal/gc/pgen_test.go b/src/cmd/compile/internal/gc/pgen_test.go
deleted file mode 100644
index b1db29825c2c1..0000000000000
--- a/src/cmd/compile/internal/gc/pgen_test.go
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2015 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 gc
-
-import (
- "cmd/compile/internal/types"
- "reflect"
- "sort"
- "testing"
-)
-
-func typeWithoutPointers() *types.Type {
- t := types.New(TSTRUCT)
- f := &types.Field{Type: types.New(TINT)}
- t.SetFields([]*types.Field{f})
- return t
-}
-
-func typeWithPointers() *types.Type {
- t := types.New(TSTRUCT)
- f := &types.Field{Type: types.NewPtr(types.New(TINT))}
- t.SetFields([]*types.Field{f})
- return t
-}
-
-func markUsed(n *Node) *Node {
- n.Name.SetUsed(true)
- return n
-}
-
-func markNeedZero(n *Node) *Node {
- n.Name.SetNeedzero(true)
- return n
-}
-
-func nodeWithClass(n Node, c Class) *Node {
- n.SetClass(c)
- n.Name = new(Name)
- return &n
-}
-
-// Test all code paths for cmpstackvarlt.
-func TestCmpstackvar(t *testing.T) {
- testdata := []struct {
- a, b *Node
- lt bool
- }{
- {
- nodeWithClass(Node{}, PAUTO),
- nodeWithClass(Node{}, PFUNC),
- false,
- },
- {
- nodeWithClass(Node{}, PFUNC),
- nodeWithClass(Node{}, PAUTO),
- true,
- },
- {
- nodeWithClass(Node{Xoffset: 0}, PFUNC),
- nodeWithClass(Node{Xoffset: 10}, PFUNC),
- true,
- },
- {
- nodeWithClass(Node{Xoffset: 20}, PFUNC),
- nodeWithClass(Node{Xoffset: 10}, PFUNC),
- false,
- },
- {
- nodeWithClass(Node{Xoffset: 10}, PFUNC),
- nodeWithClass(Node{Xoffset: 10}, PFUNC),
- false,
- },
- {
- nodeWithClass(Node{Xoffset: 10}, PPARAM),
- nodeWithClass(Node{Xoffset: 20}, PPARAMOUT),
- true,
- },
- {
- nodeWithClass(Node{Xoffset: 10}, PPARAMOUT),
- nodeWithClass(Node{Xoffset: 20}, PPARAM),
- true,
- },
- {
- markUsed(nodeWithClass(Node{}, PAUTO)),
- nodeWithClass(Node{}, PAUTO),
- true,
- },
- {
- nodeWithClass(Node{}, PAUTO),
- markUsed(nodeWithClass(Node{}, PAUTO)),
- false,
- },
- {
- nodeWithClass(Node{Type: typeWithoutPointers()}, PAUTO),
- nodeWithClass(Node{Type: typeWithPointers()}, PAUTO),
- false,
- },
- {
- nodeWithClass(Node{Type: typeWithPointers()}, PAUTO),
- nodeWithClass(Node{Type: typeWithoutPointers()}, PAUTO),
- true,
- },
- {
- markNeedZero(nodeWithClass(Node{Type: &types.Type{}}, PAUTO)),
- nodeWithClass(Node{Type: &types.Type{}, Name: &Name{}}, PAUTO),
- true,
- },
- {
- nodeWithClass(Node{Type: &types.Type{}, Name: &Name{}}, PAUTO),
- markNeedZero(nodeWithClass(Node{Type: &types.Type{}}, PAUTO)),
- false,
- },
- {
- nodeWithClass(Node{Type: &types.Type{Width: 1}, Name: &Name{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{Width: 2}, Name: &Name{}}, PAUTO),
- false,
- },
- {
- nodeWithClass(Node{Type: &types.Type{Width: 2}, Name: &Name{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{Width: 1}, Name: &Name{}}, PAUTO),
- true,
- },
- {
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
- true,
- },
- {
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
- false,
- },
- {
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
- false,
- },
- }
- for _, d := range testdata {
- got := cmpstackvarlt(d.a, d.b)
- if got != d.lt {
- t.Errorf("want %#v < %#v", d.a, d.b)
- }
- // If we expect a < b to be true, check that b < a is false.
- if d.lt && cmpstackvarlt(d.b, d.a) {
- t.Errorf("unexpected %#v < %#v", d.b, d.a)
- }
- }
-}
-
-func TestStackvarSort(t *testing.T) {
- inp := []*Node{
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Xoffset: 0, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- nodeWithClass(Node{Xoffset: 10, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- nodeWithClass(Node{Xoffset: 20, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- markUsed(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
- nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
- markNeedZero(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
- nodeWithClass(Node{Type: &types.Type{Width: 1}, Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{Width: 2}, Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
- }
- want := []*Node{
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- nodeWithClass(Node{Xoffset: 0, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- nodeWithClass(Node{Xoffset: 10, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- nodeWithClass(Node{Xoffset: 20, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
- markUsed(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
- markNeedZero(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
- nodeWithClass(Node{Type: &types.Type{Width: 2}, Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{Width: 1}, Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
- nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
- nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
- }
- sort.Sort(byStackVar(inp))
- if !reflect.DeepEqual(want, inp) {
- t.Error("sort failed")
- for i := range inp {
- g := inp[i]
- w := want[i]
- eq := reflect.DeepEqual(w, g)
- if !eq {
- t.Log(i, w, g)
- }
- }
- }
-}
diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
deleted file mode 100644
index 1b4d765d4239e..0000000000000
--- a/src/cmd/compile/internal/gc/range.go
+++ /dev/null
@@ -1,628 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/sys"
- "unicode/utf8"
-)
-
-// range
-func typecheckrange(n *Node) {
- // Typechecking order is important here:
- // 0. first typecheck range expression (slice/map/chan),
- // it is evaluated only once and so logically it is not part of the loop.
- // 1. typecheck produced values,
- // this part can declare new vars and so it must be typechecked before body,
- // because body can contain a closure that captures the vars.
- // 2. decldepth++ to denote loop body.
- // 3. typecheck body.
- // 4. decldepth--.
- typecheckrangeExpr(n)
-
- // second half of dance, the first half being typecheckrangeExpr
- n.SetTypecheck(1)
- ls := n.List.Slice()
- for i1, n1 := range ls {
- if n1.Typecheck() == 0 {
- ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign)
- }
- }
-
- decldepth++
- typecheckslice(n.Nbody.Slice(), ctxStmt)
- decldepth--
-}
-
-func typecheckrangeExpr(n *Node) {
- n.Right = typecheck(n.Right, ctxExpr)
-
- t := n.Right.Type
- if t == nil {
- return
- }
- // delicate little dance. see typecheckas2
- ls := n.List.Slice()
- for i1, n1 := range ls {
- if n1.Name == nil || n1.Name.Defn != n {
- ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign)
- }
- }
-
- if t.IsPtr() && t.Elem().IsArray() {
- t = t.Elem()
- }
- n.Type = t
-
- var t1, t2 *types.Type
- toomany := false
- switch t.Etype {
- default:
- yyerrorl(n.Pos, "cannot range over %L", n.Right)
- return
-
- case TARRAY, TSLICE:
- t1 = types.Types[TINT]
- t2 = t.Elem()
-
- case TMAP:
- t1 = t.Key()
- t2 = t.Elem()
-
- case TCHAN:
- if !t.ChanDir().CanRecv() {
- yyerrorl(n.Pos, "invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type)
- return
- }
-
- t1 = t.Elem()
- t2 = nil
- if n.List.Len() == 2 {
- toomany = true
- }
-
- case TSTRING:
- t1 = types.Types[TINT]
- t2 = types.Runetype
- }
-
- if n.List.Len() > 2 || toomany {
- yyerrorl(n.Pos, "too many variables in range")
- }
-
- var v1, v2 *Node
- if n.List.Len() != 0 {
- v1 = n.List.First()
- }
- if n.List.Len() > 1 {
- v2 = n.List.Second()
- }
-
- // this is not only an optimization but also a requirement in the spec.
- // "if the second iteration variable is the blank identifier, the range
- // clause is equivalent to the same clause with only the first variable
- // present."
- if v2.isBlank() {
- if v1 != nil {
- n.List.Set1(v1)
- }
- v2 = nil
- }
-
- if v1 != nil {
- if v1.Name != nil && v1.Name.Defn == n {
- v1.Type = t1
- } else if v1.Type != nil {
- if op, why := assignop(t1, v1.Type); op == OXXX {
- yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t1, v1, why)
- }
- }
- checkassign(n, v1)
- }
-
- if v2 != nil {
- if v2.Name != nil && v2.Name.Defn == n {
- v2.Type = t2
- } else if v2.Type != nil {
- if op, why := assignop(t2, v2.Type); op == OXXX {
- yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t2, v2, why)
- }
- }
- checkassign(n, v2)
- }
-}
-
-func cheapComputableIndex(width int64) bool {
- switch thearch.LinkArch.Family {
- // MIPS does not have R+R addressing
- // Arm64 may lack ability to generate this code in our assembler,
- // but the architecture supports it.
- case sys.PPC64, sys.S390X:
- return width == 1
- case sys.AMD64, sys.I386, sys.ARM64, sys.ARM:
- switch width {
- case 1, 2, 4, 8:
- return true
- }
- }
- return false
-}
-
-// walkrange transforms various forms of ORANGE into
-// simpler forms. The result must be assigned back to n.
-// Node n may also be modified in place, and may also be
-// the returned node.
-func walkrange(n *Node) *Node {
- if isMapClear(n) {
- m := n.Right
- lno := setlineno(m)
- n = mapClear(m)
- lineno = lno
- return n
- }
-
- // variable name conventions:
- // ohv1, hv1, hv2: hidden (old) val 1, 2
- // ha, hit: hidden aggregate, iterator
- // hn, hp: hidden len, pointer
- // hb: hidden bool
- // a, v1, v2: not hidden aggregate, val 1, 2
-
- t := n.Type
-
- a := n.Right
- lno := setlineno(a)
- n.Right = nil
-
- var v1, v2 *Node
- l := n.List.Len()
- if l > 0 {
- v1 = n.List.First()
- }
-
- if l > 1 {
- v2 = n.List.Second()
- }
-
- if v2.isBlank() {
- v2 = nil
- }
-
- if v1.isBlank() && v2 == nil {
- v1 = nil
- }
-
- if v1 == nil && v2 != nil {
- Fatalf("walkrange: v2 != nil while v1 == nil")
- }
-
- // n.List has no meaning anymore, clear it
- // to avoid erroneous processing by racewalk.
- n.List.Set(nil)
-
- var ifGuard *Node
-
- translatedLoopOp := OFOR
-
- var body []*Node
- var init []*Node
- switch t.Etype {
- default:
- Fatalf("walkrange")
-
- case TARRAY, TSLICE:
- if arrayClear(n, v1, v2, a) {
- lineno = lno
- return n
- }
-
- // order.stmt arranged for a copy of the array/slice variable if needed.
- ha := a
-
- hv1 := temp(types.Types[TINT])
- hn := temp(types.Types[TINT])
-
- init = append(init, nod(OAS, hv1, nil))
- init = append(init, nod(OAS, hn, nod(OLEN, ha, nil)))
-
- n.Left = nod(OLT, hv1, hn)
- n.Right = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)))
-
- // for range ha { body }
- if v1 == nil {
- break
- }
-
- // for v1 := range ha { body }
- if v2 == nil {
- body = []*Node{nod(OAS, v1, hv1)}
- break
- }
-
- // for v1, v2 := range ha { body }
- if cheapComputableIndex(n.Type.Elem().Width) {
- // v1, v2 = hv1, ha[hv1]
- tmp := nod(OINDEX, ha, hv1)
- tmp.SetBounded(true)
- // Use OAS2 to correctly handle assignments
- // of the form "v1, a[v1] := range".
- a := nod(OAS2, nil, nil)
- a.List.Set2(v1, v2)
- a.Rlist.Set2(hv1, tmp)
- body = []*Node{a}
- break
- }
-
- // TODO(austin): OFORUNTIL is a strange beast, but is
- // necessary for expressing the control flow we need
- // while also making "break" and "continue" work. It
- // would be nice to just lower ORANGE during SSA, but
- // racewalk needs to see many of the operations
- // involved in ORANGE's implementation. If racewalk
- // moves into SSA, consider moving ORANGE into SSA and
- // eliminating OFORUNTIL.
-
- // TODO(austin): OFORUNTIL inhibits bounds-check
- // elimination on the index variable (see #20711).
- // Enhance the prove pass to understand this.
- ifGuard = nod(OIF, nil, nil)
- ifGuard.Left = nod(OLT, hv1, hn)
- translatedLoopOp = OFORUNTIL
-
- hp := temp(types.NewPtr(n.Type.Elem()))
- tmp := nod(OINDEX, ha, nodintconst(0))
- tmp.SetBounded(true)
- init = append(init, nod(OAS, hp, nod(OADDR, tmp, nil)))
-
- // Use OAS2 to correctly handle assignments
- // of the form "v1, a[v1] := range".
- a := nod(OAS2, nil, nil)
- a.List.Set2(v1, v2)
- a.Rlist.Set2(hv1, nod(ODEREF, hp, nil))
- body = append(body, a)
-
- // Advance pointer as part of the late increment.
- //
- // This runs *after* the condition check, so we know
- // advancing the pointer is safe and won't go past the
- // end of the allocation.
- a = nod(OAS, hp, addptr(hp, t.Elem().Width))
- a = typecheck(a, ctxStmt)
- n.List.Set1(a)
-
- case TMAP:
- // order.stmt allocated the iterator for us.
- // we only use a once, so no copy needed.
- ha := a
-
- hit := prealloc[n]
- th := hit.Type
- n.Left = nil
- keysym := th.Field(0).Sym // depends on layout of iterator struct. See reflect.go:hiter
- elemsym := th.Field(1).Sym // ditto
-
- fn := syslook("mapiterinit")
-
- fn = substArgTypes(fn, t.Key(), t.Elem(), th)
- init = append(init, mkcall1(fn, nil, nil, typename(t), ha, nod(OADDR, hit, nil)))
- n.Left = nod(ONE, nodSym(ODOT, hit, keysym), nodnil())
-
- fn = syslook("mapiternext")
- fn = substArgTypes(fn, th)
- n.Right = mkcall1(fn, nil, nil, nod(OADDR, hit, nil))
-
- key := nodSym(ODOT, hit, keysym)
- key = nod(ODEREF, key, nil)
- if v1 == nil {
- body = nil
- } else if v2 == nil {
- body = []*Node{nod(OAS, v1, key)}
- } else {
- elem := nodSym(ODOT, hit, elemsym)
- elem = nod(ODEREF, elem, nil)
- a := nod(OAS2, nil, nil)
- a.List.Set2(v1, v2)
- a.Rlist.Set2(key, elem)
- body = []*Node{a}
- }
-
- case TCHAN:
- // order.stmt arranged for a copy of the channel variable.
- ha := a
-
- n.Left = nil
-
- hv1 := temp(t.Elem())
- hv1.SetTypecheck(1)
- if t.Elem().HasPointers() {
- init = append(init, nod(OAS, hv1, nil))
- }
- hb := temp(types.Types[TBOOL])
-
- n.Left = nod(ONE, hb, nodbool(false))
- a := nod(OAS2RECV, nil, nil)
- a.SetTypecheck(1)
- a.List.Set2(hv1, hb)
- a.Right = nod(ORECV, ha, nil)
- n.Left.Ninit.Set1(a)
- if v1 == nil {
- body = nil
- } else {
- body = []*Node{nod(OAS, v1, hv1)}
- }
- // Zero hv1. This prevents hv1 from being the sole, inaccessible
- // reference to an otherwise GC-able value during the next channel receive.
- // See issue 15281.
- body = append(body, nod(OAS, hv1, nil))
-
- case TSTRING:
- // Transform string range statements like "for v1, v2 = range a" into
- //
- // ha := a
- // for hv1 := 0; hv1 < len(ha); {
- // hv1t := hv1
- // hv2 := rune(ha[hv1])
- // if hv2 < utf8.RuneSelf {
- // hv1++
- // } else {
- // hv2, hv1 = decoderune(ha, hv1)
- // }
- // v1, v2 = hv1t, hv2
- // // original body
- // }
-
- // order.stmt arranged for a copy of the string variable.
- ha := a
-
- hv1 := temp(types.Types[TINT])
- hv1t := temp(types.Types[TINT])
- hv2 := temp(types.Runetype)
-
- // hv1 := 0
- init = append(init, nod(OAS, hv1, nil))
-
- // hv1 < len(ha)
- n.Left = nod(OLT, hv1, nod(OLEN, ha, nil))
-
- if v1 != nil {
- // hv1t = hv1
- body = append(body, nod(OAS, hv1t, hv1))
- }
-
- // hv2 := rune(ha[hv1])
- nind := nod(OINDEX, ha, hv1)
- nind.SetBounded(true)
- body = append(body, nod(OAS, hv2, conv(nind, types.Runetype)))
-
- // if hv2 < utf8.RuneSelf
- nif := nod(OIF, nil, nil)
- nif.Left = nod(OLT, hv2, nodintconst(utf8.RuneSelf))
-
- // hv1++
- nif.Nbody.Set1(nod(OAS, hv1, nod(OADD, hv1, nodintconst(1))))
-
- // } else {
- eif := nod(OAS2, nil, nil)
- nif.Rlist.Set1(eif)
-
- // hv2, hv1 = decoderune(ha, hv1)
- eif.List.Set2(hv2, hv1)
- fn := syslook("decoderune")
- eif.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1))
-
- body = append(body, nif)
-
- if v1 != nil {
- if v2 != nil {
- // v1, v2 = hv1t, hv2
- a := nod(OAS2, nil, nil)
- a.List.Set2(v1, v2)
- a.Rlist.Set2(hv1t, hv2)
- body = append(body, a)
- } else {
- // v1 = hv1t
- body = append(body, nod(OAS, v1, hv1t))
- }
- }
- }
-
- n.Op = translatedLoopOp
- typecheckslice(init, ctxStmt)
-
- if ifGuard != nil {
- ifGuard.Ninit.Append(init...)
- ifGuard = typecheck(ifGuard, ctxStmt)
- } else {
- n.Ninit.Append(init...)
- }
-
- typecheckslice(n.Left.Ninit.Slice(), ctxStmt)
-
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- n.Right = typecheck(n.Right, ctxStmt)
- typecheckslice(body, ctxStmt)
- n.Nbody.Prepend(body...)
-
- if ifGuard != nil {
- ifGuard.Nbody.Set1(n)
- n = ifGuard
- }
-
- n = walkstmt(n)
-
- lineno = lno
- return n
-}
-
-// isMapClear checks if n is of the form:
-//
-// for k := range m {
-// delete(m, k)
-// }
-//
-// where == for keys of map m is reflexive.
-func isMapClear(n *Node) bool {
- if Debug.N != 0 || instrumenting {
- return false
- }
-
- if n.Op != ORANGE || n.Type.Etype != TMAP || n.List.Len() != 1 {
- return false
- }
-
- k := n.List.First()
- if k == nil || k.isBlank() {
- return false
- }
-
- // Require k to be a new variable name.
- if k.Name == nil || k.Name.Defn != n {
- return false
- }
-
- if n.Nbody.Len() != 1 {
- return false
- }
-
- stmt := n.Nbody.First() // only stmt in body
- if stmt == nil || stmt.Op != ODELETE {
- return false
- }
-
- m := n.Right
- if !samesafeexpr(stmt.List.First(), m) || !samesafeexpr(stmt.List.Second(), k) {
- return false
- }
-
- // Keys where equality is not reflexive can not be deleted from maps.
- if !isreflexive(m.Type.Key()) {
- return false
- }
-
- return true
-}
-
-// mapClear constructs a call to runtime.mapclear for the map m.
-func mapClear(m *Node) *Node {
- t := m.Type
-
- // instantiate mapclear(typ *type, hmap map[any]any)
- fn := syslook("mapclear")
- fn = substArgTypes(fn, t.Key(), t.Elem())
- n := mkcall1(fn, nil, nil, typename(t), m)
-
- n = typecheck(n, ctxStmt)
- n = walkstmt(n)
-
- return n
-}
-
-// Lower n into runtime·memclr if possible, for
-// fast zeroing of slices and arrays (issue 5373).
-// Look for instances of
-//
-// for i := range a {
-// a[i] = zero
-// }
-//
-// in which the evaluation of a is side-effect-free.
-//
-// Parameters are as in walkrange: "for v1, v2 = range a".
-func arrayClear(n, v1, v2, a *Node) bool {
- if Debug.N != 0 || instrumenting {
- return false
- }
-
- if v1 == nil || v2 != nil {
- return false
- }
-
- if n.Nbody.Len() != 1 || n.Nbody.First() == nil {
- return false
- }
-
- stmt := n.Nbody.First() // only stmt in body
- if stmt.Op != OAS || stmt.Left.Op != OINDEX {
- return false
- }
-
- if !samesafeexpr(stmt.Left.Left, a) || !samesafeexpr(stmt.Left.Right, v1) {
- return false
- }
-
- elemsize := n.Type.Elem().Width
- if elemsize <= 0 || !isZero(stmt.Right) {
- return false
- }
-
- // Convert to
- // if len(a) != 0 {
- // hp = &a[0]
- // hn = len(a)*sizeof(elem(a))
- // memclr{NoHeap,Has}Pointers(hp, hn)
- // i = len(a) - 1
- // }
- n.Op = OIF
-
- n.Nbody.Set(nil)
- n.Left = nod(ONE, nod(OLEN, a, nil), nodintconst(0))
-
- // hp = &a[0]
- hp := temp(types.Types[TUNSAFEPTR])
-
- tmp := nod(OINDEX, a, nodintconst(0))
- tmp.SetBounded(true)
- tmp = nod(OADDR, tmp, nil)
- tmp = convnop(tmp, types.Types[TUNSAFEPTR])
- n.Nbody.Append(nod(OAS, hp, tmp))
-
- // hn = len(a) * sizeof(elem(a))
- hn := temp(types.Types[TUINTPTR])
-
- tmp = nod(OLEN, a, nil)
- tmp = nod(OMUL, tmp, nodintconst(elemsize))
- tmp = conv(tmp, types.Types[TUINTPTR])
- n.Nbody.Append(nod(OAS, hn, tmp))
-
- var fn *Node
- if a.Type.Elem().HasPointers() {
- // memclrHasPointers(hp, hn)
- Curfn.Func.setWBPos(stmt.Pos)
- fn = mkcall("memclrHasPointers", nil, nil, hp, hn)
- } else {
- // memclrNoHeapPointers(hp, hn)
- fn = mkcall("memclrNoHeapPointers", nil, nil, hp, hn)
- }
-
- n.Nbody.Append(fn)
-
- // i = len(a) - 1
- v1 = nod(OAS, v1, nod(OSUB, nod(OLEN, a, nil), nodintconst(1)))
-
- n.Nbody.Append(v1)
-
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- typecheckslice(n.Nbody.Slice(), ctxStmt)
- n = walkstmt(n)
- return true
-}
-
-// addptr returns (*T)(uintptr(p) + n).
-func addptr(p *Node, n int64) *Node {
- t := p.Type
-
- p = nod(OCONVNOP, p, nil)
- p.Type = types.Types[TUINTPTR]
-
- p = nod(OADD, p, nodintconst(n))
-
- p = nod(OCONVNOP, p, nil)
- p.Type = t
-
- return p
-}
diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
deleted file mode 100644
index 97e0424ce07f8..0000000000000
--- a/src/cmd/compile/internal/gc/select.go
+++ /dev/null
@@ -1,387 +0,0 @@
-// Copyright 2009 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 gc
-
-import "cmd/compile/internal/types"
-
-// select
-func typecheckselect(sel *Node) {
- var def *Node
- lno := setlineno(sel)
- typecheckslice(sel.Ninit.Slice(), ctxStmt)
- for _, ncase := range sel.List.Slice() {
- if ncase.Op != OCASE {
- setlineno(ncase)
- Fatalf("typecheckselect %v", ncase.Op)
- }
-
- if ncase.List.Len() == 0 {
- // default
- if def != nil {
- yyerrorl(ncase.Pos, "multiple defaults in select (first at %v)", def.Line())
- } else {
- def = ncase
- }
- } else if ncase.List.Len() > 1 {
- yyerrorl(ncase.Pos, "select cases cannot be lists")
- } else {
- ncase.List.SetFirst(typecheck(ncase.List.First(), ctxStmt))
- n := ncase.List.First()
- ncase.Left = n
- ncase.List.Set(nil)
- switch n.Op {
- default:
- pos := n.Pos
- if n.Op == ONAME {
- // We don't have the right position for ONAME nodes (see #15459 and
- // others). Using ncase.Pos for now as it will provide the correct
- // line number (assuming the expression follows the "case" keyword
- // on the same line). This matches the approach before 1.10.
- pos = ncase.Pos
- }
- yyerrorl(pos, "select case must be receive, send or assign recv")
-
- // convert x = <-c into OSELRECV(x, <-c).
- // remove implicit conversions; the eventual assignment
- // will reintroduce them.
- case OAS:
- if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() {
- n.Right = n.Right.Left
- }
-
- if n.Right.Op != ORECV {
- yyerrorl(n.Pos, "select assignment must have receive on right hand side")
- break
- }
-
- n.Op = OSELRECV
-
- // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
- case OAS2RECV:
- if n.Right.Op != ORECV {
- yyerrorl(n.Pos, "select assignment must have receive on right hand side")
- break
- }
-
- n.Op = OSELRECV2
- n.Left = n.List.First()
- n.List.Set1(n.List.Second())
-
- // convert <-c into OSELRECV(N, <-c)
- case ORECV:
- n = nodl(n.Pos, OSELRECV, nil, n)
-
- n.SetTypecheck(1)
- ncase.Left = n
-
- case OSEND:
- break
- }
- }
-
- typecheckslice(ncase.Nbody.Slice(), ctxStmt)
- }
-
- lineno = lno
-}
-
-func walkselect(sel *Node) {
- lno := setlineno(sel)
- if sel.Nbody.Len() != 0 {
- Fatalf("double walkselect")
- }
-
- init := sel.Ninit.Slice()
- sel.Ninit.Set(nil)
-
- init = append(init, walkselectcases(&sel.List)...)
- sel.List.Set(nil)
-
- sel.Nbody.Set(init)
- walkstmtlist(sel.Nbody.Slice())
-
- lineno = lno
-}
-
-func walkselectcases(cases *Nodes) []*Node {
- ncas := cases.Len()
- sellineno := lineno
-
- // optimization: zero-case select
- if ncas == 0 {
- return []*Node{mkcall("block", nil, nil)}
- }
-
- // optimization: one-case select: single op.
- if ncas == 1 {
- cas := cases.First()
- setlineno(cas)
- l := cas.Ninit.Slice()
- if cas.Left != nil { // not default:
- n := cas.Left
- l = append(l, n.Ninit.Slice()...)
- n.Ninit.Set(nil)
- switch n.Op {
- default:
- Fatalf("select %v", n.Op)
-
- case OSEND:
- // already ok
-
- case OSELRECV, OSELRECV2:
- if n.Op == OSELRECV || n.List.Len() == 0 {
- if n.Left == nil {
- n = n.Right
- } else {
- n.Op = OAS
- }
- break
- }
-
- if n.Left == nil {
- nblank = typecheck(nblank, ctxExpr|ctxAssign)
- n.Left = nblank
- }
-
- n.Op = OAS2
- n.List.Prepend(n.Left)
- n.Rlist.Set1(n.Right)
- n.Right = nil
- n.Left = nil
- n.SetTypecheck(0)
- n = typecheck(n, ctxStmt)
- }
-
- l = append(l, n)
- }
-
- l = append(l, cas.Nbody.Slice()...)
- l = append(l, nod(OBREAK, nil, nil))
- return l
- }
-
- // convert case value arguments to addresses.
- // this rewrite is used by both the general code and the next optimization.
- var dflt *Node
- for _, cas := range cases.Slice() {
- setlineno(cas)
- n := cas.Left
- if n == nil {
- dflt = cas
- continue
- }
- switch n.Op {
- case OSEND:
- n.Right = nod(OADDR, n.Right, nil)
- n.Right = typecheck(n.Right, ctxExpr)
-
- case OSELRECV, OSELRECV2:
- if n.Op == OSELRECV2 && n.List.Len() == 0 {
- n.Op = OSELRECV
- }
-
- if n.Left != nil {
- n.Left = nod(OADDR, n.Left, nil)
- n.Left = typecheck(n.Left, ctxExpr)
- }
- }
- }
-
- // optimization: two-case select but one is default: single non-blocking op.
- if ncas == 2 && dflt != nil {
- cas := cases.First()
- if cas == dflt {
- cas = cases.Second()
- }
-
- n := cas.Left
- setlineno(n)
- r := nod(OIF, nil, nil)
- r.Ninit.Set(cas.Ninit.Slice())
- switch n.Op {
- default:
- Fatalf("select %v", n.Op)
-
- case OSEND:
- // if selectnbsend(c, v) { body } else { default body }
- ch := n.Left
- r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
-
- case OSELRECV:
- // if selectnbrecv(&v, c) { body } else { default body }
- ch := n.Right.Left
- elem := n.Left
- if elem == nil {
- elem = nodnil()
- }
- r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch)
-
- case OSELRECV2:
- // if selectnbrecv2(&v, &received, c) { body } else { default body }
- ch := n.Right.Left
- elem := n.Left
- if elem == nil {
- elem = nodnil()
- }
- receivedp := nod(OADDR, n.List.First(), nil)
- receivedp = typecheck(receivedp, ctxExpr)
- r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch)
- }
-
- r.Left = typecheck(r.Left, ctxExpr)
- r.Nbody.Set(cas.Nbody.Slice())
- r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
- return []*Node{r, nod(OBREAK, nil, nil)}
- }
-
- if dflt != nil {
- ncas--
- }
- casorder := make([]*Node, ncas)
- nsends, nrecvs := 0, 0
-
- var init []*Node
-
- // generate sel-struct
- lineno = sellineno
- selv := temp(types.NewArray(scasetype(), int64(ncas)))
- r := nod(OAS, selv, nil)
- r = typecheck(r, ctxStmt)
- init = append(init, r)
-
- // No initialization for order; runtime.selectgo is responsible for that.
- order := temp(types.NewArray(types.Types[TUINT16], 2*int64(ncas)))
-
- var pc0, pcs *Node
- if flag_race {
- pcs = temp(types.NewArray(types.Types[TUINTPTR], int64(ncas)))
- pc0 = typecheck(nod(OADDR, nod(OINDEX, pcs, nodintconst(0)), nil), ctxExpr)
- } else {
- pc0 = nodnil()
- }
-
- // register cases
- for _, cas := range cases.Slice() {
- setlineno(cas)
-
- init = append(init, cas.Ninit.Slice()...)
- cas.Ninit.Set(nil)
-
- n := cas.Left
- if n == nil { // default:
- continue
- }
-
- var i int
- var c, elem *Node
- switch n.Op {
- default:
- Fatalf("select %v", n.Op)
- case OSEND:
- i = nsends
- nsends++
- c = n.Left
- elem = n.Right
- case OSELRECV, OSELRECV2:
- nrecvs++
- i = ncas - nrecvs
- c = n.Right.Left
- elem = n.Left
- }
-
- casorder[i] = cas
-
- setField := func(f string, val *Node) {
- r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val)
- r = typecheck(r, ctxStmt)
- init = append(init, r)
- }
-
- c = convnop(c, types.Types[TUNSAFEPTR])
- setField("c", c)
- if elem != nil {
- elem = convnop(elem, types.Types[TUNSAFEPTR])
- setField("elem", elem)
- }
-
- // TODO(mdempsky): There should be a cleaner way to
- // handle this.
- if flag_race {
- r = mkcall("selectsetpc", nil, nil, nod(OADDR, nod(OINDEX, pcs, nodintconst(int64(i))), nil))
- init = append(init, r)
- }
- }
- if nsends+nrecvs != ncas {
- Fatalf("walkselectcases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
- }
-
- // run the select
- lineno = sellineno
- chosen := temp(types.Types[TINT])
- recvOK := temp(types.Types[TBOOL])
- r = nod(OAS2, nil, nil)
- r.List.Set2(chosen, recvOK)
- fn := syslook("selectgo")
- r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, nodintconst(int64(nsends)), nodintconst(int64(nrecvs)), nodbool(dflt == nil)))
- r = typecheck(r, ctxStmt)
- init = append(init, r)
-
- // selv and order are no longer alive after selectgo.
- init = append(init, nod(OVARKILL, selv, nil))
- init = append(init, nod(OVARKILL, order, nil))
- if flag_race {
- init = append(init, nod(OVARKILL, pcs, nil))
- }
-
- // dispatch cases
- dispatch := func(cond, cas *Node) {
- cond = typecheck(cond, ctxExpr)
- cond = defaultlit(cond, nil)
-
- r := nod(OIF, cond, nil)
-
- if n := cas.Left; n != nil && n.Op == OSELRECV2 {
- x := nod(OAS, n.List.First(), recvOK)
- x = typecheck(x, ctxStmt)
- r.Nbody.Append(x)
- }
-
- r.Nbody.AppendNodes(&cas.Nbody)
- r.Nbody.Append(nod(OBREAK, nil, nil))
- init = append(init, r)
- }
-
- if dflt != nil {
- setlineno(dflt)
- dispatch(nod(OLT, chosen, nodintconst(0)), dflt)
- }
- for i, cas := range casorder {
- setlineno(cas)
- dispatch(nod(OEQ, chosen, nodintconst(int64(i))), cas)
- }
-
- return init
-}
-
-// bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
-func bytePtrToIndex(n *Node, i int64) *Node {
- s := nod(OADDR, nod(OINDEX, n, nodintconst(i)), nil)
- t := types.NewPtr(types.Types[TUINT8])
- return convnop(s, t)
-}
-
-var scase *types.Type
-
-// Keep in sync with src/runtime/select.go.
-func scasetype() *types.Type {
- if scase == nil {
- scase = tostruct([]*Node{
- namedfield("c", types.Types[TUNSAFEPTR]),
- namedfield("elem", types.Types[TUNSAFEPTR]),
- })
- scase.SetNoalg(true)
- }
- return scase
-}
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
deleted file mode 100644
index 212fcc022dba3..0000000000000
--- a/src/cmd/compile/internal/gc/sinit.go
+++ /dev/null
@@ -1,1172 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "fmt"
-)
-
-type InitEntry struct {
- Xoffset int64 // struct, array only
- Expr *Node // bytes of run-time computed expressions
-}
-
-type InitPlan struct {
- E []InitEntry
-}
-
-// An InitSchedule is used to decompose assignment statements into
-// static and dynamic initialization parts. Static initializations are
-// handled by populating variables' linker symbol data, while dynamic
-// initializations are accumulated to be executed in order.
-type InitSchedule struct {
- // out is the ordered list of dynamic initialization
- // statements.
- out []*Node
-
- initplans map[*Node]*InitPlan
- inittemps map[*Node]*Node
-}
-
-func (s *InitSchedule) append(n *Node) {
- s.out = append(s.out, n)
-}
-
-// staticInit adds an initialization statement n to the schedule.
-func (s *InitSchedule) staticInit(n *Node) {
- if !s.tryStaticInit(n) {
- if Debug.P != 0 {
- Dump("nonstatic", n)
- }
- s.append(n)
- }
-}
-
-// tryStaticInit attempts to statically execute an initialization
-// statement and reports whether it succeeded.
-func (s *InitSchedule) tryStaticInit(n *Node) bool {
- // Only worry about simple "l = r" assignments. Multiple
- // variable/expression OAS2 assignments have already been
- // replaced by multiple simple OAS assignments, and the other
- // OAS2* assignments mostly necessitate dynamic execution
- // anyway.
- if n.Op != OAS {
- return false
- }
- if n.Left.isBlank() && candiscard(n.Right) {
- return true
- }
- lno := setlineno(n)
- defer func() { lineno = lno }()
- return s.staticassign(n.Left, n.Right)
-}
-
-// like staticassign but we are copying an already
-// initialized value r.
-func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
- if r.Op != ONAME {
- return false
- }
- if r.Class() == PFUNC {
- pfuncsym(l, r)
- return true
- }
- if r.Class() != PEXTERN || r.Sym.Pkg != localpkg {
- return false
- }
- if r.Name.Defn == nil { // probably zeroed but perhaps supplied externally and of unknown value
- return false
- }
- if r.Name.Defn.Op != OAS {
- return false
- }
- if r.Type.IsString() { // perhaps overwritten by cmd/link -X (#34675)
- return false
- }
- orig := r
- r = r.Name.Defn.Right
-
- for r.Op == OCONVNOP && !types.Identical(r.Type, l.Type) {
- r = r.Left
- }
-
- switch r.Op {
- case ONAME:
- if s.staticcopy(l, r) {
- return true
- }
- // We may have skipped past one or more OCONVNOPs, so
- // use conv to ensure r is assignable to l (#13263).
- s.append(nod(OAS, l, conv(r, l.Type)))
- return true
-
- case OLITERAL:
- if isZero(r) {
- return true
- }
- litsym(l, r, int(l.Type.Width))
- return true
-
- case OADDR:
- if a := r.Left; a.Op == ONAME {
- addrsym(l, a)
- return true
- }
-
- case OPTRLIT:
- switch r.Left.Op {
- case OARRAYLIT, OSLICELIT, OSTRUCTLIT, OMAPLIT:
- // copy pointer
- addrsym(l, s.inittemps[r])
- return true
- }
-
- case OSLICELIT:
- // copy slice
- a := s.inittemps[r]
- slicesym(l, a, r.Right.Int64Val())
- return true
-
- case OARRAYLIT, OSTRUCTLIT:
- p := s.initplans[r]
-
- n := l.copy()
- for i := range p.E {
- e := &p.E[i]
- n.Xoffset = l.Xoffset + e.Xoffset
- n.Type = e.Expr.Type
- if e.Expr.Op == OLITERAL {
- litsym(n, e.Expr, int(n.Type.Width))
- continue
- }
- ll := n.sepcopy()
- if s.staticcopy(ll, e.Expr) {
- continue
- }
- // Requires computation, but we're
- // copying someone else's computation.
- rr := orig.sepcopy()
- rr.Type = ll.Type
- rr.Xoffset += e.Xoffset
- setlineno(rr)
- s.append(nod(OAS, ll, rr))
- }
-
- return true
- }
-
- return false
-}
-
-func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
- for r.Op == OCONVNOP {
- r = r.Left
- }
-
- switch r.Op {
- case ONAME:
- return s.staticcopy(l, r)
-
- case OLITERAL:
- if isZero(r) {
- return true
- }
- litsym(l, r, int(l.Type.Width))
- return true
-
- case OADDR:
- var nam Node
- if stataddr(&nam, r.Left) {
- addrsym(l, &nam)
- return true
- }
- fallthrough
-
- case OPTRLIT:
- switch r.Left.Op {
- case OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT:
- // Init pointer.
- a := staticname(r.Left.Type)
-
- s.inittemps[r] = a
- addrsym(l, a)
-
- // Init underlying literal.
- if !s.staticassign(a, r.Left) {
- s.append(nod(OAS, a, r.Left))
- }
- return true
- }
- //dump("not static ptrlit", r);
-
- case OSTR2BYTES:
- if l.Class() == PEXTERN && r.Left.Op == OLITERAL {
- sval := r.Left.StringVal()
- slicebytes(l, sval)
- return true
- }
-
- case OSLICELIT:
- s.initplan(r)
- // Init slice.
- bound := r.Right.Int64Val()
- ta := types.NewArray(r.Type.Elem(), bound)
- ta.SetNoalg(true)
- a := staticname(ta)
- s.inittemps[r] = a
- slicesym(l, a, bound)
- // Fall through to init underlying array.
- l = a
- fallthrough
-
- case OARRAYLIT, OSTRUCTLIT:
- s.initplan(r)
-
- p := s.initplans[r]
- n := l.copy()
- for i := range p.E {
- e := &p.E[i]
- n.Xoffset = l.Xoffset + e.Xoffset
- n.Type = e.Expr.Type
- if e.Expr.Op == OLITERAL {
- litsym(n, e.Expr, int(n.Type.Width))
- continue
- }
- setlineno(e.Expr)
- a := n.sepcopy()
- if !s.staticassign(a, e.Expr) {
- s.append(nod(OAS, a, e.Expr))
- }
- }
-
- return true
-
- case OMAPLIT:
- break
-
- case OCLOSURE:
- if hasemptycvars(r) {
- if Debug_closure > 0 {
- Warnl(r.Pos, "closure converted to global")
- }
- // Closures with no captured variables are globals,
- // so the assignment can be done at link time.
- pfuncsym(l, r.Func.Closure.Func.Nname)
- return true
- }
- closuredebugruntimecheck(r)
-
- case OCONVIFACE:
- // This logic is mirrored in isStaticCompositeLiteral.
- // If you change something here, change it there, and vice versa.
-
- // Determine the underlying concrete type and value we are converting from.
- val := r
- for val.Op == OCONVIFACE {
- val = val.Left
- }
- if val.Type.IsInterface() {
- // val is an interface type.
- // If val is nil, we can statically initialize l;
- // both words are zero and so there no work to do, so report success.
- // If val is non-nil, we have no concrete type to record,
- // and we won't be able to statically initialize its value, so report failure.
- return Isconst(val, CTNIL)
- }
-
- markTypeUsedInInterface(val.Type, l.Sym.Linksym())
-
- var itab *Node
- if l.Type.IsEmptyInterface() {
- itab = typename(val.Type)
- } else {
- itab = itabname(val.Type, l.Type)
- }
-
- // Create a copy of l to modify while we emit data.
- n := l.copy()
-
- // Emit itab, advance offset.
- addrsym(n, itab.Left) // itab is an OADDR node
- n.Xoffset += int64(Widthptr)
-
- // Emit data.
- if isdirectiface(val.Type) {
- if Isconst(val, CTNIL) {
- // Nil is zero, nothing to do.
- return true
- }
- // Copy val directly into n.
- n.Type = val.Type
- setlineno(val)
- a := n.sepcopy()
- if !s.staticassign(a, val) {
- s.append(nod(OAS, a, val))
- }
- } else {
- // Construct temp to hold val, write pointer to temp into n.
- a := staticname(val.Type)
- s.inittemps[val] = a
- if !s.staticassign(a, val) {
- s.append(nod(OAS, a, val))
- }
- addrsym(n, a)
- }
-
- return true
- }
-
- //dump("not static", r);
- return false
-}
-
-// initContext is the context in which static data is populated.
-// It is either in an init function or in any other function.
-// Static data populated in an init function will be written either
-// zero times (as a readonly, static data symbol) or
-// one time (during init function execution).
-// Either way, there is no opportunity for races or further modification,
-// so the data can be written to a (possibly readonly) data symbol.
-// Static data populated in any other function needs to be local to
-// that function to allow multiple instances of that function
-// to execute concurrently without clobbering each others' data.
-type initContext uint8
-
-const (
- inInitFunction initContext = iota
- inNonInitFunction
-)
-
-func (c initContext) String() string {
- if c == inInitFunction {
- return "inInitFunction"
- }
- return "inNonInitFunction"
-}
-
-// from here down is the walk analysis
-// of composite literals.
-// most of the work is to generate
-// data statements for the constant
-// part of the composite literal.
-
-var statuniqgen int // name generator for static temps
-
-// staticname returns a name backed by a (writable) static data symbol.
-// Use readonlystaticname for read-only node.
-func staticname(t *types.Type) *Node {
- // Don't use lookupN; it interns the resulting string, but these are all unique.
- n := newname(lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen)))
- statuniqgen++
- addvar(n, t, PEXTERN)
- n.Sym.Linksym().Set(obj.AttrLocal, true)
- return n
-}
-
-// readonlystaticname returns a name backed by a (writable) static data symbol.
-func readonlystaticname(t *types.Type) *Node {
- n := staticname(t)
- n.MarkReadonly()
- n.Sym.Linksym().Set(obj.AttrContentAddressable, true)
- return n
-}
-
-func (n *Node) isSimpleName() bool {
- return n.Op == ONAME && n.Class() != PAUTOHEAP && n.Class() != PEXTERN
-}
-
-func litas(l *Node, r *Node, init *Nodes) {
- a := nod(OAS, l, r)
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- init.Append(a)
-}
-
-// initGenType is a bitmap indicating the types of generation that will occur for a static value.
-type initGenType uint8
-
-const (
- initDynamic initGenType = 1 << iota // contains some dynamic values, for which init code will be generated
- initConst // contains some constant values, which may be written into data symbols
-)
-
-// getdyn calculates the initGenType for n.
-// If top is false, getdyn is recursing.
-func getdyn(n *Node, top bool) initGenType {
- switch n.Op {
- default:
- if n.isGoConst() {
- return initConst
- }
- return initDynamic
-
- case OSLICELIT:
- if !top {
- return initDynamic
- }
- if n.Right.Int64Val()/4 > int64(n.List.Len()) {
- // <25% of entries have explicit values.
- // Very rough estimation, it takes 4 bytes of instructions
- // to initialize 1 byte of result. So don't use a static
- // initializer if the dynamic initialization code would be
- // smaller than the static value.
- // See issue 23780.
- return initDynamic
- }
-
- case OARRAYLIT, OSTRUCTLIT:
- }
-
- var mode initGenType
- for _, n1 := range n.List.Slice() {
- switch n1.Op {
- case OKEY:
- n1 = n1.Right
- case OSTRUCTKEY:
- n1 = n1.Left
- }
- mode |= getdyn(n1, false)
- if mode == initDynamic|initConst {
- break
- }
- }
- return mode
-}
-
-// isStaticCompositeLiteral reports whether n is a compile-time constant.
-func isStaticCompositeLiteral(n *Node) bool {
- switch n.Op {
- case OSLICELIT:
- return false
- case OARRAYLIT:
- for _, r := range n.List.Slice() {
- if r.Op == OKEY {
- r = r.Right
- }
- if !isStaticCompositeLiteral(r) {
- return false
- }
- }
- return true
- case OSTRUCTLIT:
- for _, r := range n.List.Slice() {
- if r.Op != OSTRUCTKEY {
- Fatalf("isStaticCompositeLiteral: rhs not OSTRUCTKEY: %v", r)
- }
- if !isStaticCompositeLiteral(r.Left) {
- return false
- }
- }
- return true
- case OLITERAL:
- return true
- case OCONVIFACE:
- // See staticassign's OCONVIFACE case for comments.
- val := n
- for val.Op == OCONVIFACE {
- val = val.Left
- }
- if val.Type.IsInterface() {
- return Isconst(val, CTNIL)
- }
- if isdirectiface(val.Type) && Isconst(val, CTNIL) {
- return true
- }
- return isStaticCompositeLiteral(val)
- }
- return false
-}
-
-// initKind is a kind of static initialization: static, dynamic, or local.
-// Static initialization represents literals and
-// literal components of composite literals.
-// Dynamic initialization represents non-literals and
-// non-literal components of composite literals.
-// LocalCode initialization represents initialization
-// that occurs purely in generated code local to the function of use.
-// Initialization code is sometimes generated in passes,
-// first static then dynamic.
-type initKind uint8
-
-const (
- initKindStatic initKind = iota + 1
- initKindDynamic
- initKindLocalCode
-)
-
-// fixedlit handles struct, array, and slice literals.
-// TODO: expand documentation.
-func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) {
- isBlank := var_ == nblank
- var splitnode func(*Node) (a *Node, value *Node)
- switch n.Op {
- case OARRAYLIT, OSLICELIT:
- var k int64
- splitnode = func(r *Node) (*Node, *Node) {
- if r.Op == OKEY {
- k = indexconst(r.Left)
- if k < 0 {
- Fatalf("fixedlit: invalid index %v", r.Left)
- }
- r = r.Right
- }
- a := nod(OINDEX, var_, nodintconst(k))
- k++
- if isBlank {
- a = nblank
- }
- return a, r
- }
- case OSTRUCTLIT:
- splitnode = func(r *Node) (*Node, *Node) {
- if r.Op != OSTRUCTKEY {
- Fatalf("fixedlit: rhs not OSTRUCTKEY: %v", r)
- }
- if r.Sym.IsBlank() || isBlank {
- return nblank, r.Left
- }
- setlineno(r)
- return nodSym(ODOT, var_, r.Sym), r.Left
- }
- default:
- Fatalf("fixedlit bad op: %v", n.Op)
- }
-
- for _, r := range n.List.Slice() {
- a, value := splitnode(r)
- if a == nblank && candiscard(value) {
- continue
- }
-
- switch value.Op {
- case OSLICELIT:
- if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) {
- slicelit(ctxt, value, a, init)
- continue
- }
-
- case OARRAYLIT, OSTRUCTLIT:
- fixedlit(ctxt, kind, value, a, init)
- continue
- }
-
- islit := value.isGoConst()
- if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
- continue
- }
-
- // build list of assignments: var[index] = expr
- setlineno(a)
- a = nod(OAS, a, value)
- a = typecheck(a, ctxStmt)
- switch kind {
- case initKindStatic:
- genAsStatic(a)
- case initKindDynamic, initKindLocalCode:
- a = orderStmtInPlace(a, map[string][]*Node{})
- a = walkstmt(a)
- init.Append(a)
- default:
- Fatalf("fixedlit: bad kind %d", kind)
- }
-
- }
-}
-
-func isSmallSliceLit(n *Node) bool {
- if n.Op != OSLICELIT {
- return false
- }
-
- r := n.Right
-
- return smallintconst(r) && (n.Type.Elem().Width == 0 || r.Int64Val() <= smallArrayBytes/n.Type.Elem().Width)
-}
-
-func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
- // make an array type corresponding the number of elements we have
- t := types.NewArray(n.Type.Elem(), n.Right.Int64Val())
- dowidth(t)
-
- if ctxt == inNonInitFunction {
- // put everything into static array
- vstat := staticname(t)
-
- fixedlit(ctxt, initKindStatic, n, vstat, init)
- fixedlit(ctxt, initKindDynamic, n, vstat, init)
-
- // copy static to slice
- var_ = typecheck(var_, ctxExpr|ctxAssign)
- var nam Node
- if !stataddr(&nam, var_) || nam.Class() != PEXTERN {
- Fatalf("slicelit: %v", var_)
- }
- slicesym(&nam, vstat, t.NumElem())
- return
- }
-
- // recipe for var = []t{...}
- // 1. make a static array
- // var vstat [...]t
- // 2. assign (data statements) the constant part
- // vstat = constpart{}
- // 3. make an auto pointer to array and allocate heap to it
- // var vauto *[...]t = new([...]t)
- // 4. copy the static array to the auto array
- // *vauto = vstat
- // 5. for each dynamic part assign to the array
- // vauto[i] = dynamic part
- // 6. assign slice of allocated heap to var
- // var = vauto[:]
- //
- // an optimization is done if there is no constant part
- // 3. var vauto *[...]t = new([...]t)
- // 5. vauto[i] = dynamic part
- // 6. var = vauto[:]
-
- // if the literal contains constants,
- // make static initialized array (1),(2)
- var vstat *Node
-
- mode := getdyn(n, true)
- if mode&initConst != 0 && !isSmallSliceLit(n) {
- if ctxt == inInitFunction {
- vstat = readonlystaticname(t)
- } else {
- vstat = staticname(t)
- }
- fixedlit(ctxt, initKindStatic, n, vstat, init)
- }
-
- // make new auto *array (3 declare)
- vauto := temp(types.NewPtr(t))
-
- // set auto to point at new temp or heap (3 assign)
- var a *Node
- if x := prealloc[n]; x != nil {
- // temp allocated during order.go for dddarg
- if !types.Identical(t, x.Type) {
- panic("dotdotdot base type does not match order's assigned type")
- }
-
- if vstat == nil {
- a = nod(OAS, x, nil)
- a = typecheck(a, ctxStmt)
- init.Append(a) // zero new temp
- } else {
- // Declare that we're about to initialize all of x.
- // (Which happens at the *vauto = vstat below.)
- init.Append(nod(OVARDEF, x, nil))
- }
-
- a = nod(OADDR, x, nil)
- } else if n.Esc == EscNone {
- a = temp(t)
- if vstat == nil {
- a = nod(OAS, temp(t), nil)
- a = typecheck(a, ctxStmt)
- init.Append(a) // zero new temp
- a = a.Left
- } else {
- init.Append(nod(OVARDEF, a, nil))
- }
-
- a = nod(OADDR, a, nil)
- } else {
- a = nod(ONEW, nil, nil)
- a.List.Set1(typenod(t))
- }
-
- a = nod(OAS, vauto, a)
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- init.Append(a)
-
- if vstat != nil {
- // copy static to heap (4)
- a = nod(ODEREF, vauto, nil)
-
- a = nod(OAS, a, vstat)
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- init.Append(a)
- }
-
- // put dynamics into array (5)
- var index int64
- for _, value := range n.List.Slice() {
- if value.Op == OKEY {
- index = indexconst(value.Left)
- if index < 0 {
- Fatalf("slicelit: invalid index %v", value.Left)
- }
- value = value.Right
- }
- a := nod(OINDEX, vauto, nodintconst(index))
- a.SetBounded(true)
- index++
-
- // TODO need to check bounds?
-
- switch value.Op {
- case OSLICELIT:
- break
-
- case OARRAYLIT, OSTRUCTLIT:
- k := initKindDynamic
- if vstat == nil {
- // Generate both static and dynamic initializations.
- // See issue #31987.
- k = initKindLocalCode
- }
- fixedlit(ctxt, k, value, a, init)
- continue
- }
-
- if vstat != nil && value.isGoConst() { // already set by copy from static value
- continue
- }
-
- // build list of vauto[c] = expr
- setlineno(value)
- a = nod(OAS, a, value)
-
- a = typecheck(a, ctxStmt)
- a = orderStmtInPlace(a, map[string][]*Node{})
- a = walkstmt(a)
- init.Append(a)
- }
-
- // make slice out of heap (6)
- a = nod(OAS, var_, nod(OSLICE, vauto, nil))
-
- a = typecheck(a, ctxStmt)
- a = orderStmtInPlace(a, map[string][]*Node{})
- a = walkstmt(a)
- init.Append(a)
-}
-
-func maplit(n *Node, m *Node, init *Nodes) {
- // make the map var
- a := nod(OMAKE, nil, nil)
- a.Esc = n.Esc
- a.List.Set2(typenod(n.Type), nodintconst(int64(n.List.Len())))
- litas(m, a, init)
-
- entries := n.List.Slice()
-
- // The order pass already removed any dynamic (runtime-computed) entries.
- // All remaining entries are static. Double-check that.
- for _, r := range entries {
- if !isStaticCompositeLiteral(r.Left) || !isStaticCompositeLiteral(r.Right) {
- Fatalf("maplit: entry is not a literal: %v", r)
- }
- }
-
- if len(entries) > 25 {
- // For a large number of entries, put them in an array and loop.
-
- // build types [count]Tindex and [count]Tvalue
- tk := types.NewArray(n.Type.Key(), int64(len(entries)))
- te := types.NewArray(n.Type.Elem(), int64(len(entries)))
-
- tk.SetNoalg(true)
- te.SetNoalg(true)
-
- dowidth(tk)
- dowidth(te)
-
- // make and initialize static arrays
- vstatk := readonlystaticname(tk)
- vstate := readonlystaticname(te)
-
- datak := nod(OARRAYLIT, nil, nil)
- datae := nod(OARRAYLIT, nil, nil)
- for _, r := range entries {
- datak.List.Append(r.Left)
- datae.List.Append(r.Right)
- }
- fixedlit(inInitFunction, initKindStatic, datak, vstatk, init)
- fixedlit(inInitFunction, initKindStatic, datae, vstate, init)
-
- // loop adding structure elements to map
- // for i = 0; i < len(vstatk); i++ {
- // map[vstatk[i]] = vstate[i]
- // }
- i := temp(types.Types[TINT])
- rhs := nod(OINDEX, vstate, i)
- rhs.SetBounded(true)
-
- kidx := nod(OINDEX, vstatk, i)
- kidx.SetBounded(true)
- lhs := nod(OINDEX, m, kidx)
-
- zero := nod(OAS, i, nodintconst(0))
- cond := nod(OLT, i, nodintconst(tk.NumElem()))
- incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
- body := nod(OAS, lhs, rhs)
-
- loop := nod(OFOR, cond, incr)
- loop.Nbody.Set1(body)
- loop.Ninit.Set1(zero)
-
- loop = typecheck(loop, ctxStmt)
- loop = walkstmt(loop)
- init.Append(loop)
- return
- }
- // For a small number of entries, just add them directly.
-
- // Build list of var[c] = expr.
- // Use temporaries so that mapassign1 can have addressable key, elem.
- // TODO(josharian): avoid map key temporaries for mapfast_* assignments with literal keys.
- tmpkey := temp(m.Type.Key())
- tmpelem := temp(m.Type.Elem())
-
- for _, r := range entries {
- index, elem := r.Left, r.Right
-
- setlineno(index)
- a := nod(OAS, tmpkey, index)
- a = typecheck(a, ctxStmt)
- a = walkstmt(a)
- init.Append(a)
-
- setlineno(elem)
- a = nod(OAS, tmpelem, elem)
- a = typecheck(a, ctxStmt)
- a = walkstmt(a)
- init.Append(a)
-
- setlineno(tmpelem)
- a = nod(OAS, nod(OINDEX, m, tmpkey), tmpelem)
- a = typecheck(a, ctxStmt)
- a = walkstmt(a)
- init.Append(a)
- }
-
- a = nod(OVARKILL, tmpkey, nil)
- a = typecheck(a, ctxStmt)
- init.Append(a)
- a = nod(OVARKILL, tmpelem, nil)
- a = typecheck(a, ctxStmt)
- init.Append(a)
-}
-
-func anylit(n *Node, var_ *Node, init *Nodes) {
- t := n.Type
- switch n.Op {
- default:
- Fatalf("anylit: not lit, op=%v node=%v", n.Op, n)
-
- case ONAME:
- a := nod(OAS, var_, n)
- a = typecheck(a, ctxStmt)
- init.Append(a)
-
- case OPTRLIT:
- if !t.IsPtr() {
- Fatalf("anylit: not ptr")
- }
-
- var r *Node
- if n.Right != nil {
- // n.Right is stack temporary used as backing store.
- init.Append(nod(OAS, n.Right, nil)) // zero backing store, just in case (#18410)
- r = nod(OADDR, n.Right, nil)
- r = typecheck(r, ctxExpr)
- } else {
- r = nod(ONEW, nil, nil)
- r.SetTypecheck(1)
- r.Type = t
- r.Esc = n.Esc
- }
-
- r = walkexpr(r, init)
- a := nod(OAS, var_, r)
-
- a = typecheck(a, ctxStmt)
- init.Append(a)
-
- var_ = nod(ODEREF, var_, nil)
- var_ = typecheck(var_, ctxExpr|ctxAssign)
- anylit(n.Left, var_, init)
-
- case OSTRUCTLIT, OARRAYLIT:
- if !t.IsStruct() && !t.IsArray() {
- Fatalf("anylit: not struct/array")
- }
-
- if var_.isSimpleName() && n.List.Len() > 4 {
- // lay out static data
- vstat := readonlystaticname(t)
-
- ctxt := inInitFunction
- if n.Op == OARRAYLIT {
- ctxt = inNonInitFunction
- }
- fixedlit(ctxt, initKindStatic, n, vstat, init)
-
- // copy static to var
- a := nod(OAS, var_, vstat)
-
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- init.Append(a)
-
- // add expressions to automatic
- fixedlit(inInitFunction, initKindDynamic, n, var_, init)
- break
- }
-
- var components int64
- if n.Op == OARRAYLIT {
- components = t.NumElem()
- } else {
- components = int64(t.NumFields())
- }
- // initialization of an array or struct with unspecified components (missing fields or arrays)
- if var_.isSimpleName() || int64(n.List.Len()) < components {
- a := nod(OAS, var_, nil)
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- init.Append(a)
- }
-
- fixedlit(inInitFunction, initKindLocalCode, n, var_, init)
-
- case OSLICELIT:
- slicelit(inInitFunction, n, var_, init)
-
- case OMAPLIT:
- if !t.IsMap() {
- Fatalf("anylit: not map")
- }
- maplit(n, var_, init)
- }
-}
-
-func oaslit(n *Node, init *Nodes) bool {
- if n.Left == nil || n.Right == nil {
- // not a special composite literal assignment
- return false
- }
- if n.Left.Type == nil || n.Right.Type == nil {
- // not a special composite literal assignment
- return false
- }
- if !n.Left.isSimpleName() {
- // not a special composite literal assignment
- return false
- }
- if !types.Identical(n.Left.Type, n.Right.Type) {
- // not a special composite literal assignment
- return false
- }
-
- switch n.Right.Op {
- default:
- // not a special composite literal assignment
- return false
-
- case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
- if vmatch1(n.Left, n.Right) {
- // not a special composite literal assignment
- return false
- }
- anylit(n.Right, n.Left, init)
- }
-
- n.Op = OEMPTY
- n.Right = nil
- return true
-}
-
-func getlit(lit *Node) int {
- if smallintconst(lit) {
- return int(lit.Int64Val())
- }
- return -1
-}
-
-// stataddr sets nam to the static address of n and reports whether it succeeded.
-func stataddr(nam *Node, n *Node) bool {
- if n == nil {
- return false
- }
-
- switch n.Op {
- case ONAME:
- *nam = *n
- return true
-
- case ODOT:
- if !stataddr(nam, n.Left) {
- break
- }
- nam.Xoffset += n.Xoffset
- nam.Type = n.Type
- return true
-
- case OINDEX:
- if n.Left.Type.IsSlice() {
- break
- }
- if !stataddr(nam, n.Left) {
- break
- }
- l := getlit(n.Right)
- if l < 0 {
- break
- }
-
- // Check for overflow.
- if n.Type.Width != 0 && thearch.MAXWIDTH/n.Type.Width <= int64(l) {
- break
- }
- nam.Xoffset += int64(l) * n.Type.Width
- nam.Type = n.Type
- return true
- }
-
- return false
-}
-
-func (s *InitSchedule) initplan(n *Node) {
- if s.initplans[n] != nil {
- return
- }
- p := new(InitPlan)
- s.initplans[n] = p
- switch n.Op {
- default:
- Fatalf("initplan")
-
- case OARRAYLIT, OSLICELIT:
- var k int64
- for _, a := range n.List.Slice() {
- if a.Op == OKEY {
- k = indexconst(a.Left)
- if k < 0 {
- Fatalf("initplan arraylit: invalid index %v", a.Left)
- }
- a = a.Right
- }
- s.addvalue(p, k*n.Type.Elem().Width, a)
- k++
- }
-
- case OSTRUCTLIT:
- for _, a := range n.List.Slice() {
- if a.Op != OSTRUCTKEY {
- Fatalf("initplan structlit")
- }
- if a.Sym.IsBlank() {
- continue
- }
- s.addvalue(p, a.Xoffset, a.Left)
- }
-
- case OMAPLIT:
- for _, a := range n.List.Slice() {
- if a.Op != OKEY {
- Fatalf("initplan maplit")
- }
- s.addvalue(p, -1, a.Right)
- }
- }
-}
-
-func (s *InitSchedule) addvalue(p *InitPlan, xoffset int64, n *Node) {
- // special case: zero can be dropped entirely
- if isZero(n) {
- return
- }
-
- // special case: inline struct and array (not slice) literals
- if isvaluelit(n) {
- s.initplan(n)
- q := s.initplans[n]
- for _, qe := range q.E {
- // qe is a copy; we are not modifying entries in q.E
- qe.Xoffset += xoffset
- p.E = append(p.E, qe)
- }
- return
- }
-
- // add to plan
- p.E = append(p.E, InitEntry{Xoffset: xoffset, Expr: n})
-}
-
-func isZero(n *Node) bool {
- switch n.Op {
- case OLITERAL:
- switch u := n.Val().U.(type) {
- default:
- Dump("unexpected literal", n)
- Fatalf("isZero")
- case *NilVal:
- return true
- case string:
- return u == ""
- case bool:
- return !u
- case *Mpint:
- return u.CmpInt64(0) == 0
- case *Mpflt:
- return u.CmpFloat64(0) == 0
- case *Mpcplx:
- return u.Real.CmpFloat64(0) == 0 && u.Imag.CmpFloat64(0) == 0
- }
-
- case OARRAYLIT:
- for _, n1 := range n.List.Slice() {
- if n1.Op == OKEY {
- n1 = n1.Right
- }
- if !isZero(n1) {
- return false
- }
- }
- return true
-
- case OSTRUCTLIT:
- for _, n1 := range n.List.Slice() {
- if !isZero(n1.Left) {
- return false
- }
- }
- return true
- }
-
- return false
-}
-
-func isvaluelit(n *Node) bool {
- return n.Op == OARRAYLIT || n.Op == OSTRUCTLIT
-}
-
-func genAsStatic(as *Node) {
- if as.Left.Type == nil {
- Fatalf("genAsStatic as.Left not typechecked")
- }
-
- var nam Node
- if !stataddr(&nam, as.Left) || (nam.Class() != PEXTERN && as.Left != nblank) {
- Fatalf("genAsStatic: lhs %v", as.Left)
- }
-
- switch {
- case as.Right.Op == OLITERAL:
- litsym(&nam, as.Right, int(as.Right.Type.Width))
- case as.Right.Op == ONAME && as.Right.Class() == PFUNC:
- pfuncsym(&nam, as.Right)
- default:
- Fatalf("genAsStatic: rhs %v", as.Right)
- }
-}
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
deleted file mode 100644
index defefd76b342b..0000000000000
--- a/src/cmd/compile/internal/gc/subr.go
+++ /dev/null
@@ -1,1918 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/objabi"
- "cmd/internal/src"
- "crypto/md5"
- "encoding/binary"
- "fmt"
- "os"
- "runtime/debug"
- "sort"
- "strconv"
- "strings"
- "sync"
- "unicode"
- "unicode/utf8"
-)
-
-type Error struct {
- pos src.XPos
- msg string
-}
-
-var errors []Error
-
-// largeStack is info about a function whose stack frame is too large (rare).
-type largeStack struct {
- locals int64
- args int64
- callee int64
- pos src.XPos
-}
-
-var (
- largeStackFramesMu sync.Mutex // protects largeStackFrames
- largeStackFrames []largeStack
-)
-
-func errorexit() {
- flusherrors()
- if outfile != "" {
- os.Remove(outfile)
- }
- os.Exit(2)
-}
-
-func adderrorname(n *Node) {
- if n.Op != ODOT {
- return
- }
- old := fmt.Sprintf("%v: undefined: %v\n", n.Line(), n.Left)
- if len(errors) > 0 && errors[len(errors)-1].pos.Line() == n.Pos.Line() && errors[len(errors)-1].msg == old {
- errors[len(errors)-1].msg = fmt.Sprintf("%v: undefined: %v in %v\n", n.Line(), n.Left, n)
- }
-}
-
-func adderr(pos src.XPos, format string, args ...interface{}) {
- msg := fmt.Sprintf(format, args...)
- // Only add the position if know the position.
- // See issue golang.org/issue/11361.
- if pos.IsKnown() {
- msg = fmt.Sprintf("%v: %s", linestr(pos), msg)
- }
- errors = append(errors, Error{
- pos: pos,
- msg: msg + "\n",
- })
-}
-
-// byPos sorts errors by source position.
-type byPos []Error
-
-func (x byPos) Len() int { return len(x) }
-func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) }
-func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-
-// flusherrors sorts errors seen so far by line number, prints them to stdout,
-// and empties the errors array.
-func flusherrors() {
- Ctxt.Bso.Flush()
- if len(errors) == 0 {
- return
- }
- sort.Stable(byPos(errors))
- for i, err := range errors {
- if i == 0 || err.msg != errors[i-1].msg {
- fmt.Printf("%s", err.msg)
- }
- }
- errors = errors[:0]
-}
-
-func hcrash() {
- if Debug.h != 0 {
- flusherrors()
- if outfile != "" {
- os.Remove(outfile)
- }
- var x *int
- *x = 0
- }
-}
-
-func linestr(pos src.XPos) string {
- return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1)
-}
-
-// lasterror keeps track of the most recently issued error.
-// It is used to avoid multiple error messages on the same
-// line.
-var lasterror struct {
- syntax src.XPos // source position of last syntax error
- other src.XPos // source position of last non-syntax error
- msg string // error message of last non-syntax error
-}
-
-// sameline reports whether two positions a, b are on the same line.
-func sameline(a, b src.XPos) bool {
- p := Ctxt.PosTable.Pos(a)
- q := Ctxt.PosTable.Pos(b)
- return p.Base() == q.Base() && p.Line() == q.Line()
-}
-
-func yyerrorl(pos src.XPos, format string, args ...interface{}) {
- msg := fmt.Sprintf(format, args...)
-
- if strings.HasPrefix(msg, "syntax error") {
- nsyntaxerrors++
- // only one syntax error per line, no matter what error
- if sameline(lasterror.syntax, pos) {
- return
- }
- lasterror.syntax = pos
- } else {
- // only one of multiple equal non-syntax errors per line
- // (flusherrors shows only one of them, so we filter them
- // here as best as we can (they may not appear in order)
- // so that we don't count them here and exit early, and
- // then have nothing to show for.)
- if sameline(lasterror.other, pos) && lasterror.msg == msg {
- return
- }
- lasterror.other = pos
- lasterror.msg = msg
- }
-
- adderr(pos, "%s", msg)
-
- hcrash()
- nerrors++
- if nsavederrors+nerrors >= 10 && Debug.e == 0 {
- flusherrors()
- fmt.Printf("%v: too many errors\n", linestr(pos))
- errorexit()
- }
-}
-
-func yyerrorv(lang string, format string, args ...interface{}) {
- what := fmt.Sprintf(format, args...)
- yyerrorl(lineno, "%s requires %s or later (-lang was set to %s; check go.mod)", what, lang, flag_lang)
-}
-
-func yyerror(format string, args ...interface{}) {
- yyerrorl(lineno, format, args...)
-}
-
-func Warn(fmt_ string, args ...interface{}) {
- Warnl(lineno, fmt_, args...)
-}
-
-func Warnl(line src.XPos, fmt_ string, args ...interface{}) {
- adderr(line, fmt_, args...)
- if Debug.m != 0 {
- flusherrors()
- }
-}
-
-func Fatalf(fmt_ string, args ...interface{}) {
- flusherrors()
-
- if Debug_panic != 0 || nsavederrors+nerrors == 0 {
- fmt.Printf("%v: internal compiler error: ", linestr(lineno))
- fmt.Printf(fmt_, args...)
- fmt.Printf("\n")
-
- // If this is a released compiler version, ask for a bug report.
- if strings.HasPrefix(objabi.Version, "go") {
- fmt.Printf("\n")
- fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
- fmt.Printf("https://golang.org/issue/new\n")
- } else {
- // Not a release; dump a stack trace, too.
- fmt.Println()
- os.Stdout.Write(debug.Stack())
- fmt.Println()
- }
- }
-
- hcrash()
- errorexit()
-}
-
-// hasUniquePos reports whether n has a unique position that can be
-// used for reporting error messages.
-//
-// It's primarily used to distinguish references to named objects,
-// whose Pos will point back to their declaration position rather than
-// their usage position.
-func hasUniquePos(n *Node) bool {
- switch n.Op {
- case ONAME, OPACK:
- return false
- case OLITERAL, OTYPE:
- if n.Sym != nil {
- return false
- }
- }
-
- if !n.Pos.IsKnown() {
- if Debug.K != 0 {
- Warn("setlineno: unknown position (line 0)")
- }
- return false
- }
-
- return true
-}
-
-func setlineno(n *Node) src.XPos {
- lno := lineno
- if n != nil && hasUniquePos(n) {
- lineno = n.Pos
- }
- return lno
-}
-
-func lookup(name string) *types.Sym {
- return localpkg.Lookup(name)
-}
-
-// lookupN looks up the symbol starting with prefix and ending with
-// the decimal n. If prefix is too long, lookupN panics.
-func lookupN(prefix string, n int) *types.Sym {
- var buf [20]byte // plenty long enough for all current users
- copy(buf[:], prefix)
- b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10)
- return localpkg.LookupBytes(b)
-}
-
-// autolabel generates a new Name node for use with
-// an automatically generated label.
-// prefix is a short mnemonic (e.g. ".s" for switch)
-// to help with debugging.
-// It should begin with "." to avoid conflicts with
-// user labels.
-func autolabel(prefix string) *types.Sym {
- if prefix[0] != '.' {
- Fatalf("autolabel prefix must start with '.', have %q", prefix)
- }
- fn := Curfn
- if Curfn == nil {
- Fatalf("autolabel outside function")
- }
- n := fn.Func.Label
- fn.Func.Label++
- return lookupN(prefix, int(n))
-}
-
-// find all the exported symbols in package opkg
-// and make them available in the current package
-func importdot(opkg *types.Pkg, pack *Node) {
- n := 0
- for _, s := range opkg.Syms {
- if s.Def == nil {
- continue
- }
- if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
- continue
- }
- s1 := lookup(s.Name)
- if s1.Def != nil {
- pkgerror := fmt.Sprintf("during import %q", opkg.Path)
- redeclare(lineno, s1, pkgerror)
- continue
- }
-
- s1.Def = s.Def
- s1.Block = s.Block
- if asNode(s1.Def).Name == nil {
- Dump("s1def", asNode(s1.Def))
- Fatalf("missing Name")
- }
- asNode(s1.Def).Name.Pack = pack
- s1.Origpkg = opkg
- n++
- }
-
- if n == 0 {
- // can't possibly be used - there were no symbols
- yyerrorl(pack.Pos, "imported and not used: %q", opkg.Path)
- }
-}
-
-func nod(op Op, nleft, nright *Node) *Node {
- return nodl(lineno, op, nleft, nright)
-}
-
-func nodl(pos src.XPos, op Op, nleft, nright *Node) *Node {
- var n *Node
- switch op {
- case OCLOSURE, ODCLFUNC:
- var x struct {
- n Node
- f Func
- }
- n = &x.n
- n.Func = &x.f
- case ONAME:
- Fatalf("use newname instead")
- case OLABEL, OPACK:
- var x struct {
- n Node
- m Name
- }
- n = &x.n
- n.Name = &x.m
- default:
- n = new(Node)
- }
- n.Op = op
- n.Left = nleft
- n.Right = nright
- n.Pos = pos
- n.Xoffset = BADWIDTH
- n.Orig = n
- return n
-}
-
-// newname returns a new ONAME Node associated with symbol s.
-func newname(s *types.Sym) *Node {
- n := newnamel(lineno, s)
- n.Name.Curfn = Curfn
- return n
-}
-
-// newnamel returns a new ONAME Node associated with symbol s at position pos.
-// The caller is responsible for setting n.Name.Curfn.
-func newnamel(pos src.XPos, s *types.Sym) *Node {
- if s == nil {
- Fatalf("newnamel nil")
- }
-
- var x struct {
- n Node
- m Name
- p Param
- }
- n := &x.n
- n.Name = &x.m
- n.Name.Param = &x.p
-
- n.Op = ONAME
- n.Pos = pos
- n.Orig = n
-
- n.Sym = s
- return n
-}
-
-// nodSym makes a Node with Op op and with the Left field set to left
-// and the Sym field set to sym. This is for ODOT and friends.
-func nodSym(op Op, left *Node, sym *types.Sym) *Node {
- return nodlSym(lineno, op, left, sym)
-}
-
-// nodlSym makes a Node with position Pos, with Op op, and with the Left field set to left
-// and the Sym field set to sym. This is for ODOT and friends.
-func nodlSym(pos src.XPos, op Op, left *Node, sym *types.Sym) *Node {
- n := nodl(pos, op, left, nil)
- n.Sym = sym
- return n
-}
-
-// rawcopy returns a shallow copy of n.
-// Note: copy or sepcopy (rather than rawcopy) is usually the
-// correct choice (see comment with Node.copy, below).
-func (n *Node) rawcopy() *Node {
- copy := *n
- return ©
-}
-
-// sepcopy returns a separate shallow copy of n, with the copy's
-// Orig pointing to itself.
-func (n *Node) sepcopy() *Node {
- copy := *n
- copy.Orig = ©
- return ©
-}
-
-// copy returns shallow copy of n and adjusts the copy's Orig if
-// necessary: In general, if n.Orig points to itself, the copy's
-// Orig should point to itself as well. Otherwise, if n is modified,
-// the copy's Orig node appears modified, too, and then doesn't
-// represent the original node anymore.
-// (This caused the wrong complit Op to be used when printing error
-// messages; see issues #26855, #27765).
-func (n *Node) copy() *Node {
- copy := *n
- if n.Orig == n {
- copy.Orig = ©
- }
- return ©
-}
-
-// methcmp sorts methods by symbol.
-type methcmp []*types.Field
-
-func (x methcmp) Len() int { return len(x) }
-func (x methcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x methcmp) Less(i, j int) bool { return x[i].Sym.Less(x[j].Sym) }
-
-func nodintconst(v int64) *Node {
- u := new(Mpint)
- u.SetInt64(v)
- return nodlit(Val{u})
-}
-
-func nodnil() *Node {
- return nodlit(Val{new(NilVal)})
-}
-
-func nodbool(b bool) *Node {
- return nodlit(Val{b})
-}
-
-func nodstr(s string) *Node {
- return nodlit(Val{s})
-}
-
-// treecopy recursively copies n, with the exception of
-// ONAME, OLITERAL, OTYPE, and ONONAME leaves.
-// If pos.IsKnown(), it sets the source position of newly
-// allocated nodes to pos.
-func treecopy(n *Node, pos src.XPos) *Node {
- if n == nil {
- return nil
- }
-
- switch n.Op {
- default:
- m := n.sepcopy()
- m.Left = treecopy(n.Left, pos)
- m.Right = treecopy(n.Right, pos)
- m.List.Set(listtreecopy(n.List.Slice(), pos))
- if pos.IsKnown() {
- m.Pos = pos
- }
- if m.Name != nil && n.Op != ODCLFIELD {
- Dump("treecopy", n)
- Fatalf("treecopy Name")
- }
- return m
-
- case OPACK:
- // OPACK nodes are never valid in const value declarations,
- // but allow them like any other declared symbol to avoid
- // crashing (golang.org/issue/11361).
- fallthrough
-
- case ONAME, ONONAME, OLITERAL, OTYPE:
- return n
-
- }
-}
-
-// isNil reports whether n represents the universal untyped zero value "nil".
-func (n *Node) isNil() bool {
- // Check n.Orig because constant propagation may produce typed nil constants,
- // which don't exist in the Go spec.
- return Isconst(n.Orig, CTNIL)
-}
-
-func isptrto(t *types.Type, et types.EType) bool {
- if t == nil {
- return false
- }
- if !t.IsPtr() {
- return false
- }
- t = t.Elem()
- if t == nil {
- return false
- }
- if t.Etype != et {
- return false
- }
- return true
-}
-
-func (n *Node) isBlank() bool {
- if n == nil {
- return false
- }
- return n.Sym.IsBlank()
-}
-
-// methtype returns the underlying type, if any,
-// that owns methods with receiver parameter t.
-// The result is either a named type or an anonymous struct.
-func methtype(t *types.Type) *types.Type {
- if t == nil {
- return nil
- }
-
- // Strip away pointer if it's there.
- if t.IsPtr() {
- if t.Sym != nil {
- return nil
- }
- t = t.Elem()
- if t == nil {
- return nil
- }
- }
-
- // Must be a named type or anonymous struct.
- if t.Sym == nil && !t.IsStruct() {
- return nil
- }
-
- // Check types.
- if issimple[t.Etype] {
- return t
- }
- switch t.Etype {
- case TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRING, TSTRUCT:
- return t
- }
- return nil
-}
-
-// Is type src assignment compatible to type dst?
-// If so, return op code to use in conversion.
-// If not, return OXXX. In this case, the string return parameter may
-// hold a reason why. In all other cases, it'll be the empty string.
-func assignop(src, dst *types.Type) (Op, string) {
- if src == dst {
- return OCONVNOP, ""
- }
- if src == nil || dst == nil || src.Etype == TFORW || dst.Etype == TFORW || src.Orig == nil || dst.Orig == nil {
- return OXXX, ""
- }
-
- // 1. src type is identical to dst.
- if types.Identical(src, dst) {
- return OCONVNOP, ""
- }
-
- // 2. src and dst have identical underlying types
- // and either src or dst is not a named type or
- // both are empty interface types.
- // For assignable but different non-empty interface types,
- // we want to recompute the itab. Recomputing the itab ensures
- // that itabs are unique (thus an interface with a compile-time
- // type I has an itab with interface type I).
- if types.Identical(src.Orig, dst.Orig) {
- if src.IsEmptyInterface() {
- // Conversion between two empty interfaces
- // requires no code.
- return OCONVNOP, ""
- }
- if (src.Sym == nil || dst.Sym == nil) && !src.IsInterface() {
- // Conversion between two types, at least one unnamed,
- // needs no conversion. The exception is nonempty interfaces
- // which need to have their itab updated.
- return OCONVNOP, ""
- }
- }
-
- // 3. dst is an interface type and src implements dst.
- if dst.IsInterface() && src.Etype != TNIL {
- var missing, have *types.Field
- var ptr int
- if implements(src, dst, &missing, &have, &ptr) {
- return OCONVIFACE, ""
- }
-
- // we'll have complained about this method anyway, suppress spurious messages.
- if have != nil && have.Sym == missing.Sym && (have.Type.Broke() || missing.Type.Broke()) {
- return OCONVIFACE, ""
- }
-
- var why string
- if isptrto(src, TINTER) {
- why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src)
- } else if have != nil && have.Sym == missing.Sym && have.Nointerface() {
- why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
- } else if have != nil && have.Sym == missing.Sym {
- why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
- "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
- } else if ptr != 0 {
- why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
- } else if have != nil {
- why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
- "\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
- } else {
- why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
- }
-
- return OXXX, why
- }
-
- if isptrto(dst, TINTER) {
- why := fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst)
- return OXXX, why
- }
-
- if src.IsInterface() && dst.Etype != TBLANK {
- var missing, have *types.Field
- var ptr int
- var why string
- if implements(dst, src, &missing, &have, &ptr) {
- why = ": need type assertion"
- }
- return OXXX, why
- }
-
- // 4. src is a bidirectional channel value, dst is a channel type,
- // src and dst have identical element types, and
- // either src or dst is not a named type.
- if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
- if types.Identical(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
- return OCONVNOP, ""
- }
- }
-
- // 5. src is the predeclared identifier nil and dst is a nillable type.
- if src.Etype == TNIL {
- switch dst.Etype {
- case TPTR,
- TFUNC,
- TMAP,
- TCHAN,
- TINTER,
- TSLICE:
- return OCONVNOP, ""
- }
- }
-
- // 6. rule about untyped constants - already converted by defaultlit.
-
- // 7. Any typed value can be assigned to the blank identifier.
- if dst.Etype == TBLANK {
- return OCONVNOP, ""
- }
-
- return OXXX, ""
-}
-
-// Can we convert a value of type src to a value of type dst?
-// If so, return op code to use in conversion (maybe OCONVNOP).
-// If not, return OXXX. In this case, the string return parameter may
-// hold a reason why. In all other cases, it'll be the empty string.
-// srcConstant indicates whether the value of type src is a constant.
-func convertop(srcConstant bool, src, dst *types.Type) (Op, string) {
- if src == dst {
- return OCONVNOP, ""
- }
- if src == nil || dst == nil {
- return OXXX, ""
- }
-
- // Conversions from regular to go:notinheap are not allowed
- // (unless it's unsafe.Pointer). These are runtime-specific
- // rules.
- // (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() {
- why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem())
- return OXXX, why
- }
- // (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) {
- why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem())
- return OXXX, why
- }
-
- // 1. src can be assigned to dst.
- op, why := assignop(src, dst)
- if op != OXXX {
- return op, why
- }
-
- // The rules for interfaces are no different in conversions
- // than assignments. If interfaces are involved, stop now
- // with the good message from assignop.
- // Otherwise clear the error.
- if src.IsInterface() || dst.IsInterface() {
- return OXXX, why
- }
-
- // 2. Ignoring struct tags, src and dst have identical underlying types.
- if types.IdenticalIgnoreTags(src.Orig, dst.Orig) {
- return OCONVNOP, ""
- }
-
- // 3. src and dst are unnamed pointer types and, ignoring struct tags,
- // their base types have identical underlying types.
- if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil {
- if types.IdenticalIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
- return OCONVNOP, ""
- }
- }
-
- // 4. src and dst are both integer or floating point types.
- if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) {
- if simtype[src.Etype] == simtype[dst.Etype] {
- return OCONVNOP, ""
- }
- return OCONV, ""
- }
-
- // 5. src and dst are both complex types.
- if src.IsComplex() && dst.IsComplex() {
- if simtype[src.Etype] == simtype[dst.Etype] {
- return OCONVNOP, ""
- }
- return OCONV, ""
- }
-
- // Special case for constant conversions: any numeric
- // conversion is potentially okay. We'll validate further
- // within evconst. See #38117.
- if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
- return OCONV, ""
- }
-
- // 6. src is an integer or has type []byte or []rune
- // and dst is a string type.
- if src.IsInteger() && dst.IsString() {
- return ORUNESTR, ""
- }
-
- if src.IsSlice() && dst.IsString() {
- if src.Elem().Etype == types.Bytetype.Etype {
- return OBYTES2STR, ""
- }
- if src.Elem().Etype == types.Runetype.Etype {
- return ORUNES2STR, ""
- }
- }
-
- // 7. src is a string and dst is []byte or []rune.
- // String to slice.
- if src.IsString() && dst.IsSlice() {
- if dst.Elem().Etype == types.Bytetype.Etype {
- return OSTR2BYTES, ""
- }
- if dst.Elem().Etype == types.Runetype.Etype {
- return OSTR2RUNES, ""
- }
- }
-
- // 8. src is a pointer or uintptr and dst is unsafe.Pointer.
- if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
- return OCONVNOP, ""
- }
-
- // 9. src is unsafe.Pointer and dst is a pointer or uintptr.
- if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
- return OCONVNOP, ""
- }
-
- // src is map and dst is a pointer to corresponding hmap.
- // This rule is needed for the implementation detail that
- // go gc maps are implemented as a pointer to a hmap struct.
- if src.Etype == TMAP && dst.IsPtr() &&
- src.MapType().Hmap == dst.Elem() {
- return OCONVNOP, ""
- }
-
- return OXXX, ""
-}
-
-func assignconv(n *Node, t *types.Type, context string) *Node {
- return assignconvfn(n, t, func() string { return context })
-}
-
-// Convert node n for assignment to type t.
-func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
- if n == nil || n.Type == nil || n.Type.Broke() {
- return n
- }
-
- if t.Etype == TBLANK && n.Type.Etype == TNIL {
- yyerror("use of untyped nil")
- }
-
- n = convlit1(n, t, false, context)
- if n.Type == nil {
- return n
- }
- if t.Etype == TBLANK {
- return n
- }
-
- // Convert ideal bool from comparison to plain bool
- // if the next step is non-bool (like interface{}).
- if n.Type == types.UntypedBool && !t.IsBoolean() {
- if n.Op == ONAME || n.Op == OLITERAL {
- r := nod(OCONVNOP, n, nil)
- r.Type = types.Types[TBOOL]
- r.SetTypecheck(1)
- r.SetImplicit(true)
- n = r
- }
- }
-
- if types.Identical(n.Type, t) {
- return n
- }
-
- op, why := assignop(n.Type, t)
- if op == OXXX {
- yyerror("cannot use %L as type %v in %s%s", n, t, context(), why)
- op = OCONV
- }
-
- r := nod(op, n, nil)
- r.Type = t
- r.SetTypecheck(1)
- r.SetImplicit(true)
- r.Orig = n.Orig
- return r
-}
-
-// IsMethod reports whether n is a method.
-// n must be a function or a method.
-func (n *Node) IsMethod() bool {
- return n.Type.Recv() != nil
-}
-
-// SliceBounds returns n's slice bounds: low, high, and max in expr[low:high:max].
-// n must be a slice expression. max is nil if n is a simple slice expression.
-func (n *Node) SliceBounds() (low, high, max *Node) {
- if n.List.Len() == 0 {
- return nil, nil, nil
- }
-
- switch n.Op {
- case OSLICE, OSLICEARR, OSLICESTR:
- s := n.List.Slice()
- return s[0], s[1], nil
- case OSLICE3, OSLICE3ARR:
- s := n.List.Slice()
- return s[0], s[1], s[2]
- }
- Fatalf("SliceBounds op %v: %v", n.Op, n)
- return nil, nil, nil
-}
-
-// SetSliceBounds sets n's slice bounds, where n is a slice expression.
-// n must be a slice expression. If max is non-nil, n must be a full slice expression.
-func (n *Node) SetSliceBounds(low, high, max *Node) {
- switch n.Op {
- case OSLICE, OSLICEARR, OSLICESTR:
- if max != nil {
- Fatalf("SetSliceBounds %v given three bounds", n.Op)
- }
- s := n.List.Slice()
- if s == nil {
- if low == nil && high == nil {
- return
- }
- n.List.Set2(low, high)
- return
- }
- s[0] = low
- s[1] = high
- return
- case OSLICE3, OSLICE3ARR:
- s := n.List.Slice()
- if s == nil {
- if low == nil && high == nil && max == nil {
- return
- }
- n.List.Set3(low, high, max)
- return
- }
- s[0] = low
- s[1] = high
- s[2] = max
- return
- }
- Fatalf("SetSliceBounds op %v: %v", n.Op, n)
-}
-
-// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).
-// o must be a slicing op.
-func (o Op) IsSlice3() bool {
- switch o {
- case OSLICE, OSLICEARR, OSLICESTR:
- return false
- case OSLICE3, OSLICE3ARR:
- return true
- }
- Fatalf("IsSlice3 op %v", o)
- return false
-}
-
-// 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) backingArrayPtrLen() (ptr, len *Node) {
- var init Nodes
- c := cheapexpr(n, &init)
- if c != n || init.Len() != 0 {
- Fatalf("backingArrayPtrLen not cheap: %v", n)
- }
- ptr = nod(OSPTR, n, nil)
- 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
-}
-
-// labeledControl returns the control flow Node (for, switch, select)
-// associated with the label n, if any.
-func (n *Node) labeledControl() *Node {
- if n.Op != OLABEL {
- Fatalf("labeledControl %v", n.Op)
- }
- ctl := n.Name.Defn
- if ctl == nil {
- return nil
- }
- switch ctl.Op {
- case OFOR, OFORUNTIL, OSWITCH, OSELECT:
- return ctl
- }
- return nil
-}
-
-func syslook(name string) *Node {
- s := Runtimepkg.Lookup(name)
- if s == nil || s.Def == nil {
- Fatalf("syslook: can't find runtime.%s", name)
- }
- return asNode(s.Def)
-}
-
-// typehash computes a hash value for type t to use in type switch statements.
-func typehash(t *types.Type) uint32 {
- p := t.LongString()
-
- // Using MD5 is overkill, but reduces accidental collisions.
- h := md5.Sum([]byte(p))
- return binary.LittleEndian.Uint32(h[:4])
-}
-
-// updateHasCall checks whether expression n contains any function
-// calls and sets the n.HasCall flag if so.
-func updateHasCall(n *Node) {
- if n == nil {
- return
- }
- n.SetHasCall(calcHasCall(n))
-}
-
-func calcHasCall(n *Node) bool {
- if n.Ninit.Len() != 0 {
- // TODO(mdempsky): This seems overly conservative.
- return true
- }
-
- switch n.Op {
- case OLITERAL, ONAME, OTYPE:
- if n.HasCall() {
- Fatalf("OLITERAL/ONAME/OTYPE should never have calls: %+v", n)
- }
- return false
- case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER:
- return true
- case OANDAND, OOROR:
- // hard with instrumented code
- if instrumenting {
- return true
- }
- case OINDEX, OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR,
- ODEREF, ODOTPTR, ODOTTYPE, ODIV, OMOD:
- // These ops might panic, make sure they are done
- // before we start marshaling args for a call. See issue 16760.
- return true
-
- // When using soft-float, these ops might be rewritten to function calls
- // so we ensure they are evaluated first.
- case OADD, OSUB, ONEG, OMUL:
- if thearch.SoftFloat && (isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) {
- return true
- }
- case OLT, OEQ, ONE, OLE, OGE, OGT:
- if thearch.SoftFloat && (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype]) {
- return true
- }
- case OCONV:
- if thearch.SoftFloat && ((isFloat[n.Type.Etype] || isComplex[n.Type.Etype]) || (isFloat[n.Left.Type.Etype] || isComplex[n.Left.Type.Etype])) {
- return true
- }
- }
-
- if n.Left != nil && n.Left.HasCall() {
- return true
- }
- if n.Right != nil && n.Right.HasCall() {
- return true
- }
- return false
-}
-
-func badtype(op Op, tl, tr *types.Type) {
- var s string
- if tl != nil {
- s += fmt.Sprintf("\n\t%v", tl)
- }
- if tr != nil {
- s += fmt.Sprintf("\n\t%v", tr)
- }
-
- // common mistake: *struct and *interface.
- if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() {
- if tl.Elem().IsStruct() && tr.Elem().IsInterface() {
- s += "\n\t(*struct vs *interface)"
- } else if tl.Elem().IsInterface() && tr.Elem().IsStruct() {
- s += "\n\t(*interface vs *struct)"
- }
- }
-
- yyerror("illegal types for operand: %v%s", op, s)
-}
-
-// brcom returns !(op).
-// For example, brcom(==) is !=.
-func brcom(op Op) Op {
- switch op {
- case OEQ:
- return ONE
- case ONE:
- return OEQ
- case OLT:
- return OGE
- case OGT:
- return OLE
- case OLE:
- return OGT
- case OGE:
- return OLT
- }
- Fatalf("brcom: no com for %v\n", op)
- return op
-}
-
-// brrev returns reverse(op).
-// For example, Brrev(<) is >.
-func brrev(op Op) Op {
- switch op {
- case OEQ:
- return OEQ
- case ONE:
- return ONE
- case OLT:
- return OGT
- case OGT:
- return OLT
- case OLE:
- return OGE
- case OGE:
- return OLE
- }
- Fatalf("brrev: no rev for %v\n", op)
- return op
-}
-
-// return side effect-free n, appending side effects to init.
-// result is assignable if n is.
-func safeexpr(n *Node, init *Nodes) *Node {
- if n == nil {
- return nil
- }
-
- if n.Ninit.Len() != 0 {
- walkstmtlist(n.Ninit.Slice())
- init.AppendNodes(&n.Ninit)
- }
-
- switch n.Op {
- case ONAME, OLITERAL:
- return n
-
- case ODOT, OLEN, OCAP:
- l := safeexpr(n.Left, init)
- if l == n.Left {
- return n
- }
- r := n.copy()
- r.Left = l
- r = typecheck(r, ctxExpr)
- r = walkexpr(r, init)
- return r
-
- case ODOTPTR, ODEREF:
- l := safeexpr(n.Left, init)
- if l == n.Left {
- return n
- }
- a := n.copy()
- a.Left = l
- a = walkexpr(a, init)
- return a
-
- case OINDEX, OINDEXMAP:
- l := safeexpr(n.Left, init)
- r := safeexpr(n.Right, init)
- if l == n.Left && r == n.Right {
- return n
- }
- a := n.copy()
- a.Left = l
- a.Right = r
- a = walkexpr(a, init)
- return a
-
- case OSTRUCTLIT, OARRAYLIT, OSLICELIT:
- if isStaticCompositeLiteral(n) {
- return n
- }
- }
-
- // make a copy; must not be used as an lvalue
- if islvalue(n) {
- Fatalf("missing lvalue case in safeexpr: %v", n)
- }
- return cheapexpr(n, init)
-}
-
-func copyexpr(n *Node, t *types.Type, init *Nodes) *Node {
- l := temp(t)
- a := nod(OAS, l, n)
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- init.Append(a)
- return l
-}
-
-// return side-effect free and cheap n, appending side effects to init.
-// result may not be assignable.
-func cheapexpr(n *Node, init *Nodes) *Node {
- switch n.Op {
- case ONAME, OLITERAL:
- return n
- }
-
- return copyexpr(n, n.Type, init)
-}
-
-// Code to resolve elided DOTs in embedded types.
-
-// A Dlist stores a pointer to a TFIELD Type embedded within
-// a TSTRUCT or TINTER Type.
-type Dlist struct {
- field *types.Field
-}
-
-// dotlist is used by adddot1 to record the path of embedded fields
-// used to access a target field or method.
-// Must be non-nil so that dotpath returns a non-nil slice even if d is zero.
-var dotlist = make([]Dlist, 10)
-
-// lookdot0 returns the number of fields or methods named s associated
-// with Type t. If exactly one exists, it will be returned in *save
-// (if save is not nil).
-func lookdot0(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) int {
- u := t
- if u.IsPtr() {
- u = u.Elem()
- }
-
- c := 0
- if u.IsStruct() || u.IsInterface() {
- for _, f := range u.Fields().Slice() {
- if f.Sym == s || (ignorecase && f.IsMethod() && strings.EqualFold(f.Sym.Name, s.Name)) {
- if save != nil {
- *save = f
- }
- c++
- }
- }
- }
-
- u = t
- if t.Sym != nil && t.IsPtr() && !t.Elem().IsPtr() {
- // If t is a defined pointer type, then x.m is shorthand for (*x).m.
- u = t.Elem()
- }
- u = methtype(u)
- if u != nil {
- for _, f := range u.Methods().Slice() {
- if f.Embedded == 0 && (f.Sym == s || (ignorecase && strings.EqualFold(f.Sym.Name, s.Name))) {
- if save != nil {
- *save = f
- }
- c++
- }
- }
- }
-
- return c
-}
-
-// adddot1 returns the number of fields or methods named s at depth d in Type t.
-// If exactly one exists, it will be returned in *save (if save is not nil),
-// and dotlist will contain the path of embedded fields traversed to find it,
-// in reverse order. If none exist, more will indicate whether t contains any
-// embedded fields at depth d, so callers can decide whether to retry at
-// a greater depth.
-func adddot1(s *types.Sym, t *types.Type, d int, save **types.Field, ignorecase bool) (c int, more bool) {
- if t.Recur() {
- return
- }
- t.SetRecur(true)
- defer t.SetRecur(false)
-
- var u *types.Type
- d--
- if d < 0 {
- // We've reached our target depth. If t has any fields/methods
- // named s, then we're done. Otherwise, we still need to check
- // below for embedded fields.
- c = lookdot0(s, t, save, ignorecase)
- if c != 0 {
- return c, false
- }
- }
-
- u = t
- if u.IsPtr() {
- u = u.Elem()
- }
- if !u.IsStruct() && !u.IsInterface() {
- return c, false
- }
-
- for _, f := range u.Fields().Slice() {
- if f.Embedded == 0 || f.Sym == nil {
- continue
- }
- if d < 0 {
- // Found an embedded field at target depth.
- return c, true
- }
- a, more1 := adddot1(s, f.Type, d, save, ignorecase)
- if a != 0 && c == 0 {
- dotlist[d].field = f
- }
- c += a
- if more1 {
- more = true
- }
- }
-
- return c, more
-}
-
-// dotpath computes the unique shortest explicit selector path to fully qualify
-// a selection expression x.f, where x is of type t and f is the symbol s.
-// If no such path exists, dotpath returns nil.
-// If there are multiple shortest paths to the same depth, ambig is true.
-func dotpath(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) (path []Dlist, ambig bool) {
- // The embedding of types within structs imposes a tree structure onto
- // types: structs parent the types they embed, and types parent their
- // fields or methods. Our goal here is to find the shortest path to
- // a field or method named s in the subtree rooted at t. To accomplish
- // that, we iteratively perform depth-first searches of increasing depth
- // until we either find the named field/method or exhaust the tree.
- for d := 0; ; d++ {
- if d > len(dotlist) {
- dotlist = append(dotlist, Dlist{})
- }
- if c, more := adddot1(s, t, d, save, ignorecase); c == 1 {
- return dotlist[:d], false
- } else if c > 1 {
- return nil, true
- } else if !more {
- return nil, false
- }
- }
-}
-
-// in T.field
-// find missing fields that
-// will give shortest unique addressing.
-// modify the tree with missing type names.
-func adddot(n *Node) *Node {
- n.Left = typecheck(n.Left, ctxType|ctxExpr)
- if n.Left.Diag() {
- n.SetDiag(true)
- }
- t := n.Left.Type
- if t == nil {
- return n
- }
-
- if n.Left.Op == OTYPE {
- return n
- }
-
- s := n.Sym
- if s == nil {
- return n
- }
-
- switch path, ambig := dotpath(s, t, nil, false); {
- case path != nil:
- // rebuild elided dots
- for c := len(path) - 1; c >= 0; c-- {
- n.Left = nodSym(ODOT, n.Left, path[c].field.Sym)
- n.Left.SetImplicit(true)
- }
- case ambig:
- yyerror("ambiguous selector %v", n)
- n.Left = nil
- }
-
- return n
-}
-
-// Code to help generate trampoline functions for methods on embedded
-// types. These are approx the same as the corresponding adddot
-// routines except that they expect to be called with unique tasks and
-// they return the actual methods.
-
-type Symlink struct {
- field *types.Field
-}
-
-var slist []Symlink
-
-func expand0(t *types.Type) {
- u := t
- if u.IsPtr() {
- u = u.Elem()
- }
-
- if u.IsInterface() {
- for _, f := range u.Fields().Slice() {
- if f.Sym.Uniq() {
- continue
- }
- f.Sym.SetUniq(true)
- slist = append(slist, Symlink{field: f})
- }
-
- return
- }
-
- u = methtype(t)
- if u != nil {
- for _, f := range u.Methods().Slice() {
- if f.Sym.Uniq() {
- continue
- }
- f.Sym.SetUniq(true)
- slist = append(slist, Symlink{field: f})
- }
- }
-}
-
-func expand1(t *types.Type, top bool) {
- if t.Recur() {
- return
- }
- t.SetRecur(true)
-
- if !top {
- expand0(t)
- }
-
- u := t
- if u.IsPtr() {
- u = u.Elem()
- }
-
- if u.IsStruct() || u.IsInterface() {
- for _, f := range u.Fields().Slice() {
- if f.Embedded == 0 {
- continue
- }
- if f.Sym == nil {
- continue
- }
- expand1(f.Type, false)
- }
- }
-
- t.SetRecur(false)
-}
-
-func expandmeth(t *types.Type) {
- if t == nil || t.AllMethods().Len() != 0 {
- return
- }
-
- // mark top-level method symbols
- // so that expand1 doesn't consider them.
- for _, f := range t.Methods().Slice() {
- f.Sym.SetUniq(true)
- }
-
- // generate all reachable methods
- slist = slist[:0]
- expand1(t, true)
-
- // check each method to be uniquely reachable
- var ms []*types.Field
- for i, sl := range slist {
- slist[i].field = nil
- sl.field.Sym.SetUniq(false)
-
- var f *types.Field
- path, _ := dotpath(sl.field.Sym, t, &f, false)
- if path == nil {
- continue
- }
-
- // dotpath may have dug out arbitrary fields, we only want methods.
- if !f.IsMethod() {
- continue
- }
-
- // add it to the base type method list
- f = f.Copy()
- f.Embedded = 1 // needs a trampoline
- for _, d := range path {
- if d.field.Type.IsPtr() {
- f.Embedded = 2
- break
- }
- }
- ms = append(ms, f)
- }
-
- for _, f := range t.Methods().Slice() {
- f.Sym.SetUniq(false)
- }
-
- ms = append(ms, t.Methods().Slice()...)
- sort.Sort(methcmp(ms))
- t.AllMethods().Set(ms)
-}
-
-// Given funarg struct list, return list of ODCLFIELD Node fn args.
-func structargs(tl *types.Type, mustname bool) []*Node {
- var args []*Node
- gen := 0
- for _, t := range tl.Fields().Slice() {
- s := t.Sym
- if mustname && (s == nil || s.Name == "_") {
- // invent a name so that we can refer to it in the trampoline
- s = lookupN(".anon", gen)
- gen++
- }
- a := symfield(s, t.Type)
- a.Pos = t.Pos
- a.SetIsDDD(t.IsDDD())
- args = append(args, a)
- }
-
- return args
-}
-
-// Generate a wrapper function to convert from
-// a receiver of type T to a receiver of type U.
-// That is,
-//
-// func (t T) M() {
-// ...
-// }
-//
-// already exists; this function generates
-//
-// func (u U) M() {
-// u.M()
-// }
-//
-// where the types T and U are such that u.M() is valid
-// and calls the T.M method.
-// The resulting function is for use in method tables.
-//
-// rcvr - U
-// method - M func (t T)(), a TFIELD type struct
-// newnam - the eventual mangled name of this function
-func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
- if false && Debug.r != 0 {
- fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam)
- }
-
- // Only generate (*T).M wrappers for T.M in T's own package.
- if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
- rcvr.Elem().Sym != nil && rcvr.Elem().Sym.Pkg != localpkg {
- return
- }
-
- // Only generate I.M wrappers for I in I's own package
- // but keep doing it for error.Error (was issue #29304).
- if rcvr.IsInterface() && rcvr.Sym != nil && rcvr.Sym.Pkg != localpkg && rcvr != types.Errortype {
- return
- }
-
- lineno = autogeneratedPos
- dclcontext = PEXTERN
-
- tfn := nod(OTFUNC, nil, nil)
- tfn.Left = namedfield(".this", rcvr)
- tfn.List.Set(structargs(method.Type.Params(), true))
- tfn.Rlist.Set(structargs(method.Type.Results(), false))
-
- fn := dclfunc(newnam, tfn)
- fn.Func.SetDupok(true)
-
- nthis := asNode(tfn.Type.Recv().Nname)
-
- methodrcvr := method.Type.Recv().Type
-
- // generate nil pointer check for better error
- if rcvr.IsPtr() && rcvr.Elem() == methodrcvr {
- // generating wrapper from *T to T.
- n := nod(OIF, nil, nil)
- n.Left = nod(OEQ, nthis, nodnil())
- call := nod(OCALL, syslook("panicwrap"), nil)
- n.Nbody.Set1(call)
- fn.Nbody.Append(n)
- }
-
- dot := adddot(nodSym(OXDOT, nthis, method.Sym))
-
- // generate call
- // It's not possible to use a tail call when dynamic linking on ppc64le. The
- // bad scenario is when a local call is made to the wrapper: the wrapper will
- // call the implementation, which might be in a different module and so set
- // the TOC to the appropriate value for that module. But if it returns
- // directly to the wrapper's caller, nothing will reset it to the correct
- // value for that function.
- if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) && !(thearch.LinkArch.Name == "ppc64le" && Ctxt.Flag_dynlink) {
- // generate tail call: adjust pointer receiver and jump to embedded method.
- dot = dot.Left // skip final .M
- // TODO(mdempsky): Remove dependency on dotlist.
- if !dotlist[0].field.Type.IsPtr() {
- dot = nod(OADDR, dot, nil)
- }
- as := nod(OAS, nthis, convnop(dot, rcvr))
- fn.Nbody.Append(as)
- fn.Nbody.Append(nodSym(ORETJMP, nil, methodSym(methodrcvr, method.Sym)))
- } else {
- fn.Func.SetWrapper(true) // ignore frame for panic+recover matching
- call := nod(OCALL, dot, nil)
- call.List.Set(paramNnames(tfn.Type))
- call.SetIsDDD(tfn.Type.IsVariadic())
- if method.Type.NumResults() > 0 {
- n := nod(ORETURN, nil, nil)
- n.List.Set1(call)
- call = n
- }
- fn.Nbody.Append(call)
- }
-
- if false && Debug.r != 0 {
- dumplist("genwrapper body", fn.Nbody)
- }
-
- funcbody()
- if debug_dclstack != 0 {
- testdclstack()
- }
-
- fn = typecheck(fn, ctxStmt)
-
- Curfn = fn
- typecheckslice(fn.Nbody.Slice(), ctxStmt)
-
- // Inline calls within (*T).M wrappers. This is safe because we only
- // generate those wrappers within the same compilation unit as (T).M.
- // TODO(mdempsky): Investigate why we can't enable this more generally.
- if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym != nil {
- inlcalls(fn)
- }
- escapeFuncs([]*Node{fn}, false)
-
- Curfn = nil
- xtop = append(xtop, fn)
-}
-
-func paramNnames(ft *types.Type) []*Node {
- args := make([]*Node, ft.NumParams())
- for i, f := range ft.Params().FieldSlice() {
- args[i] = asNode(f.Nname)
- }
- return args
-}
-
-func hashmem(t *types.Type) *Node {
- sym := Runtimepkg.Lookup("memhash")
-
- n := newname(sym)
- setNodeNameFunc(n)
- n.Type = functype(nil, []*Node{
- anonfield(types.NewPtr(t)),
- anonfield(types.Types[TUINTPTR]),
- anonfield(types.Types[TUINTPTR]),
- }, []*Node{
- anonfield(types.Types[TUINTPTR]),
- })
- return n
-}
-
-func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field, followptr bool) {
- if t == nil {
- return nil, false
- }
-
- path, ambig := dotpath(s, t, &m, ignorecase)
- if path == nil {
- if ambig {
- yyerror("%v.%v is ambiguous", t, s)
- }
- return nil, false
- }
-
- for _, d := range path {
- if d.field.Type.IsPtr() {
- followptr = true
- break
- }
- }
-
- if !m.IsMethod() {
- yyerror("%v.%v is a field, not a method", t, s)
- return nil, followptr
- }
-
- return m, followptr
-}
-
-func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
- t0 := t
- if t == nil {
- return false
- }
-
- if t.IsInterface() {
- i := 0
- tms := t.Fields().Slice()
- for _, im := range iface.Fields().Slice() {
- for i < len(tms) && tms[i].Sym != im.Sym {
- i++
- }
- if i == len(tms) {
- *m = im
- *samename = nil
- *ptr = 0
- return false
- }
- tm := tms[i]
- if !types.Identical(tm.Type, im.Type) {
- *m = im
- *samename = tm
- *ptr = 0
- return false
- }
- }
-
- return true
- }
-
- t = methtype(t)
- var tms []*types.Field
- if t != nil {
- expandmeth(t)
- tms = t.AllMethods().Slice()
- }
- i := 0
- for _, im := range iface.Fields().Slice() {
- if im.Broke() {
- continue
- }
- for i < len(tms) && tms[i].Sym != im.Sym {
- i++
- }
- if i == len(tms) {
- *m = im
- *samename, _ = ifacelookdot(im.Sym, t, true)
- *ptr = 0
- return false
- }
- tm := tms[i]
- if tm.Nointerface() || !types.Identical(tm.Type, im.Type) {
- *m = im
- *samename = tm
- *ptr = 0
- return false
- }
- followptr := tm.Embedded == 2
-
- // if pointer receiver in method,
- // the method does not exist for value types.
- rcvr := tm.Type.Recv().Type
- if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !isifacemethod(tm.Type) {
- if false && Debug.r != 0 {
- yyerror("interface pointer mismatch")
- }
-
- *m = im
- *samename = nil
- *ptr = 1
- return false
- }
- }
-
- // We're going to emit an OCONVIFACE.
- // Call itabname so that (t, iface)
- // gets added to itabs early, which allows
- // us to de-virtualize calls through this
- // type/interface pair later. See peekitabs in reflect.go
- if isdirectiface(t0) && !iface.IsEmptyInterface() {
- itabname(t0, iface)
- }
- return true
-}
-
-func listtreecopy(l []*Node, pos src.XPos) []*Node {
- var out []*Node
- for _, n := range l {
- out = append(out, treecopy(n, pos))
- }
- return out
-}
-
-func liststmt(l []*Node) *Node {
- n := nod(OBLOCK, nil, nil)
- n.List.Set(l)
- if len(l) != 0 {
- n.Pos = l[0].Pos
- }
- return n
-}
-
-func (l Nodes) asblock() *Node {
- n := nod(OBLOCK, nil, nil)
- n.List = l
- if l.Len() != 0 {
- n.Pos = l.First().Pos
- }
- return n
-}
-
-func ngotype(n *Node) *types.Sym {
- if n.Type != nil {
- return typenamesym(n.Type)
- }
- return nil
-}
-
-// The result of addinit MUST be assigned back to n, e.g.
-// n.Left = addinit(n.Left, init)
-func addinit(n *Node, init []*Node) *Node {
- if len(init) == 0 {
- return n
- }
- if n.mayBeShared() {
- // Introduce OCONVNOP to hold init list.
- n = nod(OCONVNOP, n, nil)
- n.Type = n.Left.Type
- n.SetTypecheck(1)
- }
-
- n.Ninit.Prepend(init...)
- n.SetHasCall(true)
- return n
-}
-
-// The linker uses the magic symbol prefixes "go." and "type."
-// Avoid potential confusion between import paths and symbols
-// by rejecting these reserved imports for now. Also, people
-// "can do weird things in GOPATH and we'd prefer they didn't
-// do _that_ weird thing" (per rsc). See also #4257.
-var reservedimports = []string{
- "go",
- "type",
-}
-
-func isbadimport(path string, allowSpace bool) bool {
- if strings.Contains(path, "\x00") {
- yyerror("import path contains NUL")
- return true
- }
-
- for _, ri := range reservedimports {
- if path == ri {
- yyerror("import path %q is reserved and cannot be used", path)
- return true
- }
- }
-
- for _, r := range path {
- if r == utf8.RuneError {
- yyerror("import path contains invalid UTF-8 sequence: %q", path)
- return true
- }
-
- if r < 0x20 || r == 0x7f {
- yyerror("import path contains control character: %q", path)
- return true
- }
-
- if r == '\\' {
- yyerror("import path contains backslash; use slash: %q", path)
- return true
- }
-
- if !allowSpace && unicode.IsSpace(r) {
- yyerror("import path contains space character: %q", path)
- return true
- }
-
- if strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r) {
- yyerror("import path contains invalid character '%c': %q", r, path)
- return true
- }
- }
-
- return false
-}
-
-// Can this type be stored directly in an interface word?
-// Yes, if the representation is a single pointer.
-func isdirectiface(t *types.Type) bool {
- if t.Broke() {
- return false
- }
-
- switch t.Etype {
- case TPTR:
- // Pointers to notinheap types must be stored indirectly. See issue 42076.
- return !t.Elem().NotInHeap()
- case TCHAN,
- TMAP,
- TFUNC,
- TUNSAFEPTR:
- return true
-
- case TARRAY:
- // Array of 1 direct iface type can be direct.
- return t.NumElem() == 1 && isdirectiface(t.Elem())
-
- case TSTRUCT:
- // Struct with 1 field of direct iface type can be direct.
- return t.NumFields() == 1 && isdirectiface(t.Field(0).Type)
- }
-
- return false
-}
-
-// itabType loads the _type field from a runtime.itab struct.
-func itabType(itab *Node) *Node {
- typ := nodSym(ODOTPTR, itab, nil)
- typ.Type = types.NewPtr(types.Types[TUINT8])
- typ.SetTypecheck(1)
- typ.Xoffset = int64(Widthptr) // offset of _type in runtime.itab
- typ.SetBounded(true) // guaranteed not to fault
- return typ
-}
-
-// ifaceData loads the data field from an interface.
-// The concrete type must be known to have type t.
-// It follows the pointer if !isdirectiface(t).
-func ifaceData(pos src.XPos, n *Node, t *types.Type) *Node {
- if t.IsInterface() {
- Fatalf("ifaceData interface: %v", t)
- }
- ptr := nodlSym(pos, OIDATA, n, nil)
- if isdirectiface(t) {
- ptr.Type = t
- ptr.SetTypecheck(1)
- return ptr
- }
- ptr.Type = types.NewPtr(t)
- ptr.SetTypecheck(1)
- ind := nodl(pos, ODEREF, ptr, nil)
- ind.Type = t
- ind.SetTypecheck(1)
- ind.SetBounded(true)
- return ind
-}
-
-// typePos returns the position associated with t.
-// This is where t was declared or where it appeared as a type expression.
-func typePos(t *types.Type) src.XPos {
- n := asNode(t.Nod)
- if n == nil || !n.Pos.IsKnown() {
- Fatalf("bad type: %v", t)
- }
- return n.Pos
-}
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
deleted file mode 100644
index 8d9fbe300e84b..0000000000000
--- a/src/cmd/compile/internal/gc/swt.go
+++ /dev/null
@@ -1,756 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/src"
- "sort"
-)
-
-// typecheckswitch typechecks a switch statement.
-func typecheckswitch(n *Node) {
- typecheckslice(n.Ninit.Slice(), ctxStmt)
- if n.Left != nil && n.Left.Op == OTYPESW {
- typecheckTypeSwitch(n)
- } else {
- typecheckExprSwitch(n)
- }
-}
-
-func typecheckTypeSwitch(n *Node) {
- n.Left.Right = typecheck(n.Left.Right, ctxExpr)
- t := n.Left.Right.Type
- if t != nil && !t.IsInterface() {
- yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right)
- t = nil
- }
-
- // We don't actually declare the type switch's guarded
- // declaration itself. So if there are no cases, we won't
- // notice that it went unused.
- if v := n.Left.Left; v != nil && !v.isBlank() && n.List.Len() == 0 {
- yyerrorl(v.Pos, "%v declared but not used", v.Sym)
- }
-
- var defCase, nilCase *Node
- var ts typeSet
- for _, ncase := range n.List.Slice() {
- ls := ncase.List.Slice()
- if len(ls) == 0 { // default:
- if defCase != nil {
- yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
- } else {
- defCase = ncase
- }
- }
-
- for i := range ls {
- ls[i] = typecheck(ls[i], ctxExpr|ctxType)
- n1 := ls[i]
- if t == nil || n1.Type == nil {
- continue
- }
-
- var missing, have *types.Field
- var ptr int
- switch {
- case n1.isNil(): // case nil:
- if nilCase != nil {
- yyerrorl(ncase.Pos, "multiple nil cases in type switch (first at %v)", nilCase.Line())
- } else {
- nilCase = ncase
- }
- case n1.Op != OTYPE:
- yyerrorl(ncase.Pos, "%L is not a type", n1)
- case !n1.Type.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr) && !missing.Broke():
- if have != nil && !have.Broke() {
- yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
- " (wrong type for %v method)\n\thave %v%S\n\twant %v%S", n.Left.Right, n1.Type, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
- } else if ptr != 0 {
- yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
- " (%v method has pointer receiver)", n.Left.Right, n1.Type, missing.Sym)
- } else {
- yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
- " (missing %v method)", n.Left.Right, n1.Type, missing.Sym)
- }
- }
-
- if n1.Op == OTYPE {
- ts.add(ncase.Pos, n1.Type)
- }
- }
-
- if ncase.Rlist.Len() != 0 {
- // Assign the clause variable's type.
- vt := t
- if len(ls) == 1 {
- if ls[0].Op == OTYPE {
- vt = ls[0].Type
- } else if ls[0].Op != OLITERAL { // TODO(mdempsky): Should be !ls[0].isNil()
- // Invalid single-type case;
- // mark variable as broken.
- vt = nil
- }
- }
-
- // TODO(mdempsky): It should be possible to
- // still typecheck the case body.
- if vt == nil {
- continue
- }
-
- nvar := ncase.Rlist.First()
- nvar.Type = vt
- nvar = typecheck(nvar, ctxExpr|ctxAssign)
- ncase.Rlist.SetFirst(nvar)
- }
-
- typecheckslice(ncase.Nbody.Slice(), ctxStmt)
- }
-}
-
-type typeSet struct {
- m map[string][]typeSetEntry
-}
-
-type typeSetEntry struct {
- pos src.XPos
- typ *types.Type
-}
-
-func (s *typeSet) add(pos src.XPos, typ *types.Type) {
- if s.m == nil {
- s.m = make(map[string][]typeSetEntry)
- }
-
- // LongString does not uniquely identify types, so we need to
- // disambiguate collisions with types.Identical.
- // TODO(mdempsky): Add a method that *is* unique.
- ls := typ.LongString()
- prevs := s.m[ls]
- for _, prev := range prevs {
- if types.Identical(typ, prev.typ) {
- yyerrorl(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, linestr(prev.pos))
- return
- }
- }
- s.m[ls] = append(prevs, typeSetEntry{pos, typ})
-}
-
-func typecheckExprSwitch(n *Node) {
- t := types.Types[TBOOL]
- if n.Left != nil {
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- t = n.Left.Type
- }
-
- var nilonly string
- if t != nil {
- switch {
- case t.IsMap():
- nilonly = "map"
- case t.Etype == TFUNC:
- nilonly = "func"
- case t.IsSlice():
- nilonly = "slice"
-
- case !IsComparable(t):
- if t.IsStruct() {
- yyerrorl(n.Pos, "cannot switch on %L (struct containing %v cannot be compared)", n.Left, IncomparableField(t).Type)
- } else {
- yyerrorl(n.Pos, "cannot switch on %L", n.Left)
- }
- t = nil
- }
- }
-
- var defCase *Node
- var cs constSet
- for _, ncase := range n.List.Slice() {
- ls := ncase.List.Slice()
- if len(ls) == 0 { // default:
- if defCase != nil {
- yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
- } else {
- defCase = ncase
- }
- }
-
- for i := range ls {
- setlineno(ncase)
- ls[i] = typecheck(ls[i], ctxExpr)
- ls[i] = defaultlit(ls[i], t)
- n1 := ls[i]
- if t == nil || n1.Type == nil {
- continue
- }
-
- if nilonly != "" && !n1.isNil() {
- yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
- } else if t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type) {
- yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1)
- } else {
- op1, _ := assignop(n1.Type, t)
- op2, _ := assignop(t, n1.Type)
- if op1 == OXXX && op2 == OXXX {
- if n.Left != nil {
- yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
- } else {
- yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
- }
- }
- }
-
- // Don't check for duplicate bools. Although the spec allows it,
- // (1) the compiler hasn't checked it in the past, so compatibility mandates it, and
- // (2) it would disallow useful things like
- // case GOARCH == "arm" && GOARM == "5":
- // case GOARCH == "arm":
- // which would both evaluate to false for non-ARM compiles.
- if !n1.Type.IsBoolean() {
- cs.add(ncase.Pos, n1, "case", "switch")
- }
- }
-
- typecheckslice(ncase.Nbody.Slice(), ctxStmt)
- }
-}
-
-// walkswitch walks a switch statement.
-func walkswitch(sw *Node) {
- // Guard against double walk, see #25776.
- if sw.List.Len() == 0 && sw.Nbody.Len() > 0 {
- return // Was fatal, but eliminating every possible source of double-walking is hard
- }
-
- if sw.Left != nil && sw.Left.Op == OTYPESW {
- walkTypeSwitch(sw)
- } else {
- walkExprSwitch(sw)
- }
-}
-
-// walkExprSwitch generates an AST implementing sw. sw is an
-// expression switch.
-func walkExprSwitch(sw *Node) {
- lno := setlineno(sw)
-
- cond := sw.Left
- sw.Left = nil
-
- // convert switch {...} to switch true {...}
- if cond == nil {
- cond = nodbool(true)
- cond = typecheck(cond, ctxExpr)
- cond = defaultlit(cond, nil)
- }
-
- // Given "switch string(byteslice)",
- // with all cases being side-effect free,
- // use a zero-cost alias of the byte slice.
- // Do this before calling walkexpr on cond,
- // because walkexpr will lower the string
- // conversion into a runtime call.
- // See issue 24937 for more discussion.
- if cond.Op == OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
- cond.Op = OBYTES2STRTMP
- }
-
- cond = walkexpr(cond, &sw.Ninit)
- if cond.Op != OLITERAL {
- cond = copyexpr(cond, cond.Type, &sw.Nbody)
- }
-
- lineno = lno
-
- s := exprSwitch{
- exprname: cond,
- }
-
- var defaultGoto *Node
- var body Nodes
- for _, ncase := range sw.List.Slice() {
- label := autolabel(".s")
- jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
-
- // Process case dispatch.
- if ncase.List.Len() == 0 {
- if defaultGoto != nil {
- Fatalf("duplicate default case not detected during typechecking")
- }
- defaultGoto = jmp
- }
-
- for _, n1 := range ncase.List.Slice() {
- s.Add(ncase.Pos, n1, jmp)
- }
-
- // Process body.
- body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
- body.Append(ncase.Nbody.Slice()...)
- if fall, pos := hasFall(ncase.Nbody.Slice()); !fall {
- br := nod(OBREAK, nil, nil)
- br.Pos = pos
- body.Append(br)
- }
- }
- sw.List.Set(nil)
-
- if defaultGoto == nil {
- br := nod(OBREAK, nil, nil)
- br.Pos = br.Pos.WithNotStmt()
- defaultGoto = br
- }
-
- s.Emit(&sw.Nbody)
- sw.Nbody.Append(defaultGoto)
- sw.Nbody.AppendNodes(&body)
- walkstmtlist(sw.Nbody.Slice())
-}
-
-// An exprSwitch walks an expression switch.
-type exprSwitch struct {
- exprname *Node // value being switched on
-
- done Nodes
- clauses []exprClause
-}
-
-type exprClause struct {
- pos src.XPos
- lo, hi *Node
- jmp *Node
-}
-
-func (s *exprSwitch) Add(pos src.XPos, expr, jmp *Node) {
- c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
- if okforcmp[s.exprname.Type.Etype] && expr.Op == OLITERAL {
- s.clauses = append(s.clauses, c)
- return
- }
-
- s.flush()
- s.clauses = append(s.clauses, c)
- s.flush()
-}
-
-func (s *exprSwitch) Emit(out *Nodes) {
- s.flush()
- out.AppendNodes(&s.done)
-}
-
-func (s *exprSwitch) flush() {
- cc := s.clauses
- s.clauses = nil
- if len(cc) == 0 {
- return
- }
-
- // Caution: If len(cc) == 1, then cc[0] might not an OLITERAL.
- // The code below is structured to implicitly handle this case
- // (e.g., sort.Slice doesn't need to invoke the less function
- // when there's only a single slice element).
-
- if s.exprname.Type.IsString() && len(cc) >= 2 {
- // Sort strings by length and then by value. It is
- // much cheaper to compare lengths than values, and
- // all we need here is consistency. We respect this
- // sorting below.
- sort.Slice(cc, func(i, j int) bool {
- si := cc[i].lo.StringVal()
- sj := cc[j].lo.StringVal()
- if len(si) != len(sj) {
- return len(si) < len(sj)
- }
- return si < sj
- })
-
- // runLen returns the string length associated with a
- // particular run of exprClauses.
- runLen := func(run []exprClause) int64 { return int64(len(run[0].lo.StringVal())) }
-
- // Collapse runs of consecutive strings with the same length.
- var runs [][]exprClause
- start := 0
- for i := 1; i < len(cc); i++ {
- if runLen(cc[start:]) != runLen(cc[i:]) {
- runs = append(runs, cc[start:i])
- start = i
- }
- }
- runs = append(runs, cc[start:])
-
- // Perform two-level binary search.
- nlen := nod(OLEN, s.exprname, nil)
- binarySearch(len(runs), &s.done,
- func(i int) *Node {
- return nod(OLE, nlen, nodintconst(runLen(runs[i-1])))
- },
- func(i int, nif *Node) {
- run := runs[i]
- nif.Left = nod(OEQ, nlen, nodintconst(runLen(run)))
- s.search(run, &nif.Nbody)
- },
- )
- return
- }
-
- sort.Slice(cc, func(i, j int) bool {
- return compareOp(cc[i].lo.Val(), OLT, cc[j].lo.Val())
- })
-
- // Merge consecutive integer cases.
- if s.exprname.Type.IsInteger() {
- merged := cc[:1]
- for _, c := range cc[1:] {
- last := &merged[len(merged)-1]
- if last.jmp == c.jmp && last.hi.Int64Val()+1 == c.lo.Int64Val() {
- last.hi = c.lo
- } else {
- merged = append(merged, c)
- }
- }
- cc = merged
- }
-
- s.search(cc, &s.done)
-}
-
-func (s *exprSwitch) search(cc []exprClause, out *Nodes) {
- binarySearch(len(cc), out,
- func(i int) *Node {
- return nod(OLE, s.exprname, cc[i-1].hi)
- },
- func(i int, nif *Node) {
- c := &cc[i]
- nif.Left = c.test(s.exprname)
- nif.Nbody.Set1(c.jmp)
- },
- )
-}
-
-func (c *exprClause) test(exprname *Node) *Node {
- // Integer range.
- if c.hi != c.lo {
- low := nodl(c.pos, OGE, exprname, c.lo)
- high := nodl(c.pos, OLE, exprname, c.hi)
- return nodl(c.pos, OANDAND, low, high)
- }
-
- // Optimize "switch true { ...}" and "switch false { ... }".
- if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
- if exprname.BoolVal() {
- return c.lo
- } else {
- return nodl(c.pos, ONOT, c.lo, nil)
- }
- }
-
- return nodl(c.pos, OEQ, exprname, c.lo)
-}
-
-func allCaseExprsAreSideEffectFree(sw *Node) bool {
- // In theory, we could be more aggressive, allowing any
- // side-effect-free expressions in cases, but it's a bit
- // tricky because some of that information is unavailable due
- // to the introduction of temporaries during order.
- // Restricting to constants is simple and probably powerful
- // enough.
-
- for _, ncase := range sw.List.Slice() {
- if ncase.Op != OCASE {
- Fatalf("switch string(byteslice) bad op: %v", ncase.Op)
- }
- for _, v := range ncase.List.Slice() {
- if v.Op != OLITERAL {
- return false
- }
- }
- }
- return true
-}
-
-// hasFall reports whether stmts ends with a "fallthrough" statement.
-func hasFall(stmts []*Node) (bool, src.XPos) {
- // Search backwards for the index of the fallthrough
- // statement. Do not assume it'll be in the last
- // position, since in some cases (e.g. when the statement
- // list contains autotmp_ variables), one or more OVARKILL
- // nodes will be at the end of the list.
-
- i := len(stmts) - 1
- for i >= 0 && stmts[i].Op == OVARKILL {
- i--
- }
- if i < 0 {
- return false, src.NoXPos
- }
- return stmts[i].Op == OFALL, stmts[i].Pos
-}
-
-// walkTypeSwitch generates an AST that implements sw, where sw is a
-// type switch.
-func walkTypeSwitch(sw *Node) {
- var s typeSwitch
- s.facename = sw.Left.Right
- sw.Left = nil
-
- s.facename = walkexpr(s.facename, &sw.Ninit)
- s.facename = copyexpr(s.facename, s.facename.Type, &sw.Nbody)
- s.okname = temp(types.Types[TBOOL])
-
- // Get interface descriptor word.
- // For empty interfaces this will be the type.
- // For non-empty interfaces this will be the itab.
- itab := nod(OITAB, s.facename, nil)
-
- // For empty interfaces, do:
- // if e._type == nil {
- // do nil case if it exists, otherwise default
- // }
- // h := e._type.hash
- // Use a similar strategy for non-empty interfaces.
- ifNil := nod(OIF, nil, nil)
- ifNil.Left = nod(OEQ, itab, nodnil())
- lineno = lineno.WithNotStmt() // disable statement marks after the first check.
- ifNil.Left = typecheck(ifNil.Left, ctxExpr)
- ifNil.Left = defaultlit(ifNil.Left, nil)
- // ifNil.Nbody assigned at end.
- sw.Nbody.Append(ifNil)
-
- // Load hash from type or itab.
- dotHash := nodSym(ODOTPTR, itab, nil)
- dotHash.Type = types.Types[TUINT32]
- dotHash.SetTypecheck(1)
- if s.facename.Type.IsEmptyInterface() {
- dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
- } else {
- dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab
- }
- dotHash.SetBounded(true) // guaranteed not to fault
- s.hashname = copyexpr(dotHash, dotHash.Type, &sw.Nbody)
-
- br := nod(OBREAK, nil, nil)
- var defaultGoto, nilGoto *Node
- var body Nodes
- for _, ncase := range sw.List.Slice() {
- var caseVar *Node
- if ncase.Rlist.Len() != 0 {
- caseVar = ncase.Rlist.First()
- }
-
- // For single-type cases with an interface type,
- // we initialize the case variable as part of the type assertion.
- // In other cases, we initialize it in the body.
- var singleType *types.Type
- if ncase.List.Len() == 1 && ncase.List.First().Op == OTYPE {
- singleType = ncase.List.First().Type
- }
- caseVarInitialized := false
-
- label := autolabel(".s")
- jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
-
- if ncase.List.Len() == 0 { // default:
- if defaultGoto != nil {
- Fatalf("duplicate default case not detected during typechecking")
- }
- defaultGoto = jmp
- }
-
- for _, n1 := range ncase.List.Slice() {
- if n1.isNil() { // case nil:
- if nilGoto != nil {
- Fatalf("duplicate nil case not detected during typechecking")
- }
- nilGoto = jmp
- continue
- }
-
- if singleType != nil && singleType.IsInterface() {
- s.Add(ncase.Pos, n1.Type, caseVar, jmp)
- caseVarInitialized = true
- } else {
- s.Add(ncase.Pos, n1.Type, nil, jmp)
- }
- }
-
- body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
- if caseVar != nil && !caseVarInitialized {
- val := s.facename
- if singleType != nil {
- // We have a single concrete type. Extract the data.
- if singleType.IsInterface() {
- Fatalf("singleType interface should have been handled in Add")
- }
- val = ifaceData(ncase.Pos, s.facename, singleType)
- }
- l := []*Node{
- nodl(ncase.Pos, ODCL, caseVar, nil),
- nodl(ncase.Pos, OAS, caseVar, val),
- }
- typecheckslice(l, ctxStmt)
- body.Append(l...)
- }
- body.Append(ncase.Nbody.Slice()...)
- body.Append(br)
- }
- sw.List.Set(nil)
-
- if defaultGoto == nil {
- defaultGoto = br
- }
- if nilGoto == nil {
- nilGoto = defaultGoto
- }
- ifNil.Nbody.Set1(nilGoto)
-
- s.Emit(&sw.Nbody)
- sw.Nbody.Append(defaultGoto)
- sw.Nbody.AppendNodes(&body)
-
- walkstmtlist(sw.Nbody.Slice())
-}
-
-// A typeSwitch walks a type switch.
-type typeSwitch struct {
- // Temporary variables (i.e., ONAMEs) used by type switch dispatch logic:
- facename *Node // value being type-switched on
- hashname *Node // type hash of the value being type-switched on
- okname *Node // boolean used for comma-ok type assertions
-
- done Nodes
- clauses []typeClause
-}
-
-type typeClause struct {
- hash uint32
- body Nodes
-}
-
-func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar, jmp *Node) {
- var body Nodes
- if caseVar != nil {
- l := []*Node{
- nodl(pos, ODCL, caseVar, nil),
- nodl(pos, OAS, caseVar, nil),
- }
- typecheckslice(l, ctxStmt)
- body.Append(l...)
- } else {
- caseVar = nblank
- }
-
- // cv, ok = iface.(type)
- as := nodl(pos, OAS2, nil, nil)
- as.List.Set2(caseVar, s.okname) // cv, ok =
- dot := nodl(pos, ODOTTYPE, s.facename, nil)
- dot.Type = typ // iface.(type)
- as.Rlist.Set1(dot)
- as = typecheck(as, ctxStmt)
- as = walkexpr(as, &body)
- body.Append(as)
-
- // if ok { goto label }
- nif := nodl(pos, OIF, nil, nil)
- nif.Left = s.okname
- nif.Nbody.Set1(jmp)
- body.Append(nif)
-
- if !typ.IsInterface() {
- s.clauses = append(s.clauses, typeClause{
- hash: typehash(typ),
- body: body,
- })
- return
- }
-
- s.flush()
- s.done.AppendNodes(&body)
-}
-
-func (s *typeSwitch) Emit(out *Nodes) {
- s.flush()
- out.AppendNodes(&s.done)
-}
-
-func (s *typeSwitch) flush() {
- cc := s.clauses
- s.clauses = nil
- if len(cc) == 0 {
- return
- }
-
- sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
-
- // Combine adjacent cases with the same hash.
- merged := cc[:1]
- for _, c := range cc[1:] {
- last := &merged[len(merged)-1]
- if last.hash == c.hash {
- last.body.AppendNodes(&c.body)
- } else {
- merged = append(merged, c)
- }
- }
- cc = merged
-
- binarySearch(len(cc), &s.done,
- func(i int) *Node {
- return nod(OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
- },
- func(i int, nif *Node) {
- // TODO(mdempsky): Omit hash equality check if
- // there's only one type.
- c := cc[i]
- nif.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
- nif.Nbody.AppendNodes(&c.body)
- },
- )
-}
-
-// binarySearch constructs a binary search tree for handling n cases,
-// and appends it to out. It's used for efficiently implementing
-// switch statements.
-//
-// less(i) should return a boolean expression. If it evaluates true,
-// then cases before i will be tested; otherwise, cases i and later.
-//
-// base(i, nif) should setup nif (an OIF node) to test case i. In
-// particular, it should set nif.Left and nif.Nbody.
-func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, nif *Node)) {
- const binarySearchMin = 4 // minimum number of cases for binary search
-
- var do func(lo, hi int, out *Nodes)
- do = func(lo, hi int, out *Nodes) {
- n := hi - lo
- if n < binarySearchMin {
- for i := lo; i < hi; i++ {
- nif := nod(OIF, nil, nil)
- base(i, nif)
- lineno = lineno.WithNotStmt()
- nif.Left = typecheck(nif.Left, ctxExpr)
- nif.Left = defaultlit(nif.Left, nil)
- out.Append(nif)
- out = &nif.Rlist
- }
- return
- }
-
- half := lo + n/2
- nif := nod(OIF, nil, nil)
- nif.Left = less(half)
- lineno = lineno.WithNotStmt()
- nif.Left = typecheck(nif.Left, ctxExpr)
- nif.Left = defaultlit(nif.Left, nil)
- do(lo, half, &nif.Nbody)
- do(half, hi, &nif.Rlist)
- out.Append(nif)
- }
-
- do(0, n, out)
-}
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
deleted file mode 100644
index 7b4a315e05d4d..0000000000000
--- a/src/cmd/compile/internal/gc/syntax.go
+++ /dev/null
@@ -1,1196 +0,0 @@
-// Copyright 2009 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.
-
-// “Abstract” syntax representation.
-
-package gc
-
-import (
- "cmd/compile/internal/ssa"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
- "sort"
-)
-
-// A Node is a single node in the syntax tree.
-// Actually the syntax tree is a syntax DAG, because there is only one
-// node with Op=ONAME for a given instance of a variable x.
-// The same is true for Op=OTYPE and Op=OLITERAL. See Node.mayBeShared.
-type Node struct {
- // Tree structure.
- // Generic recursive walks should follow these fields.
- Left *Node
- Right *Node
- Ninit Nodes
- Nbody Nodes
- List Nodes
- Rlist Nodes
-
- // most nodes
- Type *types.Type
- Orig *Node // original form, for printing, and tracking copies of ONAMEs
-
- // func
- Func *Func
-
- // ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
- Name *Name
-
- Sym *types.Sym // various
- E interface{} // Opt or Val, see methods below
-
- // Various. Usually an offset into a struct. For example:
- // - ONAME nodes that refer to local variables use it to identify their stack frame position.
- // - ODOT, ODOTPTR, and ORESULT use it to indicate offset relative to their base address.
- // - OSTRUCTKEY uses it to store the named field's offset.
- // - Named OLITERALs use it to store their ambient iota value.
- // - OINLMARK stores an index into the inlTree data structure.
- // - OCLOSURE uses it to store ambient iota value, if any.
- // Possibly still more uses. If you find any, document them.
- Xoffset int64
-
- Pos src.XPos
-
- flags bitset32
-
- Esc uint16 // EscXXX
-
- Op Op
- aux uint8
-}
-
-func (n *Node) ResetAux() {
- n.aux = 0
-}
-
-func (n *Node) SubOp() Op {
- switch n.Op {
- case OASOP, ONAME:
- default:
- Fatalf("unexpected op: %v", n.Op)
- }
- return Op(n.aux)
-}
-
-func (n *Node) SetSubOp(op Op) {
- switch n.Op {
- case OASOP, ONAME:
- default:
- Fatalf("unexpected op: %v", n.Op)
- }
- n.aux = uint8(op)
-}
-
-func (n *Node) IndexMapLValue() bool {
- if n.Op != OINDEXMAP {
- Fatalf("unexpected op: %v", n.Op)
- }
- return n.aux != 0
-}
-
-func (n *Node) SetIndexMapLValue(b bool) {
- if n.Op != OINDEXMAP {
- Fatalf("unexpected op: %v", n.Op)
- }
- if b {
- n.aux = 1
- } else {
- n.aux = 0
- }
-}
-
-func (n *Node) TChanDir() types.ChanDir {
- if n.Op != OTCHAN {
- Fatalf("unexpected op: %v", n.Op)
- }
- return types.ChanDir(n.aux)
-}
-
-func (n *Node) SetTChanDir(dir types.ChanDir) {
- if n.Op != OTCHAN {
- Fatalf("unexpected op: %v", n.Op)
- }
- n.aux = uint8(dir)
-}
-
-func (n *Node) IsSynthetic() bool {
- name := n.Sym.Name
- return name[0] == '.' || name[0] == '~'
-}
-
-// IsAutoTmp indicates if n was created by the compiler as a temporary,
-// based on the setting of the .AutoTemp flag in n's Name.
-func (n *Node) IsAutoTmp() bool {
- if n == nil || n.Op != ONAME {
- return false
- }
- return n.Name.AutoTemp()
-}
-
-const (
- nodeClass, _ = iota, 1 << iota // PPARAM, PAUTO, PEXTERN, etc; three bits; first in the list because frequently accessed
- _, _ // second nodeClass bit
- _, _ // third nodeClass bit
- nodeWalkdef, _ // tracks state during typecheckdef; 2 == loop detected; two bits
- _, _ // second nodeWalkdef bit
- nodeTypecheck, _ // tracks state during typechecking; 2 == loop detected; two bits
- _, _ // second nodeTypecheck bit
- 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
- _, 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)) }
-func (n *Node) Walkdef() uint8 { return n.flags.get2(nodeWalkdef) }
-func (n *Node) Typecheck() uint8 { return n.flags.get2(nodeTypecheck) }
-func (n *Node) Initorder() uint8 { return n.flags.get2(nodeInitorder) }
-
-func (n *Node) HasBreak() bool { return n.flags&nodeHasBreak != 0 }
-func (n *Node) NoInline() bool { return n.flags&nodeNoInline != 0 }
-func (n *Node) Implicit() bool { return n.flags&nodeImplicit != 0 }
-func (n *Node) IsDDD() bool { return n.flags&nodeIsDDD != 0 }
-func (n *Node) Diag() bool { return n.flags&nodeDiag != 0 }
-func (n *Node) Colas() bool { return n.flags&nodeColas != 0 }
-func (n *Node) NonNil() bool { return n.flags&nodeNonNil != 0 }
-func (n *Node) Transient() bool { return n.flags&nodeTransient != 0 }
-func (n *Node) Bounded() bool { return n.flags&nodeBounded != 0 }
-func (n *Node) HasCall() bool { return n.flags&nodeHasCall != 0 }
-func (n *Node) Likely() bool { return n.flags&nodeLikely != 0 }
-func (n *Node) HasVal() bool { return n.flags&nodeHasVal != 0 }
-func (n *Node) HasOpt() bool { return n.flags&nodeHasOpt != 0 }
-func (n *Node) Embedded() bool { return n.flags&nodeEmbedded != 0 }
-
-func (n *Node) SetClass(b Class) { n.flags.set3(nodeClass, uint8(b)) }
-func (n *Node) SetWalkdef(b uint8) { n.flags.set2(nodeWalkdef, b) }
-func (n *Node) SetTypecheck(b uint8) { n.flags.set2(nodeTypecheck, b) }
-func (n *Node) SetInitorder(b uint8) { n.flags.set2(nodeInitorder, b) }
-
-func (n *Node) SetHasBreak(b bool) { n.flags.set(nodeHasBreak, b) }
-func (n *Node) SetNoInline(b bool) { n.flags.set(nodeNoInline, b) }
-func (n *Node) SetImplicit(b bool) { n.flags.set(nodeImplicit, b) }
-func (n *Node) SetIsDDD(b bool) { n.flags.set(nodeIsDDD, b) }
-func (n *Node) SetDiag(b bool) { n.flags.set(nodeDiag, b) }
-func (n *Node) SetColas(b bool) { n.flags.set(nodeColas, b) }
-func (n *Node) SetTransient(b bool) { n.flags.set(nodeTransient, b) }
-func (n *Node) SetHasCall(b bool) { n.flags.set(nodeHasCall, b) }
-func (n *Node) SetLikely(b bool) { n.flags.set(nodeLikely, b) }
-func (n *Node) SetHasVal(b bool) { n.flags.set(nodeHasVal, b) }
-func (n *Node) SetHasOpt(b bool) { n.flags.set(nodeHasOpt, b) }
-func (n *Node) SetEmbedded(b bool) { n.flags.set(nodeEmbedded, b) }
-
-// MarkNonNil marks a pointer n as being guaranteed non-nil,
-// on all code paths, at all times.
-// During conversion to SSA, non-nil pointers won't have nil checks
-// inserted before dereferencing. See state.exprPtr.
-func (n *Node) MarkNonNil() {
- if !n.Type.IsPtr() && !n.Type.IsUnsafePtr() {
- Fatalf("MarkNonNil(%v), type %v", n, n.Type)
- }
- n.flags.set(nodeNonNil, true)
-}
-
-// SetBounded indicates whether operation n does not need safety checks.
-// When n is an index or slice operation, n does not need bounds checks.
-// When n is a dereferencing operation, n does not need nil checks.
-// When n is a makeslice+copy operation, n does not need length and cap checks.
-func (n *Node) SetBounded(b bool) {
- switch n.Op {
- case OINDEX, OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
- // No bounds checks needed.
- case ODOTPTR, ODEREF:
- // No nil check needed.
- case OMAKESLICECOPY:
- // No length and cap checks needed
- // since new slice and copied over slice data have same length.
- default:
- Fatalf("SetBounded(%v)", n)
- }
- n.flags.set(nodeBounded, b)
-}
-
-// MarkReadonly indicates that n is an ONAME with readonly contents.
-func (n *Node) MarkReadonly() {
- if n.Op != ONAME {
- Fatalf("Node.MarkReadonly %v", n.Op)
- }
- n.Name.SetReadonly(true)
- // Mark the linksym as readonly immediately
- // so that the SSA backend can use this information.
- // It will be overridden later during dumpglobls.
- n.Sym.Linksym().Type = objabi.SRODATA
-}
-
-// Val returns the Val for the node.
-func (n *Node) Val() Val {
- if !n.HasVal() {
- return Val{}
- }
- return Val{n.E}
-}
-
-// SetVal sets the Val for the node, which must not have been used with SetOpt.
-func (n *Node) SetVal(v Val) {
- if n.HasOpt() {
- Debug.h = 1
- Dump("have Opt", n)
- Fatalf("have Opt")
- }
- n.SetHasVal(true)
- n.E = v.U
-}
-
-// Opt returns the optimizer data for the node.
-func (n *Node) Opt() interface{} {
- if !n.HasOpt() {
- return nil
- }
- return n.E
-}
-
-// SetOpt sets the optimizer data for the node, which must not have been used with SetVal.
-// SetOpt(nil) is ignored for Vals to simplify call sites that are clearing Opts.
-func (n *Node) SetOpt(x interface{}) {
- if x == nil && n.HasVal() {
- return
- }
- if n.HasVal() {
- Debug.h = 1
- Dump("have Val", n)
- Fatalf("have Val")
- }
- n.SetHasOpt(true)
- n.E = x
-}
-
-func (n *Node) Iota() int64 {
- return n.Xoffset
-}
-
-func (n *Node) SetIota(x int64) {
- n.Xoffset = x
-}
-
-// 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 {
- switch n.Op {
- case ONAME, OLITERAL, OTYPE:
- return true
- }
- return false
-}
-
-// isMethodExpression reports whether n represents a method expression T.M.
-func (n *Node) isMethodExpression() bool {
- return n.Op == ONAME && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME
-}
-
-// funcname returns the name (without the package) of the function n.
-func (n *Node) funcname() string {
- if n == nil || n.Func == nil || n.Func.Nname == nil {
- return ""
- }
- return n.Func.Nname.Sym.Name
-}
-
-// pkgFuncName returns the name of the function referenced by n, with package prepended.
-// This differs from the compiler's internal convention where local functions lack a package
-// because the ultimate consumer of this is a human looking at an IDE; package is only empty
-// if the compilation package is actually the empty string.
-func (n *Node) pkgFuncName() string {
- var s *types.Sym
- if n == nil {
- return ""
- }
- if n.Op == ONAME {
- s = n.Sym
- } else {
- if n.Func == nil || n.Func.Nname == nil {
- return ""
- }
- s = n.Func.Nname.Sym
- }
- pkg := s.Pkg
-
- p := myimportpath
- if pkg != nil && pkg.Path != "" {
- p = pkg.Path
- }
- if p == "" {
- return s.Name
- }
- return p + "." + s.Name
-}
-
-// The compiler needs *Node to be assignable to cmd/compile/internal/ssa.Sym.
-func (n *Node) CanBeAnSSASym() {
-}
-
-// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
-type Name struct {
- Pack *Node // real package for import . names
- Pkg *types.Pkg // pkg for OPACK nodes
- // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2).
- // For a closure var, the ONAME node of the outer captured variable
- Defn *Node
- // The ODCLFUNC node (for a static function/method or a closure) in which
- // local variable or param is declared.
- Curfn *Node
- Param *Param // additional fields for ONAME, OTYPE
- Decldepth int32 // declaration loop depth, increased for every loop or label
- // Unique number for ONAME nodes within a function. Function outputs
- // (results) are numbered starting at one, followed by function inputs
- // (parameters), and then local variables. Vargen is used to distinguish
- // local variables/params with the same name.
- Vargen int32
- flags bitset16
-}
-
-const (
- nameCaptured = 1 << iota // is the variable captured by a closure
- nameReadonly
- nameByval // is the variable captured by value or by reference
- 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
- nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy
- nameAssigned // is the variable ever assigned to
- nameAddrtaken // address taken, even if not moved to heap
- nameInlFormal // PAUTO created by inliner, derived from callee formal
- nameInlLocal // PAUTO created by inliner, derived from callee local
- nameOpenDeferSlot // if temporary var storing info for open-coded defers
- nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section
-)
-
-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) 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 }
-func (n *Name) IsOutputParamHeapAddr() bool { return n.flags&nameIsOutputParamHeapAddr != 0 }
-func (n *Name) Assigned() bool { return n.flags&nameAssigned != 0 }
-func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken != 0 }
-func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 }
-func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 }
-func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 }
-func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 }
-
-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) 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) }
-func (n *Name) SetIsOutputParamHeapAddr(b bool) { n.flags.set(nameIsOutputParamHeapAddr, b) }
-func (n *Name) SetAssigned(b bool) { n.flags.set(nameAssigned, b) }
-func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken, b) }
-func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) }
-func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) }
-func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) }
-func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) }
-
-type Param struct {
- Ntype *Node
- Heapaddr *Node // temp holding heap address of param
-
- // ONAME PAUTOHEAP
- Stackcopy *Node // the PPARAM/PPARAMOUT on-stack slot (moved func params only)
-
- // ONAME closure linkage
- // Consider:
- //
- // func f() {
- // x := 1 // x1
- // func() {
- // use(x) // x2
- // func() {
- // use(x) // x3
- // --- parser is here ---
- // }()
- // }()
- // }
- //
- // There is an original declaration of x and then a chain of mentions of x
- // leading into the current function. Each time x is mentioned in a new closure,
- // we create a variable representing x for use in that specific closure,
- // since the way you get to x is different in each closure.
- //
- // Let's number the specific variables as shown in the code:
- // x1 is the original x, x2 is when mentioned in the closure,
- // and x3 is when mentioned in the closure in the closure.
- //
- // We keep these linked (assume N > 1):
- //
- // - x1.Defn = original declaration statement for x (like most variables)
- // - x1.Innermost = current innermost closure x (in this case x3), or nil for none
- // - x1.IsClosureVar() = false
- //
- // - xN.Defn = x1, N > 1
- // - xN.IsClosureVar() = true, N > 1
- // - x2.Outer = nil
- // - xN.Outer = x(N-1), N > 2
- //
- //
- // When we look up x in the symbol table, we always get x1.
- // Then we can use x1.Innermost (if not nil) to get the x
- // for the innermost known closure function,
- // but the first reference in a closure will find either no x1.Innermost
- // or an x1.Innermost with .Funcdepth < Funcdepth.
- // In that case, a new xN must be created, linked in with:
- //
- // xN.Defn = x1
- // xN.Outer = x1.Innermost
- // x1.Innermost = xN
- //
- // When we finish the function, we'll process its closure variables
- // and find xN and pop it off the list using:
- //
- // x1 := xN.Defn
- // x1.Innermost = xN.Outer
- //
- // We leave x1.Innermost set so that we can still get to the original
- // variable quickly. Not shown here, but once we're
- // done parsing a function and no longer need xN.Outer for the
- // lexical x reference links as described above, funcLit
- // recomputes xN.Outer as the semantic x reference link tree,
- // even filling in x in intermediate closures that might not
- // have mentioned it along the way to inner closures that did.
- // See funcLit for details.
- //
- // During the eventual compilation, then, for closure variables we have:
- //
- // xN.Defn = original variable
- // xN.Outer = variable captured in next outward scope
- // to make closure where xN appears
- //
- // Because of the sharding of pieces of the node, x.Defn means x.Name.Defn
- // and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
- Innermost *Node
- Outer *Node
-
- // OTYPE & ONAME //go:embed info,
- // sharing storage to reduce gc.Param size.
- // Extra is nil, or else *Extra is a *paramType or an *embedFileList.
- Extra *interface{}
-}
-
-type paramType struct {
- flag PragmaFlag
- alias bool
-}
-
-type irEmbed struct {
- Pos src.XPos
- Patterns []string
-}
-
-type embedList []irEmbed
-
-// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
-func (p *Param) Pragma() PragmaFlag {
- if p.Extra == nil {
- return 0
- }
- return (*p.Extra).(*paramType).flag
-}
-
-// SetPragma sets the PragmaFlag for p, which must be for an OTYPE.
-func (p *Param) SetPragma(flag PragmaFlag) {
- if p.Extra == nil {
- if flag == 0 {
- return
- }
- p.Extra = new(interface{})
- *p.Extra = ¶mType{flag: flag}
- return
- }
- (*p.Extra).(*paramType).flag = flag
-}
-
-// Alias reports whether p, which must be for an OTYPE, is a type alias.
-func (p *Param) Alias() bool {
- if p.Extra == nil {
- return false
- }
- t, ok := (*p.Extra).(*paramType)
- if !ok {
- return false
- }
- return t.alias
-}
-
-// SetAlias sets whether p, which must be for an OTYPE, is a type alias.
-func (p *Param) SetAlias(alias bool) {
- if p.Extra == nil {
- if !alias {
- return
- }
- p.Extra = new(interface{})
- *p.Extra = ¶mType{alias: alias}
- return
- }
- (*p.Extra).(*paramType).alias = alias
-}
-
-// EmbedList returns the list of embedded files for p,
-// which must be for an ONAME var.
-func (p *Param) EmbedList() []irEmbed {
- if p.Extra == nil {
- return nil
- }
- return *(*p.Extra).(*embedList)
-}
-
-// SetEmbedList sets the list of embedded files for p,
-// which must be for an ONAME var.
-func (p *Param) SetEmbedList(list []irEmbed) {
- if p.Extra == nil {
- if len(list) == 0 {
- return
- }
- f := embedList(list)
- p.Extra = new(interface{})
- *p.Extra = &f
- return
- }
- *(*p.Extra).(*embedList) = list
-}
-
-// Functions
-//
-// A simple function declaration is represented as an ODCLFUNC node f
-// and an ONAME node n. They're linked to one another through
-// f.Func.Nname == n and n.Name.Defn == f. When functions are
-// referenced by name in an expression, the function's ONAME node is
-// used directly.
-//
-// Function names have n.Class() == PFUNC. This distinguishes them
-// from variables of function type.
-//
-// Confusingly, n.Func and f.Func both exist, but commonly point to
-// different Funcs. (Exception: an OCALLPART's Func does point to its
-// ODCLFUNC's Func.)
-//
-// A method declaration is represented like functions, except n.Sym
-// will be the qualified method name (e.g., "T.m") and
-// f.Func.Shortname is the bare method name (e.g., "m").
-//
-// Method expressions are represented as ONAME/PFUNC nodes like
-// function names, but their Left and Right fields still point to the
-// type and method, respectively. They can be distinguished from
-// normal functions with isMethodExpression. Also, unlike function
-// name nodes, method expression nodes exist for each method
-// expression. The declaration ONAME can be accessed with
-// x.Type.Nname(), where x is the method expression ONAME node.
-//
-// Method values are represented by ODOTMETH/ODOTINTER when called
-// immediately, and OCALLPART otherwise. They are like method
-// expressions, except that for ODOTMETH/ODOTINTER the method name is
-// stored in Sym instead of Right.
-//
-// Closures are represented by OCLOSURE node c. They link back and
-// forth with the ODCLFUNC via Func.Closure; that is, c.Func.Closure
-// == f and f.Func.Closure == c.
-//
-// Function bodies are stored in f.Nbody, and inline function bodies
-// are stored in n.Func.Inl. Pragmas are stored in f.Func.Pragma.
-//
-// Imported functions skip the ODCLFUNC, so n.Name.Defn is nil. They
-// also use Dcl instead of Inldcl.
-
-// Func holds Node fields used only with function-like nodes.
-type Func struct {
- Shortname *types.Sym
- // Extra entry code for the function. For example, allocate and initialize
- // memory for escaping parameters. However, just for OCLOSURE, Enter is a
- // list of ONAME nodes of captured variables
- Enter Nodes
- Exit Nodes
- // ONAME nodes for closure params, each should have closurevar set
- Cvars Nodes
- // ONAME nodes for all params/locals for this func/closure, does NOT
- // include closurevars until transformclosure runs.
- Dcl []*Node
-
- // Parents records the parent scope of each scope within a
- // function. The root scope (0) has no parent, so the i'th
- // scope's parent is stored at Parents[i-1].
- Parents []ScopeID
-
- // Marks records scope boundary changes.
- Marks []Mark
-
- // Closgen tracks how many closures have been generated within
- // this function. Used by closurename for creating unique
- // function names.
- Closgen int
-
- FieldTrack map[*types.Sym]struct{}
- DebugInfo *ssa.FuncDebug
- Ntype *Node // signature
- Top int // top context (ctxCallee, etc)
- Closure *Node // OCLOSURE <-> ODCLFUNC (see header comment above)
- Nname *Node // The ONAME node associated with an ODCLFUNC (both have same Type)
- lsym *obj.LSym
-
- Inl *Inline
-
- Label int32 // largest auto-generated label in this function
-
- Endlineno src.XPos
- WBPos src.XPos // position of first write barrier; see SetWBPos
-
- Pragma PragmaFlag // go:xxx function annotations
-
- flags bitset16
- numDefers int // number of defer calls in the function
- numReturns int // number of explicit returns in the function
-
- // nwbrCalls records the LSyms of functions called by this
- // function for go:nowritebarrierrec analysis. Only filled in
- // if nowritebarrierrecCheck != nil.
- nwbrCalls *[]nowritebarrierrecCallSym
-}
-
-// An Inline holds fields used for function bodies that can be inlined.
-type Inline struct {
- Cost int32 // heuristic cost of inlining this function
-
- // Copies of Func.Dcl and Nbody for use during inlining.
- Dcl []*Node
- Body []*Node
-}
-
-// A Mark represents a scope boundary.
-type Mark struct {
- // Pos is the position of the token that marks the scope
- // change.
- Pos src.XPos
-
- // Scope identifies the innermost scope to the right of Pos.
- Scope ScopeID
-}
-
-// A ScopeID represents a lexical scope within a function.
-type ScopeID int32
-
-const (
- funcDupok = 1 << iota // duplicate definitions ok
- funcWrapper // is method wrapper
- funcNeedctxt // function uses context register (has closure variables)
- funcReflectMethod // function calls reflect.Type.Method or MethodByName
- // true if closure inside a function; false if a simple function or a
- // closure in a global variable initialization
- funcIsHiddenClosure
- funcHasDefer // contains a defer statement
- funcNilCheckDisabled // disable nil checks when compiling this function
- funcInlinabilityChecked // inliner has already determined whether the function is inlinable
- funcExportInline // include inline body in export data
- funcInstrumentBody // add race/msan instrumentation during SSA construction
- funcOpenCodedDeferDisallowed // can't do open-coded defers
-)
-
-func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
-func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 }
-func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 }
-func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 }
-func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 }
-func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 }
-func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
-func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
-func (f *Func) ExportInline() bool { return f.flags&funcExportInline != 0 }
-func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 }
-func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
-
-func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
-func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
-func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) }
-func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) }
-func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) }
-func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) }
-func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
-func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
-func (f *Func) SetExportInline(b bool) { f.flags.set(funcExportInline, b) }
-func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) }
-func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
-
-func (f *Func) setWBPos(pos src.XPos) {
- if Debug_wb != 0 {
- Warnl(pos, "write barrier")
- }
- if !f.WBPos.IsKnown() {
- f.WBPos = pos
- }
-}
-
-//go:generate stringer -type=Op -trimprefix=O
-
-type Op uint8
-
-// Node ops.
-const (
- OXXX Op = iota
-
- // names
- ONAME // var or func name
- // Unnamed arg or return value: f(int, string) (int, error) { etc }
- // Also used for a qualified package identifier that hasn't been resolved yet.
- ONONAME
- OTYPE // type name
- OPACK // import
- OLITERAL // literal
-
- // expressions
- OADD // Left + Right
- OSUB // Left - Right
- OOR // Left | Right
- OXOR // Left ^ Right
- OADDSTR // +{List} (string addition, list elements are strings)
- OADDR // &Left
- OANDAND // Left && Right
- OAPPEND // append(List); after walk, Left may contain elem type descriptor
- OBYTES2STR // Type(Left) (Type is string, Left is a []byte)
- OBYTES2STRTMP // Type(Left) (Type is string, Left is a []byte, ephemeral)
- ORUNES2STR // Type(Left) (Type is string, Left is a []rune)
- OSTR2BYTES // Type(Left) (Type is []byte, Left is a string)
- OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral)
- OSTR2RUNES // Type(Left) (Type is []rune, Left is a string)
- // Left = Right or (if Colas=true) Left := Right
- // If Colas, then Ninit includes a DCL node for Left.
- OAS
- // List = Rlist (x, y, z = a, b, c) or (if Colas=true) List := Rlist
- // If Colas, then Ninit includes DCL nodes for List
- OAS2
- OAS2DOTTYPE // List = Right (x, ok = I.(int))
- OAS2FUNC // List = Right (x, y = f())
- OAS2MAPR // List = Right (x, ok = m["foo"])
- OAS2RECV // List = Right (x, ok = <-c)
- OASOP // Left Etype= Right (x += y)
- OCALL // Left(List) (function call, method call or type conversion)
-
- // OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
- // 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))
- OCALLINTER // Left(List/Rlist) (interface method call x.Method(args))
- OCALLPART // Left.Right (method expression x.Method, not called)
- OCAP // cap(Left)
- OCLOSE // close(Left)
- OCLOSURE // func Type { Func.Closure.Nbody } (func literal)
- OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
- OMAPLIT // Type{List} (composite literal, Type is map)
- OSTRUCTLIT // Type{List} (composite literal, Type is struct)
- OARRAYLIT // Type{List} (composite literal, Type is array)
- OSLICELIT // Type{List} (composite literal, Type is slice) Right.Int64() = slice length.
- OPTRLIT // &Left (left is composite literal)
- OCONV // Type(Left) (type conversion)
- OCONVIFACE // Type(Left) (type conversion, to interface)
- OCONVNOP // Type(Left) (type conversion, no effect)
- OCOPY // copy(Left, Right)
- ODCL // var Left (declares Left of type Left.Type)
-
- // Used during parsing but don't last.
- ODCLFUNC // func f() or func (r) f()
- ODCLFIELD // struct field, interface field, or func/method argument/return value.
- ODCLCONST // const pi = 3.14
- ODCLTYPE // type Int int or type Int = int
-
- ODELETE // delete(List)
- ODOT // Left.Sym (Left is of struct type)
- ODOTPTR // Left.Sym (Left is of pointer to struct type)
- ODOTMETH // Left.Sym (Left is non-interface, Right is method name)
- ODOTINTER // Left.Sym (Left is interface, Right is method name)
- OXDOT // Left.Sym (before rewrite to one of the preceding)
- ODOTTYPE // Left.Right or Left.Type (.Right during parsing, .Type once resolved); after walk, .Right contains address of interface type descriptor and .Right.Right contains address of concrete type descriptor
- ODOTTYPE2 // Left.Right or Left.Type (.Right during parsing, .Type once resolved; on rhs of OAS2DOTTYPE); after walk, .Right contains address of interface type descriptor
- OEQ // Left == Right
- ONE // Left != Right
- OLT // Left < Right
- OLE // Left <= Right
- OGE // Left >= Right
- OGT // Left > Right
- ODEREF // *Left
- OINDEX // Left[Right] (index of array or slice)
- OINDEXMAP // Left[Right] (index of map)
- OKEY // Left:Right (key:value in struct/array/map literal)
- OSTRUCTKEY // Sym:Left (key:value in struct literal, after type checking)
- OLEN // len(Left)
- OMAKE // make(List) (before type checking converts to one of the following)
- OMAKECHAN // make(Type, Left) (type is chan)
- OMAKEMAP // make(Type, Left) (type is map)
- OMAKESLICE // make(Type, Left, Right) (type is slice)
- OMAKESLICECOPY // makeslicecopy(Type, Left, Right) (type is slice; Left is length and Right is the copied from slice)
- // OMAKESLICECOPY is created by the order pass and corresponds to:
- // s = make(Type, Left); copy(s, Right)
- //
- // Bounded can be set on the node when Left == len(Right) is known at compile time.
- //
- // This node is created so the walk pass can optimize this pattern which would
- // otherwise be hard to detect after the order pass.
- OMUL // Left * Right
- ODIV // Left / Right
- OMOD // Left % Right
- OLSH // Left << Right
- ORSH // Left >> Right
- OAND // Left & Right
- OANDNOT // Left &^ Right
- ONEW // new(Left); corresponds to calls to new in source code
- ONEWOBJ // runtime.newobject(n.Type); introduced by walk; Left is type descriptor
- ONOT // !Left
- OBITNOT // ^Left
- OPLUS // +Left
- ONEG // -Left
- OOROR // Left || Right
- OPANIC // panic(Left)
- OPRINT // print(List)
- OPRINTN // println(List)
- OPAREN // (Left)
- OSEND // Left <- Right
- OSLICE // Left[List[0] : List[1]] (Left is untypechecked or slice)
- OSLICEARR // Left[List[0] : List[1]] (Left is array)
- OSLICESTR // Left[List[0] : List[1]] (Left is string)
- OSLICE3 // Left[List[0] : List[1] : List[2]] (Left is untypedchecked or slice)
- OSLICE3ARR // Left[List[0] : List[1] : List[2]] (Left is array)
- OSLICEHEADER // sliceheader{Left, List[0], List[1]} (Left is unsafe.Pointer, List[0] is length, List[1] is capacity)
- ORECOVER // recover()
- ORECV // <-Left
- ORUNESTR // Type(Left) (Type is string, Left is rune)
- OSELRECV // Left = <-Right.Left: (appears as .Left of OCASE; Right.Op == ORECV)
- OSELRECV2 // List = <-Right.Left: (appears as .Left of OCASE; count(List) == 2, Right.Op == ORECV)
- OIOTA // iota
- OREAL // real(Left)
- OIMAG // imag(Left)
- OCOMPLEX // complex(Left, Right) or complex(List[0]) where List[0] is a 2-result function call
- OALIGNOF // unsafe.Alignof(Left)
- OOFFSETOF // unsafe.Offsetof(Left)
- OSIZEOF // unsafe.Sizeof(Left)
-
- // statements
- OBLOCK // { List } (block of code)
- OBREAK // break [Sym]
- // OCASE: case List: Nbody (List==nil means default)
- // For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL
- // for nil), and, if a type-switch variable is specified, Rlist is an
- // ONAME for the version of the type-switch variable with the specified
- // type.
- OCASE
- OCONTINUE // continue [Sym]
- ODEFER // defer Left (Left must be call)
- OEMPTY // no-op (empty statement)
- OFALL // fallthrough
- OFOR // for Ninit; Left; Right { Nbody }
- // OFORUNTIL is like OFOR, but the test (Left) is applied after the body:
- // Ninit
- // top: { Nbody } // Execute the body at least once
- // cont: Right
- // if Left { // And then test the loop condition
- // List // Before looping to top, execute List
- // goto top
- // }
- // OFORUNTIL is created by walk. There's no way to write this in Go code.
- OFORUNTIL
- OGOTO // goto Sym
- OIF // if Ninit; Left { Nbody } else { Rlist }
- OLABEL // Sym:
- OGO // go Left (Left must be call)
- ORANGE // for List = range Right { Nbody }
- ORETURN // return List
- OSELECT // select { List } (List is list of OCASE)
- OSWITCH // switch Ninit; Left { List } (List is a list of OCASE)
- // OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH)
- // Left is nil if there is no type-switch variable
- OTYPESW
-
- // types
- OTCHAN // chan int
- OTMAP // map[string]int
- OTSTRUCT // struct{}
- OTINTER // interface{}
- // OTFUNC: func() - Left is receiver field, List is list of param fields, Rlist is
- // list of result fields.
- OTFUNC
- OTARRAY // []int, [8]int, [N]int or [...]int
-
- // misc
- ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}.
- 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.
- OCLOSUREVAR // variable reference 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.
-
- // arch-specific opcodes
- ORETJMP // return to other function
- OGETG // runtime.getg() (read g pointer)
-
- OEND
-)
-
-// Nodes is a pointer to a slice of *Node.
-// For fields that are not used in most nodes, this is used instead of
-// a slice to save space.
-type Nodes struct{ slice *[]*Node }
-
-// asNodes returns a slice of *Node as a Nodes value.
-func asNodes(s []*Node) Nodes {
- return Nodes{&s}
-}
-
-// Slice returns the entries in Nodes as a slice.
-// Changes to the slice entries (as in s[i] = n) will be reflected in
-// the Nodes.
-func (n Nodes) Slice() []*Node {
- if n.slice == nil {
- return nil
- }
- return *n.slice
-}
-
-// Len returns the number of entries in Nodes.
-func (n Nodes) Len() int {
- if n.slice == nil {
- return 0
- }
- return len(*n.slice)
-}
-
-// Index returns the i'th element of Nodes.
-// It panics if n does not have at least i+1 elements.
-func (n Nodes) Index(i int) *Node {
- return (*n.slice)[i]
-}
-
-// First returns the first element of Nodes (same as n.Index(0)).
-// It panics if n has no elements.
-func (n Nodes) First() *Node {
- return (*n.slice)[0]
-}
-
-// Second returns the second element of Nodes (same as n.Index(1)).
-// It panics if n has fewer than two elements.
-func (n Nodes) Second() *Node {
- return (*n.slice)[1]
-}
-
-// Set sets n to a slice.
-// This takes ownership of the slice.
-func (n *Nodes) Set(s []*Node) {
- if len(s) == 0 {
- n.slice = nil
- } else {
- // Copy s and take address of t rather than s to avoid
- // allocation in the case where len(s) == 0 (which is
- // over 3x more common, dynamically, for make.bash).
- t := s
- n.slice = &t
- }
-}
-
-// Set1 sets n to a slice containing a single node.
-func (n *Nodes) Set1(n1 *Node) {
- n.slice = &[]*Node{n1}
-}
-
-// Set2 sets n to a slice containing two nodes.
-func (n *Nodes) Set2(n1, n2 *Node) {
- n.slice = &[]*Node{n1, n2}
-}
-
-// Set3 sets n to a slice containing three nodes.
-func (n *Nodes) Set3(n1, n2, n3 *Node) {
- n.slice = &[]*Node{n1, n2, n3}
-}
-
-// MoveNodes sets n to the contents of n2, then clears n2.
-func (n *Nodes) MoveNodes(n2 *Nodes) {
- n.slice = n2.slice
- n2.slice = nil
-}
-
-// SetIndex sets the i'th element of Nodes to node.
-// It panics if n does not have at least i+1 elements.
-func (n Nodes) SetIndex(i int, node *Node) {
- (*n.slice)[i] = node
-}
-
-// SetFirst sets the first element of Nodes to node.
-// It panics if n does not have at least one elements.
-func (n Nodes) SetFirst(node *Node) {
- (*n.slice)[0] = node
-}
-
-// SetSecond sets the second element of Nodes to node.
-// It panics if n does not have at least two elements.
-func (n Nodes) SetSecond(node *Node) {
- (*n.slice)[1] = node
-}
-
-// Addr returns the address of the i'th element of Nodes.
-// It panics if n does not have at least i+1 elements.
-func (n Nodes) Addr(i int) **Node {
- return &(*n.slice)[i]
-}
-
-// Append appends entries to Nodes.
-func (n *Nodes) Append(a ...*Node) {
- if len(a) == 0 {
- return
- }
- if n.slice == nil {
- s := make([]*Node, len(a))
- copy(s, a)
- n.slice = &s
- return
- }
- *n.slice = append(*n.slice, a...)
-}
-
-// Prepend prepends entries to Nodes.
-// If a slice is passed in, this will take ownership of it.
-func (n *Nodes) Prepend(a ...*Node) {
- if len(a) == 0 {
- return
- }
- if n.slice == nil {
- n.slice = &a
- } else {
- *n.slice = append(a, *n.slice...)
- }
-}
-
-// AppendNodes appends the contents of *n2 to n, then clears n2.
-func (n *Nodes) AppendNodes(n2 *Nodes) {
- switch {
- case n2.slice == nil:
- case n.slice == nil:
- n.slice = n2.slice
- default:
- *n.slice = append(*n.slice, *n2.slice...)
- }
- n2.slice = nil
-}
-
-// inspect invokes f on each node in an AST in depth-first order.
-// If f(n) returns false, inspect skips visiting n's children.
-func inspect(n *Node, f func(*Node) bool) {
- if n == nil || !f(n) {
- return
- }
- inspectList(n.Ninit, f)
- inspect(n.Left, f)
- inspect(n.Right, f)
- inspectList(n.List, f)
- inspectList(n.Nbody, f)
- inspectList(n.Rlist, f)
-}
-
-func inspectList(l Nodes, f func(*Node) bool) {
- for _, n := range l.Slice() {
- inspect(n, f)
- }
-}
-
-// nodeQueue is a FIFO queue of *Node. The zero value of nodeQueue is
-// a ready-to-use empty queue.
-type nodeQueue struct {
- ring []*Node
- head, tail int
-}
-
-// empty reports whether q contains no Nodes.
-func (q *nodeQueue) empty() bool {
- return q.head == q.tail
-}
-
-// pushRight appends n to the right of the queue.
-func (q *nodeQueue) pushRight(n *Node) {
- if len(q.ring) == 0 {
- q.ring = make([]*Node, 16)
- } else if q.head+len(q.ring) == q.tail {
- // Grow the ring.
- nring := make([]*Node, len(q.ring)*2)
- // Copy the old elements.
- part := q.ring[q.head%len(q.ring):]
- if q.tail-q.head <= len(part) {
- part = part[:q.tail-q.head]
- copy(nring, part)
- } else {
- pos := copy(nring, part)
- copy(nring[pos:], q.ring[:q.tail%len(q.ring)])
- }
- q.ring, q.head, q.tail = nring, 0, q.tail-q.head
- }
-
- q.ring[q.tail%len(q.ring)] = n
- q.tail++
-}
-
-// popLeft pops a node from the left of the queue. It panics if q is
-// empty.
-func (q *nodeQueue) popLeft() *Node {
- if q.empty() {
- panic("dequeue empty")
- }
- n := q.ring[q.head%len(q.ring)]
- q.head++
- return n
-}
-
-// NodeSet is a set of Nodes.
-type NodeSet map[*Node]struct{}
-
-// Has reports whether s contains n.
-func (s NodeSet) Has(n *Node) bool {
- _, isPresent := s[n]
- return isPresent
-}
-
-// Add adds n to s.
-func (s *NodeSet) Add(n *Node) {
- if *s == nil {
- *s = make(map[*Node]struct{})
- }
- (*s)[n] = struct{}{}
-}
-
-// Sorted returns s sorted according to less.
-func (s NodeSet) Sorted(less func(*Node, *Node) bool) []*Node {
- var res []*Node
- for n := range s {
- res = append(res, n)
- }
- sort.Slice(res, func(i, j int) bool { return less(res[i], res[j]) })
- return res
-}
diff --git a/src/cmd/compile/internal/gc/trace.go b/src/cmd/compile/internal/gc/trace.go
index ed4b5a268ddc6..c6eb23a09091c 100644
--- a/src/cmd/compile/internal/gc/trace.go
+++ b/src/cmd/compile/internal/gc/trace.go
@@ -9,6 +9,8 @@ package gc
import (
"os"
tracepkg "runtime/trace"
+
+ "cmd/compile/internal/base"
)
func init() {
@@ -18,10 +20,10 @@ func init() {
func traceHandlerGo17(traceprofile string) {
f, err := os.Create(traceprofile)
if err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
if err := tracepkg.Start(f); err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
- atExit(tracepkg.Stop)
+ base.AtExit(tracepkg.Stop)
}
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
deleted file mode 100644
index c0b05035f033c..0000000000000
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ /dev/null
@@ -1,4019 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "fmt"
- "strings"
-)
-
-// To enable tracing support (-t flag), set enableTrace to true.
-const enableTrace = false
-
-var trace bool
-var traceIndent []byte
-var skipDowidthForTracing bool
-
-func tracePrint(title string, n *Node) func(np **Node) {
- indent := traceIndent
-
- // guard against nil
- var pos, op string
- var tc uint8
- if n != nil {
- pos = linestr(n.Pos)
- op = n.Op.String()
- tc = n.Typecheck()
- }
-
- skipDowidthForTracing = true
- defer func() { skipDowidthForTracing = false }()
- fmt.Printf("%s: %s%s %p %s %v tc=%d\n", pos, indent, title, n, op, n, tc)
- traceIndent = append(traceIndent, ". "...)
-
- return func(np **Node) {
- traceIndent = traceIndent[:len(traceIndent)-2]
-
- // if we have a result, use that
- if np != nil {
- n = *np
- }
-
- // guard against nil
- // use outer pos, op so we don't get empty pos/op if n == nil (nicer output)
- var tc uint8
- var typ *types.Type
- if n != nil {
- pos = linestr(n.Pos)
- op = n.Op.String()
- tc = n.Typecheck()
- typ = n.Type
- }
-
- skipDowidthForTracing = true
- defer func() { skipDowidthForTracing = false }()
- fmt.Printf("%s: %s=> %p %s %v tc=%d type=%#L\n", pos, indent, n, op, n, tc, typ)
- }
-}
-
-const (
- ctxStmt = 1 << iota // evaluated at statement level
- ctxExpr // evaluated in value context
- ctxType // evaluated in type context
- ctxCallee // call-only expressions are ok
- ctxMultiOK // multivalue function returns are ok
- ctxAssign // assigning to expression
-)
-
-// type checks the whole tree of an expression.
-// calculates expression types.
-// evaluates compile time constants.
-// marks variables that escape the local frame.
-// rewrites n.Op to be more specific in some cases.
-
-var typecheckdefstack []*Node
-
-// resolve ONONAME to definition, if any.
-func resolve(n *Node) (res *Node) {
- if n == nil || n.Op != ONONAME {
- return n
- }
-
- // only trace if there's work to do
- if enableTrace && trace {
- defer tracePrint("resolve", n)(&res)
- }
-
- if n.Sym.Pkg != localpkg {
- if inimport {
- Fatalf("recursive inimport")
- }
- inimport = true
- expandDecl(n)
- inimport = false
- return n
- }
-
- r := asNode(n.Sym.Def)
- if r == nil {
- return n
- }
-
- if r.Op == OIOTA {
- if x := getIotaValue(); x >= 0 {
- return nodintconst(x)
- }
- return n
- }
-
- return r
-}
-
-func typecheckslice(l []*Node, top int) {
- for i := range l {
- l[i] = typecheck(l[i], top)
- }
-}
-
-var _typekind = []string{
- TINT: "int",
- TUINT: "uint",
- TINT8: "int8",
- TUINT8: "uint8",
- TINT16: "int16",
- TUINT16: "uint16",
- TINT32: "int32",
- TUINT32: "uint32",
- TINT64: "int64",
- TUINT64: "uint64",
- TUINTPTR: "uintptr",
- TCOMPLEX64: "complex64",
- TCOMPLEX128: "complex128",
- TFLOAT32: "float32",
- TFLOAT64: "float64",
- TBOOL: "bool",
- TSTRING: "string",
- TPTR: "pointer",
- TUNSAFEPTR: "unsafe.Pointer",
- TSTRUCT: "struct",
- TINTER: "interface",
- TCHAN: "chan",
- TMAP: "map",
- TARRAY: "array",
- TSLICE: "slice",
- TFUNC: "func",
- TNIL: "nil",
- TIDEAL: "untyped number",
-}
-
-func typekind(t *types.Type) string {
- if t.IsUntyped() {
- return fmt.Sprintf("%v", t)
- }
- et := t.Etype
- if int(et) < len(_typekind) {
- s := _typekind[et]
- if s != "" {
- return s
- }
- }
- return fmt.Sprintf("etype=%d", et)
-}
-
-func cycleFor(start *Node) []*Node {
- // Find the start node in typecheck_tcstack.
- // We know that it must exist because each time we mark
- // a node with n.SetTypecheck(2) we push it on the stack,
- // and each time we mark a node with n.SetTypecheck(2) we
- // pop it from the stack. We hit a cycle when we encounter
- // a node marked 2 in which case is must be on the stack.
- i := len(typecheck_tcstack) - 1
- for i > 0 && typecheck_tcstack[i] != start {
- i--
- }
-
- // collect all nodes with same Op
- var cycle []*Node
- for _, n := range typecheck_tcstack[i:] {
- if n.Op == start.Op {
- cycle = append(cycle, n)
- }
- }
-
- return cycle
-}
-
-func cycleTrace(cycle []*Node) string {
- var s string
- for i, n := range cycle {
- s += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cycle[(i+1)%len(cycle)])
- }
- return s
-}
-
-var typecheck_tcstack []*Node
-
-// typecheck type checks node n.
-// The result of typecheck MUST be assigned back to n, e.g.
-// n.Left = typecheck(n.Left, top)
-func typecheck(n *Node, top int) (res *Node) {
- // cannot type check until all the source has been parsed
- if !typecheckok {
- Fatalf("early typecheck")
- }
-
- if n == nil {
- return nil
- }
-
- // only trace if there's work to do
- if enableTrace && trace {
- defer tracePrint("typecheck", n)(&res)
- }
-
- lno := setlineno(n)
-
- // Skip over parens.
- for n.Op == OPAREN {
- n = n.Left
- }
-
- // Resolve definition of name and value of iota lazily.
- n = resolve(n)
-
- // Skip typecheck if already done.
- // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed.
- if n.Typecheck() == 1 {
- switch n.Op {
- case ONAME, OTYPE, OLITERAL, OPACK:
- break
-
- default:
- lineno = lno
- return n
- }
- }
-
- if n.Typecheck() == 2 {
- // Typechecking loop. Trying printing a meaningful message,
- // otherwise a stack trace of typechecking.
- switch n.Op {
- // We can already diagnose variables used as types.
- case ONAME:
- if top&(ctxExpr|ctxType) == ctxType {
- yyerror("%v is not a type", n)
- }
-
- case OTYPE:
- // Only report a type cycle if we are expecting a type.
- // Otherwise let other code report an error.
- if top&ctxType == ctxType {
- // A cycle containing only alias types is an error
- // since it would expand indefinitely when aliases
- // are substituted.
- cycle := cycleFor(n)
- for _, n1 := range cycle {
- if n1.Name != nil && !n1.Name.Param.Alias() {
- // Cycle is ok. But if n is an alias type and doesn't
- // have a type yet, we have a recursive type declaration
- // with aliases that we can't handle properly yet.
- // Report an error rather than crashing later.
- if n.Name != nil && n.Name.Param.Alias() && n.Type == nil {
- lineno = n.Pos
- Fatalf("cannot handle alias type declaration (issue #25838): %v", n)
- }
- lineno = lno
- return n
- }
- }
- yyerrorl(n.Pos, "invalid recursive type alias %v%s", n, cycleTrace(cycle))
- }
-
- case OLITERAL:
- if top&(ctxExpr|ctxType) == ctxType {
- yyerror("%v is not a type", n)
- break
- }
- yyerrorl(n.Pos, "constant definition loop%s", cycleTrace(cycleFor(n)))
- }
-
- if nsavederrors+nerrors == 0 {
- var trace string
- for i := len(typecheck_tcstack) - 1; i >= 0; i-- {
- x := typecheck_tcstack[i]
- trace += fmt.Sprintf("\n\t%v %v", x.Line(), x)
- }
- yyerror("typechecking loop involving %v%s", n, trace)
- }
-
- lineno = lno
- return n
- }
-
- n.SetTypecheck(2)
-
- typecheck_tcstack = append(typecheck_tcstack, n)
- n = typecheck1(n, top)
-
- n.SetTypecheck(1)
-
- last := len(typecheck_tcstack) - 1
- typecheck_tcstack[last] = nil
- typecheck_tcstack = typecheck_tcstack[:last]
-
- lineno = lno
- return n
-}
-
-// indexlit implements typechecking of untyped values as
-// array/slice indexes. It is almost equivalent to defaultlit
-// but also accepts untyped numeric values representable as
-// value of type int (see also checkmake for comparison).
-// The result of indexlit MUST be assigned back to n, e.g.
-// n.Left = indexlit(n.Left)
-func indexlit(n *Node) *Node {
- if n != nil && n.Type != nil && n.Type.Etype == TIDEAL {
- return defaultlit(n, types.Types[TINT])
- }
- return n
-}
-
-// The result of typecheck1 MUST be assigned back to n, e.g.
-// n.Left = typecheck1(n.Left, top)
-func typecheck1(n *Node, top int) (res *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheck1", n)(&res)
- }
-
- switch n.Op {
- case OLITERAL, ONAME, ONONAME, OTYPE:
- if n.Sym == nil {
- break
- }
-
- if n.Op == ONAME && n.SubOp() != 0 && top&ctxCallee == 0 {
- yyerror("use of builtin %v not in function call", n.Sym)
- n.Type = nil
- return n
- }
-
- typecheckdef(n)
- if n.Op == ONONAME {
- n.Type = nil
- return n
- }
- }
-
- ok := 0
- switch n.Op {
- // until typecheck is complete, do nothing.
- default:
- Dump("typecheck", n)
-
- Fatalf("typecheck %v", n.Op)
-
- // names
- case OLITERAL:
- ok |= ctxExpr
-
- if n.Type == nil && n.Val().Ctype() == CTSTR {
- n.Type = types.UntypedString
- }
-
- case ONONAME:
- ok |= ctxExpr
-
- case ONAME:
- if n.Name.Decldepth == 0 {
- n.Name.Decldepth = decldepth
- }
- if n.SubOp() != 0 {
- ok |= ctxCallee
- break
- }
-
- if top&ctxAssign == 0 {
- // not a write to the variable
- if n.isBlank() {
- yyerror("cannot use _ as value")
- n.Type = nil
- return n
- }
-
- n.Name.SetUsed(true)
- }
-
- ok |= ctxExpr
-
- case OPACK:
- yyerror("use of package %v without selector", n.Sym)
- n.Type = nil
- return n
-
- case ODDD:
- break
-
- // types (ODEREF is with exprs)
- case OTYPE:
- ok |= ctxType
-
- if n.Type == nil {
- return n
- }
-
- case OTARRAY:
- ok |= ctxType
- r := typecheck(n.Right, ctxType)
- if r.Type == nil {
- n.Type = nil
- return n
- }
-
- var t *types.Type
- if n.Left == nil {
- t = types.NewSlice(r.Type)
- } else if n.Left.Op == ODDD {
- if !n.Diag() {
- n.SetDiag(true)
- yyerror("use of [...] array outside of array literal")
- }
- n.Type = nil
- return n
- } else {
- n.Left = indexlit(typecheck(n.Left, ctxExpr))
- l := n.Left
- if consttype(l) != CTINT {
- switch {
- case l.Type == nil:
- // Error already reported elsewhere.
- case l.Type.IsInteger() && l.Op != OLITERAL:
- yyerror("non-constant array bound %v", l)
- default:
- yyerror("invalid array bound %v", l)
- }
- n.Type = nil
- return n
- }
-
- v := l.Val()
- if doesoverflow(v, types.Types[TINT]) {
- yyerror("array bound is too large")
- n.Type = nil
- return n
- }
-
- bound := v.U.(*Mpint).Int64()
- if bound < 0 {
- yyerror("array bound must be non-negative")
- n.Type = nil
- return n
- }
- t = types.NewArray(r.Type, bound)
- }
-
- setTypeNode(n, t)
- n.Left = nil
- n.Right = nil
- checkwidth(t)
-
- case OTMAP:
- ok |= ctxType
- n.Left = typecheck(n.Left, ctxType)
- n.Right = typecheck(n.Right, ctxType)
- l := n.Left
- r := n.Right
- if l.Type == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- if l.Type.NotInHeap() {
- yyerror("incomplete (or unallocatable) map key not allowed")
- }
- if r.Type.NotInHeap() {
- yyerror("incomplete (or unallocatable) map value not allowed")
- }
-
- setTypeNode(n, types.NewMap(l.Type, r.Type))
- mapqueue = append(mapqueue, n) // check map keys when all types are settled
- n.Left = nil
- n.Right = nil
-
- case OTCHAN:
- ok |= ctxType
- n.Left = typecheck(n.Left, ctxType)
- l := n.Left
- if l.Type == nil {
- n.Type = nil
- return n
- }
- if l.Type.NotInHeap() {
- yyerror("chan of incomplete (or unallocatable) type not allowed")
- }
-
- setTypeNode(n, types.NewChan(l.Type, n.TChanDir()))
- n.Left = nil
- n.ResetAux()
-
- case OTSTRUCT:
- ok |= ctxType
- setTypeNode(n, tostruct(n.List.Slice()))
- n.List.Set(nil)
-
- case OTINTER:
- ok |= ctxType
- setTypeNode(n, tointerface(n.List.Slice()))
-
- case OTFUNC:
- ok |= ctxType
- setTypeNode(n, functype(n.Left, n.List.Slice(), n.Rlist.Slice()))
- n.Left = nil
- n.List.Set(nil)
- n.Rlist.Set(nil)
-
- // type or expr
- case ODEREF:
- n.Left = typecheck(n.Left, ctxExpr|ctxType)
- l := n.Left
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if l.Op == OTYPE {
- ok |= ctxType
- setTypeNode(n, types.NewPtr(l.Type))
- n.Left = nil
- // Ensure l.Type gets dowidth'd for the backend. Issue 20174.
- checkwidth(l.Type)
- break
- }
-
- if !t.IsPtr() {
- if top&(ctxExpr|ctxStmt) != 0 {
- yyerror("invalid indirect of %L", n.Left)
- n.Type = nil
- return n
- }
-
- break
- }
-
- ok |= ctxExpr
- n.Type = t.Elem()
-
- // arithmetic exprs
- case OASOP,
- OADD,
- OAND,
- OANDAND,
- OANDNOT,
- ODIV,
- OEQ,
- OGE,
- OGT,
- OLE,
- OLT,
- OLSH,
- ORSH,
- OMOD,
- OMUL,
- ONE,
- OOR,
- OOROR,
- OSUB,
- OXOR:
- var l *Node
- var op Op
- var r *Node
- if n.Op == OASOP {
- ok |= ctxStmt
- n.Left = typecheck(n.Left, ctxExpr)
- n.Right = typecheck(n.Right, ctxExpr)
- l = n.Left
- r = n.Right
- checkassign(n, n.Left)
- if l.Type == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- if n.Implicit() && !okforarith[l.Type.Etype] {
- yyerror("invalid operation: %v (non-numeric type %v)", n, l.Type)
- n.Type = nil
- return n
- }
- // TODO(marvin): Fix Node.EType type union.
- op = n.SubOp()
- } else {
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- n.Right = typecheck(n.Right, ctxExpr)
- l = n.Left
- r = n.Right
- if l.Type == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- op = n.Op
- }
- if op == OLSH || op == ORSH {
- r = defaultlit(r, types.Types[TUINT])
- n.Right = r
- t := r.Type
- if !t.IsInteger() {
- yyerror("invalid operation: %v (shift count type %v, must be integer)", n, r.Type)
- n.Type = nil
- return n
- }
- if t.IsSigned() && !langSupported(1, 13, curpkg()) {
- yyerrorv("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type)
- n.Type = nil
- return n
- }
- t = l.Type
- if t != nil && t.Etype != TIDEAL && !t.IsInteger() {
- yyerror("invalid operation: %v (shift of type %v)", n, t)
- n.Type = nil
- return n
- }
-
- // no defaultlit for left
- // the outer context gives the type
- n.Type = l.Type
- if (l.Type == types.UntypedFloat || l.Type == types.UntypedComplex) && r.Op == OLITERAL {
- n.Type = types.UntypedInt
- }
-
- break
- }
-
- // For "x == x && len(s)", it's better to report that "len(s)" (type int)
- // can't be used with "&&" than to report that "x == x" (type untyped bool)
- // can't be converted to int (see issue #41500).
- if n.Op == OANDAND || n.Op == OOROR {
- if !n.Left.Type.IsBoolean() {
- yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Left.Type))
- n.Type = nil
- return n
- }
- if !n.Right.Type.IsBoolean() {
- yyerror("invalid operation: %v (operator %v not defined on %s)", n, n.Op, typekind(n.Right.Type))
- n.Type = nil
- return n
- }
- }
-
- // ideal mixed with non-ideal
- l, r = defaultlit2(l, r, false)
-
- n.Left = l
- n.Right = r
- if l.Type == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- t := l.Type
- if t.Etype == TIDEAL {
- t = r.Type
- }
- et := t.Etype
- if et == TIDEAL {
- et = TINT
- }
- aop := OXXX
- if iscmp[n.Op] && t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
- // comparison is okay as long as one side is
- // assignable to the other. convert so they have
- // the same type.
- //
- // the only conversion that isn't a no-op is concrete == interface.
- // in that case, check comparability of the concrete type.
- // The conversion allocates, so only do it if the concrete type is huge.
- converted := false
- if r.Type.Etype != TBLANK {
- aop, _ = assignop(l.Type, r.Type)
- if aop != OXXX {
- if r.Type.IsInterface() && !l.Type.IsInterface() && !IsComparable(l.Type) {
- yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type))
- n.Type = nil
- return n
- }
-
- dowidth(l.Type)
- if r.Type.IsInterface() == l.Type.IsInterface() || l.Type.Width >= 1<<16 {
- l = nod(aop, l, nil)
- l.Type = r.Type
- l.SetTypecheck(1)
- n.Left = l
- }
-
- t = r.Type
- converted = true
- }
- }
-
- if !converted && l.Type.Etype != TBLANK {
- aop, _ = assignop(r.Type, l.Type)
- if aop != OXXX {
- if l.Type.IsInterface() && !r.Type.IsInterface() && !IsComparable(r.Type) {
- yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type))
- n.Type = nil
- return n
- }
-
- dowidth(r.Type)
- if r.Type.IsInterface() == l.Type.IsInterface() || r.Type.Width >= 1<<16 {
- r = nod(aop, r, nil)
- r.Type = l.Type
- r.SetTypecheck(1)
- n.Right = r
- }
-
- t = l.Type
- }
- }
-
- et = t.Etype
- }
-
- if t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
- l, r = defaultlit2(l, r, true)
- if l.Type == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- if l.Type.IsInterface() == r.Type.IsInterface() || aop == 0 {
- yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
- n.Type = nil
- return n
- }
- }
-
- if t.Etype == TIDEAL {
- t = mixUntyped(l.Type, r.Type)
- }
- if dt := defaultType(t); !okfor[op][dt.Etype] {
- yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t))
- n.Type = nil
- return n
- }
-
- // okfor allows any array == array, map == map, func == func.
- // restrict to slice/map/func == nil and nil == slice/map/func.
- if l.Type.IsArray() && !IsComparable(l.Type) {
- yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type)
- n.Type = nil
- return n
- }
-
- if l.Type.IsSlice() && !l.isNil() && !r.isNil() {
- yyerror("invalid operation: %v (slice can only be compared to nil)", n)
- n.Type = nil
- return n
- }
-
- if l.Type.IsMap() && !l.isNil() && !r.isNil() {
- yyerror("invalid operation: %v (map can only be compared to nil)", n)
- n.Type = nil
- return n
- }
-
- if l.Type.Etype == TFUNC && !l.isNil() && !r.isNil() {
- yyerror("invalid operation: %v (func can only be compared to nil)", n)
- n.Type = nil
- return n
- }
-
- if l.Type.IsStruct() {
- if f := IncomparableField(l.Type); f != nil {
- yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type)
- n.Type = nil
- return n
- }
- }
-
- if iscmp[n.Op] {
- evconst(n)
- t = types.UntypedBool
- if n.Op != OLITERAL {
- l, r = defaultlit2(l, r, true)
- n.Left = l
- n.Right = r
- }
- }
-
- if et == TSTRING && n.Op == OADD {
- // create OADDSTR node with list of strings in x + y + z + (w + v) + ...
- n.Op = OADDSTR
-
- if l.Op == OADDSTR {
- n.List.Set(l.List.Slice())
- } else {
- n.List.Set1(l)
- }
- if r.Op == OADDSTR {
- n.List.AppendNodes(&r.List)
- } else {
- n.List.Append(r)
- }
- n.Left = nil
- n.Right = nil
- }
-
- if (op == ODIV || op == OMOD) && Isconst(r, CTINT) {
- if r.Val().U.(*Mpint).CmpInt64(0) == 0 {
- yyerror("division by zero")
- n.Type = nil
- return n
- }
- }
-
- n.Type = t
-
- case OBITNOT, ONEG, ONOT, OPLUS:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- l := n.Left
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- 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
- }
-
- n.Type = t
-
- // exprs
- case OADDR:
- ok |= ctxExpr
-
- n.Left = typecheck(n.Left, ctxExpr)
- if n.Left.Type == nil {
- n.Type = nil
- return n
- }
-
- switch n.Left.Op {
- case OARRAYLIT, OMAPLIT, OSLICELIT, OSTRUCTLIT:
- n.Op = OPTRLIT
-
- default:
- checklvalue(n.Left, "take the address of")
- r := outervalue(n.Left)
- if r.Op == ONAME {
- if r.Orig != r {
- Fatalf("found non-orig name node %v", r) // TODO(mdempsky): What does this mean?
- }
- r.Name.SetAddrtaken(true)
- if r.Name.IsClosureVar() && !capturevarscomplete {
- // Mark the original variable as Addrtaken so that capturevars
- // knows not to pass it by value.
- // But if the capturevars phase is complete, don't touch it,
- // in case l.Name's containing function has not yet been compiled.
- r.Name.Defn.Name.SetAddrtaken(true)
- }
- }
- n.Left = defaultlit(n.Left, nil)
- if n.Left.Type == nil {
- n.Type = nil
- return n
- }
- }
-
- n.Type = types.NewPtr(n.Left.Type)
-
- case OCOMPLIT:
- ok |= ctxExpr
- n = typecheckcomplit(n)
- if n.Type == nil {
- return n
- }
-
- case OXDOT, ODOT:
- if n.Op == OXDOT {
- n = adddot(n)
- n.Op = ODOT
- if n.Left == nil {
- n.Type = nil
- return n
- }
- }
-
- n.Left = typecheck(n.Left, ctxExpr|ctxType)
-
- n.Left = defaultlit(n.Left, nil)
-
- t := n.Left.Type
- if t == nil {
- adderrorname(n)
- n.Type = nil
- return n
- }
-
- s := n.Sym
-
- if n.Left.Op == OTYPE {
- n = typecheckMethodExpr(n)
- if n.Type == nil {
- return n
- }
- ok = ctxExpr
- break
- }
-
- if t.IsPtr() && !t.Elem().IsInterface() {
- t = t.Elem()
- if t == nil {
- n.Type = nil
- return n
- }
- n.Op = ODOTPTR
- checkwidth(t)
- }
-
- if n.Sym.IsBlank() {
- yyerror("cannot refer to blank field or method")
- n.Type = nil
- return n
- }
-
- if lookdot(n, t, 0) == nil {
- // Legitimate field or method lookup failed, try to explain the error
- switch {
- case t.IsEmptyInterface():
- yyerror("%v undefined (type %v is interface with no methods)", n, n.Left.Type)
-
- case t.IsPtr() && t.Elem().IsInterface():
- // Pointer to interface is almost always a mistake.
- yyerror("%v undefined (type %v is pointer to interface, not interface)", n, n.Left.Type)
-
- case lookdot(n, t, 1) != nil:
- // Field or method matches by name, but it is not exported.
- yyerror("%v undefined (cannot refer to unexported field or method %v)", n, n.Sym)
-
- default:
- if mt := lookdot(n, t, 2); mt != nil && visible(mt.Sym) { // Case-insensitive lookup.
- yyerror("%v undefined (type %v has no field or method %v, but does have %v)", n, n.Left.Type, n.Sym, mt.Sym)
- } else {
- yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Sym)
- }
- }
- n.Type = nil
- return n
- }
-
- switch n.Op {
- case ODOTINTER, ODOTMETH:
- if top&ctxCallee != 0 {
- ok |= ctxCallee
- } else {
- typecheckpartialcall(n, s)
- ok |= ctxExpr
- }
-
- default:
- ok |= ctxExpr
- }
-
- case ODOTTYPE:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- l := n.Left
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if !t.IsInterface() {
- yyerror("invalid type assertion: %v (non-interface type %v on left)", n, t)
- n.Type = nil
- return n
- }
-
- if n.Right != nil {
- n.Right = typecheck(n.Right, ctxType)
- n.Type = n.Right.Type
- n.Right = nil
- if n.Type == nil {
- return n
- }
- }
-
- if n.Type != nil && !n.Type.IsInterface() {
- var missing, have *types.Field
- var ptr int
- if !implements(n.Type, t, &missing, &have, &ptr) {
- if have != nil && have.Sym == missing.Sym {
- yyerror("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+
- "\t\thave %v%0S\n\t\twant %v%0S", n.Type, t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
- } else if ptr != 0 {
- yyerror("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type, t, missing.Sym)
- } else if have != nil {
- yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+
- "\t\thave %v%0S\n\t\twant %v%0S", n.Type, t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
- } else {
- yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type, t, missing.Sym)
- }
- n.Type = nil
- return n
- }
- }
-
- case OINDEX:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- n.Left = implicitstar(n.Left)
- l := n.Left
- n.Right = typecheck(n.Right, ctxExpr)
- r := n.Right
- t := l.Type
- if t == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- switch t.Etype {
- default:
- yyerror("invalid operation: %v (type %v does not support indexing)", n, t)
- n.Type = nil
- return n
-
- case TSTRING, TARRAY, TSLICE:
- n.Right = indexlit(n.Right)
- if t.IsString() {
- n.Type = types.Bytetype
- } else {
- n.Type = t.Elem()
- }
- why := "string"
- if t.IsArray() {
- why = "array"
- } else if t.IsSlice() {
- why = "slice"
- }
-
- if n.Right.Type != nil && !n.Right.Type.IsInteger() {
- yyerror("non-integer %s index %v", why, n.Right)
- break
- }
-
- if !n.Bounded() && Isconst(n.Right, CTINT) {
- x := n.Right.Int64Val()
- if x < 0 {
- yyerror("invalid %s index %v (index must be non-negative)", why, n.Right)
- } else if t.IsArray() && x >= t.NumElem() {
- yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.NumElem())
- } else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.StringVal())) {
- yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.StringVal()))
- } else if n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
- yyerror("invalid %s index %v (index too large)", why, n.Right)
- }
- }
-
- case TMAP:
- n.Right = assignconv(n.Right, t.Key(), "map index")
- n.Type = t.Elem()
- n.Op = OINDEXMAP
- n.ResetAux()
- }
-
- case ORECV:
- ok |= ctxStmt | ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- l := n.Left
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if !t.IsChan() {
- yyerror("invalid operation: %v (receive from non-chan type %v)", n, t)
- n.Type = nil
- return n
- }
-
- if !t.ChanDir().CanRecv() {
- yyerror("invalid operation: %v (receive from send-only type %v)", n, t)
- n.Type = nil
- return n
- }
-
- n.Type = t.Elem()
-
- case OSEND:
- ok |= ctxStmt
- n.Left = typecheck(n.Left, ctxExpr)
- n.Right = typecheck(n.Right, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- t := n.Left.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if !t.IsChan() {
- yyerror("invalid operation: %v (send to non-chan type %v)", n, t)
- n.Type = nil
- return n
- }
-
- if !t.ChanDir().CanSend() {
- yyerror("invalid operation: %v (send to receive-only type %v)", n, t)
- n.Type = nil
- return n
- }
-
- n.Right = assignconv(n.Right, t.Elem(), "send")
- if n.Right.Type == nil {
- n.Type = nil
- return n
- }
- n.Type = nil
-
- case OSLICEHEADER:
- // Errors here are Fatalf instead of yyerror because only the compiler
- // can construct an OSLICEHEADER node.
- // Components used in OSLICEHEADER that are supplied by parsed source code
- // have already been typechecked in e.g. OMAKESLICE earlier.
- ok |= ctxExpr
-
- t := n.Type
- if t == nil {
- Fatalf("no type specified for OSLICEHEADER")
- }
-
- if !t.IsSlice() {
- Fatalf("invalid type %v for OSLICEHEADER", n.Type)
- }
-
- if n.Left == nil || n.Left.Type == nil || !n.Left.Type.IsUnsafePtr() {
- Fatalf("need unsafe.Pointer for OSLICEHEADER")
- }
-
- if x := n.List.Len(); x != 2 {
- Fatalf("expected 2 params (len, cap) for OSLICEHEADER, got %d", x)
- }
-
- n.Left = typecheck(n.Left, ctxExpr)
- l := typecheck(n.List.First(), ctxExpr)
- c := typecheck(n.List.Second(), ctxExpr)
- l = defaultlit(l, types.Types[TINT])
- c = defaultlit(c, types.Types[TINT])
-
- if Isconst(l, CTINT) && l.Int64Val() < 0 {
- Fatalf("len for OSLICEHEADER must be non-negative")
- }
-
- if Isconst(c, CTINT) && c.Int64Val() < 0 {
- Fatalf("cap for OSLICEHEADER must be non-negative")
- }
-
- if Isconst(l, CTINT) && Isconst(c, CTINT) && l.Val().U.(*Mpint).Cmp(c.Val().U.(*Mpint)) > 0 {
- Fatalf("len larger than cap for OSLICEHEADER")
- }
-
- n.List.SetFirst(l)
- n.List.SetSecond(c)
-
- case OMAKESLICECOPY:
- // Errors here are Fatalf instead of yyerror because only the compiler
- // can construct an OMAKESLICECOPY node.
- // Components used in OMAKESCLICECOPY that are supplied by parsed source code
- // have already been typechecked in OMAKE and OCOPY earlier.
- ok |= ctxExpr
-
- t := n.Type
-
- if t == nil {
- Fatalf("no type specified for OMAKESLICECOPY")
- }
-
- if !t.IsSlice() {
- Fatalf("invalid type %v for OMAKESLICECOPY", n.Type)
- }
-
- if n.Left == nil {
- Fatalf("missing len argument for OMAKESLICECOPY")
- }
-
- if n.Right == nil {
- Fatalf("missing slice argument to copy for OMAKESLICECOPY")
- }
-
- n.Left = typecheck(n.Left, ctxExpr)
- n.Right = typecheck(n.Right, ctxExpr)
-
- n.Left = defaultlit(n.Left, types.Types[TINT])
-
- if !n.Left.Type.IsInteger() && n.Type.Etype != TIDEAL {
- yyerror("non-integer len argument in OMAKESLICECOPY")
- }
-
- if Isconst(n.Left, CTINT) {
- if n.Left.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
- Fatalf("len for OMAKESLICECOPY too large")
- }
- if n.Left.Int64Val() < 0 {
- Fatalf("len for OMAKESLICECOPY must be non-negative")
- }
- }
-
- case OSLICE, OSLICE3:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- low, high, max := n.SliceBounds()
- hasmax := n.Op.IsSlice3()
- low = typecheck(low, ctxExpr)
- high = typecheck(high, ctxExpr)
- max = typecheck(max, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- low = indexlit(low)
- high = indexlit(high)
- max = indexlit(max)
- n.SetSliceBounds(low, high, max)
- l := n.Left
- if l.Type == nil {
- n.Type = nil
- return n
- }
- if l.Type.IsArray() {
- if !islvalue(n.Left) {
- yyerror("invalid operation %v (slice of unaddressable value)", n)
- n.Type = nil
- return n
- }
-
- n.Left = nod(OADDR, n.Left, nil)
- n.Left.SetImplicit(true)
- n.Left = typecheck(n.Left, ctxExpr)
- l = n.Left
- }
- t := l.Type
- var tp *types.Type
- if t.IsString() {
- if hasmax {
- yyerror("invalid operation %v (3-index slice of string)", n)
- n.Type = nil
- return n
- }
- n.Type = t
- n.Op = OSLICESTR
- } else if t.IsPtr() && t.Elem().IsArray() {
- tp = t.Elem()
- n.Type = types.NewSlice(tp.Elem())
- dowidth(n.Type)
- if hasmax {
- n.Op = OSLICE3ARR
- } else {
- n.Op = OSLICEARR
- }
- } else if t.IsSlice() {
- n.Type = t
- } else {
- yyerror("cannot slice %v (type %v)", l, t)
- n.Type = nil
- return n
- }
-
- if low != nil && !checksliceindex(l, low, tp) {
- n.Type = nil
- return n
- }
- if high != nil && !checksliceindex(l, high, tp) {
- n.Type = nil
- return n
- }
- if max != nil && !checksliceindex(l, max, tp) {
- n.Type = nil
- return n
- }
- if !checksliceconst(low, high) || !checksliceconst(low, max) || !checksliceconst(high, max) {
- n.Type = nil
- return n
- }
-
- // call and call like
- case OCALL:
- typecheckslice(n.Ninit.Slice(), ctxStmt) // imported rewritten f(g()) calls (#30907)
- n.Left = typecheck(n.Left, ctxExpr|ctxType|ctxCallee)
- if n.Left.Diag() {
- n.SetDiag(true)
- }
-
- l := n.Left
-
- if l.Op == ONAME && l.SubOp() != 0 {
- if n.IsDDD() && l.SubOp() != OAPPEND {
- yyerror("invalid use of ... with builtin %v", l)
- }
-
- // builtin: OLEN, OCAP, etc.
- n.Op = l.SubOp()
- n.Left = n.Right
- n.Right = nil
- n = typecheck1(n, top)
- return n
- }
-
- n.Left = defaultlit(n.Left, nil)
- l = n.Left
- if l.Op == OTYPE {
- if n.IsDDD() {
- if !l.Type.Broke() {
- yyerror("invalid use of ... in type conversion to %v", l.Type)
- }
- n.SetDiag(true)
- }
-
- // pick off before type-checking arguments
- ok |= ctxExpr
-
- // turn CALL(type, arg) into CONV(arg) w/ type
- n.Left = nil
-
- n.Op = OCONV
- n.Type = l.Type
- if !onearg(n, "conversion to %v", l.Type) {
- n.Type = nil
- return n
- }
- n = typecheck1(n, top)
- return n
- }
-
- typecheckargs(n)
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- checkwidth(t)
-
- switch l.Op {
- case ODOTINTER:
- n.Op = OCALLINTER
-
- case ODOTMETH:
- n.Op = OCALLMETH
-
- // typecheckaste was used here but there wasn't enough
- // information further down the call chain to know if we
- // were testing a method receiver for unexported fields.
- // It isn't necessary, so just do a sanity check.
- tp := t.Recv().Type
-
- if l.Left == nil || !types.Identical(l.Left.Type, tp) {
- Fatalf("method receiver")
- }
-
- default:
- n.Op = OCALLFUNC
- if t.Etype != TFUNC {
- name := l.String()
- if isBuiltinFuncName(name) && l.Name.Defn != nil {
- // be more specific when the function
- // name matches a predeclared function
- yyerror("cannot call non-function %s (type %v), declared at %s",
- name, t, linestr(l.Name.Defn.Pos))
- } else {
- yyerror("cannot call non-function %s (type %v)", name, t)
- }
- n.Type = nil
- return n
- }
- }
-
- typecheckaste(OCALL, n.Left, n.IsDDD(), t.Params(), n.List, func() string { return fmt.Sprintf("argument to %v", n.Left) })
- ok |= ctxStmt
- if t.NumResults() == 0 {
- break
- }
- ok |= ctxExpr
- if t.NumResults() == 1 {
- n.Type = l.Type.Results().Field(0).Type
-
- if n.Op == OCALLFUNC && n.Left.Op == ONAME && isRuntimePkg(n.Left.Sym.Pkg) && n.Left.Sym.Name == "getg" {
- // Emit code for runtime.getg() directly instead of calling function.
- // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk,
- // so that the ordering pass can make sure to preserve the semantics of the original code
- // (in particular, the exact time of the function call) by introducing temporaries.
- // In this case, we know getg() always returns the same result within a given function
- // and we want to avoid the temporaries, so we do the rewrite earlier than is typical.
- n.Op = OGETG
- }
-
- break
- }
-
- // multiple return
- if top&(ctxMultiOK|ctxStmt) == 0 {
- yyerror("multiple-value %v() in single-value context", l)
- break
- }
-
- n.Type = l.Type.Results()
-
- case OALIGNOF, OOFFSETOF, OSIZEOF:
- ok |= ctxExpr
- if !onearg(n, "%v", n.Op) {
- n.Type = nil
- return n
- }
- n.Type = types.Types[TUINTPTR]
-
- case OCAP, OLEN:
- ok |= ctxExpr
- if !onearg(n, "%v", n.Op) {
- n.Type = nil
- return n
- }
-
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- n.Left = implicitstar(n.Left)
- l := n.Left
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
-
- var ok bool
- if n.Op == OLEN {
- ok = okforlen[t.Etype]
- } else {
- ok = okforcap[t.Etype]
- }
- if !ok {
- yyerror("invalid argument %L for %v", l, n.Op)
- n.Type = nil
- return n
- }
-
- n.Type = types.Types[TINT]
-
- case OREAL, OIMAG:
- ok |= ctxExpr
- if !onearg(n, "%v", n.Op) {
- n.Type = nil
- return n
- }
-
- n.Left = typecheck(n.Left, ctxExpr)
- l := n.Left
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
-
- // Determine result type.
- switch t.Etype {
- case TIDEAL:
- n.Type = types.UntypedFloat
- case TCOMPLEX64:
- n.Type = types.Types[TFLOAT32]
- case TCOMPLEX128:
- n.Type = types.Types[TFLOAT64]
- default:
- yyerror("invalid argument %L for %v", l, n.Op)
- n.Type = nil
- return n
- }
-
- case OCOMPLEX:
- ok |= ctxExpr
- typecheckargs(n)
- if !twoarg(n) {
- n.Type = nil
- return n
- }
- l := n.Left
- r := n.Right
- if l.Type == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- l, r = defaultlit2(l, r, false)
- if l.Type == nil || r.Type == nil {
- n.Type = nil
- return n
- }
- n.Left = l
- n.Right = r
-
- if !types.Identical(l.Type, r.Type) {
- yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
- n.Type = nil
- return n
- }
-
- var t *types.Type
- switch l.Type.Etype {
- default:
- yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type)
- n.Type = nil
- return n
-
- case TIDEAL:
- t = types.UntypedComplex
-
- case TFLOAT32:
- t = types.Types[TCOMPLEX64]
-
- case TFLOAT64:
- t = types.Types[TCOMPLEX128]
- }
- n.Type = t
-
- case OCLOSE:
- if !onearg(n, "%v", n.Op) {
- n.Type = nil
- return n
- }
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- l := n.Left
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if !t.IsChan() {
- yyerror("invalid operation: %v (non-chan type %v)", n, t)
- n.Type = nil
- return n
- }
-
- if !t.ChanDir().CanSend() {
- yyerror("invalid operation: %v (cannot close receive-only channel)", n)
- n.Type = nil
- return n
- }
-
- ok |= ctxStmt
-
- case ODELETE:
- ok |= ctxStmt
- typecheckargs(n)
- args := n.List
- if args.Len() == 0 {
- yyerror("missing arguments to delete")
- n.Type = nil
- return n
- }
-
- if args.Len() == 1 {
- yyerror("missing second (key) argument to delete")
- n.Type = nil
- return n
- }
-
- if args.Len() != 2 {
- yyerror("too many arguments to delete")
- n.Type = nil
- return n
- }
-
- l := args.First()
- r := args.Second()
- if l.Type != nil && !l.Type.IsMap() {
- yyerror("first argument to delete must be map; have %L", l.Type)
- n.Type = nil
- return n
- }
-
- args.SetSecond(assignconv(r, l.Type.Key(), "delete"))
-
- case OAPPEND:
- ok |= ctxExpr
- typecheckargs(n)
- args := n.List
- if args.Len() == 0 {
- yyerror("missing arguments to append")
- n.Type = nil
- return n
- }
-
- t := args.First().Type
- if t == nil {
- n.Type = nil
- return n
- }
-
- n.Type = t
- if !t.IsSlice() {
- if Isconst(args.First(), CTNIL) {
- yyerror("first argument to append must be typed slice; have untyped nil")
- n.Type = nil
- return n
- }
-
- yyerror("first argument to append must be slice; have %L", t)
- n.Type = nil
- return n
- }
-
- if n.IsDDD() {
- if args.Len() == 1 {
- yyerror("cannot use ... on first argument to append")
- n.Type = nil
- return n
- }
-
- if args.Len() != 2 {
- yyerror("too many arguments to append")
- n.Type = nil
- return n
- }
-
- if t.Elem().IsKind(TUINT8) && args.Second().Type.IsString() {
- args.SetSecond(defaultlit(args.Second(), types.Types[TSTRING]))
- break
- }
-
- args.SetSecond(assignconv(args.Second(), t.Orig, "append"))
- break
- }
-
- as := args.Slice()[1:]
- for i, n := range as {
- if n.Type == nil {
- continue
- }
- as[i] = assignconv(n, t.Elem(), "append")
- checkwidth(as[i].Type) // ensure width is calculated for backend
- }
-
- case OCOPY:
- ok |= ctxStmt | ctxExpr
- typecheckargs(n)
- if !twoarg(n) {
- n.Type = nil
- return n
- }
- n.Type = types.Types[TINT]
- if n.Left.Type == nil || n.Right.Type == nil {
- n.Type = nil
- return n
- }
- n.Left = defaultlit(n.Left, nil)
- n.Right = defaultlit(n.Right, nil)
- if n.Left.Type == nil || n.Right.Type == nil {
- n.Type = nil
- return n
- }
-
- // copy([]byte, string)
- if n.Left.Type.IsSlice() && n.Right.Type.IsString() {
- if types.Identical(n.Left.Type.Elem(), types.Bytetype) {
- break
- }
- yyerror("arguments to copy have different element types: %L and string", n.Left.Type)
- n.Type = nil
- return n
- }
-
- if !n.Left.Type.IsSlice() || !n.Right.Type.IsSlice() {
- if !n.Left.Type.IsSlice() && !n.Right.Type.IsSlice() {
- yyerror("arguments to copy must be slices; have %L, %L", n.Left.Type, n.Right.Type)
- } else if !n.Left.Type.IsSlice() {
- yyerror("first argument to copy should be slice; have %L", n.Left.Type)
- } else {
- yyerror("second argument to copy should be slice or string; have %L", n.Right.Type)
- }
- n.Type = nil
- return n
- }
-
- if !types.Identical(n.Left.Type.Elem(), n.Right.Type.Elem()) {
- yyerror("arguments to copy have different element types: %L and %L", n.Left.Type, n.Right.Type)
- n.Type = nil
- return n
- }
-
- case OCONV:
- ok |= ctxExpr
- checkwidth(n.Type) // ensure width is calculated for backend
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = convlit1(n.Left, n.Type, true, nil)
- t := n.Left.Type
- if t == nil || n.Type == nil {
- n.Type = nil
- return n
- }
- var why string
- n.Op, why = convertop(n.Left.Op == OLITERAL, t, n.Type)
- 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)
- }
- n.Op = OCONV
- n.Type = nil
- return n
- }
-
- switch n.Op {
- case OCONVNOP:
- if t.Etype == n.Type.Etype {
- switch t.Etype {
- case TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
- // Floating point casts imply rounding and
- // so the conversion must be kept.
- n.Op = OCONV
- }
- }
-
- // do not convert to []byte literal. See CL 125796.
- // generated code and compiler memory footprint is better without it.
- case OSTR2BYTES:
- break
-
- case OSTR2RUNES:
- if n.Left.Op == OLITERAL {
- n = stringtoruneslit(n)
- }
- }
-
- case OMAKE:
- ok |= ctxExpr
- args := n.List.Slice()
- if len(args) == 0 {
- yyerror("missing argument to make")
- n.Type = nil
- return n
- }
-
- n.List.Set(nil)
- l := args[0]
- l = typecheck(l, ctxType)
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
-
- i := 1
- switch t.Etype {
- default:
- yyerror("cannot make type %v", t)
- n.Type = nil
- return n
-
- case TSLICE:
- if i >= len(args) {
- yyerror("missing len argument to make(%v)", t)
- n.Type = nil
- return n
- }
-
- l = args[i]
- i++
- l = typecheck(l, ctxExpr)
- var r *Node
- if i < len(args) {
- r = args[i]
- i++
- r = typecheck(r, ctxExpr)
- }
-
- if l.Type == nil || (r != nil && r.Type == nil) {
- n.Type = nil
- return n
- }
- if !checkmake(t, "len", &l) || r != nil && !checkmake(t, "cap", &r) {
- n.Type = nil
- return n
- }
- if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && l.Val().U.(*Mpint).Cmp(r.Val().U.(*Mpint)) > 0 {
- yyerror("len larger than cap in make(%v)", t)
- n.Type = nil
- return n
- }
-
- n.Left = l
- n.Right = r
- n.Op = OMAKESLICE
-
- case TMAP:
- if i < len(args) {
- l = args[i]
- i++
- l = typecheck(l, ctxExpr)
- l = defaultlit(l, types.Types[TINT])
- if l.Type == nil {
- n.Type = nil
- return n
- }
- if !checkmake(t, "size", &l) {
- n.Type = nil
- return n
- }
- n.Left = l
- } else {
- n.Left = nodintconst(0)
- }
- n.Op = OMAKEMAP
-
- case TCHAN:
- l = nil
- if i < len(args) {
- l = args[i]
- i++
- l = typecheck(l, ctxExpr)
- l = defaultlit(l, types.Types[TINT])
- if l.Type == nil {
- n.Type = nil
- return n
- }
- if !checkmake(t, "buffer", &l) {
- n.Type = nil
- return n
- }
- n.Left = l
- } else {
- n.Left = nodintconst(0)
- }
- n.Op = OMAKECHAN
- }
-
- if i < len(args) {
- yyerror("too many arguments to make(%v)", t)
- n.Op = OMAKE
- n.Type = nil
- return n
- }
-
- n.Type = t
-
- case ONEW:
- ok |= ctxExpr
- args := n.List
- if args.Len() == 0 {
- yyerror("missing argument to new")
- n.Type = nil
- return n
- }
-
- l := args.First()
- l = typecheck(l, ctxType)
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if args.Len() > 1 {
- yyerror("too many arguments to new(%v)", t)
- n.Type = nil
- return n
- }
-
- n.Left = l
- n.Type = types.NewPtr(t)
-
- case OPRINT, OPRINTN:
- ok |= ctxStmt
- typecheckargs(n)
- ls := n.List.Slice()
- for i1, n1 := range ls {
- // Special case for print: int constant is int64, not int.
- if Isconst(n1, CTINT) {
- ls[i1] = defaultlit(ls[i1], types.Types[TINT64])
- } else {
- ls[i1] = defaultlit(ls[i1], nil)
- }
- }
-
- case OPANIC:
- ok |= ctxStmt
- if !onearg(n, "panic") {
- n.Type = nil
- return n
- }
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, types.Types[TINTER])
- if n.Left.Type == nil {
- n.Type = nil
- return n
- }
-
- case ORECOVER:
- ok |= ctxExpr | ctxStmt
- if n.List.Len() != 0 {
- yyerror("too many arguments to recover")
- n.Type = nil
- return n
- }
-
- n.Type = types.Types[TINTER]
-
- case OCLOSURE:
- ok |= ctxExpr
- typecheckclosure(n, top)
- if n.Type == nil {
- return n
- }
-
- case OITAB:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- t := n.Left.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if !t.IsInterface() {
- Fatalf("OITAB of %v", t)
- }
- n.Type = types.NewPtr(types.Types[TUINTPTR])
-
- case OIDATA:
- // Whoever creates the OIDATA node must know a priori the concrete type at that moment,
- // usually by just having checked the OITAB.
- Fatalf("cannot typecheck interface data %v", n)
-
- case OSPTR:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- t := n.Left.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if !t.IsSlice() && !t.IsString() {
- Fatalf("OSPTR of %v", t)
- }
- if t.IsString() {
- n.Type = types.NewPtr(types.Types[TUINT8])
- } else {
- n.Type = types.NewPtr(t.Elem())
- }
-
- case OCLOSUREVAR:
- ok |= ctxExpr
-
- case OCFUNC:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
- n.Type = types.Types[TUINTPTR]
-
- case OCONVNOP:
- ok |= ctxExpr
- n.Left = typecheck(n.Left, ctxExpr)
-
- // statements
- case OAS:
- ok |= ctxStmt
-
- typecheckas(n)
-
- // Code that creates temps does not bother to set defn, so do it here.
- if n.Left.Op == ONAME && n.Left.IsAutoTmp() {
- n.Left.Name.Defn = n
- }
-
- case OAS2:
- ok |= ctxStmt
- typecheckas2(n)
-
- case OBREAK,
- OCONTINUE,
- ODCL,
- OEMPTY,
- OGOTO,
- OFALL,
- OVARKILL,
- OVARLIVE:
- ok |= ctxStmt
-
- case OLABEL:
- ok |= ctxStmt
- decldepth++
- if n.Sym.IsBlank() {
- // Empty identifier is valid but useless.
- // Eliminate now to simplify life later.
- // See issues 7538, 11589, 11593.
- n.Op = OEMPTY
- n.Left = nil
- }
-
- case ODEFER:
- ok |= ctxStmt
- n.Left = typecheck(n.Left, ctxStmt|ctxExpr)
- if !n.Left.Diag() {
- checkdefergo(n)
- }
-
- case OGO:
- ok |= ctxStmt
- n.Left = typecheck(n.Left, ctxStmt|ctxExpr)
- checkdefergo(n)
-
- case OFOR, OFORUNTIL:
- ok |= ctxStmt
- typecheckslice(n.Ninit.Slice(), ctxStmt)
- decldepth++
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- if n.Left != nil {
- t := n.Left.Type
- if t != nil && !t.IsBoolean() {
- yyerror("non-bool %L used as for condition", n.Left)
- }
- }
- n.Right = typecheck(n.Right, ctxStmt)
- if n.Op == OFORUNTIL {
- typecheckslice(n.List.Slice(), ctxStmt)
- }
- typecheckslice(n.Nbody.Slice(), ctxStmt)
- decldepth--
-
- case OIF:
- ok |= ctxStmt
- typecheckslice(n.Ninit.Slice(), ctxStmt)
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- if n.Left != nil {
- t := n.Left.Type
- if t != nil && !t.IsBoolean() {
- yyerror("non-bool %L used as if condition", n.Left)
- }
- }
- typecheckslice(n.Nbody.Slice(), ctxStmt)
- typecheckslice(n.Rlist.Slice(), ctxStmt)
-
- case ORETURN:
- ok |= ctxStmt
- typecheckargs(n)
- if Curfn == nil {
- yyerror("return outside function")
- n.Type = nil
- return n
- }
-
- if Curfn.Type.FuncType().Outnamed && n.List.Len() == 0 {
- break
- }
- typecheckaste(ORETURN, nil, false, Curfn.Type.Results(), n.List, func() string { return "return argument" })
-
- case ORETJMP:
- ok |= ctxStmt
-
- case OSELECT:
- ok |= ctxStmt
- typecheckselect(n)
-
- case OSWITCH:
- ok |= ctxStmt
- typecheckswitch(n)
-
- case ORANGE:
- ok |= ctxStmt
- typecheckrange(n)
-
- case OTYPESW:
- yyerror("use of .(type) outside type switch")
- n.Type = nil
- return n
-
- case ODCLFUNC:
- ok |= ctxStmt
- typecheckfunc(n)
-
- case ODCLCONST:
- ok |= ctxStmt
- n.Left = typecheck(n.Left, ctxExpr)
-
- case ODCLTYPE:
- ok |= ctxStmt
- n.Left = typecheck(n.Left, ctxType)
- checkwidth(n.Left.Type)
- }
-
- t := n.Type
- if t != nil && !t.IsFuncArgStruct() && n.Op != OTYPE {
- switch t.Etype {
- case TFUNC, // might have TANY; wait until it's called
- TANY, TFORW, TIDEAL, TNIL, TBLANK:
- break
-
- default:
- checkwidth(t)
- }
- }
-
- evconst(n)
- if n.Op == OTYPE && top&ctxType == 0 {
- if !n.Type.Broke() {
- yyerror("type %v is not an expression", n.Type)
- }
- n.Type = nil
- return n
- }
-
- if top&(ctxExpr|ctxType) == ctxType && n.Op != OTYPE {
- yyerror("%v is not a type", n)
- n.Type = nil
- return n
- }
-
- // TODO(rsc): simplify
- if (top&(ctxCallee|ctxExpr|ctxType) != 0) && top&ctxStmt == 0 && ok&(ctxExpr|ctxType|ctxCallee) == 0 {
- yyerror("%v used as value", n)
- n.Type = nil
- return n
- }
-
- if (top&ctxStmt != 0) && top&(ctxCallee|ctxExpr|ctxType) == 0 && ok&ctxStmt == 0 {
- if !n.Diag() {
- yyerror("%v evaluated but not used", n)
- n.SetDiag(true)
- }
-
- n.Type = nil
- return n
- }
-
- return n
-}
-
-func typecheckargs(n *Node) {
- if n.List.Len() != 1 || n.IsDDD() {
- typecheckslice(n.List.Slice(), ctxExpr)
- return
- }
-
- typecheckslice(n.List.Slice(), ctxExpr|ctxMultiOK)
- t := n.List.First().Type
- if t == nil || !t.IsFuncArgStruct() {
- return
- }
-
- // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
-
- // Save n as n.Orig for fmt.go.
- if n.Orig == n {
- n.Orig = n.sepcopy()
- }
-
- as := nod(OAS2, nil, nil)
- as.Rlist.AppendNodes(&n.List)
-
- // If we're outside of function context, then this call will
- // be executed during the generated init function. However,
- // init.go hasn't yet created it. Instead, associate the
- // temporary variables with dummyInitFn for now, and init.go
- // will reassociate them later when it's appropriate.
- static := Curfn == nil
- if static {
- Curfn = dummyInitFn
- }
- for _, f := range t.FieldSlice() {
- t := temp(f.Type)
- as.Ninit.Append(nod(ODCL, t, nil))
- as.List.Append(t)
- n.List.Append(t)
- }
- if static {
- Curfn = nil
- }
-
- as = typecheck(as, ctxStmt)
- n.Ninit.Append(as)
-}
-
-func checksliceindex(l *Node, r *Node, tp *types.Type) bool {
- t := r.Type
- if t == nil {
- return false
- }
- if !t.IsInteger() {
- yyerror("invalid slice index %v (type %v)", r, t)
- return false
- }
-
- if r.Op == OLITERAL {
- if r.Int64Val() < 0 {
- yyerror("invalid slice index %v (index must be non-negative)", r)
- return false
- } else if tp != nil && tp.NumElem() >= 0 && r.Int64Val() > tp.NumElem() {
- yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.NumElem())
- return false
- } else if Isconst(l, CTSTR) && r.Int64Val() > int64(len(l.StringVal())) {
- yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.StringVal()))
- return false
- } else if r.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
- yyerror("invalid slice index %v (index too large)", r)
- return false
- }
- }
-
- return true
-}
-
-func checksliceconst(lo *Node, hi *Node) bool {
- if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && lo.Val().U.(*Mpint).Cmp(hi.Val().U.(*Mpint)) > 0 {
- yyerror("invalid slice index: %v > %v", lo, hi)
- return false
- }
-
- return true
-}
-
-func checkdefergo(n *Node) {
- what := "defer"
- if n.Op == OGO {
- what = "go"
- }
-
- switch n.Left.Op {
- // ok
- case OCALLINTER,
- OCALLMETH,
- OCALLFUNC,
- OCLOSE,
- OCOPY,
- ODELETE,
- OPANIC,
- OPRINT,
- OPRINTN,
- ORECOVER:
- return
-
- case OAPPEND,
- OCAP,
- OCOMPLEX,
- OIMAG,
- OLEN,
- OMAKE,
- OMAKESLICE,
- OMAKECHAN,
- OMAKEMAP,
- ONEW,
- OREAL,
- OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof
- if n.Left.Orig != nil && n.Left.Orig.Op == OCONV {
- break
- }
- yyerrorl(n.Pos, "%s discards result of %v", what, n.Left)
- return
- }
-
- // type is broken or missing, most likely a method call on a broken type
- // we will warn about the broken type elsewhere. no need to emit a potentially confusing error
- if n.Left.Type == nil || n.Left.Type.Broke() {
- return
- }
-
- if !n.Diag() {
- // The syntax made sure it was a call, so this must be
- // a conversion.
- n.SetDiag(true)
- yyerrorl(n.Pos, "%s requires function call, not conversion", what)
- }
-}
-
-// The result of implicitstar MUST be assigned back to n, e.g.
-// n.Left = implicitstar(n.Left)
-func implicitstar(n *Node) *Node {
- // insert implicit * if needed for fixed array
- t := n.Type
- if t == nil || !t.IsPtr() {
- return n
- }
- t = t.Elem()
- if t == nil {
- return n
- }
- if !t.IsArray() {
- return n
- }
- n = nod(ODEREF, n, nil)
- n.SetImplicit(true)
- n = typecheck(n, ctxExpr)
- return n
-}
-
-func onearg(n *Node, f string, args ...interface{}) bool {
- if n.Left != nil {
- return true
- }
- if n.List.Len() == 0 {
- p := fmt.Sprintf(f, args...)
- yyerror("missing argument to %s: %v", p, n)
- return false
- }
-
- if n.List.Len() > 1 {
- p := fmt.Sprintf(f, args...)
- yyerror("too many arguments to %s: %v", p, n)
- n.Left = n.List.First()
- n.List.Set(nil)
- return false
- }
-
- n.Left = n.List.First()
- n.List.Set(nil)
- return true
-}
-
-func twoarg(n *Node) bool {
- if n.Left != nil {
- return true
- }
- if n.List.Len() != 2 {
- if n.List.Len() < 2 {
- yyerror("not enough arguments in call to %v", n)
- } else {
- yyerror("too many arguments in call to %v", n)
- }
- return false
- }
- n.Left = n.List.First()
- n.Right = n.List.Second()
- n.List.Set(nil)
- return true
-}
-
-func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dostrcmp int) *types.Field {
- var r *types.Field
- for _, f := range fs.Slice() {
- if dostrcmp != 0 && f.Sym.Name == s.Name {
- return f
- }
- if dostrcmp == 2 && strings.EqualFold(f.Sym.Name, s.Name) {
- return f
- }
- if f.Sym != s {
- continue
- }
- if r != nil {
- if errnode != nil {
- yyerror("ambiguous selector %v", errnode)
- } else if t.IsPtr() {
- yyerror("ambiguous selector (%v).%v", t, s)
- } else {
- yyerror("ambiguous selector %v.%v", t, s)
- }
- break
- }
-
- r = f
- }
-
- return r
-}
-
-// typecheckMethodExpr checks selector expressions (ODOT) where the
-// base expression is a type expression (OTYPE).
-func typecheckMethodExpr(n *Node) (res *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheckMethodExpr", n)(&res)
- }
-
- t := n.Left.Type
-
- // Compute the method set for t.
- var ms *types.Fields
- if t.IsInterface() {
- ms = t.Fields()
- } else {
- mt := methtype(t)
- if mt == nil {
- yyerror("%v undefined (type %v has no method %v)", n, t, n.Sym)
- n.Type = nil
- return n
- }
- expandmeth(mt)
- ms = mt.AllMethods()
-
- // The method expression T.m requires a wrapper when T
- // is different from m's declared receiver type. We
- // normally generate these wrappers while writing out
- // runtime type descriptors, which is always done for
- // types declared at package scope. However, we need
- // to make sure to generate wrappers for anonymous
- // receiver types too.
- if mt.Sym == nil {
- addsignat(t)
- }
- }
-
- s := n.Sym
- m := lookdot1(n, s, t, ms, 0)
- if m == nil {
- if lookdot1(n, s, t, ms, 1) != nil {
- yyerror("%v undefined (cannot refer to unexported method %v)", n, s)
- } else if _, ambig := dotpath(s, t, nil, false); ambig {
- yyerror("%v undefined (ambiguous selector)", n) // method or field
- } else {
- yyerror("%v undefined (type %v has no method %v)", n, t, s)
- }
- n.Type = nil
- return n
- }
-
- if !isMethodApplicable(t, m) {
- yyerror("invalid method expression %v (needs pointer receiver: (*%v).%S)", n, t, s)
- n.Type = nil
- return n
- }
-
- n.Op = ONAME
- if n.Name == nil {
- n.Name = new(Name)
- }
- n.Right = newname(n.Sym)
- n.Sym = methodSym(t, n.Sym)
- n.Type = methodfunc(m.Type, n.Left.Type)
- n.Xoffset = 0
- n.SetClass(PFUNC)
- // methodSym already marked n.Sym as a function.
-
- // Issue 25065. Make sure that we emit the symbol for a local method.
- if Ctxt.Flag_dynlink && !inimport && (t.Sym == nil || t.Sym.Pkg == localpkg) {
- makefuncsym(n.Sym)
- }
-
- return n
-}
-
-// isMethodApplicable reports whether method m can be called on a
-// value of type t. This is necessary because we compute a single
-// method set for both T and *T, but some *T methods are not
-// applicable to T receivers.
-func isMethodApplicable(t *types.Type, m *types.Field) bool {
- return t.IsPtr() || !m.Type.Recv().Type.IsPtr() || isifacemethod(m.Type) || m.Embedded == 2
-}
-
-func derefall(t *types.Type) *types.Type {
- for t != nil && t.IsPtr() {
- t = t.Elem()
- }
- return t
-}
-
-func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
- s := n.Sym
-
- dowidth(t)
- var f1 *types.Field
- if t.IsStruct() || t.IsInterface() {
- f1 = lookdot1(n, s, t, t.Fields(), dostrcmp)
- }
-
- var f2 *types.Field
- if n.Left.Type == t || n.Left.Type.Sym == nil {
- mt := methtype(t)
- if mt != nil {
- f2 = lookdot1(n, s, mt, mt.Methods(), dostrcmp)
- }
- }
-
- if f1 != nil {
- if dostrcmp > 1 || f1.Broke() {
- // Already in the process of diagnosing an error.
- return f1
- }
- if f2 != nil {
- yyerror("%v is both field and method", n.Sym)
- }
- if f1.Offset == BADWIDTH {
- Fatalf("lookdot badwidth %v %p", f1, f1)
- }
- n.Xoffset = f1.Offset
- n.Type = f1.Type
- if t.IsInterface() {
- if n.Left.Type.IsPtr() {
- n.Left = nod(ODEREF, n.Left, nil) // implicitstar
- n.Left.SetImplicit(true)
- n.Left = typecheck(n.Left, ctxExpr)
- }
-
- n.Op = ODOTINTER
- } else {
- n.SetOpt(f1)
- }
-
- return f1
- }
-
- if f2 != nil {
- if dostrcmp > 1 {
- // Already in the process of diagnosing an error.
- return f2
- }
- tt := n.Left.Type
- dowidth(tt)
- rcvr := f2.Type.Recv().Type
- if !types.Identical(rcvr, tt) {
- if rcvr.IsPtr() && types.Identical(rcvr.Elem(), tt) {
- checklvalue(n.Left, "call pointer method on")
- n.Left = nod(OADDR, n.Left, nil)
- n.Left.SetImplicit(true)
- n.Left = typecheck(n.Left, ctxType|ctxExpr)
- } else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) {
- n.Left = nod(ODEREF, n.Left, nil)
- n.Left.SetImplicit(true)
- n.Left = typecheck(n.Left, ctxType|ctxExpr)
- } else if tt.IsPtr() && tt.Elem().IsPtr() && types.Identical(derefall(tt), derefall(rcvr)) {
- yyerror("calling method %v with receiver %L requires explicit dereference", n.Sym, n.Left)
- for tt.IsPtr() {
- // Stop one level early for method with pointer receiver.
- if rcvr.IsPtr() && !tt.Elem().IsPtr() {
- break
- }
- n.Left = nod(ODEREF, n.Left, nil)
- n.Left.SetImplicit(true)
- n.Left = typecheck(n.Left, ctxType|ctxExpr)
- tt = tt.Elem()
- }
- } else {
- Fatalf("method mismatch: %v for %v", rcvr, tt)
- }
- }
-
- pll := n
- ll := n.Left
- for ll.Left != nil && (ll.Op == ODOT || ll.Op == ODOTPTR || ll.Op == ODEREF) {
- pll = ll
- ll = ll.Left
- }
- if pll.Implicit() && ll.Type.IsPtr() && ll.Type.Sym != nil && asNode(ll.Type.Sym.Def) != nil && asNode(ll.Type.Sym.Def).Op == OTYPE {
- // It is invalid to automatically dereference a named pointer type when selecting a method.
- // Make n.Left == ll to clarify error message.
- n.Left = ll
- return nil
- }
-
- n.Sym = methodSym(n.Left.Type, f2.Sym)
- n.Xoffset = f2.Offset
- n.Type = f2.Type
- n.Op = ODOTMETH
-
- return f2
- }
-
- return nil
-}
-
-func nokeys(l Nodes) bool {
- for _, n := range l.Slice() {
- if n.Op == OKEY || n.Op == OSTRUCTKEY {
- return false
- }
- }
- return true
-}
-
-func hasddd(t *types.Type) bool {
- for _, tl := range t.Fields().Slice() {
- if tl.IsDDD() {
- return true
- }
- }
-
- return false
-}
-
-// typecheck assignment: type list = expression list
-func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes, desc func() string) {
- var t *types.Type
- var i int
-
- lno := lineno
- defer func() { lineno = lno }()
-
- if tstruct.Broke() {
- return
- }
-
- var n *Node
- if nl.Len() == 1 {
- n = nl.First()
- }
-
- n1 := tstruct.NumFields()
- n2 := nl.Len()
- if !hasddd(tstruct) {
- if n2 > n1 {
- goto toomany
- }
- if n2 < n1 {
- goto notenough
- }
- } else {
- if !isddd {
- if n2 < n1-1 {
- goto notenough
- }
- } else {
- if n2 > n1 {
- goto toomany
- }
- if n2 < n1 {
- goto notenough
- }
- }
- }
-
- i = 0
- for _, tl := range tstruct.Fields().Slice() {
- t = tl.Type
- if tl.IsDDD() {
- if isddd {
- if i >= nl.Len() {
- goto notenough
- }
- if nl.Len()-i > 1 {
- goto toomany
- }
- n = nl.Index(i)
- setlineno(n)
- if n.Type != nil {
- nl.SetIndex(i, assignconvfn(n, t, desc))
- }
- return
- }
-
- // TODO(mdempsky): Make into ... call with implicit slice.
- for ; i < nl.Len(); i++ {
- n = nl.Index(i)
- setlineno(n)
- if n.Type != nil {
- nl.SetIndex(i, assignconvfn(n, t.Elem(), desc))
- }
- }
- return
- }
-
- if i >= nl.Len() {
- goto notenough
- }
- n = nl.Index(i)
- setlineno(n)
- if n.Type != nil {
- nl.SetIndex(i, assignconvfn(n, t, desc))
- }
- i++
- }
-
- if i < nl.Len() {
- goto toomany
- }
- if isddd {
- if call != nil {
- yyerror("invalid use of ... in call to %v", call)
- } else {
- yyerror("invalid use of ... in %v", op)
- }
- }
- return
-
-notenough:
- 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.
- // Method expressions have the form T.M, and the compiler has
- // rewritten those to ONAME nodes but left T in Left.
- if call.isMethodExpression() {
- yyerror("not enough arguments in call to method expression %v%s", call, details)
- } else {
- yyerror("not enough arguments in call to %v%s", call, details)
- }
- } else {
- yyerror("not enough arguments to %v%s", op, details)
- }
- if n != nil {
- n.SetDiag(true)
- }
- }
- return
-
-toomany:
- details := errorDetails(nl, tstruct, isddd)
- if call != nil {
- yyerror("too many arguments in call to %v%s", call, details)
- } else {
- yyerror("too many arguments to %v%s", op, details)
- }
-}
-
-func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string {
- // If we don't know any type at a call site, let's suppress any return
- // message signatures. See Issue https://golang.org/issues/19012.
- if tstruct == nil {
- return ""
- }
- // If any node has an unknown type, suppress it as well
- for _, n := range nl.Slice() {
- if n.Type == nil {
- return ""
- }
- }
- return fmt.Sprintf("\n\thave %s\n\twant %v", nl.sigerr(isddd), tstruct)
-}
-
-// sigrepr is a type's representation to the outside world,
-// in string representations of return signatures
-// e.g in error messages about wrong arguments to return.
-func sigrepr(t *types.Type, isddd bool) string {
- switch t {
- case types.UntypedString:
- return "string"
- case types.UntypedBool:
- return "bool"
- }
-
- if t.Etype == TIDEAL {
- // "untyped number" is not commonly used
- // outside of the compiler, so let's use "number".
- // TODO(mdempsky): Revisit this.
- return "number"
- }
-
- // Turn []T... argument to ...T for clearer error message.
- if isddd {
- if !t.IsSlice() {
- Fatalf("bad type for ... argument: %v", t)
- }
- return "..." + t.Elem().String()
- }
- return t.String()
-}
-
-// sigerr returns the signature of the types at the call or return.
-func (nl Nodes) sigerr(isddd bool) string {
- if nl.Len() < 1 {
- return "()"
- }
-
- var typeStrings []string
- for i, n := range nl.Slice() {
- isdddArg := isddd && i == nl.Len()-1
- typeStrings = append(typeStrings, sigrepr(n.Type, isdddArg))
- }
-
- return fmt.Sprintf("(%s)", strings.Join(typeStrings, ", "))
-}
-
-// type check composite
-func fielddup(name string, hash map[string]bool) {
- if hash[name] {
- yyerror("duplicate field name in struct literal: %s", name)
- return
- }
- hash[name] = true
-}
-
-// iscomptype reports whether type t is a composite literal type.
-func iscomptype(t *types.Type) bool {
- switch t.Etype {
- case TARRAY, TSLICE, TSTRUCT, TMAP:
- return true
- default:
- return false
- }
-}
-
-// pushtype adds elided type information for composite literals if
-// appropriate, and returns the resulting expression.
-func pushtype(n *Node, t *types.Type) *Node {
- if n == nil || n.Op != OCOMPLIT || n.Right != nil {
- return n
- }
-
- switch {
- case iscomptype(t):
- // For T, return T{...}.
- n.Right = typenod(t)
-
- case t.IsPtr() && iscomptype(t.Elem()):
- // For *T, return &T{...}.
- n.Right = typenod(t.Elem())
-
- n = nodl(n.Pos, OADDR, n, nil)
- n.SetImplicit(true)
- }
-
- return n
-}
-
-// The result of typecheckcomplit MUST be assigned back to n, e.g.
-// n.Left = typecheckcomplit(n.Left)
-func typecheckcomplit(n *Node) (res *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheckcomplit", n)(&res)
- }
-
- lno := lineno
- defer func() {
- lineno = lno
- }()
-
- if n.Right == nil {
- yyerrorl(n.Pos, "missing type in composite literal")
- n.Type = nil
- return n
- }
-
- // Save original node (including n.Right)
- n.Orig = n.copy()
-
- setlineno(n.Right)
-
- // Need to handle [...]T arrays specially.
- if n.Right.Op == OTARRAY && n.Right.Left != nil && n.Right.Left.Op == ODDD {
- n.Right.Right = typecheck(n.Right.Right, ctxType)
- if n.Right.Right.Type == nil {
- n.Type = nil
- return n
- }
- elemType := n.Right.Right.Type
-
- length := typecheckarraylit(elemType, -1, n.List.Slice(), "array literal")
-
- n.Op = OARRAYLIT
- n.Type = types.NewArray(elemType, length)
- n.Right = nil
- return n
- }
-
- n.Right = typecheck(n.Right, ctxType)
- t := n.Right.Type
- if t == nil {
- n.Type = nil
- return n
- }
- n.Type = t
-
- switch t.Etype {
- default:
- yyerror("invalid composite literal type %v", t)
- n.Type = nil
-
- case TARRAY:
- typecheckarraylit(t.Elem(), t.NumElem(), n.List.Slice(), "array literal")
- n.Op = OARRAYLIT
- n.Right = nil
-
- case TSLICE:
- length := typecheckarraylit(t.Elem(), -1, n.List.Slice(), "slice literal")
- n.Op = OSLICELIT
- n.Right = nodintconst(length)
-
- case TMAP:
- var cs constSet
- for i3, l := range n.List.Slice() {
- setlineno(l)
- if l.Op != OKEY {
- n.List.SetIndex(i3, typecheck(l, ctxExpr))
- yyerror("missing key in map literal")
- continue
- }
-
- r := l.Left
- r = pushtype(r, t.Key())
- r = typecheck(r, ctxExpr)
- l.Left = assignconv(r, t.Key(), "map key")
- cs.add(lineno, l.Left, "key", "map literal")
-
- r = l.Right
- r = pushtype(r, t.Elem())
- r = typecheck(r, ctxExpr)
- l.Right = assignconv(r, t.Elem(), "map value")
- }
-
- n.Op = OMAPLIT
- n.Right = nil
-
- case TSTRUCT:
- // Need valid field offsets for Xoffset below.
- dowidth(t)
-
- errored := false
- if n.List.Len() != 0 && nokeys(n.List) {
- // simple list of variables
- ls := n.List.Slice()
- for i, n1 := range ls {
- setlineno(n1)
- n1 = typecheck(n1, ctxExpr)
- ls[i] = n1
- if i >= t.NumFields() {
- if !errored {
- yyerror("too many values in %v", n)
- errored = true
- }
- continue
- }
-
- f := t.Field(i)
- s := f.Sym
- if s != nil && !types.IsExported(s.Name) && s.Pkg != localpkg {
- yyerror("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
- }
- // No pushtype allowed here. Must name fields for that.
- n1 = assignconv(n1, f.Type, "field value")
- n1 = nodSym(OSTRUCTKEY, n1, f.Sym)
- n1.Xoffset = f.Offset
- ls[i] = n1
- }
- if len(ls) < t.NumFields() {
- yyerror("too few values in %v", n)
- }
- } else {
- hash := make(map[string]bool)
-
- // keyed list
- ls := n.List.Slice()
- for i, l := range ls {
- setlineno(l)
-
- if l.Op == OKEY {
- key := l.Left
-
- l.Op = OSTRUCTKEY
- l.Left = l.Right
- l.Right = nil
-
- // An OXDOT uses the Sym field to hold
- // the field to the right of the dot,
- // so s will be non-nil, but an OXDOT
- // is never a valid struct literal key.
- if key.Sym == nil || key.Op == OXDOT || key.Sym.IsBlank() {
- yyerror("invalid field name %v in struct initializer", key)
- l.Left = typecheck(l.Left, ctxExpr)
- continue
- }
-
- // Sym might have resolved to name in other top-level
- // package, because of import dot. Redirect to correct sym
- // before we do the lookup.
- s := key.Sym
- if s.Pkg != localpkg && types.IsExported(s.Name) {
- s1 := lookup(s.Name)
- if s1.Origpkg == s.Pkg {
- s = s1
- }
- }
- l.Sym = s
- }
-
- if l.Op != OSTRUCTKEY {
- if !errored {
- yyerror("mixture of field:value and value initializers")
- errored = true
- }
- ls[i] = typecheck(ls[i], ctxExpr)
- continue
- }
-
- f := lookdot1(nil, l.Sym, t, t.Fields(), 0)
- if f == nil {
- if ci := lookdot1(nil, l.Sym, t, t.Fields(), 2); ci != nil { // Case-insensitive lookup.
- if visible(ci.Sym) {
- yyerror("unknown field '%v' in struct literal of type %v (but does have %v)", l.Sym, t, ci.Sym)
- } else if nonexported(l.Sym) && l.Sym.Name == ci.Sym.Name { // Ensure exactness before the suggestion.
- yyerror("cannot refer to unexported field '%v' in struct literal of type %v", l.Sym, t)
- } else {
- yyerror("unknown field '%v' in struct literal of type %v", l.Sym, t)
- }
- continue
- }
- var f *types.Field
- p, _ := dotpath(l.Sym, t, &f, true)
- if p == nil || f.IsMethod() {
- yyerror("unknown field '%v' in struct literal of type %v", l.Sym, t)
- continue
- }
- // dotpath returns the parent embedded types in reverse order.
- var ep []string
- for ei := len(p) - 1; ei >= 0; ei-- {
- ep = append(ep, p[ei].field.Sym.Name)
- }
- ep = append(ep, l.Sym.Name)
- yyerror("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), t)
- continue
- }
- fielddup(f.Sym.Name, hash)
- l.Xoffset = f.Offset
-
- // No pushtype allowed here. Tried and rejected.
- l.Left = typecheck(l.Left, ctxExpr)
- l.Left = assignconv(l.Left, f.Type, "field value")
- }
- }
-
- n.Op = OSTRUCTLIT
- n.Right = nil
- }
-
- return n
-}
-
-// typecheckarraylit type-checks a sequence of slice/array literal elements.
-func typecheckarraylit(elemType *types.Type, bound int64, elts []*Node, ctx string) int64 {
- // If there are key/value pairs, create a map to keep seen
- // keys so we can check for duplicate indices.
- var indices map[int64]bool
- for _, elt := range elts {
- if elt.Op == OKEY {
- indices = make(map[int64]bool)
- break
- }
- }
-
- var key, length int64
- for i, elt := range elts {
- setlineno(elt)
- vp := &elts[i]
- if elt.Op == OKEY {
- elt.Left = typecheck(elt.Left, ctxExpr)
- key = indexconst(elt.Left)
- if key < 0 {
- if !elt.Left.Diag() {
- if key == -2 {
- yyerror("index too large")
- } else {
- yyerror("index must be non-negative integer constant")
- }
- elt.Left.SetDiag(true)
- }
- key = -(1 << 30) // stay negative for a while
- }
- vp = &elt.Right
- }
-
- r := *vp
- r = pushtype(r, elemType)
- r = typecheck(r, ctxExpr)
- *vp = assignconv(r, elemType, ctx)
-
- if key >= 0 {
- if indices != nil {
- if indices[key] {
- yyerror("duplicate index in %s: %d", ctx, key)
- } else {
- indices[key] = true
- }
- }
-
- if bound >= 0 && key >= bound {
- yyerror("array index %d out of bounds [0:%d]", key, bound)
- bound = -1
- }
- }
-
- key++
- if key > length {
- length = key
- }
- }
-
- return length
-}
-
-// visible reports whether sym is exported or locally defined.
-func visible(sym *types.Sym) bool {
- return sym != nil && (types.IsExported(sym.Name) || sym.Pkg == localpkg)
-}
-
-// nonexported reports whether sym is an unexported field.
-func nonexported(sym *types.Sym) bool {
- return sym != nil && !types.IsExported(sym.Name)
-}
-
-// lvalue etc
-func islvalue(n *Node) bool {
- switch n.Op {
- case OINDEX:
- if n.Left.Type != nil && n.Left.Type.IsArray() {
- return islvalue(n.Left)
- }
- if n.Left.Type != nil && n.Left.Type.IsString() {
- return false
- }
- fallthrough
- case ODEREF, ODOTPTR, OCLOSUREVAR:
- return true
-
- case ODOT:
- return islvalue(n.Left)
-
- case ONAME:
- if n.Class() == PFUNC {
- return false
- }
- return true
- }
-
- return false
-}
-
-func checklvalue(n *Node, verb string) {
- if !islvalue(n) {
- yyerror("cannot %s %v", verb, n)
- }
-}
-
-func checkassign(stmt *Node, n *Node) {
- // Variables declared in ORANGE are assigned on every iteration.
- if n.Name == nil || n.Name.Defn != stmt || stmt.Op == ORANGE {
- r := outervalue(n)
- if r.Op == ONAME {
- r.Name.SetAssigned(true)
- if r.Name.IsClosureVar() {
- r.Name.Defn.Name.SetAssigned(true)
- }
- }
- }
-
- if islvalue(n) {
- return
- }
- if n.Op == OINDEXMAP {
- n.SetIndexMapLValue(true)
- return
- }
-
- // have already complained about n being invalid
- if n.Type == nil {
- return
- }
-
- switch {
- case n.Op == ODOT && n.Left.Op == OINDEXMAP:
- yyerror("cannot assign to struct field %v in map", n)
- 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
-}
-
-func checkassignlist(stmt *Node, l Nodes) {
- for _, n := range l.Slice() {
- checkassign(stmt, n)
- }
-}
-
-// samesafeexpr checks whether it is safe to reuse one of l and r
-// instead of computing both. samesafeexpr assumes that l and r are
-// used in the same statement or expression. In order for it to be
-// safe to reuse l or r, they must:
-// * be the same expression
-// * not have side-effects (no function calls, no channel ops);
-// however, panics are ok
-// * not cause inappropriate aliasing; e.g. two string to []byte
-// conversions, must result in two distinct slices
-//
-// The handling of OINDEXMAP is subtle. OINDEXMAP can occur both
-// as an lvalue (map assignment) and an rvalue (map access). This is
-// currently OK, since the only place samesafeexpr gets used on an
-// lvalue expression is for OSLICE and OAPPEND optimizations, and it
-// is correct in those settings.
-func samesafeexpr(l *Node, r *Node) bool {
- if l.Op != r.Op || !types.Identical(l.Type, r.Type) {
- return false
- }
-
- switch l.Op {
- case ONAME, OCLOSUREVAR:
- return l == r
-
- case ODOT, ODOTPTR:
- return l.Sym != nil && r.Sym != nil && l.Sym == r.Sym && samesafeexpr(l.Left, r.Left)
-
- case ODEREF, OCONVNOP,
- ONOT, OBITNOT, OPLUS, ONEG:
- return samesafeexpr(l.Left, r.Left)
-
- case OCONV:
- // Some conversions can't be reused, such as []byte(str).
- // Allow only numeric-ish types. This is a bit conservative.
- return issimple[l.Type.Etype] && samesafeexpr(l.Left, r.Left)
-
- case OINDEX, OINDEXMAP,
- OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
- return samesafeexpr(l.Left, r.Left) && samesafeexpr(l.Right, r.Right)
-
- case OLITERAL:
- return eqval(l.Val(), r.Val())
- }
-
- return false
-}
-
-// type check assignment.
-// if this assignment is the definition of a var on the left side,
-// fill in the var's type.
-func typecheckas(n *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheckas", n)(nil)
- }
-
- // delicate little dance.
- // the definition of n may refer to this assignment
- // as its definition, in which case it will call typecheckas.
- // in that case, do not call typecheck back, or it will cycle.
- // if the variable has a type (ntype) then typechecking
- // will not look at defn, so it is okay (and desirable,
- // so that the conversion below happens).
- n.Left = resolve(n.Left)
-
- if n.Left.Name == nil || n.Left.Name.Defn != n || n.Left.Name.Param.Ntype != nil {
- n.Left = typecheck(n.Left, ctxExpr|ctxAssign)
- }
-
- // Use ctxMultiOK so we can emit an "N variables but M values" error
- // to be consistent with typecheckas2 (#26616).
- n.Right = typecheck(n.Right, ctxExpr|ctxMultiOK)
- checkassign(n, n.Left)
- if n.Right != nil && n.Right.Type != nil {
- if n.Right.Type.IsFuncArgStruct() {
- yyerror("assignment mismatch: 1 variable but %v returns %d values", n.Right.Left, n.Right.Type.NumFields())
- // Multi-value RHS isn't actually valid for OAS; nil out
- // to indicate failed typechecking.
- n.Right.Type = nil
- } else if n.Left.Type != nil {
- n.Right = assignconv(n.Right, n.Left.Type, "assignment")
- }
- }
-
- if n.Left.Name != nil && n.Left.Name.Defn == n && n.Left.Name.Param.Ntype == nil {
- n.Right = defaultlit(n.Right, nil)
- n.Left.Type = n.Right.Type
- }
-
- // second half of dance.
- // now that right is done, typecheck the left
- // just to get it over with. see dance above.
- n.SetTypecheck(1)
-
- if n.Left.Typecheck() == 0 {
- n.Left = typecheck(n.Left, ctxExpr|ctxAssign)
- }
- if !n.Left.isBlank() {
- checkwidth(n.Left.Type) // ensure width is calculated for backend
- }
-}
-
-func checkassignto(src *types.Type, dst *Node) {
- if op, why := assignop(src, dst.Type); op == OXXX {
- yyerror("cannot assign %v to %L in multiple assignment%s", src, dst, why)
- return
- }
-}
-
-func typecheckas2(n *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheckas2", n)(nil)
- }
-
- ls := n.List.Slice()
- for i1, n1 := range ls {
- // delicate little dance.
- n1 = resolve(n1)
- ls[i1] = n1
-
- if n1.Name == nil || n1.Name.Defn != n || n1.Name.Param.Ntype != nil {
- ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign)
- }
- }
-
- cl := n.List.Len()
- cr := n.Rlist.Len()
- if cl > 1 && cr == 1 {
- n.Rlist.SetFirst(typecheck(n.Rlist.First(), ctxExpr|ctxMultiOK))
- } else {
- typecheckslice(n.Rlist.Slice(), ctxExpr)
- }
- checkassignlist(n, n.List)
-
- var l *Node
- var r *Node
- if cl == cr {
- // easy
- ls := n.List.Slice()
- rs := n.Rlist.Slice()
- for il, nl := range ls {
- nr := rs[il]
- if nl.Type != nil && nr.Type != nil {
- rs[il] = assignconv(nr, nl.Type, "assignment")
- }
- if nl.Name != nil && nl.Name.Defn == n && nl.Name.Param.Ntype == nil {
- rs[il] = defaultlit(rs[il], nil)
- nl.Type = rs[il].Type
- }
- }
-
- goto out
- }
-
- l = n.List.First()
- r = n.Rlist.First()
-
- // x,y,z = f()
- if cr == 1 {
- if r.Type == nil {
- goto out
- }
- switch r.Op {
- case OCALLMETH, OCALLINTER, OCALLFUNC:
- if !r.Type.IsFuncArgStruct() {
- break
- }
- cr = r.Type.NumFields()
- if cr != cl {
- goto mismatch
- }
- n.Op = OAS2FUNC
- n.Right = r
- n.Rlist.Set(nil)
- for i, l := range n.List.Slice() {
- f := r.Type.Field(i)
- if f.Type != nil && l.Type != nil {
- checkassignto(f.Type, l)
- }
- if l.Name != nil && l.Name.Defn == n && l.Name.Param.Ntype == nil {
- l.Type = f.Type
- }
- }
- goto out
- }
- }
-
- // x, ok = y
- if cl == 2 && cr == 1 {
- if r.Type == nil {
- goto out
- }
- switch r.Op {
- case OINDEXMAP, ORECV, ODOTTYPE:
- switch r.Op {
- case OINDEXMAP:
- n.Op = OAS2MAPR
- case ORECV:
- n.Op = OAS2RECV
- case ODOTTYPE:
- n.Op = OAS2DOTTYPE
- r.Op = ODOTTYPE2
- }
- n.Right = r
- n.Rlist.Set(nil)
- if l.Type != nil {
- checkassignto(r.Type, l)
- }
- if l.Name != nil && l.Name.Defn == n {
- l.Type = r.Type
- }
- l := n.List.Second()
- if l.Type != nil && !l.Type.IsBoolean() {
- checkassignto(types.Types[TBOOL], l)
- }
- if l.Name != nil && l.Name.Defn == n && l.Name.Param.Ntype == nil {
- l.Type = types.Types[TBOOL]
- }
- goto out
- }
- }
-
-mismatch:
- switch r.Op {
- default:
- yyerror("assignment mismatch: %d variables but %d values", cl, cr)
- case OCALLFUNC, OCALLMETH, OCALLINTER:
- yyerror("assignment mismatch: %d variables but %v returns %d values", cl, r.Left, cr)
- }
-
- // second half of dance
-out:
- n.SetTypecheck(1)
- ls = n.List.Slice()
- for i1, n1 := range ls {
- if n1.Typecheck() == 0 {
- ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign)
- }
- }
-}
-
-// type check function definition
-func typecheckfunc(n *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheckfunc", n)(nil)
- }
-
- for _, ln := range n.Func.Dcl {
- if ln.Op == ONAME && (ln.Class() == PPARAM || ln.Class() == PPARAMOUT) {
- ln.Name.Decldepth = 1
- }
- }
-
- n.Func.Nname = typecheck(n.Func.Nname, ctxExpr|ctxAssign)
- t := n.Func.Nname.Type
- if t == nil {
- return
- }
- n.Type = t
- t.FuncType().Nname = asTypesNode(n.Func.Nname)
- rcvr := t.Recv()
- if rcvr != nil && n.Func.Shortname != nil {
- m := addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0)
- if m == nil {
- return
- }
-
- n.Func.Nname.Sym = methodSym(rcvr.Type, n.Func.Shortname)
- declare(n.Func.Nname, PFUNC)
- }
-
- if Ctxt.Flag_dynlink && !inimport && n.Func.Nname != nil {
- makefuncsym(n.Func.Nname.Sym)
- }
-}
-
-// The result of stringtoruneslit MUST be assigned back to n, e.g.
-// n.Left = stringtoruneslit(n.Left)
-func stringtoruneslit(n *Node) *Node {
- if n.Left.Op != OLITERAL || n.Left.Val().Ctype() != CTSTR {
- Fatalf("stringtoarraylit %v", n)
- }
-
- var l []*Node
- i := 0
- for _, r := range n.Left.StringVal() {
- l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r))))
- i++
- }
-
- nn := nod(OCOMPLIT, nil, typenod(n.Type))
- nn.List.Set(l)
- nn = typecheck(nn, ctxExpr)
- return nn
-}
-
-var mapqueue []*Node
-
-func checkMapKeys() {
- for _, n := range mapqueue {
- k := n.Type.MapType().Key
- if !k.Broke() && !IsComparable(k) {
- yyerrorl(n.Pos, "invalid map key type %v", k)
- }
- }
- mapqueue = nil
-}
-
-func setUnderlying(t, underlying *types.Type) {
- if underlying.Etype == TFORW {
- // This type isn't computed yet; when it is, update n.
- underlying.ForwardType().Copyto = append(underlying.ForwardType().Copyto, t)
- return
- }
-
- n := asNode(t.Nod)
- ft := t.ForwardType()
- cache := t.Cache
-
- // TODO(mdempsky): Fix Type rekinding.
- *t = *underlying
-
- // Restore unnecessarily clobbered attributes.
- t.Nod = asTypesNode(n)
- t.Sym = n.Sym
- if n.Name != nil {
- t.Vargen = n.Name.Vargen
- }
- t.Cache = cache
- t.SetDeferwidth(false)
-
- // spec: "The declared type does not inherit any methods bound
- // to the existing type, but the method set of an interface
- // type [...] remains unchanged."
- if !t.IsInterface() {
- *t.Methods() = types.Fields{}
- *t.AllMethods() = types.Fields{}
- }
-
- // Propagate go:notinheap pragma from the Name to the Type.
- if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma()&NotInHeap != 0 {
- t.SetNotInHeap(true)
- }
-
- // Update types waiting on this type.
- for _, w := range ft.Copyto {
- setUnderlying(w, t)
- }
-
- // Double-check use of type as embedded type.
- if ft.Embedlineno.IsKnown() {
- if t.IsPtr() || t.IsUnsafePtr() {
- yyerrorl(ft.Embedlineno, "embedded type cannot be a pointer")
- }
- }
-}
-
-func typecheckdeftype(n *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheckdeftype", n)(nil)
- }
-
- n.SetTypecheck(1)
- n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, ctxType)
- t := n.Name.Param.Ntype.Type
- if t == nil {
- n.SetDiag(true)
- n.Type = nil
- } else if n.Type == nil {
- n.SetDiag(true)
- } else {
- // copy new type and clear fields
- // that don't come along.
- setUnderlying(n.Type, t)
- }
-}
-
-func typecheckdef(n *Node) {
- if enableTrace && trace {
- defer tracePrint("typecheckdef", n)(nil)
- }
-
- lno := setlineno(n)
-
- if n.Op == ONONAME {
- if !n.Diag() {
- n.SetDiag(true)
-
- // Note: adderrorname looks for this string and
- // adds context about the outer expression
- yyerrorl(lineno, "undefined: %v", n.Sym)
- }
- lineno = lno
- return
- }
-
- if n.Walkdef() == 1 {
- lineno = lno
- return
- }
-
- typecheckdefstack = append(typecheckdefstack, n)
- if n.Walkdef() == 2 {
- flusherrors()
- fmt.Printf("typecheckdef loop:")
- for i := len(typecheckdefstack) - 1; i >= 0; i-- {
- n := typecheckdefstack[i]
- fmt.Printf(" %v", n.Sym)
- }
- fmt.Printf("\n")
- Fatalf("typecheckdef loop")
- }
-
- n.SetWalkdef(2)
-
- if n.Type != nil || n.Sym == nil { // builtin or no name
- goto ret
- }
-
- switch n.Op {
- default:
- Fatalf("typecheckdef %v", n.Op)
-
- case OLITERAL:
- if n.Name.Param.Ntype != nil {
- n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, ctxType)
- n.Type = n.Name.Param.Ntype.Type
- n.Name.Param.Ntype = nil
- if n.Type == nil {
- n.SetDiag(true)
- goto ret
- }
- }
-
- e := n.Name.Defn
- n.Name.Defn = nil
- if e == nil {
- Dump("typecheckdef nil defn", n)
- yyerrorl(n.Pos, "xxx")
- }
-
- e = typecheck(e, ctxExpr)
- if e.Type == nil {
- goto ret
- }
- if !e.isGoConst() {
- if !e.Diag() {
- if Isconst(e, CTNIL) {
- yyerrorl(n.Pos, "const initializer cannot be nil")
- } else {
- yyerrorl(n.Pos, "const initializer %v is not a constant", e)
- }
- e.SetDiag(true)
- }
- goto ret
- }
-
- t := n.Type
- if t != nil {
- if !okforconst[t.Etype] {
- yyerrorl(n.Pos, "invalid constant type %v", t)
- goto ret
- }
-
- if !e.Type.IsUntyped() && !types.Identical(t, e.Type) {
- yyerrorl(n.Pos, "cannot use %L as type %v in const initializer", e, t)
- goto ret
- }
-
- e = convlit(e, t)
- }
-
- n.SetVal(e.Val())
- n.Type = e.Type
-
- case ONAME:
- if n.Name.Param.Ntype != nil {
- n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, ctxType)
- n.Type = n.Name.Param.Ntype.Type
- if n.Type == nil {
- n.SetDiag(true)
- goto ret
- }
- }
-
- if n.Type != nil {
- break
- }
- if n.Name.Defn == nil {
- if n.SubOp() != 0 { // like OPRINTN
- break
- }
- if nsavederrors+nerrors > 0 {
- // Can have undefined variables in x := foo
- // that make x have an n.name.Defn == nil.
- // If there are other errors anyway, don't
- // bother adding to the noise.
- break
- }
-
- Fatalf("var without type, init: %v", n.Sym)
- }
-
- if n.Name.Defn.Op == ONAME {
- n.Name.Defn = typecheck(n.Name.Defn, ctxExpr)
- n.Type = n.Name.Defn.Type
- break
- }
-
- n.Name.Defn = typecheck(n.Name.Defn, ctxStmt) // fills in n.Type
-
- case OTYPE:
- if p := n.Name.Param; p.Alias() {
- // Type alias declaration: Simply use the rhs type - no need
- // to create a new type.
- // If we have a syntax error, p.Ntype may be nil.
- if p.Ntype != nil {
- p.Ntype = typecheck(p.Ntype, ctxType)
- n.Type = p.Ntype.Type
- if n.Type == nil {
- n.SetDiag(true)
- goto ret
- }
- // For package-level type aliases, set n.Sym.Def so we can identify
- // it as a type alias during export. See also #31959.
- if n.Name.Curfn == nil {
- n.Sym.Def = asTypesNode(p.Ntype)
- }
- }
- break
- }
-
- // regular type declaration
- defercheckwidth()
- n.SetWalkdef(1)
- setTypeNode(n, types.New(TFORW))
- n.Type.Sym = n.Sym
- nerrors0 := nerrors
- typecheckdeftype(n)
- if n.Type.Etype == TFORW && nerrors > nerrors0 {
- // Something went wrong during type-checking,
- // but it was reported. Silence future errors.
- n.Type.SetBroke(true)
- }
- resumecheckwidth()
- }
-
-ret:
- if n.Op != OLITERAL && n.Type != nil && n.Type.IsUntyped() {
- Fatalf("got %v for %v", n.Type, n)
- }
- last := len(typecheckdefstack) - 1
- if typecheckdefstack[last] != n {
- Fatalf("typecheckdefstack mismatch")
- }
- typecheckdefstack[last] = nil
- typecheckdefstack = typecheckdefstack[:last]
-
- lineno = lno
- n.SetWalkdef(1)
-}
-
-func checkmake(t *types.Type, arg string, np **Node) bool {
- n := *np
- if !n.Type.IsInteger() && n.Type.Etype != TIDEAL {
- yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type)
- return false
- }
-
- // Do range checks for constants before defaultlit
- // to avoid redundant "constant NNN overflows int" errors.
- switch consttype(n) {
- case CTINT, CTRUNE, CTFLT, CTCPLX:
- v := toint(n.Val()).U.(*Mpint)
- if v.CmpInt64(0) < 0 {
- yyerror("negative %s argument in make(%v)", arg, t)
- return false
- }
- if v.Cmp(maxintval[TINT]) > 0 {
- yyerror("%s argument too large in make(%v)", arg, t)
- return false
- }
- }
-
- // defaultlit is necessary for non-constants too: n might be 1.1< 0 {
- return
- }
- switch n.Op {
- case OIF:
- if !Isconst(n.Left, CTBOOL) || n.Nbody.Len() > 0 || n.Rlist.Len() > 0 {
- return
- }
- case OFOR:
- if !Isconst(n.Left, CTBOOL) || n.Left.BoolVal() {
- return
- }
- default:
- return
- }
- }
-
- fn.Nbody.Set([]*Node{nod(OEMPTY, nil, nil)})
-}
-
-func deadcodeslice(nn Nodes) {
- var lastLabel = -1
- for i, n := range nn.Slice() {
- if n != nil && n.Op == OLABEL {
- lastLabel = i
- }
- }
- for i, n := range nn.Slice() {
- // Cut is set to true when all nodes after i'th position
- // should be removed.
- // In other words, it marks whole slice "tail" as dead.
- cut := false
- if n == nil {
- continue
- }
- if n.Op == OIF {
- n.Left = deadcodeexpr(n.Left)
- if Isconst(n.Left, CTBOOL) {
- var body Nodes
- if n.Left.BoolVal() {
- n.Rlist = Nodes{}
- body = n.Nbody
- } else {
- n.Nbody = Nodes{}
- body = n.Rlist
- }
- // If "then" or "else" branch ends with panic or return statement,
- // it is safe to remove all statements after this node.
- // isterminating is not used to avoid goto-related complications.
- // We must be careful not to deadcode-remove labels, as they
- // might be the target of a goto. See issue 28616.
- if body := body.Slice(); len(body) != 0 {
- switch body[(len(body) - 1)].Op {
- case ORETURN, ORETJMP, OPANIC:
- if i > lastLabel {
- cut = true
- }
- }
- }
- }
- }
-
- deadcodeslice(n.Ninit)
- deadcodeslice(n.Nbody)
- deadcodeslice(n.List)
- deadcodeslice(n.Rlist)
- if cut {
- *nn.slice = nn.Slice()[:i+1]
- break
- }
- }
-}
-
-func deadcodeexpr(n *Node) *Node {
- // Perform dead-code elimination on short-circuited boolean
- // expressions involving constants with the intent of
- // producing a constant 'if' condition.
- switch n.Op {
- case OANDAND:
- n.Left = deadcodeexpr(n.Left)
- n.Right = deadcodeexpr(n.Right)
- if Isconst(n.Left, CTBOOL) {
- if n.Left.BoolVal() {
- return n.Right // true && x => x
- } else {
- return n.Left // false && x => false
- }
- }
- case OOROR:
- n.Left = deadcodeexpr(n.Left)
- n.Right = deadcodeexpr(n.Right)
- if Isconst(n.Left, CTBOOL) {
- if n.Left.BoolVal() {
- return n.Left // true || x => true
- } else {
- return n.Right // false || x => x
- }
- }
- }
- return n
-}
-
-// setTypeNode sets n to an OTYPE node representing t.
-func setTypeNode(n *Node, t *types.Type) {
- n.Op = OTYPE
- n.Type = t
- n.Type.Nod = asTypesNode(n)
-}
-
-// getIotaValue returns the current value for "iota",
-// or -1 if not within a ConstSpec.
-func getIotaValue() int64 {
- if i := len(typecheckdefstack); i > 0 {
- if x := typecheckdefstack[i-1]; x.Op == OLITERAL {
- return x.Iota()
- }
- }
-
- if Curfn != nil && Curfn.Iota() >= 0 {
- return Curfn.Iota()
- }
-
- return -1
-}
-
-// curpkg returns the current package, based on Curfn.
-func curpkg() *types.Pkg {
- fn := Curfn
- if fn == nil {
- // Initialization expressions for package-scope variables.
- return localpkg
- }
-
- // TODO(mdempsky): Standardize on either ODCLFUNC or ONAME for
- // Curfn, rather than mixing them.
- if fn.Op == ODCLFUNC {
- fn = fn.Func.Nname
- }
-
- return fnpkg(fn)
-}
diff --git a/src/cmd/compile/internal/gc/types.go b/src/cmd/compile/internal/gc/types.go
deleted file mode 100644
index 748f8458bdb51..0000000000000
--- a/src/cmd/compile/internal/gc/types.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
-)
-
-// convenience constants
-const (
- Txxx = types.Txxx
-
- TINT8 = types.TINT8
- TUINT8 = types.TUINT8
- TINT16 = types.TINT16
- TUINT16 = types.TUINT16
- TINT32 = types.TINT32
- TUINT32 = types.TUINT32
- TINT64 = types.TINT64
- TUINT64 = types.TUINT64
- TINT = types.TINT
- TUINT = types.TUINT
- TUINTPTR = types.TUINTPTR
-
- TCOMPLEX64 = types.TCOMPLEX64
- TCOMPLEX128 = types.TCOMPLEX128
-
- TFLOAT32 = types.TFLOAT32
- TFLOAT64 = types.TFLOAT64
-
- TBOOL = types.TBOOL
-
- TPTR = types.TPTR
- TFUNC = types.TFUNC
- TSLICE = types.TSLICE
- TARRAY = types.TARRAY
- TSTRUCT = types.TSTRUCT
- TCHAN = types.TCHAN
- TMAP = types.TMAP
- TINTER = types.TINTER
- TFORW = types.TFORW
- TANY = types.TANY
- TSTRING = types.TSTRING
- TUNSAFEPTR = types.TUNSAFEPTR
-
- // pseudo-types for literals
- TIDEAL = types.TIDEAL
- TNIL = types.TNIL
- TBLANK = types.TBLANK
-
- // pseudo-types for frame layout
- TFUNCARGS = types.TFUNCARGS
- TCHANARGS = types.TCHANARGS
-
- NTYPE = types.NTYPE
-)
diff --git a/src/cmd/compile/internal/gc/types_acc.go b/src/cmd/compile/internal/gc/types_acc.go
deleted file mode 100644
index 7240f726f6296..0000000000000
--- a/src/cmd/compile/internal/gc/types_acc.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 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 convertions between *types.Node and *Node.
-// TODO(gri) try to eliminate these soon
-
-package gc
-
-import (
- "cmd/compile/internal/types"
- "unsafe"
-)
-
-func asNode(n *types.Node) *Node { return (*Node)(unsafe.Pointer(n)) }
-func asTypesNode(n *Node) *types.Node { return (*types.Node)(unsafe.Pointer(n)) }
diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go
deleted file mode 100644
index ff8cabd8e38c2..0000000000000
--- a/src/cmd/compile/internal/gc/universe.go
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2009 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.
-
-// TODO(gri) This file should probably become part of package types.
-
-package gc
-
-import "cmd/compile/internal/types"
-
-// builtinpkg is a fake package that declares the universe block.
-var builtinpkg *types.Pkg
-
-var basicTypes = [...]struct {
- name string
- etype types.EType
-}{
- {"int8", TINT8},
- {"int16", TINT16},
- {"int32", TINT32},
- {"int64", TINT64},
- {"uint8", TUINT8},
- {"uint16", TUINT16},
- {"uint32", TUINT32},
- {"uint64", TUINT64},
- {"float32", TFLOAT32},
- {"float64", TFLOAT64},
- {"complex64", TCOMPLEX64},
- {"complex128", TCOMPLEX128},
- {"bool", TBOOL},
- {"string", TSTRING},
-}
-
-var typedefs = [...]struct {
- name string
- etype types.EType
- sameas32 types.EType
- sameas64 types.EType
-}{
- {"int", TINT, TINT32, TINT64},
- {"uint", TUINT, TUINT32, TUINT64},
- {"uintptr", TUINTPTR, TUINT32, TUINT64},
-}
-
-var builtinFuncs = [...]struct {
- name string
- op Op
-}{
- {"append", OAPPEND},
- {"cap", OCAP},
- {"close", OCLOSE},
- {"complex", OCOMPLEX},
- {"copy", OCOPY},
- {"delete", ODELETE},
- {"imag", OIMAG},
- {"len", OLEN},
- {"make", OMAKE},
- {"new", ONEW},
- {"panic", OPANIC},
- {"print", OPRINT},
- {"println", OPRINTN},
- {"real", OREAL},
- {"recover", ORECOVER},
-}
-
-// isBuiltinFuncName reports whether name matches a builtin function
-// name.
-func isBuiltinFuncName(name string) bool {
- for _, fn := range &builtinFuncs {
- if fn.name == name {
- return true
- }
- }
- return false
-}
-
-var unsafeFuncs = [...]struct {
- name string
- op Op
-}{
- {"Alignof", OALIGNOF},
- {"Offsetof", OOFFSETOF},
- {"Sizeof", OSIZEOF},
-}
-
-// initUniverse initializes the universe block.
-func initUniverse() {
- lexinit()
- typeinit()
- lexinit1()
-}
-
-// lexinit initializes known symbols and the basic types.
-func lexinit() {
- for _, s := range &basicTypes {
- etype := s.etype
- if int(etype) >= len(types.Types) {
- Fatalf("lexinit: %s bad etype", s.name)
- }
- s2 := builtinpkg.Lookup(s.name)
- t := types.Types[etype]
- if t == nil {
- t = types.New(etype)
- t.Sym = s2
- if etype != TANY && etype != TSTRING {
- dowidth(t)
- }
- types.Types[etype] = t
- }
- s2.Def = asTypesNode(typenod(t))
- asNode(s2.Def).Name = new(Name)
- }
-
- for _, s := range &builtinFuncs {
- s2 := builtinpkg.Lookup(s.name)
- s2.Def = asTypesNode(newname(s2))
- asNode(s2.Def).SetSubOp(s.op)
- }
-
- for _, s := range &unsafeFuncs {
- s2 := unsafepkg.Lookup(s.name)
- s2.Def = asTypesNode(newname(s2))
- asNode(s2.Def).SetSubOp(s.op)
- }
-
- types.UntypedString = types.New(TSTRING)
- types.UntypedBool = types.New(TBOOL)
- types.Types[TANY] = types.New(TANY)
-
- s := builtinpkg.Lookup("true")
- s.Def = asTypesNode(nodbool(true))
- asNode(s.Def).Sym = lookup("true")
- asNode(s.Def).Name = new(Name)
- asNode(s.Def).Type = types.UntypedBool
-
- s = builtinpkg.Lookup("false")
- s.Def = asTypesNode(nodbool(false))
- asNode(s.Def).Sym = lookup("false")
- asNode(s.Def).Name = new(Name)
- asNode(s.Def).Type = types.UntypedBool
-
- s = lookup("_")
- s.Block = -100
- s.Def = asTypesNode(newname(s))
- types.Types[TBLANK] = types.New(TBLANK)
- asNode(s.Def).Type = types.Types[TBLANK]
- nblank = asNode(s.Def)
-
- s = builtinpkg.Lookup("_")
- s.Block = -100
- s.Def = asTypesNode(newname(s))
- types.Types[TBLANK] = types.New(TBLANK)
- asNode(s.Def).Type = types.Types[TBLANK]
-
- types.Types[TNIL] = types.New(TNIL)
- s = builtinpkg.Lookup("nil")
- var v Val
- v.U = new(NilVal)
- s.Def = asTypesNode(nodlit(v))
- asNode(s.Def).Sym = s
- asNode(s.Def).Name = new(Name)
-
- s = builtinpkg.Lookup("iota")
- s.Def = asTypesNode(nod(OIOTA, nil, nil))
- asNode(s.Def).Sym = s
- asNode(s.Def).Name = new(Name)
-}
-
-func typeinit() {
- if Widthptr == 0 {
- Fatalf("typeinit before betypeinit")
- }
-
- for et := types.EType(0); et < NTYPE; et++ {
- simtype[et] = et
- }
-
- types.Types[TPTR] = types.New(TPTR)
- dowidth(types.Types[TPTR])
-
- t := types.New(TUNSAFEPTR)
- types.Types[TUNSAFEPTR] = t
- t.Sym = unsafepkg.Lookup("Pointer")
- t.Sym.Def = asTypesNode(typenod(t))
- asNode(t.Sym.Def).Name = new(Name)
- dowidth(types.Types[TUNSAFEPTR])
-
- for et := TINT8; et <= TUINT64; et++ {
- isInt[et] = true
- }
- isInt[TINT] = true
- isInt[TUINT] = true
- isInt[TUINTPTR] = true
-
- isFloat[TFLOAT32] = true
- isFloat[TFLOAT64] = true
-
- isComplex[TCOMPLEX64] = true
- isComplex[TCOMPLEX128] = true
-
- // initialize okfor
- for et := types.EType(0); et < NTYPE; et++ {
- if isInt[et] || et == TIDEAL {
- okforeq[et] = true
- okforcmp[et] = true
- okforarith[et] = true
- okforadd[et] = true
- okforand[et] = true
- okforconst[et] = true
- issimple[et] = true
- minintval[et] = new(Mpint)
- maxintval[et] = new(Mpint)
- }
-
- if isFloat[et] {
- okforeq[et] = true
- okforcmp[et] = true
- okforadd[et] = true
- okforarith[et] = true
- okforconst[et] = true
- issimple[et] = true
- minfltval[et] = newMpflt()
- maxfltval[et] = newMpflt()
- }
-
- if isComplex[et] {
- okforeq[et] = true
- okforadd[et] = true
- okforarith[et] = true
- okforconst[et] = true
- issimple[et] = true
- }
- }
-
- issimple[TBOOL] = true
-
- okforadd[TSTRING] = true
-
- okforbool[TBOOL] = true
-
- okforcap[TARRAY] = true
- okforcap[TCHAN] = true
- okforcap[TSLICE] = true
-
- okforconst[TBOOL] = true
- okforconst[TSTRING] = true
-
- okforlen[TARRAY] = true
- okforlen[TCHAN] = true
- okforlen[TMAP] = true
- okforlen[TSLICE] = true
- okforlen[TSTRING] = true
-
- okforeq[TPTR] = true
- okforeq[TUNSAFEPTR] = true
- okforeq[TINTER] = true
- okforeq[TCHAN] = true
- okforeq[TSTRING] = true
- okforeq[TBOOL] = true
- okforeq[TMAP] = true // nil only; refined in typecheck
- okforeq[TFUNC] = true // nil only; refined in typecheck
- okforeq[TSLICE] = true // nil only; refined in typecheck
- okforeq[TARRAY] = true // only if element type is comparable; refined in typecheck
- okforeq[TSTRUCT] = true // only if all struct fields are comparable; refined in typecheck
-
- okforcmp[TSTRING] = true
-
- var i int
- for i = 0; i < len(okfor); i++ {
- okfor[i] = okfornone[:]
- }
-
- // binary
- okfor[OADD] = okforadd[:]
- okfor[OAND] = okforand[:]
- okfor[OANDAND] = okforbool[:]
- okfor[OANDNOT] = okforand[:]
- okfor[ODIV] = okforarith[:]
- okfor[OEQ] = okforeq[:]
- okfor[OGE] = okforcmp[:]
- okfor[OGT] = okforcmp[:]
- okfor[OLE] = okforcmp[:]
- okfor[OLT] = okforcmp[:]
- okfor[OMOD] = okforand[:]
- okfor[OMUL] = okforarith[:]
- okfor[ONE] = okforeq[:]
- okfor[OOR] = okforand[:]
- okfor[OOROR] = okforbool[:]
- okfor[OSUB] = okforarith[:]
- okfor[OXOR] = okforand[:]
- okfor[OLSH] = okforand[:]
- okfor[ORSH] = okforand[:]
-
- // unary
- okfor[OBITNOT] = okforand[:]
- okfor[ONEG] = okforarith[:]
- okfor[ONOT] = okforbool[:]
- okfor[OPLUS] = okforarith[:]
-
- // special
- okfor[OCAP] = okforcap[:]
- okfor[OLEN] = okforlen[:]
-
- // comparison
- iscmp[OLT] = true
- iscmp[OGT] = true
- iscmp[OGE] = true
- iscmp[OLE] = true
- iscmp[OEQ] = true
- iscmp[ONE] = true
-
- maxintval[TINT8].SetString("0x7f")
- minintval[TINT8].SetString("-0x80")
- maxintval[TINT16].SetString("0x7fff")
- minintval[TINT16].SetString("-0x8000")
- maxintval[TINT32].SetString("0x7fffffff")
- minintval[TINT32].SetString("-0x80000000")
- maxintval[TINT64].SetString("0x7fffffffffffffff")
- minintval[TINT64].SetString("-0x8000000000000000")
-
- maxintval[TUINT8].SetString("0xff")
- maxintval[TUINT16].SetString("0xffff")
- maxintval[TUINT32].SetString("0xffffffff")
- maxintval[TUINT64].SetString("0xffffffffffffffff")
-
- // f is valid float if min < f < max. (min and max are not themselves valid.)
- maxfltval[TFLOAT32].SetString("33554431p103") // 2^24-1 p (127-23) + 1/2 ulp
- minfltval[TFLOAT32].SetString("-33554431p103")
- maxfltval[TFLOAT64].SetString("18014398509481983p970") // 2^53-1 p (1023-52) + 1/2 ulp
- minfltval[TFLOAT64].SetString("-18014398509481983p970")
-
- maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32]
- minfltval[TCOMPLEX64] = minfltval[TFLOAT32]
- maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64]
- minfltval[TCOMPLEX128] = minfltval[TFLOAT64]
-
- types.Types[TINTER] = types.New(TINTER) // empty interface
-
- // simple aliases
- simtype[TMAP] = TPTR
- simtype[TCHAN] = TPTR
- simtype[TFUNC] = TPTR
- simtype[TUNSAFEPTR] = TPTR
-
- slicePtrOffset = 0
- sliceLenOffset = Rnd(slicePtrOffset+int64(Widthptr), int64(Widthptr))
- sliceCapOffset = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
- sizeofSlice = Rnd(sliceCapOffset+int64(Widthptr), int64(Widthptr))
-
- // string is same as slice wo the cap
- sizeofString = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
-
- dowidth(types.Types[TSTRING])
- dowidth(types.UntypedString)
-}
-
-func makeErrorInterface() *types.Type {
- field := types.NewField()
- field.Type = types.Types[TSTRING]
- f := functypefield(fakeRecvField(), nil, []*types.Field{field})
-
- field = types.NewField()
- field.Sym = lookup("Error")
- field.Type = f
-
- t := types.New(TINTER)
- t.SetInterface([]*types.Field{field})
- return t
-}
-
-func lexinit1() {
- // error type
- s := builtinpkg.Lookup("error")
- types.Errortype = makeErrorInterface()
- types.Errortype.Sym = s
- types.Errortype.Orig = makeErrorInterface()
- s.Def = asTypesNode(typenod(types.Errortype))
- dowidth(types.Errortype)
-
- // We create separate byte and rune types for better error messages
- // rather than just creating type alias *types.Sym's for the uint8 and
- // int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false.
- // TODO(gri) Should we get rid of this special case (at the cost
- // of less informative error messages involving bytes and runes)?
- // (Alternatively, we could introduce an OTALIAS node representing
- // type aliases, albeit at the cost of having to deal with it everywhere).
-
- // byte alias
- s = builtinpkg.Lookup("byte")
- types.Bytetype = types.New(TUINT8)
- types.Bytetype.Sym = s
- s.Def = asTypesNode(typenod(types.Bytetype))
- asNode(s.Def).Name = new(Name)
- dowidth(types.Bytetype)
-
- // rune alias
- s = builtinpkg.Lookup("rune")
- types.Runetype = types.New(TINT32)
- types.Runetype.Sym = s
- s.Def = asTypesNode(typenod(types.Runetype))
- asNode(s.Def).Name = new(Name)
- dowidth(types.Runetype)
-
- // backend-dependent builtin types (e.g. int).
- for _, s := range &typedefs {
- s1 := builtinpkg.Lookup(s.name)
-
- sameas := s.sameas32
- if Widthptr == 8 {
- sameas = s.sameas64
- }
-
- simtype[s.etype] = sameas
- minfltval[s.etype] = minfltval[sameas]
- maxfltval[s.etype] = maxfltval[sameas]
- minintval[s.etype] = minintval[sameas]
- maxintval[s.etype] = maxintval[sameas]
-
- t := types.New(s.etype)
- t.Sym = s1
- types.Types[s.etype] = t
- s1.Def = asTypesNode(typenod(t))
- asNode(s1.Def).Name = new(Name)
- s1.Origpkg = builtinpkg
-
- dowidth(t)
- }
-}
-
-// finishUniverse makes the universe block visible within the current package.
-func finishUniverse() {
- // Operationally, this is similar to a dot import of builtinpkg, except
- // that we silently skip symbols that are already declared in the
- // package block rather than emitting a redeclared symbol error.
-
- for _, s := range builtinpkg.Syms {
- if s.Def == nil {
- continue
- }
- s1 := lookup(s.Name)
- if s1.Def != nil {
- continue
- }
-
- s1.Def = s.Def
- s1.Block = s.Block
- }
-
- nodfp = newname(lookup(".fp"))
- nodfp.Type = types.Types[TINT32]
- nodfp.SetClass(PPARAM)
- nodfp.Name.SetUsed(true)
-}
diff --git a/src/cmd/compile/internal/gc/unsafe.go b/src/cmd/compile/internal/gc/unsafe.go
deleted file mode 100644
index 2233961561230..0000000000000
--- a/src/cmd/compile/internal/gc/unsafe.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2009 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 gc
-
-// evalunsafe evaluates a package unsafe operation and returns the result.
-func evalunsafe(n *Node) int64 {
- switch n.Op {
- case OALIGNOF, OSIZEOF:
- n.Left = typecheck(n.Left, ctxExpr)
- n.Left = defaultlit(n.Left, nil)
- tr := n.Left.Type
- if tr == nil {
- return 0
- }
- dowidth(tr)
- if n.Op == OALIGNOF {
- return int64(tr.Align)
- }
- return tr.Width
-
- case OOFFSETOF:
- // must be a selector.
- if n.Left.Op != OXDOT {
- yyerror("invalid expression %v", n)
- return 0
- }
-
- // Remember base of selector to find it back after dot insertion.
- // Since r->left may be mutated by typechecking, check it explicitly
- // first to track it correctly.
- n.Left.Left = typecheck(n.Left.Left, ctxExpr)
- base := n.Left.Left
-
- n.Left = typecheck(n.Left, ctxExpr)
- if n.Left.Type == nil {
- return 0
- }
- switch n.Left.Op {
- case ODOT, ODOTPTR:
- break
- case OCALLPART:
- yyerror("invalid expression %v: argument is a method value", n)
- return 0
- default:
- yyerror("invalid expression %v", n)
- return 0
- }
-
- // Sum offsets for dots until we reach base.
- var v int64
- for r := n.Left; r != base; r = r.Left {
- switch r.Op {
- case ODOTPTR:
- // For Offsetof(s.f), s may itself be a pointer,
- // but accessing f must not otherwise involve
- // indirection via embedded pointer types.
- if r.Left != base {
- yyerror("invalid expression %v: selector implies indirection of embedded %v", n, r.Left)
- return 0
- }
- fallthrough
- case ODOT:
- v += r.Xoffset
- default:
- Dump("unsafenmagic", n.Left)
- Fatalf("impossible %#v node after dot insertion", r.Op)
- }
- }
- return v
- }
-
- Fatalf("unexpected op %v", n.Op)
- return 0
-}
diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go
index 58be2f82530a9..4baddbc029a8d 100644
--- a/src/cmd/compile/internal/gc/util.go
+++ b/src/cmd/compile/internal/gc/util.go
@@ -8,59 +8,35 @@ import (
"os"
"runtime"
"runtime/pprof"
-)
-
-// Line returns n's position as a string. If n has been inlined,
-// it uses the outermost position where n has been inlined.
-func (n *Node) Line() string {
- return linestr(n.Pos)
-}
-var atExitFuncs []func()
-
-func atExit(f func()) {
- atExitFuncs = append(atExitFuncs, f)
-}
-
-func Exit(code int) {
- for i := len(atExitFuncs) - 1; i >= 0; i-- {
- f := atExitFuncs[i]
- atExitFuncs = atExitFuncs[:i]
- f()
- }
- os.Exit(code)
-}
+ "cmd/compile/internal/base"
+)
var (
- blockprofile string
- cpuprofile string
- memprofile string
memprofilerate int64
- traceprofile string
traceHandler func(string)
- mutexprofile string
)
func startProfile() {
- if cpuprofile != "" {
- f, err := os.Create(cpuprofile)
+ if base.Flag.CPUProfile != "" {
+ f, err := os.Create(base.Flag.CPUProfile)
if err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
- atExit(pprof.StopCPUProfile)
+ base.AtExit(pprof.StopCPUProfile)
}
- if memprofile != "" {
+ if base.Flag.MemProfile != "" {
if memprofilerate != 0 {
runtime.MemProfileRate = int(memprofilerate)
}
- f, err := os.Create(memprofile)
+ f, err := os.Create(base.Flag.MemProfile)
if err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
- atExit(func() {
+ base.AtExit(func() {
// Profile all outstanding allocations.
runtime.GC()
// compilebench parses the memory profile to extract memstats,
@@ -68,36 +44,36 @@ func startProfile() {
// See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap.
const writeLegacyFormat = 1
if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
})
} else {
// Not doing memory profiling; disable it entirely.
runtime.MemProfileRate = 0
}
- if blockprofile != "" {
- f, err := os.Create(blockprofile)
+ if base.Flag.BlockProfile != "" {
+ f, err := os.Create(base.Flag.BlockProfile)
if err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
runtime.SetBlockProfileRate(1)
- atExit(func() {
+ base.AtExit(func() {
pprof.Lookup("block").WriteTo(f, 0)
f.Close()
})
}
- if mutexprofile != "" {
- f, err := os.Create(mutexprofile)
+ if base.Flag.MutexProfile != "" {
+ f, err := os.Create(base.Flag.MutexProfile)
if err != nil {
- Fatalf("%v", err)
+ base.Fatalf("%v", err)
}
startMutexProfiling()
- atExit(func() {
+ base.AtExit(func() {
pprof.Lookup("mutex").WriteTo(f, 0)
f.Close()
})
}
- if traceprofile != "" && traceHandler != nil {
- traceHandler(traceprofile)
+ if base.Flag.TraceProfile != "" && traceHandler != nil {
+ traceHandler(base.Flag.TraceProfile)
}
}
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
deleted file mode 100644
index 98ebb2399179f..0000000000000
--- a/src/cmd/compile/internal/gc/walk.go
+++ /dev/null
@@ -1,4112 +0,0 @@
-// Copyright 2009 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 gc
-
-import (
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/sys"
- "encoding/binary"
- "fmt"
- "strings"
-)
-
-// The constant is known to runtime.
-const tmpstringbufsize = 32
-const zeroValSize = 1024 // must match value of runtime/map.go:maxZero
-
-func walk(fn *Node) {
- Curfn = fn
-
- if Debug.W != 0 {
- s := fmt.Sprintf("\nbefore walk %v", Curfn.Func.Nname.Sym)
- dumplist(s, Curfn.Nbody)
- }
-
- lno := lineno
-
- // Final typecheck for any unused variables.
- for i, ln := range fn.Func.Dcl {
- if ln.Op == ONAME && (ln.Class() == PAUTO || ln.Class() == PAUTOHEAP) {
- ln = typecheck(ln, ctxExpr|ctxAssign)
- fn.Func.Dcl[i] = ln
- }
- }
-
- // Propagate the used flag for typeswitch variables up to the NONAME in its definition.
- for _, ln := range fn.Func.Dcl {
- if ln.Op == ONAME && (ln.Class() == PAUTO || ln.Class() == PAUTOHEAP) && ln.Name.Defn != nil && ln.Name.Defn.Op == OTYPESW && ln.Name.Used() {
- ln.Name.Defn.Left.Name.SetUsed(true)
- }
- }
-
- for _, ln := range fn.Func.Dcl {
- if ln.Op != ONAME || (ln.Class() != PAUTO && ln.Class() != PAUTOHEAP) || ln.Sym.Name[0] == '&' || ln.Name.Used() {
- continue
- }
- if defn := ln.Name.Defn; defn != nil && defn.Op == OTYPESW {
- if defn.Left.Name.Used() {
- continue
- }
- yyerrorl(defn.Left.Pos, "%v declared but not used", ln.Sym)
- defn.Left.Name.SetUsed(true) // suppress repeats
- } else {
- yyerrorl(ln.Pos, "%v declared but not used", ln.Sym)
- }
- }
-
- lineno = lno
- if nerrors != 0 {
- return
- }
- walkstmtlist(Curfn.Nbody.Slice())
- if Debug.W != 0 {
- s := fmt.Sprintf("after walk %v", Curfn.Func.Nname.Sym)
- dumplist(s, Curfn.Nbody)
- }
-
- zeroResults()
- heapmoves()
- if Debug.W != 0 && Curfn.Func.Enter.Len() > 0 {
- s := fmt.Sprintf("enter %v", Curfn.Func.Nname.Sym)
- dumplist(s, Curfn.Func.Enter)
- }
-}
-
-func walkstmtlist(s []*Node) {
- for i := range s {
- s[i] = walkstmt(s[i])
- }
-}
-
-func paramoutheap(fn *Node) bool {
- for _, ln := range fn.Func.Dcl {
- switch ln.Class() {
- case PPARAMOUT:
- if ln.isParamStackCopy() || ln.Name.Addrtaken() {
- return true
- }
-
- case PAUTO:
- // stop early - parameters are over
- return false
- }
- }
-
- return false
-}
-
-// The result of walkstmt MUST be assigned back to n, e.g.
-// n.Left = walkstmt(n.Left)
-func walkstmt(n *Node) *Node {
- if n == nil {
- return n
- }
-
- setlineno(n)
-
- walkstmtlist(n.Ninit.Slice())
-
- switch n.Op {
- default:
- if n.Op == ONAME {
- yyerror("%v is not a top level statement", n.Sym)
- } else {
- yyerror("%v is not a top level statement", n.Op)
- }
- Dump("nottop", n)
-
- case OAS,
- OASOP,
- OAS2,
- OAS2DOTTYPE,
- OAS2RECV,
- OAS2FUNC,
- OAS2MAPR,
- OCLOSE,
- OCOPY,
- OCALLMETH,
- OCALLINTER,
- OCALL,
- OCALLFUNC,
- ODELETE,
- OSEND,
- OPRINT,
- OPRINTN,
- OPANIC,
- OEMPTY,
- ORECOVER,
- OGETG:
- if n.Typecheck() == 0 {
- Fatalf("missing typecheck: %+v", n)
- }
- wascopy := n.Op == OCOPY
- init := n.Ninit
- n.Ninit.Set(nil)
- n = walkexpr(n, &init)
- n = addinit(n, init.Slice())
- if wascopy && n.Op == OCONVNOP {
- n.Op = OEMPTY // don't leave plain values as statements.
- }
-
- // special case for a receive where we throw away
- // the value received.
- case ORECV:
- if n.Typecheck() == 0 {
- Fatalf("missing typecheck: %+v", n)
- }
- init := n.Ninit
- n.Ninit.Set(nil)
-
- n.Left = walkexpr(n.Left, &init)
- n = mkcall1(chanfn("chanrecv1", 2, n.Left.Type), nil, &init, n.Left, nodnil())
- n = walkexpr(n, &init)
-
- n = addinit(n, init.Slice())
-
- case OBREAK,
- OCONTINUE,
- OFALL,
- OGOTO,
- OLABEL,
- ODCLCONST,
- ODCLTYPE,
- OCHECKNIL,
- OVARDEF,
- OVARKILL,
- OVARLIVE:
- break
-
- case ODCL:
- v := n.Left
- if v.Class() == PAUTOHEAP {
- if compiling_runtime {
- yyerror("%v escapes to heap, not allowed in runtime", v)
- }
- if prealloc[v] == nil {
- prealloc[v] = callnew(v.Type)
- }
- nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v])
- nn.SetColas(true)
- nn = typecheck(nn, ctxStmt)
- return walkstmt(nn)
- }
-
- case OBLOCK:
- walkstmtlist(n.List.Slice())
-
- case OCASE:
- yyerror("case statement out of place")
-
- case ODEFER:
- Curfn.Func.SetHasDefer(true)
- Curfn.Func.numDefers++
- if Curfn.Func.numDefers > maxOpenDefers {
- // Don't allow open-coded defers if there are more than
- // 8 defers in the function, since we use a single
- // byte to record active defers.
- Curfn.Func.SetOpenCodedDeferDisallowed(true)
- }
- if n.Esc != EscNever {
- // If n.Esc is not EscNever, then this defer occurs in a loop,
- // so open-coded defers cannot be used in this function.
- Curfn.Func.SetOpenCodedDeferDisallowed(true)
- }
- fallthrough
- case OGO:
- switch n.Left.Op {
- case OPRINT, OPRINTN:
- n.Left = wrapCall(n.Left, &n.Ninit)
-
- case ODELETE:
- if mapfast(n.Left.List.First().Type) == mapslow {
- n.Left = wrapCall(n.Left, &n.Ninit)
- } else {
- n.Left = walkexpr(n.Left, &n.Ninit)
- }
-
- case OCOPY:
- n.Left = copyany(n.Left, &n.Ninit, true)
-
- 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:
- if n.Left != nil {
- walkstmtlist(n.Left.Ninit.Slice())
- init := n.Left.Ninit
- n.Left.Ninit.Set(nil)
- n.Left = walkexpr(n.Left, &init)
- n.Left = addinit(n.Left, init.Slice())
- }
-
- n.Right = walkstmt(n.Right)
- if n.Op == OFORUNTIL {
- walkstmtlist(n.List.Slice())
- }
- walkstmtlist(n.Nbody.Slice())
-
- case OIF:
- n.Left = walkexpr(n.Left, &n.Ninit)
- walkstmtlist(n.Nbody.Slice())
- walkstmtlist(n.Rlist.Slice())
-
- case ORETURN:
- Curfn.Func.numReturns++
- if n.List.Len() == 0 {
- break
- }
- if (Curfn.Type.FuncType().Outnamed && n.List.Len() > 1) || paramoutheap(Curfn) || Curfn.Func.HasDefer() {
- // assign to the function out parameters,
- // so that reorder3 can fix up conflicts
- var rl []*Node
-
- for _, ln := range Curfn.Func.Dcl {
- cl := ln.Class()
- if cl == PAUTO || cl == PAUTOHEAP {
- break
- }
- if cl == PPARAMOUT {
- if ln.isParamStackCopy() {
- ln = walkexpr(typecheck(nod(ODEREF, ln.Name.Param.Heapaddr, nil), ctxExpr), nil)
- }
- rl = append(rl, ln)
- }
- }
-
- if got, want := n.List.Len(), len(rl); got != want {
- // order should have rewritten multi-value function calls
- // with explicit OAS2FUNC nodes.
- Fatalf("expected %v return arguments, have %v", want, got)
- }
-
- // move function calls out, to make reorder3's job easier.
- walkexprlistsafe(n.List.Slice(), &n.Ninit)
-
- ll := ascompatee(n.Op, rl, n.List.Slice(), &n.Ninit)
- n.List.Set(reorder3(ll))
- break
- }
- walkexprlist(n.List.Slice(), &n.Ninit)
-
- // For each return parameter (lhs), assign the corresponding result (rhs).
- lhs := Curfn.Type.Results()
- rhs := n.List.Slice()
- res := make([]*Node, lhs.NumFields())
- for i, nl := range lhs.FieldSlice() {
- nname := asNode(nl.Nname)
- if nname.isParamHeapCopy() {
- nname = nname.Name.Param.Stackcopy
- }
- a := nod(OAS, nname, rhs[i])
- res[i] = convas(a, &n.Ninit)
- }
- n.List.Set(res)
-
- case ORETJMP:
- break
-
- case OINLMARK:
- break
-
- case OSELECT:
- walkselect(n)
-
- case OSWITCH:
- walkswitch(n)
-
- case ORANGE:
- n = walkrange(n)
- }
-
- if n.Op == ONAME {
- Fatalf("walkstmt ended up with name: %+v", n)
- }
- return n
-}
-
-// walk the whole tree of the body of an
-// expression or simple statement.
-// the types expressions are calculated.
-// compile-time constants are evaluated.
-// complex side effects like statements are appended to init
-func walkexprlist(s []*Node, init *Nodes) {
- for i := range s {
- s[i] = walkexpr(s[i], init)
- }
-}
-
-func walkexprlistsafe(s []*Node, init *Nodes) {
- for i, n := range s {
- s[i] = safeexpr(n, init)
- s[i] = walkexpr(s[i], init)
- }
-}
-
-func walkexprlistcheap(s []*Node, init *Nodes) {
- for i, n := range s {
- s[i] = cheapexpr(n, init)
- s[i] = walkexpr(s[i], init)
- }
-}
-
-// convFuncName builds the runtime function name for interface conversion.
-// It also reports whether the function expects the data by address.
-// Not all names are possible. For example, we never generate convE2E or convE2I.
-func convFuncName(from, to *types.Type) (fnname string, needsaddr bool) {
- tkind := to.Tie()
- switch from.Tie() {
- case 'I':
- if tkind == 'I' {
- return "convI2I", false
- }
- case 'T':
- switch {
- case from.Size() == 2 && from.Align == 2:
- return "convT16", false
- case from.Size() == 4 && from.Align == 4 && !from.HasPointers():
- return "convT32", false
- case from.Size() == 8 && from.Align == types.Types[TUINT64].Align && !from.HasPointers():
- return "convT64", false
- }
- if sc := from.SoleComponent(); sc != nil {
- switch {
- case sc.IsString():
- return "convTstring", false
- case sc.IsSlice():
- return "convTslice", false
- }
- }
-
- switch tkind {
- case 'E':
- if !from.HasPointers() {
- return "convT2Enoptr", true
- }
- return "convT2E", true
- case 'I':
- if !from.HasPointers() {
- return "convT2Inoptr", true
- }
- return "convT2I", true
- }
- }
- Fatalf("unknown conv func %c2%c", from.Tie(), to.Tie())
- panic("unreachable")
-}
-
-// The result of walkexpr MUST be assigned back to n, e.g.
-// n.Left = walkexpr(n.Left, init)
-func walkexpr(n *Node, init *Nodes) *Node {
- if n == nil {
- return n
- }
-
- // Eagerly checkwidth all expressions for the back end.
- if n.Type != nil && !n.Type.WidthCalculated() {
- switch n.Type.Etype {
- case TBLANK, TNIL, TIDEAL:
- default:
- checkwidth(n.Type)
- }
- }
-
- if init == &n.Ninit {
- // not okay to use n->ninit when walking n,
- // because we might replace n with some other node
- // and would lose the init list.
- Fatalf("walkexpr init == &n->ninit")
- }
-
- if n.Ninit.Len() != 0 {
- walkstmtlist(n.Ninit.Slice())
- init.AppendNodes(&n.Ninit)
- }
-
- lno := setlineno(n)
-
- if Debug.w > 1 {
- Dump("before walk expr", n)
- }
-
- if n.Typecheck() != 1 {
- Fatalf("missed typecheck: %+v", n)
- }
-
- if n.Type.IsUntyped() {
- Fatalf("expression has untyped type: %+v", n)
- }
-
- if n.Op == ONAME && n.Class() == PAUTOHEAP {
- nn := nod(ODEREF, n.Name.Param.Heapaddr, nil)
- nn = typecheck(nn, ctxExpr)
- nn = walkexpr(nn, init)
- nn.Left.MarkNonNil()
- return nn
- }
-
-opswitch:
- switch n.Op {
- default:
- Dump("walk", n)
- Fatalf("walkexpr: switch 1 unknown op %+S", n)
-
- case ONONAME, OEMPTY, OGETG, ONEWOBJ:
-
- case OTYPE, ONAME, OLITERAL:
- // TODO(mdempsky): Just return n; see discussion on CL 38655.
- // Perhaps refactor to use Node.mayBeShared for these instead.
- // If these return early, make sure to still call
- // stringsym for constant strings.
-
- case ONOT, ONEG, OPLUS, OBITNOT, OREAL, OIMAG, ODOTMETH, ODOTINTER,
- ODEREF, OSPTR, OITAB, OIDATA, OADDR:
- n.Left = walkexpr(n.Left, init)
-
- case OEFACE, OAND, OANDNOT, OSUB, OMUL, OADD, OOR, OXOR, OLSH, ORSH:
- n.Left = walkexpr(n.Left, init)
- n.Right = walkexpr(n.Right, init)
-
- case ODOT, ODOTPTR:
- usefield(n)
- n.Left = walkexpr(n.Left, init)
-
- case ODOTTYPE, ODOTTYPE2:
- n.Left = walkexpr(n.Left, init)
- // Set up interface type addresses for back end.
- n.Right = typename(n.Type)
- if n.Op == ODOTTYPE {
- n.Right.Right = typename(n.Left.Type)
- }
- if !n.Type.IsInterface() && !n.Left.Type.IsEmptyInterface() {
- n.List.Set1(itabname(n.Type, n.Left.Type))
- }
-
- case OLEN, OCAP:
- if isRuneCount(n) {
- // Replace len([]rune(string)) with runtime.countrunes(string).
- n = mkcall("countrunes", n.Type, init, conv(n.Left.Left, types.Types[TSTRING]))
- break
- }
-
- n.Left = walkexpr(n.Left, init)
-
- // replace len(*[10]int) with 10.
- // delayed until now to preserve side effects.
- t := n.Left.Type
-
- if t.IsPtr() {
- t = t.Elem()
- }
- if t.IsArray() {
- safeexpr(n.Left, init)
- setintconst(n, t.NumElem())
- n.SetTypecheck(1)
- }
-
- case OCOMPLEX:
- // Use results from call expression as arguments for complex.
- if n.Left == nil && n.Right == nil {
- n.Left = n.List.First()
- n.Right = n.List.Second()
- }
- n.Left = walkexpr(n.Left, init)
- n.Right = walkexpr(n.Right, init)
-
- case OEQ, ONE, OLT, OLE, OGT, OGE:
- n = walkcompare(n, init)
-
- case OANDAND, OOROR:
- n.Left = walkexpr(n.Left, init)
-
- // cannot put side effects from n.Right on init,
- // because they cannot run before n.Left is checked.
- // save elsewhere and store on the eventual n.Right.
- var ll Nodes
-
- n.Right = walkexpr(n.Right, &ll)
- n.Right = addinit(n.Right, ll.Slice())
-
- case OPRINT, OPRINTN:
- n = walkprint(n, init)
-
- case OPANIC:
- n = mkcall("gopanic", nil, init, n.Left)
-
- case ORECOVER:
- n = mkcall("gorecover", n.Type, init, nod(OADDR, nodfp, nil))
-
- case OCLOSUREVAR, OCFUNC:
-
- case OCALLINTER, OCALLFUNC, OCALLMETH:
- if n.Op == OCALLINTER || n.Op == OCALLMETH {
- // We expect both interface call reflect.Type.Method and concrete
- // call reflect.(*rtype).Method.
- usemethod(n)
- }
- if n.Op == OCALLINTER {
- markUsedIfaceMethod(n)
- }
-
- if n.Op == OCALLFUNC && n.Left.Op == 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.
- n.List.Prepend(n.Left.Func.Enter.Slice()...)
-
- n.Left.Func.Enter.Set(nil)
-
- // Replace OCLOSURE with ONAME/PFUNC.
- n.Left = n.Left.Func.Closure.Func.Nname
-
- // Update type of OCALLFUNC node.
- // Output arguments had not changed, but their offsets could.
- if n.Left.Type.NumResults() == 1 {
- n.Type = n.Left.Type.Results().Field(0).Type
- } else {
- n.Type = n.Left.Type.Results()
- }
- }
-
- walkCall(n, init)
-
- case OAS, OASOP:
- init.AppendNodes(&n.Ninit)
-
- // Recognize m[k] = append(m[k], ...) so we can reuse
- // the mapassign call.
- mapAppend := n.Left.Op == OINDEXMAP && n.Right.Op == OAPPEND
- if mapAppend && !samesafeexpr(n.Left, n.Right.List.First()) {
- Fatalf("not same expressions: %v != %v", n.Left, n.Right.List.First())
- }
-
- n.Left = walkexpr(n.Left, init)
- n.Left = safeexpr(n.Left, init)
-
- if mapAppend {
- n.Right.List.SetFirst(n.Left)
- }
-
- if n.Op == OASOP {
- // Rewrite x op= y into x = x op y.
- n.Right = nod(n.SubOp(), n.Left, n.Right)
- n.Right = typecheck(n.Right, ctxExpr)
-
- n.Op = OAS
- n.ResetAux()
- }
-
- if oaslit(n, init) {
- break
- }
-
- if n.Right == nil {
- // TODO(austin): Check all "implicit zeroing"
- break
- }
-
- if !instrumenting && isZero(n.Right) {
- break
- }
-
- switch n.Right.Op {
- default:
- n.Right = walkexpr(n.Right, init)
-
- case ORECV:
- // x = <-c; n.Left is x, n.Right.Left is c.
- // order.stmt made sure x is addressable.
- n.Right.Left = walkexpr(n.Right.Left, init)
-
- n1 := nod(OADDR, n.Left, nil)
- r := n.Right.Left // the channel
- n = mkcall1(chanfn("chanrecv1", 2, r.Type), nil, init, r, n1)
- n = walkexpr(n, init)
- break opswitch
-
- case OAPPEND:
- // x = append(...)
- r := n.Right
- if r.Type.Elem().NotInHeap() {
- yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", r.Type.Elem())
- }
- switch {
- case isAppendOfMake(r):
- // x = append(y, make([]T, y)...)
- r = extendslice(r, init)
- case r.IsDDD():
- r = appendslice(r, init) // also works for append(slice, string).
- default:
- r = walkappend(r, init, n)
- }
- n.Right = r
- if r.Op == OAPPEND {
- // Left in place for back end.
- // Do not add a new write barrier.
- // Set up address of type for back end.
- r.Left = typename(r.Type.Elem())
- break opswitch
- }
- // Otherwise, lowered for race detector.
- // Treat as ordinary assignment.
- }
-
- if n.Left != nil && n.Right != nil {
- n = convas(n, init)
- }
-
- case OAS2:
- init.AppendNodes(&n.Ninit)
- walkexprlistsafe(n.List.Slice(), init)
- walkexprlistsafe(n.Rlist.Slice(), init)
- ll := ascompatee(OAS, n.List.Slice(), n.Rlist.Slice(), init)
- ll = reorder3(ll)
- n = liststmt(ll)
-
- // a,b,... = fn()
- case OAS2FUNC:
- init.AppendNodes(&n.Ninit)
-
- r := n.Right
- walkexprlistsafe(n.List.Slice(), init)
- r = walkexpr(r, init)
-
- if isIntrinsicCall(r) {
- n.Right = r
- break
- }
- init.Append(r)
-
- ll := ascompatet(n.List, r.Type)
- n = liststmt(ll)
-
- // x, y = <-c
- // order.stmt made sure x is addressable or blank.
- case OAS2RECV:
- init.AppendNodes(&n.Ninit)
-
- r := n.Right
- walkexprlistsafe(n.List.Slice(), init)
- r.Left = walkexpr(r.Left, init)
- var n1 *Node
- if n.List.First().isBlank() {
- n1 = nodnil()
- } else {
- n1 = nod(OADDR, n.List.First(), nil)
- }
- fn := chanfn("chanrecv2", 2, r.Left.Type)
- ok := n.List.Second()
- call := mkcall1(fn, types.Types[TBOOL], init, r.Left, n1)
- n = nod(OAS, ok, call)
- n = typecheck(n, ctxStmt)
-
- // a,b = m[i]
- case OAS2MAPR:
- init.AppendNodes(&n.Ninit)
-
- r := n.Right
- walkexprlistsafe(n.List.Slice(), init)
- r.Left = walkexpr(r.Left, init)
- r.Right = walkexpr(r.Right, init)
- t := r.Left.Type
-
- fast := mapfast(t)
- var key *Node
- if fast != mapslow {
- // fast versions take key by value
- key = r.Right
- } else {
- // standard version takes key by reference
- // order.expr made sure key is addressable.
- key = nod(OADDR, r.Right, nil)
- }
-
- // from:
- // a,b = m[i]
- // to:
- // var,b = mapaccess2*(t, m, i)
- // a = *var
- a := n.List.First()
-
- if w := t.Elem().Width; w <= zeroValSize {
- fn := mapfn(mapaccess2[fast], t)
- r = mkcall1(fn, fn.Type.Results(), init, typename(t), r.Left, key)
- } else {
- fn := mapfn("mapaccess2_fat", t)
- z := zeroaddr(w)
- r = mkcall1(fn, fn.Type.Results(), init, typename(t), r.Left, key, z)
- }
-
- // mapaccess2* returns a typed bool, but due to spec changes,
- // the boolean result of i.(T) is now untyped so we make it the
- // same type as the variable on the lhs.
- if ok := n.List.Second(); !ok.isBlank() && ok.Type.IsBoolean() {
- r.Type.Field(1).Type = ok.Type
- }
- n.Right = r
- n.Op = OAS2FUNC
-
- // don't generate a = *var if a is _
- if !a.isBlank() {
- var_ := temp(types.NewPtr(t.Elem()))
- var_.SetTypecheck(1)
- var_.MarkNonNil() // mapaccess always returns a non-nil pointer
- n.List.SetFirst(var_)
- n = walkexpr(n, init)
- init.Append(n)
- n = nod(OAS, a, nod(ODEREF, var_, nil))
- }
-
- n = typecheck(n, ctxStmt)
- n = walkexpr(n, init)
-
- case ODELETE:
- init.AppendNodes(&n.Ninit)
- map_ := n.List.First()
- key := n.List.Second()
- map_ = walkexpr(map_, init)
- key = walkexpr(key, init)
-
- t := map_.Type
- fast := mapfast(t)
- if fast == mapslow {
- // order.stmt made sure key is addressable.
- key = nod(OADDR, key, nil)
- }
- n = mkcall1(mapfndel(mapdelete[fast], t), nil, init, typename(t), map_, key)
-
- case OAS2DOTTYPE:
- walkexprlistsafe(n.List.Slice(), init)
- n.Right = walkexpr(n.Right, init)
-
- case OCONVIFACE:
- n.Left = walkexpr(n.Left, init)
-
- fromType := n.Left.Type
- toType := n.Type
-
- if !fromType.IsInterface() && !Curfn.Func.Nname.isBlank() { // skip unnamed functions (func _())
- markTypeUsedInInterface(fromType, Curfn.Func.lsym)
- }
-
- // typeword generates the type word of the interface value.
- typeword := func() *Node {
- if toType.IsEmptyInterface() {
- return typename(fromType)
- }
- return itabname(fromType, toType)
- }
-
- // Optimize convT2E or convT2I as a two-word copy when T is pointer-shaped.
- if isdirectiface(fromType) {
- l := nod(OEFACE, typeword(), n.Left)
- l.Type = toType
- l.SetTypecheck(n.Typecheck())
- n = l
- break
- }
-
- if staticuint64s == nil {
- staticuint64s = newname(Runtimepkg.Lookup("staticuint64s"))
- staticuint64s.SetClass(PEXTERN)
- // The actual type is [256]uint64, but we use [256*8]uint8 so we can address
- // individual bytes.
- staticuint64s.Type = types.NewArray(types.Types[TUINT8], 256*8)
- zerobase = newname(Runtimepkg.Lookup("zerobase"))
- zerobase.SetClass(PEXTERN)
- zerobase.Type = types.Types[TUINTPTR]
- }
-
- // Optimize convT2{E,I} for many cases in which T is not pointer-shaped,
- // by using an existing addressable value identical to n.Left
- // or creating one on the stack.
- var value *Node
- switch {
- case fromType.Size() == 0:
- // n.Left is zero-sized. Use zerobase.
- cheapexpr(n.Left, init) // Evaluate n.Left for side-effects. See issue 19246.
- value = zerobase
- case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()):
- // n.Left is a bool/byte. Use staticuint64s[n.Left * 8] on little-endian
- // and staticuint64s[n.Left * 8 + 7] on big-endian.
- n.Left = cheapexpr(n.Left, init)
- // byteindex widens n.Left so that the multiplication doesn't overflow.
- index := nod(OLSH, byteindex(n.Left), nodintconst(3))
- if thearch.LinkArch.ByteOrder == binary.BigEndian {
- index = nod(OADD, index, nodintconst(7))
- }
- value = nod(OINDEX, staticuint64s, index)
- value.SetBounded(true)
- case n.Left.Class() == PEXTERN && n.Left.Name != nil && n.Left.Name.Readonly():
- // n.Left is a readonly global; use it directly.
- value = n.Left
- case !fromType.IsInterface() && n.Esc == EscNone && fromType.Width <= 1024:
- // n.Left does not escape. Use a stack temporary initialized to n.Left.
- value = temp(fromType)
- init.Append(typecheck(nod(OAS, value, n.Left), ctxStmt))
- }
-
- if value != nil {
- // Value is identical to n.Left.
- // Construct the interface directly: {type/itab, &value}.
- l := nod(OEFACE, typeword(), typecheck(nod(OADDR, value, nil), ctxExpr))
- l.Type = toType
- l.SetTypecheck(n.Typecheck())
- n = l
- break
- }
-
- // Implement interface to empty interface conversion.
- // tmp = i.itab
- // if tmp != nil {
- // tmp = tmp.type
- // }
- // e = iface{tmp, i.data}
- if toType.IsEmptyInterface() && fromType.IsInterface() && !fromType.IsEmptyInterface() {
- // Evaluate the input interface.
- c := temp(fromType)
- init.Append(nod(OAS, c, n.Left))
-
- // Get the itab out of the interface.
- tmp := temp(types.NewPtr(types.Types[TUINT8]))
- init.Append(nod(OAS, tmp, typecheck(nod(OITAB, c, nil), ctxExpr)))
-
- // Get the type out of the itab.
- nif := nod(OIF, typecheck(nod(ONE, tmp, nodnil()), ctxExpr), nil)
- nif.Nbody.Set1(nod(OAS, tmp, itabType(tmp)))
- init.Append(nif)
-
- // Build the result.
- e := nod(OEFACE, tmp, ifaceData(n.Pos, c, types.NewPtr(types.Types[TUINT8])))
- e.Type = toType // assign type manually, typecheck doesn't understand OEFACE.
- e.SetTypecheck(1)
- n = e
- break
- }
-
- fnname, needsaddr := convFuncName(fromType, toType)
-
- if !needsaddr && !fromType.IsInterface() {
- // Use a specialized conversion routine that only returns a data pointer.
- // ptr = convT2X(val)
- // e = iface{typ/tab, ptr}
- fn := syslook(fnname)
- dowidth(fromType)
- fn = substArgTypes(fn, fromType)
- dowidth(fn.Type)
- call := nod(OCALL, fn, nil)
- call.List.Set1(n.Left)
- call = typecheck(call, ctxExpr)
- call = walkexpr(call, init)
- call = safeexpr(call, init)
- e := nod(OEFACE, typeword(), call)
- e.Type = toType
- e.SetTypecheck(1)
- n = e
- break
- }
-
- var tab *Node
- if fromType.IsInterface() {
- // convI2I
- tab = typename(toType)
- } else {
- // convT2x
- tab = typeword()
- }
-
- v := n.Left
- if needsaddr {
- // Types of large or unknown size are passed by reference.
- // Orderexpr arranged for n.Left to be a temporary for all
- // the conversions it could see. Comparison of an interface
- // with a non-interface, especially in a switch on interface value
- // with non-interface cases, is not visible to order.stmt, so we
- // have to fall back on allocating a temp here.
- if !islvalue(v) {
- v = copyexpr(v, v.Type, init)
- }
- v = nod(OADDR, v, nil)
- }
-
- dowidth(fromType)
- fn := syslook(fnname)
- fn = substArgTypes(fn, fromType, toType)
- dowidth(fn.Type)
- n = nod(OCALL, fn, nil)
- n.List.Set2(tab, v)
- n = typecheck(n, ctxExpr)
- n = walkexpr(n, init)
-
- case OCONV, OCONVNOP:
- n.Left = walkexpr(n.Left, init)
- if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
- if n.Type.IsPtr() && n.Left.Type.IsUnsafePtr() { // unsafe.Pointer to *T
- n = walkCheckPtrAlignment(n, init, nil)
- break
- }
- if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() { // uintptr to unsafe.Pointer
- n = walkCheckPtrArithmetic(n, init)
- break
- }
- }
- param, result := rtconvfn(n.Left.Type, n.Type)
- if param == Txxx {
- break
- }
- fn := basicnames[param] + "to" + basicnames[result]
- n = conv(mkcall(fn, types.Types[result], init, conv(n.Left, types.Types[param])), n.Type)
-
- case ODIV, OMOD:
- n.Left = walkexpr(n.Left, init)
- n.Right = walkexpr(n.Right, init)
-
- // rewrite complex div into function call.
- et := n.Left.Type.Etype
-
- if isComplex[et] && n.Op == ODIV {
- t := n.Type
- n = mkcall("complex128div", types.Types[TCOMPLEX128], init, conv(n.Left, types.Types[TCOMPLEX128]), conv(n.Right, types.Types[TCOMPLEX128]))
- n = conv(n, t)
- break
- }
-
- // Nothing to do for float divisions.
- if isFloat[et] {
- break
- }
-
- // rewrite 64-bit div and mod on 32-bit architectures.
- // TODO: Remove this code once we can introduce
- // runtime calls late in SSA processing.
- if Widthreg < 8 && (et == TINT64 || et == TUINT64) {
- if n.Right.Op == OLITERAL {
- // Leave div/mod by constant powers of 2 or small 16-bit constants.
- // The SSA backend will handle those.
- switch et {
- case TINT64:
- c := n.Right.Int64Val()
- if c < 0 {
- c = -c
- }
- if c != 0 && c&(c-1) == 0 {
- break opswitch
- }
- case TUINT64:
- c := uint64(n.Right.Int64Val())
- if c < 1<<16 {
- break opswitch
- }
- if c != 0 && c&(c-1) == 0 {
- break opswitch
- }
- }
- }
- var fn string
- if et == TINT64 {
- fn = "int64"
- } else {
- fn = "uint64"
- }
- if n.Op == ODIV {
- fn += "div"
- } else {
- fn += "mod"
- }
- n = mkcall(fn, n.Type, init, conv(n.Left, types.Types[et]), conv(n.Right, types.Types[et]))
- }
-
- case OINDEX:
- n.Left = walkexpr(n.Left, init)
-
- // save the original node for bounds checking elision.
- // If it was a ODIV/OMOD walk might rewrite it.
- r := n.Right
-
- n.Right = walkexpr(n.Right, init)
-
- // if range of type cannot exceed static array bound,
- // disable bounds check.
- if n.Bounded() {
- break
- }
- t := n.Left.Type
- if t != nil && t.IsPtr() {
- t = t.Elem()
- }
- if t.IsArray() {
- n.SetBounded(bounded(r, t.NumElem()))
- if Debug.m != 0 && n.Bounded() && !Isconst(n.Right, CTINT) {
- Warn("index bounds check elided")
- }
- if smallintconst(n.Right) && !n.Bounded() {
- yyerror("index out of bounds")
- }
- } else if Isconst(n.Left, CTSTR) {
- n.SetBounded(bounded(r, int64(len(n.Left.StringVal()))))
- if Debug.m != 0 && n.Bounded() && !Isconst(n.Right, CTINT) {
- Warn("index bounds check elided")
- }
- if smallintconst(n.Right) && !n.Bounded() {
- yyerror("index out of bounds")
- }
- }
-
- if Isconst(n.Right, CTINT) {
- if n.Right.Val().U.(*Mpint).CmpInt64(0) < 0 || n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
- yyerror("index out of bounds")
- }
- }
-
- case OINDEXMAP:
- // Replace m[k] with *map{access1,assign}(maptype, m, &k)
- n.Left = walkexpr(n.Left, init)
- n.Right = walkexpr(n.Right, init)
- map_ := n.Left
- key := n.Right
- t := map_.Type
- if n.IndexMapLValue() {
- // This m[k] expression is on the left-hand side of an assignment.
- fast := mapfast(t)
- if fast == mapslow {
- // standard version takes key by reference.
- // order.expr made sure key is addressable.
- key = nod(OADDR, key, nil)
- }
- n = mkcall1(mapfn(mapassign[fast], t), nil, init, typename(t), map_, key)
- } else {
- // m[k] is not the target of an assignment.
- fast := mapfast(t)
- if fast == mapslow {
- // standard version takes key by reference.
- // order.expr made sure key is addressable.
- key = nod(OADDR, key, nil)
- }
-
- if w := t.Elem().Width; w <= zeroValSize {
- n = mkcall1(mapfn(mapaccess1[fast], t), types.NewPtr(t.Elem()), init, typename(t), map_, key)
- } else {
- z := zeroaddr(w)
- n = mkcall1(mapfn("mapaccess1_fat", t), types.NewPtr(t.Elem()), init, typename(t), map_, key, z)
- }
- }
- n.Type = types.NewPtr(t.Elem())
- n.MarkNonNil() // mapaccess1* and mapassign always return non-nil pointers.
- n = nod(ODEREF, n, nil)
- n.Type = t.Elem()
- n.SetTypecheck(1)
-
- case ORECV:
- Fatalf("walkexpr ORECV") // should see inside OAS only
-
- case OSLICEHEADER:
- n.Left = walkexpr(n.Left, init)
- n.List.SetFirst(walkexpr(n.List.First(), init))
- n.List.SetSecond(walkexpr(n.List.Second(), init))
-
- case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
- checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.IsUnsafePtr()
- if checkSlice {
- n.Left.Left = walkexpr(n.Left.Left, init)
- } else {
- n.Left = walkexpr(n.Left, init)
- }
- low, high, max := n.SliceBounds()
- low = walkexpr(low, init)
- if low != nil && isZero(low) {
- // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k].
- low = nil
- }
- high = walkexpr(high, init)
- max = walkexpr(max, init)
- n.SetSliceBounds(low, high, max)
- if checkSlice {
- n.Left = walkCheckPtrAlignment(n.Left, init, max)
- }
- if n.Op.IsSlice3() {
- if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
- // Reduce x[i:j:cap(x)] to x[i:j].
- if n.Op == OSLICE3 {
- n.Op = OSLICE
- } else {
- n.Op = OSLICEARR
- }
- n = reduceSlice(n)
- }
- } else {
- n = reduceSlice(n)
- }
-
- case ONEW:
- if n.Type.Elem().NotInHeap() {
- 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 {
- Fatalf("large ONEW with EscNone: %v", n)
- }
- r := temp(n.Type.Elem())
- r = nod(OAS, r, nil) // zero temp
- r = typecheck(r, ctxStmt)
- init.Append(r)
- r = nod(OADDR, r.Left, nil)
- r = typecheck(r, ctxExpr)
- n = r
- } else {
- n = callnew(n.Type.Elem())
- }
-
- case OADDSTR:
- n = addstr(n, init)
-
- case OAPPEND:
- // order should make sure we only see OAS(node, OAPPEND), which we handle above.
- Fatalf("append outside assignment")
-
- case OCOPY:
- n = copyany(n, init, instrumenting && !compiling_runtime)
-
- // cannot use chanfn - closechan takes any, not chan any
- case OCLOSE:
- fn := syslook("closechan")
-
- fn = substArgTypes(fn, n.Left.Type)
- n = mkcall1(fn, nil, init, n.Left)
-
- case OMAKECHAN:
- // When size fits into int, use makechan instead of
- // makechan64, which is faster and shorter on 32 bit platforms.
- size := n.Left
- fnname := "makechan64"
- argtype := types.Types[TINT64]
-
- // Type checking guarantees that TIDEAL size is positive and fits in an int.
- // The case of size overflow when converting TUINT or TUINTPTR to TINT
- // will be handled by the negative range checks in makechan during runtime.
- if size.Type.IsKind(TIDEAL) || maxintval[size.Type.Etype].Cmp(maxintval[TUINT]) <= 0 {
- fnname = "makechan"
- argtype = types.Types[TINT]
- }
-
- n = mkcall1(chanfn(fnname, 1, n.Type), n.Type, init, typename(n.Type), conv(size, argtype))
-
- case OMAKEMAP:
- t := n.Type
- hmapType := hmap(t)
- hint := n.Left
-
- // var h *hmap
- var h *Node
- if n.Esc == EscNone {
- // Allocate hmap on stack.
-
- // var hv hmap
- hv := temp(hmapType)
- zero := nod(OAS, hv, nil)
- zero = typecheck(zero, ctxStmt)
- init.Append(zero)
- // h = &hv
- h = nod(OADDR, hv, nil)
-
- // Allocate one bucket pointed to by hmap.buckets on stack if hint
- // is not larger than BUCKETSIZE. In case hint is larger than
- // BUCKETSIZE runtime.makemap will allocate the buckets on the heap.
- // Maximum key and elem size is 128 bytes, larger objects
- // are stored with an indirection. So max bucket size is 2048+eps.
- if !Isconst(hint, CTINT) ||
- hint.Val().U.(*Mpint).CmpInt64(BUCKETSIZE) <= 0 {
-
- // In case hint is larger than BUCKETSIZE runtime.makemap
- // will allocate the buckets on the heap, see #20184
- //
- // if hint <= BUCKETSIZE {
- // var bv bmap
- // b = &bv
- // h.buckets = b
- // }
-
- nif := nod(OIF, nod(OLE, hint, nodintconst(BUCKETSIZE)), nil)
- nif.SetLikely(true)
-
- // var bv bmap
- bv := temp(bmap(t))
- zero = nod(OAS, bv, nil)
- nif.Nbody.Append(zero)
-
- // b = &bv
- b := nod(OADDR, bv, nil)
-
- // h.buckets = b
- bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap
- na := nod(OAS, nodSym(ODOT, h, bsym), b)
- nif.Nbody.Append(na)
-
- nif = typecheck(nif, ctxStmt)
- nif = walkstmt(nif)
- init.Append(nif)
- }
- }
-
- if Isconst(hint, CTINT) && hint.Val().U.(*Mpint).CmpInt64(BUCKETSIZE) <= 0 {
- // Handling make(map[any]any) and
- // make(map[any]any, hint) where hint <= BUCKETSIZE
- // special allows for faster map initialization and
- // improves binary size by using calls with fewer arguments.
- // For hint <= BUCKETSIZE overLoadFactor(hint, 0) is false
- // and no buckets will be allocated by makemap. Therefore,
- // no buckets need to be allocated in this code path.
- if n.Esc == EscNone {
- // Only need to initialize h.hash0 since
- // hmap h has been allocated on the stack already.
- // h.hash0 = fastrand()
- rand := mkcall("fastrand", types.Types[TUINT32], init)
- hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap
- a := nod(OAS, nodSym(ODOT, h, hashsym), rand)
- a = typecheck(a, ctxStmt)
- a = walkexpr(a, init)
- init.Append(a)
- n = convnop(h, t)
- } else {
- // Call runtime.makehmap to allocate an
- // hmap on the heap and initialize hmap's hash0 field.
- fn := syslook("makemap_small")
- fn = substArgTypes(fn, t.Key(), t.Elem())
- n = mkcall1(fn, n.Type, init)
- }
- } else {
- if n.Esc != EscNone {
- h = nodnil()
- }
- // Map initialization with a variable or large hint is
- // more complicated. We therefore generate a call to
- // runtime.makemap to initialize hmap and allocate the
- // map buckets.
-
- // When hint fits into int, use makemap instead of
- // makemap64, which is faster and shorter on 32 bit platforms.
- fnname := "makemap64"
- argtype := types.Types[TINT64]
-
- // Type checking guarantees that TIDEAL hint is positive and fits in an int.
- // See checkmake call in TMAP case of OMAKE case in OpSwitch in typecheck1 function.
- // The case of hint overflow when converting TUINT or TUINTPTR to TINT
- // will be handled by the negative range checks in makemap during runtime.
- if hint.Type.IsKind(TIDEAL) || maxintval[hint.Type.Etype].Cmp(maxintval[TUINT]) <= 0 {
- fnname = "makemap"
- argtype = types.Types[TINT]
- }
-
- fn := syslook(fnname)
- fn = substArgTypes(fn, hmapType, t.Key(), t.Elem())
- n = mkcall1(fn, n.Type, init, typename(n.Type), conv(hint, argtype), h)
- }
-
- case OMAKESLICE:
- l := n.Left
- r := n.Right
- if r == nil {
- r = safeexpr(l, init)
- l = r
- }
- t := n.Type
- if t.Elem().NotInHeap() {
- yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
- }
- if n.Esc == EscNone {
- if why := heapAllocReason(n); why != "" {
- Fatalf("%v has EscNone, but %v", n, why)
- }
- // var arr [r]T
- // n = arr[:l]
- i := indexconst(r)
- if i < 0 {
- Fatalf("walkexpr: invalid index %v", r)
- }
-
- // cap is constrained to [0,2^31) or [0,2^63) depending on whether
- // we're in 32-bit or 64-bit systems. So it's safe to do:
- //
- // if uint64(len) > cap {
- // if len < 0 { panicmakeslicelen() }
- // panicmakeslicecap()
- // }
- nif := nod(OIF, nod(OGT, conv(l, types.Types[TUINT64]), nodintconst(i)), nil)
- niflen := nod(OIF, nod(OLT, l, nodintconst(0)), nil)
- niflen.Nbody.Set1(mkcall("panicmakeslicelen", nil, init))
- nif.Nbody.Append(niflen, mkcall("panicmakeslicecap", nil, init))
- nif = typecheck(nif, ctxStmt)
- init.Append(nif)
-
- t = types.NewArray(t.Elem(), i) // [r]T
- var_ := temp(t)
- a := nod(OAS, var_, nil) // zero temp
- a = typecheck(a, ctxStmt)
- init.Append(a)
- r := nod(OSLICE, var_, nil) // arr[:l]
- r.SetSliceBounds(nil, l, nil)
- r = conv(r, n.Type) // in case n.Type is named.
- r = typecheck(r, ctxExpr)
- r = walkexpr(r, init)
- n = r
- } else {
- // n escapes; set up a call to makeslice.
- // When len and cap can fit into int, use makeslice instead of
- // makeslice64, which is faster and shorter on 32 bit platforms.
-
- len, cap := l, r
-
- fnname := "makeslice64"
- argtype := types.Types[TINT64]
-
- // Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
- // The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
- // will be handled by the negative range checks in makeslice during runtime.
- if (len.Type.IsKind(TIDEAL) || maxintval[len.Type.Etype].Cmp(maxintval[TUINT]) <= 0) &&
- (cap.Type.IsKind(TIDEAL) || maxintval[cap.Type.Etype].Cmp(maxintval[TUINT]) <= 0) {
- fnname = "makeslice"
- argtype = types.Types[TINT]
- }
-
- m := nod(OSLICEHEADER, nil, nil)
- m.Type = t
-
- fn := syslook(fnname)
- m.Left = mkcall1(fn, types.Types[TUNSAFEPTR], init, typename(t.Elem()), conv(len, argtype), conv(cap, argtype))
- m.Left.MarkNonNil()
- m.List.Set2(conv(len, types.Types[TINT]), conv(cap, types.Types[TINT]))
-
- m = typecheck(m, ctxExpr)
- m = walkexpr(m, init)
- n = m
- }
-
- case OMAKESLICECOPY:
- if n.Esc == EscNone {
- Fatalf("OMAKESLICECOPY with EscNone: %v", n)
- }
-
- t := n.Type
- if t.Elem().NotInHeap() {
- yyerror("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
- }
-
- length := conv(n.Left, types.Types[TINT])
- copylen := nod(OLEN, n.Right, nil)
- copyptr := nod(OSPTR, n.Right, nil)
-
- if !t.Elem().HasPointers() && n.Bounded() {
- // When len(to)==len(from) and elements have no pointers:
- // replace make+copy with runtime.mallocgc+runtime.memmove.
-
- // We do not check for overflow of len(to)*elem.Width here
- // since len(from) is an existing checked slice capacity
- // with same elem.Width for the from slice.
- size := nod(OMUL, conv(length, types.Types[TUINTPTR]), conv(nodintconst(t.Elem().Width), types.Types[TUINTPTR]))
-
- // instantiate mallocgc(size uintptr, typ *byte, needszero bool) unsafe.Pointer
- fn := syslook("mallocgc")
- sh := nod(OSLICEHEADER, nil, nil)
- sh.Left = mkcall1(fn, types.Types[TUNSAFEPTR], init, size, nodnil(), nodbool(false))
- sh.Left.MarkNonNil()
- sh.List.Set2(length, length)
- sh.Type = t
-
- s := temp(t)
- r := typecheck(nod(OAS, s, sh), ctxStmt)
- r = walkexpr(r, init)
- init.Append(r)
-
- // instantiate memmove(to *any, frm *any, size uintptr)
- fn = syslook("memmove")
- fn = substArgTypes(fn, t.Elem(), t.Elem())
- ncopy := mkcall1(fn, nil, init, nod(OSPTR, s, nil), copyptr, size)
- ncopy = typecheck(ncopy, ctxStmt)
- ncopy = walkexpr(ncopy, init)
- init.Append(ncopy)
-
- n = s
- } else { // Replace make+copy with runtime.makeslicecopy.
- // instantiate makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
- fn := syslook("makeslicecopy")
- s := nod(OSLICEHEADER, nil, nil)
- s.Left = mkcall1(fn, types.Types[TUNSAFEPTR], init, typename(t.Elem()), length, copylen, conv(copyptr, types.Types[TUNSAFEPTR]))
- s.Left.MarkNonNil()
- s.List.Set2(length, length)
- s.Type = t
- n = typecheck(s, ctxExpr)
- n = walkexpr(n, init)
- }
-
- case ORUNESTR:
- a := nodnil()
- if n.Esc == EscNone {
- t := types.NewArray(types.Types[TUINT8], 4)
- a = nod(OADDR, temp(t), nil)
- }
- // intstring(*[4]byte, rune)
- n = mkcall("intstring", n.Type, init, a, conv(n.Left, types.Types[TINT64]))
-
- case OBYTES2STR, ORUNES2STR:
- a := nodnil()
- if n.Esc == EscNone {
- // Create temporary buffer for string on stack.
- t := types.NewArray(types.Types[TUINT8], tmpstringbufsize)
- a = nod(OADDR, temp(t), nil)
- }
- if n.Op == ORUNES2STR {
- // slicerunetostring(*[32]byte, []rune) string
- n = mkcall("slicerunetostring", n.Type, init, a, n.Left)
- } else {
- // slicebytetostring(*[32]byte, ptr *byte, n int) string
- n.Left = cheapexpr(n.Left, init)
- ptr, len := n.Left.backingArrayPtrLen()
- n = mkcall("slicebytetostring", n.Type, init, a, ptr, len)
- }
-
- case OBYTES2STRTMP:
- n.Left = walkexpr(n.Left, init)
- if !instrumenting {
- // Let the backend handle OBYTES2STRTMP directly
- // to avoid a function call to slicebytetostringtmp.
- break
- }
- // slicebytetostringtmp(ptr *byte, n int) string
- n.Left = cheapexpr(n.Left, init)
- ptr, len := n.Left.backingArrayPtrLen()
- n = mkcall("slicebytetostringtmp", n.Type, init, ptr, len)
-
- case OSTR2BYTES:
- s := n.Left
- if Isconst(s, CTSTR) {
- sc := s.StringVal()
-
- // Allocate a [n]byte of the right size.
- t := types.NewArray(types.Types[TUINT8], int64(len(sc)))
- var a *Node
- if n.Esc == EscNone && len(sc) <= int(maxImplicitStackVarSize) {
- a = nod(OADDR, temp(t), nil)
- } else {
- a = callnew(t)
- }
- p := temp(t.PtrTo()) // *[n]byte
- init.Append(typecheck(nod(OAS, p, a), ctxStmt))
-
- // Copy from the static string data to the [n]byte.
- if len(sc) > 0 {
- as := nod(OAS,
- nod(ODEREF, p, nil),
- nod(ODEREF, convnop(nod(OSPTR, s, nil), t.PtrTo()), nil))
- as = typecheck(as, ctxStmt)
- as = walkstmt(as)
- init.Append(as)
- }
-
- // Slice the [n]byte to a []byte.
- n.Op = OSLICEARR
- n.Left = p
- n = walkexpr(n, init)
- break
- }
-
- a := nodnil()
- if n.Esc == EscNone {
- // Create temporary buffer for slice on stack.
- t := types.NewArray(types.Types[TUINT8], tmpstringbufsize)
- a = nod(OADDR, temp(t), nil)
- }
- // stringtoslicebyte(*32[byte], string) []byte
- n = mkcall("stringtoslicebyte", n.Type, init, a, conv(s, types.Types[TSTRING]))
-
- case OSTR2BYTESTMP:
- // []byte(string) conversion that creates a slice
- // referring to the actual string bytes.
- // This conversion is handled later by the backend and
- // is only for use by internal compiler optimizations
- // that know that the slice won't be mutated.
- // The only such case today is:
- // for i, c := range []byte(string)
- n.Left = walkexpr(n.Left, init)
-
- case OSTR2RUNES:
- a := nodnil()
- if n.Esc == EscNone {
- // Create temporary buffer for slice on stack.
- t := types.NewArray(types.Types[TINT32], tmpstringbufsize)
- a = nod(OADDR, temp(t), nil)
- }
- // stringtoslicerune(*[32]rune, string) []rune
- n = mkcall("stringtoslicerune", n.Type, init, a, conv(n.Left, types.Types[TSTRING]))
-
- case OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT, OPTRLIT:
- if isStaticCompositeLiteral(n) && !canSSAType(n.Type) {
- // n can be directly represented in the read-only data section.
- // Make direct reference to the static data. See issue 12841.
- vstat := readonlystaticname(n.Type)
- fixedlit(inInitFunction, initKindStatic, n, vstat, init)
- n = vstat
- n = typecheck(n, ctxExpr)
- break
- }
- var_ := temp(n.Type)
- anylit(n, var_, init)
- n = var_
-
- case OSEND:
- n1 := n.Right
- n1 = assignconv(n1, n.Left.Type.Elem(), "chan send")
- n1 = walkexpr(n1, init)
- n1 = nod(OADDR, n1, nil)
- n = mkcall1(chanfn("chansend1", 2, n.Left.Type), nil, init, n.Left, n1)
-
- case OCLOSURE:
- n = walkclosure(n, init)
-
- case OCALLPART:
- n = walkpartialcall(n, init)
- }
-
- // Expressions that are constant at run time but not
- // considered const by the language spec are not turned into
- // constants until walk. For example, if n is y%1 == 0, the
- // walk of y%1 may have replaced it by 0.
- // Check whether n with its updated args is itself now a constant.
- t := n.Type
- evconst(n)
- if n.Type != t {
- Fatalf("evconst changed Type: %v had type %v, now %v", n, t, n.Type)
- }
- if n.Op == OLITERAL {
- n = typecheck(n, ctxExpr)
- // Emit string symbol now to avoid emitting
- // any concurrently during the backend.
- if s, ok := n.Val().U.(string); ok {
- _ = stringsym(n.Pos, s)
- }
- }
-
- updateHasCall(n)
-
- if Debug.w != 0 && n != nil {
- Dump("after walk expr", n)
- }
-
- lineno = lno
- return n
-}
-
-// markTypeUsedInInterface marks that type t is converted to an interface.
-// This information is used in the linker in dead method elimination.
-func markTypeUsedInInterface(t *types.Type, from *obj.LSym) {
- tsym := typenamesym(t).Linksym()
- // Emit a marker relocation. The linker will know the type is converted
- // to an interface if "from" is reachable.
- r := obj.Addrel(from)
- r.Sym = tsym
- r.Type = objabi.R_USEIFACE
-}
-
-// markUsedIfaceMethod marks that an interface method is used in the current
-// function. n is OCALLINTER node.
-func markUsedIfaceMethod(n *Node) {
- ityp := n.Left.Left.Type
- tsym := typenamesym(ityp).Linksym()
- r := obj.Addrel(Curfn.Func.lsym)
- r.Sym = tsym
- // n.Left.Xoffset is the method index * Widthptr (the offset of code pointer
- // in itab).
- midx := n.Left.Xoffset / int64(Widthptr)
- r.Add = ifaceMethodOffset(ityp, midx)
- r.Type = objabi.R_USEIFACEMETHOD
-}
-
-// rtconvfn returns the parameter and result types that will be used by a
-// runtime function to convert from type src to type dst. The runtime function
-// name can be derived from the names of the returned types.
-//
-// If no such function is necessary, it returns (Txxx, Txxx).
-func rtconvfn(src, dst *types.Type) (param, result types.EType) {
- if thearch.SoftFloat {
- return Txxx, Txxx
- }
-
- switch thearch.LinkArch.Family {
- case sys.ARM, sys.MIPS:
- if src.IsFloat() {
- switch dst.Etype {
- case TINT64, TUINT64:
- return TFLOAT64, dst.Etype
- }
- }
- if dst.IsFloat() {
- switch src.Etype {
- case TINT64, TUINT64:
- return src.Etype, TFLOAT64
- }
- }
-
- case sys.I386:
- if src.IsFloat() {
- switch dst.Etype {
- case TINT64, TUINT64:
- return TFLOAT64, dst.Etype
- case TUINT32, TUINT, TUINTPTR:
- return TFLOAT64, TUINT32
- }
- }
- if dst.IsFloat() {
- switch src.Etype {
- case TINT64, TUINT64:
- return src.Etype, TFLOAT64
- case TUINT32, TUINT, TUINTPTR:
- return TUINT32, TFLOAT64
- }
- }
- }
- return Txxx, Txxx
-}
-
-// TODO(josharian): combine this with its caller and simplify
-func reduceSlice(n *Node) *Node {
- low, high, max := n.SliceBounds()
- if high != nil && high.Op == OLEN && samesafeexpr(n.Left, high.Left) {
- // Reduce x[i:len(x)] to x[i:].
- high = nil
- }
- n.SetSliceBounds(low, high, max)
- if (n.Op == OSLICE || n.Op == OSLICESTR) && low == nil && high == nil {
- // Reduce x[:] to x.
- if Debug_slice > 0 {
- Warn("slice: omit slice operation")
- }
- return n.Left
- }
- return n
-}
-
-func ascompatee1(l *Node, r *Node, init *Nodes) *Node {
- // convas will turn map assigns into function calls,
- // making it impossible for reorder3 to work.
- n := nod(OAS, l, r)
-
- if l.Op == OINDEXMAP {
- return n
- }
-
- return convas(n, init)
-}
-
-func ascompatee(op Op, nl, nr []*Node, init *Nodes) []*Node {
- // check assign expression list to
- // an expression list. called in
- // expr-list = expr-list
-
- // ensure order of evaluation for function calls
- for i := range nl {
- nl[i] = safeexpr(nl[i], init)
- }
- for i1 := range nr {
- nr[i1] = safeexpr(nr[i1], init)
- }
-
- var nn []*Node
- i := 0
- for ; i < len(nl); i++ {
- if i >= len(nr) {
- break
- }
- // Do not generate 'x = x' during return. See issue 4014.
- if op == ORETURN && samesafeexpr(nl[i], nr[i]) {
- continue
- }
- nn = append(nn, ascompatee1(nl[i], nr[i], init))
- }
-
- // cannot happen: caller checked that lists had same length
- if i < len(nl) || i < len(nr) {
- var nln, nrn Nodes
- nln.Set(nl)
- nrn.Set(nr)
- Fatalf("error in shape across %+v %v %+v / %d %d [%s]", nln, op, nrn, len(nl), len(nr), Curfn.funcname())
- }
- return nn
-}
-
-// fncall reports whether assigning an rvalue of type rt to an lvalue l might involve a function call.
-func fncall(l *Node, rt *types.Type) bool {
- if l.HasCall() || l.Op == OINDEXMAP {
- return true
- }
- if types.Identical(l.Type, rt) {
- return false
- }
- // There might be a conversion required, which might involve a runtime call.
- return true
-}
-
-// check assign type list to
-// an expression list. called in
-// expr-list = func()
-func ascompatet(nl Nodes, nr *types.Type) []*Node {
- if nl.Len() != nr.NumFields() {
- Fatalf("ascompatet: assignment count mismatch: %d = %d", nl.Len(), nr.NumFields())
- }
-
- var nn, mm Nodes
- for i, l := range nl.Slice() {
- if l.isBlank() {
- continue
- }
- r := nr.Field(i)
-
- // Any assignment to an lvalue that might cause a function call must be
- // deferred until all the returned values have been read.
- if fncall(l, r.Type) {
- tmp := temp(r.Type)
- tmp = typecheck(tmp, ctxExpr)
- a := nod(OAS, l, tmp)
- a = convas(a, &mm)
- mm.Append(a)
- l = tmp
- }
-
- res := nod(ORESULT, nil, nil)
- res.Xoffset = Ctxt.FixedFrameSize() + r.Offset
- res.Type = r.Type
- res.SetTypecheck(1)
-
- a := nod(OAS, l, res)
- a = convas(a, &nn)
- updateHasCall(a)
- if a.HasCall() {
- Dump("ascompatet ucount", a)
- Fatalf("ascompatet: too many function calls evaluating parameters")
- }
-
- nn.Append(a)
- }
- return append(nn.Slice(), mm.Slice()...)
-}
-
-// package all the arguments that match a ... T parameter into a []T.
-func mkdotargslice(typ *types.Type, args []*Node) *Node {
- var n *Node
- if len(args) == 0 {
- n = nodnil()
- n.Type = typ
- } else {
- n = nod(OCOMPLIT, nil, typenod(typ))
- n.List.Append(args...)
- n.SetImplicit(true)
- }
-
- n = typecheck(n, ctxExpr)
- if n.Type == nil {
- Fatalf("mkdotargslice: typecheck failed")
- }
- return n
-}
-
-// fixVariadicCall rewrites calls to variadic functions to use an
-// explicit ... argument if one is not already present.
-func fixVariadicCall(call *Node) {
- fntype := call.Left.Type
- if !fntype.IsVariadic() || call.IsDDD() {
- return
- }
-
- vi := fntype.NumParams() - 1
- vt := fntype.Params().Field(vi).Type
-
- args := call.List.Slice()
- extra := args[vi:]
- slice := mkdotargslice(vt, extra)
- for i := range extra {
- extra[i] = nil // allow GC
- }
-
- call.List.Set(append(args[:vi], slice))
- call.SetIsDDD(true)
-}
-
-func walkCall(n *Node, init *Nodes) {
- if n.Rlist.Len() != 0 {
- return // already walked
- }
-
- params := n.Left.Type.Params()
- args := n.List.Slice()
-
- n.Left = walkexpr(n.Left, init)
- walkexprlist(args, init)
-
- // If this is a method call, add the receiver at the beginning of the args.
- if n.Op == OCALLMETH {
- withRecv := make([]*Node, len(args)+1)
- withRecv[0] = n.Left.Left
- n.Left.Left = nil
- copy(withRecv[1:], args)
- args = withRecv
- }
-
- // For any argument whose evaluation might require a function call,
- // store that argument into a temporary variable,
- // to prevent that calls from clobbering arguments already on the stack.
- // When instrumenting, all arguments might require function calls.
- var tempAssigns []*Node
- for i, arg := range args {
- updateHasCall(arg)
- // Determine param type.
- var t *types.Type
- if n.Op == OCALLMETH {
- if i == 0 {
- t = n.Left.Type.Recv().Type
- } else {
- t = params.Field(i - 1).Type
- }
- } else {
- t = params.Field(i).Type
- }
- if instrumenting || fncall(arg, t) {
- // make assignment of fncall to tempAt
- tmp := temp(t)
- a := nod(OAS, tmp, arg)
- a = convas(a, init)
- tempAssigns = append(tempAssigns, a)
- // replace arg with temp
- args[i] = tmp
- }
- }
-
- n.List.Set(tempAssigns)
- n.Rlist.Set(args)
-}
-
-// generate code for print
-func walkprint(nn *Node, init *Nodes) *Node {
- // Hoist all the argument evaluation up before the lock.
- walkexprlistcheap(nn.List.Slice(), init)
-
- // For println, add " " between elements and "\n" at the end.
- if nn.Op == OPRINTN {
- s := nn.List.Slice()
- t := make([]*Node, 0, len(s)*2)
- for i, n := range s {
- if i != 0 {
- t = append(t, nodstr(" "))
- }
- t = append(t, n)
- }
- t = append(t, nodstr("\n"))
- nn.List.Set(t)
- }
-
- // Collapse runs of constant strings.
- s := nn.List.Slice()
- t := make([]*Node, 0, len(s))
- for i := 0; i < len(s); {
- var strs []string
- for i < len(s) && Isconst(s[i], CTSTR) {
- strs = append(strs, s[i].StringVal())
- i++
- }
- if len(strs) > 0 {
- t = append(t, nodstr(strings.Join(strs, "")))
- }
- if i < len(s) {
- t = append(t, s[i])
- i++
- }
- }
- nn.List.Set(t)
-
- calls := []*Node{mkcall("printlock", nil, init)}
- for i, n := range nn.List.Slice() {
- if n.Op == OLITERAL {
- switch n.Val().Ctype() {
- case CTRUNE:
- n = defaultlit(n, types.Runetype)
-
- case CTINT:
- n = defaultlit(n, types.Types[TINT64])
-
- case CTFLT:
- n = defaultlit(n, types.Types[TFLOAT64])
- }
- }
-
- if n.Op != OLITERAL && n.Type != nil && n.Type.Etype == TIDEAL {
- n = defaultlit(n, types.Types[TINT64])
- }
- n = defaultlit(n, nil)
- nn.List.SetIndex(i, n)
- if n.Type == nil || n.Type.Etype == TFORW {
- continue
- }
-
- var on *Node
- switch n.Type.Etype {
- case TINTER:
- if n.Type.IsEmptyInterface() {
- on = syslook("printeface")
- } else {
- on = syslook("printiface")
- }
- on = substArgTypes(on, n.Type) // any-1
- case TPTR:
- if n.Type.Elem().NotInHeap() {
- on = syslook("printuintptr")
- n = nod(OCONV, n, nil)
- n.Type = types.Types[TUNSAFEPTR]
- n = nod(OCONV, n, nil)
- n.Type = types.Types[TUINTPTR]
- break
- }
- fallthrough
- case TCHAN, TMAP, TFUNC, TUNSAFEPTR:
- on = syslook("printpointer")
- on = substArgTypes(on, n.Type) // any-1
- case TSLICE:
- on = syslook("printslice")
- on = substArgTypes(on, n.Type) // any-1
- case TUINT, TUINT8, TUINT16, TUINT32, TUINT64, TUINTPTR:
- if isRuntimePkg(n.Type.Sym.Pkg) && n.Type.Sym.Name == "hex" {
- on = syslook("printhex")
- } else {
- on = syslook("printuint")
- }
- case TINT, TINT8, TINT16, TINT32, TINT64:
- on = syslook("printint")
- case TFLOAT32, TFLOAT64:
- on = syslook("printfloat")
- case TCOMPLEX64, TCOMPLEX128:
- on = syslook("printcomplex")
- case TBOOL:
- on = syslook("printbool")
- case TSTRING:
- cs := ""
- if Isconst(n, CTSTR) {
- cs = n.StringVal()
- }
- switch cs {
- case " ":
- on = syslook("printsp")
- case "\n":
- on = syslook("printnl")
- default:
- on = syslook("printstring")
- }
- default:
- badtype(OPRINT, n.Type, nil)
- continue
- }
-
- r := nod(OCALL, on, nil)
- if params := on.Type.Params().FieldSlice(); len(params) > 0 {
- t := params[0].Type
- if !types.Identical(t, n.Type) {
- n = nod(OCONV, n, nil)
- n.Type = t
- }
- r.List.Append(n)
- }
- calls = append(calls, r)
- }
-
- calls = append(calls, mkcall("printunlock", nil, init))
-
- typecheckslice(calls, ctxStmt)
- walkexprlist(calls, init)
-
- r := nod(OEMPTY, nil, nil)
- r = typecheck(r, ctxStmt)
- r = walkexpr(r, init)
- r.Ninit.Set(calls)
- return r
-}
-
-func callnew(t *types.Type) *Node {
- dowidth(t)
- n := nod(ONEWOBJ, typename(t), nil)
- n.Type = types.NewPtr(t)
- n.SetTypecheck(1)
- n.MarkNonNil()
- return n
-}
-
-// isReflectHeaderDataField reports whether l is an expression p.Data
-// where p has type reflect.SliceHeader or reflect.StringHeader.
-func isReflectHeaderDataField(l *Node) bool {
- if l.Type != types.Types[TUINTPTR] {
- return false
- }
-
- var tsym *types.Sym
- switch l.Op {
- case ODOT:
- tsym = l.Left.Type.Sym
- case ODOTPTR:
- tsym = l.Left.Type.Elem().Sym
- default:
- return false
- }
-
- if tsym == nil || l.Sym.Name != "Data" || tsym.Pkg.Path != "reflect" {
- return false
- }
- return tsym.Name == "SliceHeader" || tsym.Name == "StringHeader"
-}
-
-func convas(n *Node, init *Nodes) *Node {
- if n.Op != OAS {
- Fatalf("convas: not OAS %v", n.Op)
- }
- defer updateHasCall(n)
-
- n.SetTypecheck(1)
-
- if n.Left == nil || n.Right == nil {
- return n
- }
-
- lt := n.Left.Type
- rt := n.Right.Type
- if lt == nil || rt == nil {
- return n
- }
-
- if n.Left.isBlank() {
- n.Right = defaultlit(n.Right, nil)
- return n
- }
-
- if !types.Identical(lt, rt) {
- n.Right = assignconv(n.Right, lt, "assignment")
- n.Right = walkexpr(n.Right, init)
- }
- dowidth(n.Right.Type)
-
- return n
-}
-
-// from ascompat[ee]
-// a,b = c,d
-// simultaneous assignment. there cannot
-// be later use of an earlier lvalue.
-//
-// function calls have been removed.
-func reorder3(all []*Node) []*Node {
- // If a needed expression may be affected by an
- // earlier assignment, make an early copy of that
- // expression and use the copy instead.
- var early []*Node
-
- var mapinit Nodes
- for i, n := range all {
- l := n.Left
-
- // Save subexpressions needed on left side.
- // Drill through non-dereferences.
- for {
- if l.Op == ODOT || l.Op == OPAREN {
- l = l.Left
- continue
- }
-
- if l.Op == OINDEX && l.Left.Type.IsArray() {
- l.Right = reorder3save(l.Right, all, i, &early)
- l = l.Left
- continue
- }
-
- break
- }
-
- switch l.Op {
- default:
- Fatalf("reorder3 unexpected lvalue %#v", l.Op)
-
- case ONAME:
- break
-
- case OINDEX, OINDEXMAP:
- l.Left = reorder3save(l.Left, all, i, &early)
- l.Right = reorder3save(l.Right, all, i, &early)
- if l.Op == OINDEXMAP {
- all[i] = convas(all[i], &mapinit)
- }
-
- case ODEREF, ODOTPTR:
- l.Left = reorder3save(l.Left, all, i, &early)
- }
-
- // Save expression on right side.
- all[i].Right = reorder3save(all[i].Right, all, i, &early)
- }
-
- early = append(mapinit.Slice(), early...)
- return append(early, all...)
-}
-
-// if the evaluation of *np would be affected by the
-// assignments in all up to but not including the ith assignment,
-// copy into a temporary during *early and
-// replace *np with that temp.
-// The result of reorder3save MUST be assigned back to n, e.g.
-// n.Left = reorder3save(n.Left, all, i, early)
-func reorder3save(n *Node, all []*Node, i int, early *[]*Node) *Node {
- if !aliased(n, all[:i]) {
- return n
- }
-
- q := temp(n.Type)
- q = nod(OAS, q, n)
- q = typecheck(q, ctxStmt)
- *early = append(*early, q)
- return q.Left
-}
-
-// what's the outer value that a write to n affects?
-// outer value means containing struct or array.
-func outervalue(n *Node) *Node {
- for {
- switch n.Op {
- case OXDOT:
- Fatalf("OXDOT in walk")
- case ODOT, OPAREN, OCONVNOP:
- n = n.Left
- continue
- case OINDEX:
- if n.Left.Type != nil && n.Left.Type.IsArray() {
- n = n.Left
- continue
- }
- }
-
- return n
- }
-}
-
-// Is it possible that the computation of r might be
-// affected by assignments in all?
-func aliased(r *Node, all []*Node) bool {
- if r == nil {
- return false
- }
-
- // Treat all fields of a struct as referring to the whole struct.
- // We could do better but we would have to keep track of the fields.
- for r.Op == ODOT {
- r = r.Left
- }
-
- // Look for obvious aliasing: a variable being assigned
- // during the all list and appearing in n.
- // Also record whether there are any writes to addressable
- // memory (either main memory or variables whose addresses
- // have been taken).
- memwrite := false
- for _, as := range all {
- // We can ignore assignments to blank.
- if as.Left.isBlank() {
- continue
- }
-
- l := outervalue(as.Left)
- if l.Op != ONAME {
- memwrite = true
- continue
- }
-
- switch l.Class() {
- default:
- Fatalf("unexpected class: %v, %v", l, l.Class())
-
- case PAUTOHEAP, PEXTERN:
- memwrite = true
- continue
-
- case PPARAMOUT:
- // Assignments to a result parameter in a function with defers
- // becomes visible early if evaluation of any later expression
- // panics (#43835).
- if Curfn.Func.HasDefer() {
- return true
- }
- fallthrough
- case PAUTO, PPARAM:
- if l.Name.Addrtaken() {
- memwrite = true
- continue
- }
-
- if vmatch2(l, r) {
- // Direct hit: l appears in r.
- return true
- }
- }
- }
-
- // The variables being written do not appear in r.
- // However, r might refer to computed addresses
- // that are being written.
-
- // If no computed addresses are affected by the writes, no aliasing.
- if !memwrite {
- return false
- }
-
- // If r does not refer to computed addresses
- // (that is, if r only refers to variables whose addresses
- // have not been taken), no aliasing.
- if varexpr(r) {
- return false
- }
-
- // Otherwise, both the writes and r refer to computed memory addresses.
- // Assume that they might conflict.
- return true
-}
-
-// does the evaluation of n only refer to variables
-// whose addresses have not been taken?
-// (and no other memory)
-func varexpr(n *Node) bool {
- if n == nil {
- return true
- }
-
- switch n.Op {
- case OLITERAL:
- return true
-
- case ONAME:
- switch n.Class() {
- case PAUTO, PPARAM, PPARAMOUT:
- if !n.Name.Addrtaken() {
- return true
- }
- }
-
- return false
-
- case OADD,
- OSUB,
- OOR,
- OXOR,
- OMUL,
- ODIV,
- OMOD,
- OLSH,
- ORSH,
- OAND,
- OANDNOT,
- OPLUS,
- ONEG,
- OBITNOT,
- OPAREN,
- OANDAND,
- OOROR,
- OCONV,
- OCONVNOP,
- OCONVIFACE,
- ODOTTYPE:
- return varexpr(n.Left) && varexpr(n.Right)
-
- case ODOT: // but not ODOTPTR
- // Should have been handled in aliased.
- Fatalf("varexpr unexpected ODOT")
- }
-
- // Be conservative.
- return false
-}
-
-// is the name l mentioned in r?
-func vmatch2(l *Node, r *Node) bool {
- if r == nil {
- return false
- }
- switch r.Op {
- // match each right given left
- case ONAME:
- return l == r
-
- case OLITERAL:
- return false
- }
-
- if vmatch2(l, r.Left) {
- return true
- }
- if vmatch2(l, r.Right) {
- return true
- }
- for _, n := range r.List.Slice() {
- if vmatch2(l, n) {
- return true
- }
- }
- return false
-}
-
-// is any name mentioned in l also mentioned in r?
-// called by sinit.go
-func vmatch1(l *Node, r *Node) bool {
- // isolate all left sides
- if l == nil || r == nil {
- return false
- }
- switch l.Op {
- case ONAME:
- switch l.Class() {
- case PPARAM, PAUTO:
- break
-
- default:
- // assignment to non-stack variable must be
- // delayed if right has function calls.
- if r.HasCall() {
- return true
- }
- }
-
- return vmatch2(l, r)
-
- case OLITERAL:
- return false
- }
-
- if vmatch1(l.Left, r) {
- return true
- }
- if vmatch1(l.Right, r) {
- return true
- }
- for _, n := range l.List.Slice() {
- if vmatch1(n, r) {
- return true
- }
- }
- return false
-}
-
-// paramstoheap returns code to allocate memory for heap-escaped parameters
-// and to copy non-result parameters' values from the stack.
-func paramstoheap(params *types.Type) []*Node {
- var nn []*Node
- for _, t := range params.Fields().Slice() {
- v := asNode(t.Nname)
- if v != nil && v.Sym != nil && strings.HasPrefix(v.Sym.Name, "~r") { // unnamed result
- v = nil
- }
- if v == nil {
- continue
- }
-
- if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil {
- nn = append(nn, walkstmt(nod(ODCL, v, nil)))
- if stackcopy.Class() == PPARAM {
- nn = append(nn, walkstmt(typecheck(nod(OAS, v, stackcopy), ctxStmt)))
- }
- }
- }
-
- return nn
-}
-
-// zeroResults zeros the return values at the start of the function.
-// We need to do this very early in the function. Defer might stop a
-// panic and show the return values as they exist at the time of
-// panic. For precise stacks, the garbage collector assumes results
-// are always live, so we need to zero them before any allocations,
-// even allocations to move params/results to the heap.
-// The generated code is added to Curfn's Enter list.
-func zeroResults() {
- for _, f := range Curfn.Type.Results().Fields().Slice() {
- v := asNode(f.Nname)
- if v != nil && v.Name.Param.Heapaddr != nil {
- // The local which points to the return value is the
- // thing that needs zeroing. This is already handled
- // by a Needzero annotation in plive.go:livenessepilogue.
- continue
- }
- if v.isParamHeapCopy() {
- // TODO(josharian/khr): Investigate whether we can switch to "continue" here,
- // and document more in either case.
- // In the review of CL 114797, Keith wrote (roughly):
- // I don't think the zeroing below matters.
- // The stack return value will never be marked as live anywhere in the function.
- // It is not written to until deferreturn returns.
- v = v.Name.Param.Stackcopy
- }
- // Zero the stack location containing f.
- Curfn.Func.Enter.Append(nodl(Curfn.Pos, OAS, v, nil))
- }
-}
-
-// returnsfromheap returns code to copy values for heap-escaped parameters
-// back to the stack.
-func returnsfromheap(params *types.Type) []*Node {
- var nn []*Node
- for _, t := range params.Fields().Slice() {
- v := asNode(t.Nname)
- if v == nil {
- continue
- }
- if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil && stackcopy.Class() == PPARAMOUT {
- nn = append(nn, walkstmt(typecheck(nod(OAS, stackcopy, v), ctxStmt)))
- }
- }
-
- return nn
-}
-
-// heapmoves generates code to handle migrating heap-escaped parameters
-// between the stack and the heap. The generated code is added to Curfn's
-// Enter and Exit lists.
-func heapmoves() {
- lno := lineno
- lineno = Curfn.Pos
- nn := paramstoheap(Curfn.Type.Recvs())
- nn = append(nn, paramstoheap(Curfn.Type.Params())...)
- nn = append(nn, paramstoheap(Curfn.Type.Results())...)
- Curfn.Func.Enter.Append(nn...)
- lineno = Curfn.Func.Endlineno
- Curfn.Func.Exit.Append(returnsfromheap(Curfn.Type.Results())...)
- lineno = lno
-}
-
-func vmkcall(fn *Node, t *types.Type, init *Nodes, va []*Node) *Node {
- if fn.Type == nil || fn.Type.Etype != TFUNC {
- Fatalf("mkcall %v %v", fn, fn.Type)
- }
-
- n := fn.Type.NumParams()
- if n != len(va) {
- Fatalf("vmkcall %v needs %v args got %v", fn, n, len(va))
- }
-
- r := nod(OCALL, fn, nil)
- r.List.Set(va)
- if fn.Type.NumResults() > 0 {
- r = typecheck(r, ctxExpr|ctxMultiOK)
- } else {
- r = typecheck(r, ctxStmt)
- }
- r = walkexpr(r, init)
- r.Type = t
- return r
-}
-
-func mkcall(name string, t *types.Type, init *Nodes, args ...*Node) *Node {
- return vmkcall(syslook(name), t, init, args)
-}
-
-func mkcall1(fn *Node, t *types.Type, init *Nodes, args ...*Node) *Node {
- return vmkcall(fn, t, init, args)
-}
-
-func conv(n *Node, t *types.Type) *Node {
- if types.Identical(n.Type, t) {
- return n
- }
- n = nod(OCONV, n, nil)
- n.Type = t
- n = typecheck(n, ctxExpr)
- return n
-}
-
-// convnop converts node n to type t using the OCONVNOP op
-// and typechecks the result with ctxExpr.
-func convnop(n *Node, t *types.Type) *Node {
- if types.Identical(n.Type, t) {
- return n
- }
- n = nod(OCONVNOP, n, nil)
- n.Type = t
- n = typecheck(n, ctxExpr)
- return n
-}
-
-// byteindex converts n, which is byte-sized, to an int used to index into an array.
-// We cannot use conv, because we allow converting bool to int here,
-// which is forbidden in user code.
-func byteindex(n *Node) *Node {
- // We cannot convert from bool to int directly.
- // While converting from int8 to int is possible, it would yield
- // the wrong result for negative values.
- // Reinterpreting the value as an unsigned byte solves both cases.
- if !types.Identical(n.Type, types.Types[TUINT8]) {
- n = nod(OCONV, n, nil)
- n.Type = types.Types[TUINT8]
- n.SetTypecheck(1)
- }
- n = nod(OCONV, n, nil)
- n.Type = types.Types[TINT]
- n.SetTypecheck(1)
- return n
-}
-
-func chanfn(name string, n int, t *types.Type) *Node {
- if !t.IsChan() {
- Fatalf("chanfn %v", t)
- }
- fn := syslook(name)
- switch n {
- default:
- Fatalf("chanfn %d", n)
- case 1:
- fn = substArgTypes(fn, t.Elem())
- case 2:
- fn = substArgTypes(fn, t.Elem(), t.Elem())
- }
- return fn
-}
-
-func mapfn(name string, t *types.Type) *Node {
- if !t.IsMap() {
- Fatalf("mapfn %v", t)
- }
- fn := syslook(name)
- fn = substArgTypes(fn, t.Key(), t.Elem(), t.Key(), t.Elem())
- return fn
-}
-
-func mapfndel(name string, t *types.Type) *Node {
- if !t.IsMap() {
- Fatalf("mapfn %v", t)
- }
- fn := syslook(name)
- fn = substArgTypes(fn, t.Key(), t.Elem(), t.Key())
- return fn
-}
-
-const (
- mapslow = iota
- mapfast32
- mapfast32ptr
- mapfast64
- mapfast64ptr
- mapfaststr
- nmapfast
-)
-
-type mapnames [nmapfast]string
-
-func mkmapnames(base string, ptr string) mapnames {
- return mapnames{base, base + "_fast32", base + "_fast32" + ptr, base + "_fast64", base + "_fast64" + ptr, base + "_faststr"}
-}
-
-var mapaccess1 = mkmapnames("mapaccess1", "")
-var mapaccess2 = mkmapnames("mapaccess2", "")
-var mapassign = mkmapnames("mapassign", "ptr")
-var mapdelete = mkmapnames("mapdelete", "")
-
-func mapfast(t *types.Type) int {
- // Check runtime/map.go:maxElemSize before changing.
- if t.Elem().Width > 128 {
- return mapslow
- }
- switch algtype(t.Key()) {
- case AMEM32:
- if !t.Key().HasPointers() {
- return mapfast32
- }
- if Widthptr == 4 {
- return mapfast32ptr
- }
- Fatalf("small pointer %v", t.Key())
- case AMEM64:
- if !t.Key().HasPointers() {
- return mapfast64
- }
- if Widthptr == 8 {
- return mapfast64ptr
- }
- // Two-word object, at least one of which is a pointer.
- // Use the slow path.
- case ASTRING:
- return mapfaststr
- }
- return mapslow
-}
-
-func writebarrierfn(name string, l *types.Type, r *types.Type) *Node {
- fn := syslook(name)
- fn = substArgTypes(fn, l, r)
- return fn
-}
-
-func addstr(n *Node, init *Nodes) *Node {
- // order.expr rewrote OADDSTR to have a list of strings.
- c := n.List.Len()
-
- if c < 2 {
- Fatalf("addstr count %d too small", c)
- }
-
- buf := nodnil()
- if n.Esc == EscNone {
- sz := int64(0)
- for _, n1 := range n.List.Slice() {
- if n1.Op == OLITERAL {
- sz += int64(len(n1.StringVal()))
- }
- }
-
- // Don't allocate the buffer if the result won't fit.
- if sz < tmpstringbufsize {
- // Create temporary buffer for result string on stack.
- t := types.NewArray(types.Types[TUINT8], tmpstringbufsize)
- buf = nod(OADDR, temp(t), nil)
- }
- }
-
- // build list of string arguments
- args := []*Node{buf}
- for _, n2 := range n.List.Slice() {
- args = append(args, conv(n2, types.Types[TSTRING]))
- }
-
- var fn string
- if c <= 5 {
- // small numbers of strings use direct runtime helpers.
- // note: order.expr knows this cutoff too.
- fn = fmt.Sprintf("concatstring%d", c)
- } else {
- // large numbers of strings are passed to the runtime as a slice.
- fn = "concatstrings"
-
- t := types.NewSlice(types.Types[TSTRING])
- slice := nod(OCOMPLIT, nil, typenod(t))
- if prealloc[n] != nil {
- prealloc[slice] = prealloc[n]
- }
- slice.List.Set(args[1:]) // skip buf arg
- args = []*Node{buf, slice}
- slice.Esc = EscNone
- }
-
- cat := syslook(fn)
- r := nod(OCALL, cat, nil)
- r.List.Set(args)
- r = typecheck(r, ctxExpr)
- r = walkexpr(r, init)
- r.Type = n.Type
-
- return r
-}
-
-func walkAppendArgs(n *Node, init *Nodes) {
- walkexprlistsafe(n.List.Slice(), init)
-
- // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
- // and n are name or literal, but those may index the slice we're
- // modifying here. Fix explicitly.
- ls := n.List.Slice()
- for i1, n1 := range ls {
- ls[i1] = cheapexpr(n1, init)
- }
-}
-
-// expand append(l1, l2...) to
-// init {
-// s := l1
-// n := len(s) + len(l2)
-// // Compare as uint so growslice can panic on overflow.
-// if uint(n) > uint(cap(s)) {
-// s = growslice(s, n)
-// }
-// s = s[:n]
-// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
-// }
-// s
-//
-// l2 is allowed to be a string.
-func appendslice(n *Node, init *Nodes) *Node {
- walkAppendArgs(n, init)
-
- l1 := n.List.First()
- l2 := n.List.Second()
- l2 = cheapexpr(l2, init)
- n.List.SetSecond(l2)
-
- var nodes Nodes
-
- // var s []T
- s := temp(l1.Type)
- nodes.Append(nod(OAS, s, l1)) // s = l1
-
- elemtype := s.Type.Elem()
-
- // n := len(s) + len(l2)
- nn := temp(types.Types[TINT])
- nodes.Append(nod(OAS, nn, nod(OADD, nod(OLEN, s, nil), nod(OLEN, l2, nil))))
-
- // if uint(n) > uint(cap(s))
- nif := nod(OIF, nil, nil)
- nuint := conv(nn, types.Types[TUINT])
- scapuint := conv(nod(OCAP, s, nil), types.Types[TUINT])
- nif.Left = nod(OGT, nuint, scapuint)
-
- // instantiate growslice(typ *type, []any, int) []any
- fn := syslook("growslice")
- fn = substArgTypes(fn, elemtype, elemtype)
-
- // s = growslice(T, s, n)
- nif.Nbody.Set1(nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(elemtype), s, nn)))
- nodes.Append(nif)
-
- // s = s[:n]
- nt := nod(OSLICE, s, nil)
- nt.SetSliceBounds(nil, nn, nil)
- nt.SetBounded(true)
- nodes.Append(nod(OAS, s, nt))
-
- var ncopy *Node
- if elemtype.HasPointers() {
- // copy(s[len(l1):], l2)
- nptr1 := nod(OSLICE, s, nil)
- nptr1.Type = s.Type
- nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil)
- nptr1 = cheapexpr(nptr1, &nodes)
-
- nptr2 := l2
-
- Curfn.Func.setWBPos(n.Pos)
-
- // 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.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(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
-
- 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))
- nptr1.SetBounded(true)
- nptr1 = nod(OADDR, nptr1, nil)
-
- nptr2 := nod(OSPTR, l2, nil)
-
- nwid := cheapexpr(conv(nod(OLEN, l2, nil), types.Types[TUINTPTR]), &nodes)
- nwid = nod(OMUL, nwid, nodintconst(elemtype.Width))
-
- // instantiate func memmove(to *any, frm *any, length uintptr)
- fn := syslook("memmove")
- fn = substArgTypes(fn, elemtype, elemtype)
- ncopy = mkcall1(fn, nil, &nodes, nptr1, nptr2, nwid)
- }
- ln := append(nodes.Slice(), ncopy)
-
- typecheckslice(ln, ctxStmt)
- walkstmtlist(ln)
- init.Append(ln...)
- return s
-}
-
-// isAppendOfMake reports whether n is of the form append(x , make([]T, y)...).
-// isAppendOfMake assumes n has already been typechecked.
-func isAppendOfMake(n *Node) bool {
- if Debug.N != 0 || instrumenting {
- return false
- }
-
- if n.Typecheck() == 0 {
- Fatalf("missing typecheck: %+v", n)
- }
-
- if n.Op != OAPPEND || !n.IsDDD() || n.List.Len() != 2 {
- return false
- }
-
- second := n.List.Second()
- if second.Op != OMAKESLICE || second.Right != nil {
- return false
- }
-
- // y must be either an integer constant or the largest possible positive value
- // of variable y needs to fit into an uint.
-
- // typecheck made sure that constant arguments to make are not negative and fit into an int.
-
- // The care of overflow of the len argument to make will be handled by an explicit check of int(len) < 0 during runtime.
- y := second.Left
- if !Isconst(y, CTINT) && maxintval[y.Type.Etype].Cmp(maxintval[TUINT]) > 0 {
- return false
- }
-
- return true
-}
-
-// extendslice rewrites append(l1, make([]T, l2)...) to
-// init {
-// if l2 >= 0 { // Empty if block here for more meaningful node.SetLikely(true)
-// } else {
-// panicmakeslicelen()
-// }
-// s := l1
-// n := len(s) + l2
-// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2.
-// // cap is a positive int and n can become negative when len(s) + l2
-// // overflows int. Interpreting n when negative as uint makes it larger
-// // than cap(s). growslice will check the int n arg and panic if n is
-// // negative. This prevents the overflow from being undetected.
-// if uint(n) > uint(cap(s)) {
-// s = growslice(T, s, n)
-// }
-// s = s[:n]
-// lptr := &l1[0]
-// sptr := &s[0]
-// if lptr == sptr || !T.HasPointers() {
-// // growslice did not clear the whole underlying array (or did not get called)
-// hp := &s[len(l1)]
-// hn := l2 * sizeof(T)
-// memclr(hp, hn)
-// }
-// }
-// s
-func extendslice(n *Node, init *Nodes) *Node {
- // isAppendOfMake made sure all possible positive values of l2 fit into an uint.
- // The case of l2 overflow when converting from e.g. uint to int is handled by an explicit
- // check of l2 < 0 at runtime which is generated below.
- l2 := conv(n.List.Second().Left, types.Types[TINT])
- l2 = typecheck(l2, ctxExpr)
- n.List.SetSecond(l2) // walkAppendArgs expects l2 in n.List.Second().
-
- walkAppendArgs(n, init)
-
- l1 := n.List.First()
- l2 = n.List.Second() // re-read l2, as it may have been updated by walkAppendArgs
-
- var nodes []*Node
-
- // if l2 >= 0 (likely happens), do nothing
- nifneg := nod(OIF, nod(OGE, l2, nodintconst(0)), nil)
- nifneg.SetLikely(true)
-
- // else panicmakeslicelen()
- nifneg.Rlist.Set1(mkcall("panicmakeslicelen", nil, init))
- nodes = append(nodes, nifneg)
-
- // s := l1
- s := temp(l1.Type)
- nodes = append(nodes, nod(OAS, s, l1))
-
- elemtype := s.Type.Elem()
-
- // n := len(s) + l2
- nn := temp(types.Types[TINT])
- nodes = append(nodes, nod(OAS, nn, nod(OADD, nod(OLEN, s, nil), l2)))
-
- // if uint(n) > uint(cap(s))
- nuint := conv(nn, types.Types[TUINT])
- capuint := conv(nod(OCAP, s, nil), types.Types[TUINT])
- nif := nod(OIF, nod(OGT, nuint, capuint), nil)
-
- // instantiate growslice(typ *type, old []any, newcap int) []any
- fn := syslook("growslice")
- fn = substArgTypes(fn, elemtype, elemtype)
-
- // s = growslice(T, s, n)
- nif.Nbody.Set1(nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(elemtype), s, nn)))
- nodes = append(nodes, nif)
-
- // s = s[:n]
- nt := nod(OSLICE, s, nil)
- nt.SetSliceBounds(nil, nn, nil)
- nt.SetBounded(true)
- nodes = append(nodes, nod(OAS, s, nt))
-
- // lptr := &l1[0]
- l1ptr := temp(l1.Type.Elem().PtrTo())
- tmp := nod(OSPTR, l1, nil)
- nodes = append(nodes, nod(OAS, l1ptr, tmp))
-
- // sptr := &s[0]
- sptr := temp(elemtype.PtrTo())
- tmp = nod(OSPTR, s, nil)
- nodes = append(nodes, nod(OAS, sptr, tmp))
-
- // hp := &s[len(l1)]
- hp := nod(OINDEX, s, nod(OLEN, l1, nil))
- hp.SetBounded(true)
- hp = nod(OADDR, hp, nil)
- hp = convnop(hp, types.Types[TUNSAFEPTR])
-
- // hn := l2 * sizeof(elem(s))
- hn := nod(OMUL, l2, nodintconst(elemtype.Width))
- hn = conv(hn, types.Types[TUINTPTR])
-
- clrname := "memclrNoHeapPointers"
- hasPointers := elemtype.HasPointers()
- if hasPointers {
- clrname = "memclrHasPointers"
- Curfn.Func.setWBPos(n.Pos)
- }
-
- var clr Nodes
- clrfn := mkcall(clrname, nil, &clr, hp, hn)
- clr.Append(clrfn)
-
- if hasPointers {
- // if l1ptr == sptr
- nifclr := nod(OIF, nod(OEQ, l1ptr, sptr), nil)
- nifclr.Nbody = clr
- nodes = append(nodes, nifclr)
- } else {
- nodes = append(nodes, clr.Slice()...)
- }
-
- typecheckslice(nodes, ctxStmt)
- walkstmtlist(nodes)
- init.Append(nodes...)
- return s
-}
-
-// Rewrite append(src, x, y, z) so that any side effects in
-// x, y, z (including runtime panics) are evaluated in
-// initialization statements before the append.
-// For normal code generation, stop there and leave the
-// rest to cgen_append.
-//
-// For race detector, expand append(src, a [, b]* ) to
-//
-// init {
-// s := src
-// const argc = len(args) - 1
-// if cap(s) - len(s) < argc {
-// s = growslice(s, len(s)+argc)
-// }
-// n := len(s)
-// s = s[:n+argc]
-// s[n] = a
-// s[n+1] = b
-// ...
-// }
-// s
-func walkappend(n *Node, init *Nodes, dst *Node) *Node {
- if !samesafeexpr(dst, n.List.First()) {
- n.List.SetFirst(safeexpr(n.List.First(), init))
- n.List.SetFirst(walkexpr(n.List.First(), init))
- }
- walkexprlistsafe(n.List.Slice()[1:], init)
-
- nsrc := n.List.First()
-
- // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
- // and n are name or literal, but those may index the slice we're
- // modifying here. Fix explicitly.
- // Using cheapexpr also makes sure that the evaluation
- // of all arguments (and especially any panics) happen
- // before we begin to modify the slice in a visible way.
- ls := n.List.Slice()[1:]
- for i, n := range ls {
- n = cheapexpr(n, init)
- if !types.Identical(n.Type, nsrc.Type.Elem()) {
- n = assignconv(n, nsrc.Type.Elem(), "append")
- n = walkexpr(n, init)
- }
- ls[i] = n
- }
-
- argc := n.List.Len() - 1
- if argc < 1 {
- return nsrc
- }
-
- // General case, with no function calls left as arguments.
- // Leave for gen, except that instrumentation requires old form.
- if !instrumenting || compiling_runtime {
- return n
- }
-
- var l []*Node
-
- ns := temp(nsrc.Type)
- l = append(l, nod(OAS, ns, nsrc)) // s = src
-
- na := nodintconst(int64(argc)) // const argc
- nx := nod(OIF, nil, nil) // if cap(s) - len(s) < argc
- nx.Left = nod(OLT, nod(OSUB, nod(OCAP, ns, nil), nod(OLEN, ns, nil)), na)
-
- fn := syslook("growslice") // growslice(, old []T, mincap int) (ret []T)
- fn = substArgTypes(fn, ns.Type.Elem(), ns.Type.Elem())
-
- nx.Nbody.Set1(nod(OAS, ns,
- mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type.Elem()), ns,
- nod(OADD, nod(OLEN, ns, nil), na))))
-
- l = append(l, nx)
-
- nn := temp(types.Types[TINT])
- l = append(l, nod(OAS, nn, nod(OLEN, ns, nil))) // n = len(s)
-
- nx = nod(OSLICE, ns, nil) // ...s[:n+argc]
- nx.SetSliceBounds(nil, nod(OADD, nn, na), nil)
- nx.SetBounded(true)
- l = append(l, nod(OAS, ns, nx)) // s = s[:n+argc]
-
- ls = n.List.Slice()[1:]
- for i, n := range ls {
- nx = nod(OINDEX, ns, nn) // s[n] ...
- nx.SetBounded(true)
- l = append(l, nod(OAS, nx, n)) // s[n] = arg
- if i+1 < len(ls) {
- l = append(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))) // n = n + 1
- }
- }
-
- typecheckslice(l, ctxStmt)
- walkstmtlist(l)
- init.Append(l...)
- return ns
-}
-
-// Lower copy(a, b) to a memmove call or a runtime call.
-//
-// init {
-// n := len(a)
-// if n > len(b) { n = len(b) }
-// if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) }
-// }
-// n;
-//
-// Also works if b is a string.
-//
-func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
- if n.Left.Type.Elem().HasPointers() {
- 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.backingArrayPtrLen()
- n.Right = cheapexpr(n.Right, init)
- ptrR, lenR := n.Right.backingArrayPtrLen()
- return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR)
- }
-
- if runtimecall {
- // rely on runtime to instrument:
- // copy(n.Left, n.Right)
- // n.Right can be a slice or string.
-
- n.Left = cheapexpr(n.Left, init)
- ptrL, lenL := n.Left.backingArrayPtrLen()
- n.Right = cheapexpr(n.Right, init)
- 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))
- }
-
- n.Left = walkexpr(n.Left, init)
- n.Right = walkexpr(n.Right, init)
- nl := temp(n.Left.Type)
- nr := temp(n.Right.Type)
- var l []*Node
- l = append(l, nod(OAS, nl, n.Left))
- l = append(l, nod(OAS, nr, n.Right))
-
- nfrm := nod(OSPTR, nr, nil)
- nto := nod(OSPTR, nl, nil)
-
- nlen := temp(types.Types[TINT])
-
- // n = len(to)
- l = append(l, nod(OAS, nlen, nod(OLEN, nl, nil)))
-
- // if n > len(frm) { n = len(frm) }
- nif := nod(OIF, nil, nil)
-
- nif.Left = nod(OGT, nlen, nod(OLEN, nr, nil))
- nif.Nbody.Append(nod(OAS, nlen, nod(OLEN, nr, nil)))
- l = append(l, nif)
-
- // if to.ptr != frm.ptr { memmove( ... ) }
- ne := nod(OIF, nod(ONE, nto, nfrm), nil)
- ne.SetLikely(true)
- l = append(l, ne)
-
- fn := syslook("memmove")
- fn = substArgTypes(fn, nl.Type.Elem(), nl.Type.Elem())
- nwid := temp(types.Types[TUINTPTR])
- setwid := nod(OAS, nwid, conv(nlen, types.Types[TUINTPTR]))
- ne.Nbody.Append(setwid)
- nwid = nod(OMUL, nwid, nodintconst(nl.Type.Elem().Width))
- call := mkcall1(fn, nil, init, nto, nfrm, nwid)
- ne.Nbody.Append(call)
-
- typecheckslice(l, ctxStmt)
- walkstmtlist(l)
- init.Append(l...)
- return nlen
-}
-
-func eqfor(t *types.Type) (n *Node, needsize bool) {
- // Should only arrive here with large memory or
- // a struct/array containing a non-memory field/element.
- // Small memory is handled inline, and single non-memory
- // is handled by walkcompare.
- switch a, _ := algtype1(t); a {
- case AMEM:
- n := syslook("memequal")
- n = substArgTypes(n, t, t)
- return n, true
- case ASPECIAL:
- sym := typesymprefix(".eq", t)
- n := newname(sym)
- setNodeNameFunc(n)
- n.Type = functype(nil, []*Node{
- anonfield(types.NewPtr(t)),
- anonfield(types.NewPtr(t)),
- }, []*Node{
- anonfield(types.Types[TBOOL]),
- })
- return n, false
- }
- Fatalf("eqfor %v", t)
- return nil, false
-}
-
-// The result of walkcompare MUST be assigned back to n, e.g.
-// n.Left = walkcompare(n.Left, init)
-func walkcompare(n *Node, init *Nodes) *Node {
- if n.Left.Type.IsInterface() && n.Right.Type.IsInterface() && n.Left.Op != OLITERAL && n.Right.Op != OLITERAL {
- return walkcompareInterface(n, init)
- }
-
- if n.Left.Type.IsString() && n.Right.Type.IsString() {
- return walkcompareString(n, init)
- }
-
- n.Left = walkexpr(n.Left, init)
- n.Right = walkexpr(n.Right, init)
-
- // Given mixed interface/concrete comparison,
- // rewrite into types-equal && data-equal.
- // This is efficient, avoids allocations, and avoids runtime calls.
- if n.Left.Type.IsInterface() != n.Right.Type.IsInterface() {
- // Preserve side-effects in case of short-circuiting; see #32187.
- l := cheapexpr(n.Left, init)
- r := cheapexpr(n.Right, init)
- // Swap so that l is the interface value and r is the concrete value.
- if n.Right.Type.IsInterface() {
- l, r = r, l
- }
-
- // Handle both == and !=.
- eq := n.Op
- andor := OOROR
- if eq == OEQ {
- andor = OANDAND
- }
- // Check for types equal.
- // For empty interface, this is:
- // l.tab == type(r)
- // For non-empty interface, this is:
- // l.tab != nil && l.tab._type == type(r)
- var eqtype *Node
- tab := nod(OITAB, l, nil)
- rtyp := typename(r.Type)
- if l.Type.IsEmptyInterface() {
- tab.Type = types.NewPtr(types.Types[TUINT8])
- tab.SetTypecheck(1)
- eqtype = nod(eq, tab, rtyp)
- } else {
- nonnil := nod(brcom(eq), nodnil(), tab)
- match := nod(eq, itabType(tab), rtyp)
- eqtype = nod(andor, nonnil, match)
- }
- // Check for data equal.
- eqdata := nod(eq, ifaceData(n.Pos, l, r.Type), r)
- // Put it all together.
- expr := nod(andor, eqtype, eqdata)
- n = finishcompare(n, expr, init)
- return n
- }
-
- // Must be comparison of array or struct.
- // Otherwise back end handles it.
- // While we're here, decide whether to
- // inline or call an eq alg.
- t := n.Left.Type
- var inline bool
-
- maxcmpsize := int64(4)
- unalignedLoad := canMergeLoads()
- if unalignedLoad {
- // Keep this low enough to generate less code than a function call.
- maxcmpsize = 2 * int64(thearch.LinkArch.RegSize)
- }
-
- switch t.Etype {
- default:
- if Debug_libfuzzer != 0 && t.IsInteger() {
- n.Left = cheapexpr(n.Left, init)
- n.Right = cheapexpr(n.Right, init)
-
- // If exactly one comparison operand is
- // constant, invoke the constcmp functions
- // instead, and arrange for the constant
- // operand to be the first argument.
- l, r := n.Left, n.Right
- if r.Op == OLITERAL {
- l, r = r, l
- }
- constcmp := l.Op == OLITERAL && r.Op != OLITERAL
-
- var fn string
- var paramType *types.Type
- switch t.Size() {
- case 1:
- fn = "libfuzzerTraceCmp1"
- if constcmp {
- fn = "libfuzzerTraceConstCmp1"
- }
- paramType = types.Types[TUINT8]
- case 2:
- fn = "libfuzzerTraceCmp2"
- if constcmp {
- fn = "libfuzzerTraceConstCmp2"
- }
- paramType = types.Types[TUINT16]
- case 4:
- fn = "libfuzzerTraceCmp4"
- if constcmp {
- fn = "libfuzzerTraceConstCmp4"
- }
- paramType = types.Types[TUINT32]
- case 8:
- fn = "libfuzzerTraceCmp8"
- if constcmp {
- fn = "libfuzzerTraceConstCmp8"
- }
- paramType = types.Types[TUINT64]
- default:
- Fatalf("unexpected integer size %d for %v", t.Size(), t)
- }
- init.Append(mkcall(fn, nil, init, tracecmpArg(l, paramType, init), tracecmpArg(r, paramType, init)))
- }
- return n
- case TARRAY:
- // We can compare several elements at once with 2/4/8 byte integer compares
- inline = t.NumElem() <= 1 || (issimple[t.Elem().Etype] && (t.NumElem() <= 4 || t.Elem().Width*t.NumElem() <= maxcmpsize))
- case TSTRUCT:
- inline = t.NumComponents(types.IgnoreBlankFields) <= 4
- }
-
- cmpl := n.Left
- for cmpl != nil && cmpl.Op == OCONVNOP {
- cmpl = cmpl.Left
- }
- cmpr := n.Right
- for cmpr != nil && cmpr.Op == OCONVNOP {
- cmpr = cmpr.Left
- }
-
- // Chose not to inline. Call equality function directly.
- if !inline {
- // eq algs take pointers; cmpl and cmpr must be addressable
- if !islvalue(cmpl) || !islvalue(cmpr) {
- Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
- }
-
- fn, needsize := eqfor(t)
- call := nod(OCALL, fn, nil)
- call.List.Append(nod(OADDR, cmpl, nil))
- call.List.Append(nod(OADDR, cmpr, nil))
- if needsize {
- call.List.Append(nodintconst(t.Width))
- }
- res := call
- if n.Op != OEQ {
- res = nod(ONOT, res, nil)
- }
- n = finishcompare(n, res, init)
- return n
- }
-
- // inline: build boolean expression comparing element by element
- andor := OANDAND
- if n.Op == ONE {
- andor = OOROR
- }
- var expr *Node
- compare := func(el, er *Node) {
- a := nod(n.Op, el, er)
- if expr == nil {
- expr = a
- } else {
- expr = nod(andor, expr, a)
- }
- }
- cmpl = safeexpr(cmpl, init)
- cmpr = safeexpr(cmpr, init)
- if t.IsStruct() {
- for _, f := range t.Fields().Slice() {
- sym := f.Sym
- if sym.IsBlank() {
- continue
- }
- compare(
- nodSym(OXDOT, cmpl, sym),
- nodSym(OXDOT, cmpr, sym),
- )
- }
- } else {
- step := int64(1)
- remains := t.NumElem() * t.Elem().Width
- combine64bit := unalignedLoad && Widthreg == 8 && t.Elem().Width <= 4 && t.Elem().IsInteger()
- combine32bit := unalignedLoad && t.Elem().Width <= 2 && t.Elem().IsInteger()
- combine16bit := unalignedLoad && t.Elem().Width == 1 && t.Elem().IsInteger()
- for i := int64(0); remains > 0; {
- var convType *types.Type
- switch {
- case remains >= 8 && combine64bit:
- convType = types.Types[TINT64]
- step = 8 / t.Elem().Width
- case remains >= 4 && combine32bit:
- convType = types.Types[TUINT32]
- step = 4 / t.Elem().Width
- case remains >= 2 && combine16bit:
- convType = types.Types[TUINT16]
- step = 2 / t.Elem().Width
- default:
- step = 1
- }
- if step == 1 {
- compare(
- nod(OINDEX, cmpl, nodintconst(i)),
- nod(OINDEX, cmpr, nodintconst(i)),
- )
- i++
- remains -= t.Elem().Width
- } else {
- elemType := t.Elem().ToUnsigned()
- cmplw := nod(OINDEX, cmpl, nodintconst(i))
- cmplw = conv(cmplw, elemType) // convert to unsigned
- cmplw = conv(cmplw, convType) // widen
- cmprw := nod(OINDEX, cmpr, nodintconst(i))
- cmprw = conv(cmprw, elemType)
- cmprw = conv(cmprw, convType)
- // For code like this: uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ...
- // ssa will generate a single large load.
- for offset := int64(1); offset < step; offset++ {
- lb := nod(OINDEX, cmpl, nodintconst(i+offset))
- lb = conv(lb, elemType)
- lb = conv(lb, convType)
- lb = nod(OLSH, lb, nodintconst(8*t.Elem().Width*offset))
- cmplw = nod(OOR, cmplw, lb)
- rb := nod(OINDEX, cmpr, nodintconst(i+offset))
- rb = conv(rb, elemType)
- rb = conv(rb, convType)
- rb = nod(OLSH, rb, nodintconst(8*t.Elem().Width*offset))
- cmprw = nod(OOR, cmprw, rb)
- }
- compare(cmplw, cmprw)
- i += step
- remains -= step * t.Elem().Width
- }
- }
- }
- if expr == nil {
- expr = nodbool(n.Op == OEQ)
- // We still need to use cmpl and cmpr, in case they contain
- // an expression which might panic. See issue 23837.
- t := temp(cmpl.Type)
- a1 := nod(OAS, t, cmpl)
- a1 = typecheck(a1, ctxStmt)
- a2 := nod(OAS, t, cmpr)
- a2 = typecheck(a2, ctxStmt)
- init.Append(a1, a2)
- }
- n = finishcompare(n, expr, init)
- return n
-}
-
-func tracecmpArg(n *Node, t *types.Type, init *Nodes) *Node {
- // Ugly hack to avoid "constant -1 overflows uintptr" errors, etc.
- if n.Op == OLITERAL && n.Type.IsSigned() && n.Int64Val() < 0 {
- n = copyexpr(n, n.Type, init)
- }
-
- return conv(n, t)
-}
-
-func walkcompareInterface(n *Node, init *Nodes) *Node {
- n.Right = cheapexpr(n.Right, init)
- n.Left = cheapexpr(n.Left, init)
- eqtab, eqdata := eqinterface(n.Left, n.Right)
- var cmp *Node
- if n.Op == OEQ {
- cmp = nod(OANDAND, eqtab, eqdata)
- } else {
- eqtab.Op = ONE
- cmp = nod(OOROR, eqtab, nod(ONOT, eqdata, nil))
- }
- return finishcompare(n, cmp, init)
-}
-
-func walkcompareString(n *Node, init *Nodes) *Node {
- // Rewrite comparisons to short constant strings as length+byte-wise comparisons.
- var cs, ncs *Node // const string, non-const string
- switch {
- case Isconst(n.Left, CTSTR) && Isconst(n.Right, CTSTR):
- // ignore; will be constant evaluated
- case Isconst(n.Left, CTSTR):
- cs = n.Left
- ncs = n.Right
- case Isconst(n.Right, CTSTR):
- cs = n.Right
- ncs = n.Left
- }
- if cs != nil {
- cmp := n.Op
- // Our comparison below assumes that the non-constant string
- // is on the left hand side, so rewrite "" cmp x to x cmp "".
- // See issue 24817.
- if Isconst(n.Left, CTSTR) {
- cmp = brrev(cmp)
- }
-
- // maxRewriteLen was chosen empirically.
- // It is the value that minimizes cmd/go file size
- // across most architectures.
- // See the commit description for CL 26758 for details.
- maxRewriteLen := 6
- // Some architectures can load unaligned byte sequence as 1 word.
- // So we can cover longer strings with the same amount of code.
- canCombineLoads := canMergeLoads()
- combine64bit := false
- if canCombineLoads {
- // Keep this low enough to generate less code than a function call.
- maxRewriteLen = 2 * thearch.LinkArch.RegSize
- combine64bit = thearch.LinkArch.RegSize >= 8
- }
-
- var and Op
- switch cmp {
- case OEQ:
- and = OANDAND
- case ONE:
- and = OOROR
- default:
- // Don't do byte-wise comparisons for <, <=, etc.
- // They're fairly complicated.
- // Length-only checks are ok, though.
- maxRewriteLen = 0
- }
- if s := cs.StringVal(); len(s) <= maxRewriteLen {
- if len(s) > 0 {
- ncs = safeexpr(ncs, init)
- }
- r := nod(cmp, nod(OLEN, ncs, nil), nodintconst(int64(len(s))))
- remains := len(s)
- for i := 0; remains > 0; {
- if remains == 1 || !canCombineLoads {
- cb := nodintconst(int64(s[i]))
- ncb := nod(OINDEX, ncs, nodintconst(int64(i)))
- r = nod(and, r, nod(cmp, ncb, cb))
- remains--
- i++
- continue
- }
- var step int
- var convType *types.Type
- switch {
- case remains >= 8 && combine64bit:
- convType = types.Types[TINT64]
- step = 8
- case remains >= 4:
- convType = types.Types[TUINT32]
- step = 4
- case remains >= 2:
- convType = types.Types[TUINT16]
- step = 2
- }
- ncsubstr := nod(OINDEX, ncs, nodintconst(int64(i)))
- ncsubstr = conv(ncsubstr, convType)
- csubstr := int64(s[i])
- // Calculate large constant from bytes as sequence of shifts and ors.
- // Like this: uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ...
- // ssa will combine this into a single large load.
- for offset := 1; offset < step; offset++ {
- b := nod(OINDEX, ncs, nodintconst(int64(i+offset)))
- b = conv(b, convType)
- b = nod(OLSH, b, nodintconst(int64(8*offset)))
- ncsubstr = nod(OOR, ncsubstr, b)
- csubstr |= int64(s[i+offset]) << uint8(8*offset)
- }
- csubstrPart := nodintconst(csubstr)
- // Compare "step" bytes as once
- r = nod(and, r, nod(cmp, csubstrPart, ncsubstr))
- remains -= step
- i += step
- }
- return finishcompare(n, r, init)
- }
- }
-
- var r *Node
- if n.Op == OEQ || n.Op == ONE {
- // prepare for rewrite below
- n.Left = cheapexpr(n.Left, init)
- n.Right = cheapexpr(n.Right, init)
- eqlen, eqmem := eqstring(n.Left, n.Right)
- // quick check of len before full compare for == or !=.
- // memequal then tests equality up to length len.
- if n.Op == OEQ {
- // len(left) == len(right) && memequal(left, right, len)
- r = nod(OANDAND, eqlen, eqmem)
- } else {
- // len(left) != len(right) || !memequal(left, right, len)
- eqlen.Op = ONE
- r = nod(OOROR, eqlen, nod(ONOT, eqmem, nil))
- }
- } else {
- // sys_cmpstring(s1, s2) :: 0
- r = mkcall("cmpstring", types.Types[TINT], init, conv(n.Left, types.Types[TSTRING]), conv(n.Right, types.Types[TSTRING]))
- r = nod(n.Op, r, nodintconst(0))
- }
-
- return finishcompare(n, r, init)
-}
-
-// The result of finishcompare MUST be assigned back to n, e.g.
-// n.Left = finishcompare(n.Left, x, r, init)
-func finishcompare(n, r *Node, init *Nodes) *Node {
- r = typecheck(r, ctxExpr)
- r = conv(r, n.Type)
- r = walkexpr(r, init)
- return r
-}
-
-// return 1 if integer n must be in range [0, max), 0 otherwise
-func bounded(n *Node, max int64) bool {
- if n.Type == nil || !n.Type.IsInteger() {
- return false
- }
-
- sign := n.Type.IsSigned()
- bits := int32(8 * n.Type.Width)
-
- if smallintconst(n) {
- v := n.Int64Val()
- return 0 <= v && v < max
- }
-
- switch n.Op {
- case OAND, OANDNOT:
- v := int64(-1)
- switch {
- case smallintconst(n.Left):
- v = n.Left.Int64Val()
- case smallintconst(n.Right):
- v = n.Right.Int64Val()
- if n.Op == OANDNOT {
- v = ^v
- if !sign {
- v &= 1< 0 && v >= 2 {
- bits--
- v >>= 1
- }
- }
-
- case ORSH:
- if !sign && smallintconst(n.Right) {
- v := n.Right.Int64Val()
- if v > int64(bits) {
- return true
- }
- bits -= int32(v)
- }
- }
-
- if !sign && bits <= 62 && 1< 0 {
- Fatalf("substArgTypes: too many argument types")
- }
- return n
-}
-
-// canMergeLoads reports whether the backend optimization passes for
-// the current architecture can combine adjacent loads into a single
-// larger, possibly unaligned, load. Note that currently the
-// optimizations must be able to handle little endian byte order.
-func canMergeLoads() bool {
- switch thearch.LinkArch.Family {
- case sys.ARM64, sys.AMD64, sys.I386, sys.S390X:
- return true
- case sys.PPC64:
- // Load combining only supported on ppc64le.
- return thearch.LinkArch.ByteOrder == binary.LittleEndian
- }
- return false
-}
-
-// isRuneCount reports whether n is of the form len([]rune(string)).
-// These are optimized into a call to runtime.countrunes.
-func isRuneCount(n *Node) bool {
- return Debug.N == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES
-}
-
-func walkCheckPtrAlignment(n *Node, init *Nodes, count *Node) *Node {
- if !n.Type.IsPtr() {
- Fatalf("expected pointer type: %v", n.Type)
- }
- elem := n.Type.Elem()
- if count != nil {
- if !elem.IsArray() {
- Fatalf("expected array type: %v", elem)
- }
- elem = elem.Elem()
- }
-
- size := elem.Size()
- if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) {
- return n
- }
-
- if count == nil {
- count = nodintconst(1)
- }
-
- n.Left = cheapexpr(n.Left, init)
- init.Append(mkcall("checkptrAlignment", nil, init, convnop(n.Left, types.Types[TUNSAFEPTR]), typename(elem), conv(count, types.Types[TUINTPTR])))
- return n
-}
-
-var walkCheckPtrArithmeticMarker byte
-
-func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
- // Calling cheapexpr(n, init) below leads to a recursive call
- // to walkexpr, which leads us back here again. Use n.Opt to
- // prevent infinite loops.
- if opt := n.Opt(); opt == &walkCheckPtrArithmeticMarker {
- return n
- } else if opt != nil {
- // We use n.Opt() here because today it's not used for OCONVNOP. If that changes,
- // there's no guarantee that temporarily replacing it is safe, so just hard fail here.
- Fatalf("unexpected Opt: %v", opt)
- }
- n.SetOpt(&walkCheckPtrArithmeticMarker)
- defer n.SetOpt(nil)
-
- // TODO(mdempsky): Make stricter. We only need to exempt
- // reflect.Value.Pointer and reflect.Value.UnsafeAddr.
- switch n.Left.Op {
- case OCALLFUNC, OCALLMETH, OCALLINTER:
- return n
- }
-
- if n.Left.Op == ODOTPTR && isReflectHeaderDataField(n.Left) {
- return n
- }
-
- // Find original unsafe.Pointer operands involved in this
- // arithmetic expression.
- //
- // "It is valid both to add and to subtract offsets from a
- // pointer in this way. It is also valid to use &^ to round
- // pointers, usually for alignment."
- var originals []*Node
- var walk func(n *Node)
- walk = func(n *Node) {
- switch n.Op {
- case OADD:
- walk(n.Left)
- walk(n.Right)
- case OSUB, OANDNOT:
- walk(n.Left)
- case OCONVNOP:
- if n.Left.Type.IsUnsafePtr() {
- n.Left = cheapexpr(n.Left, init)
- originals = append(originals, convnop(n.Left, types.Types[TUNSAFEPTR]))
- }
- }
- }
- walk(n.Left)
-
- n = cheapexpr(n, init)
-
- slice := mkdotargslice(types.NewSlice(types.Types[TUNSAFEPTR]), originals)
- slice.Esc = EscNone
-
- init.Append(mkcall("checkptrArithmetic", nil, init, convnop(n, types.Types[TUNSAFEPTR]), slice))
- // TODO(khr): Mark backing store of slice as dead. This will allow us to reuse
- // the backing store for multiple calls to checkptrArithmetic.
-
- return n
-}
-
-// checkPtr reports whether pointer checking should be enabled for
-// function fn at a given level. See debugHelpFooter for defined
-// levels.
-func checkPtr(fn *Node, level int) bool {
- return Debug_checkptr >= level && fn.Func.Pragma&NoCheckPtr == 0
-}
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go
new file mode 100644
index 0000000000000..9f9bb87dd5397
--- /dev/null
+++ b/src/cmd/compile/internal/inline/inl.go
@@ -0,0 +1,1420 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// The inlining facility makes 2 passes: first caninl determines which
+// functions are suitable for inlining, and for those that are it
+// saves a copy of the body. Then InlineCalls walks each function body to
+// expand calls to inlinable functions.
+//
+// The Debug.l flag controls the aggressiveness. Note that main() swaps level 0 and 1,
+// making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and
+// are not supported.
+// 0: disabled
+// 1: 80-nodes leaf functions, oneliners, panic, lazy typechecking (default)
+// 2: (unassigned)
+// 3: (unassigned)
+// 4: allow non-leaf functions
+//
+// At some point this may get another default and become switch-offable with -N.
+//
+// The -d typcheckinl flag enables early typechecking of all imported bodies,
+// which is useful to flush out bugs.
+//
+// The Debug.m flag enables diagnostic output. a single -m is useful for verifying
+// which calls get inlined or not, more is for debugging, and may go away at any point.
+
+package inline
+
+import (
+ "fmt"
+ "go/constant"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/logopt"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+// Inlining budget parameters, gathered in one place
+const (
+ inlineMaxBudget = 80
+ inlineExtraAppendCost = 0
+ // default is to inline if there's at most one call. -l=4 overrides this by using 1 instead.
+ inlineExtraCallCost = 57 // 57 was benchmarked to provided most benefit with no bad surprises; see https://github.com/golang/go/issues/19348#issuecomment-439370742
+ inlineExtraPanicCost = 1 // do not penalize inlining panics.
+ inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
+
+ inlineBigFunctionNodes = 5000 // Functions with this many nodes are considered "big".
+ inlineBigFunctionMaxCost = 20 // Max cost of inlinee when inlining into a "big" function.
+)
+
+func InlinePackage() {
+ // Find functions that can be inlined and clone them before walk expands them.
+ ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) {
+ numfns := numNonClosures(list)
+ for _, n := range list {
+ if !recursive || numfns > 1 {
+ // We allow inlining if there is no
+ // recursion, or the recursion cycle is
+ // across more than one function.
+ CanInline(n)
+ } else {
+ if base.Flag.LowerM > 1 {
+ fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
+ }
+ }
+ InlineCalls(n)
+ }
+ })
+}
+
+// CanInline determines whether fn is inlineable.
+// If so, CanInline saves fn->nbody in fn->inl and substitutes it with a copy.
+// fn and ->nbody will already have been typechecked.
+func CanInline(fn *ir.Func) {
+ if fn.Nname == nil {
+ base.Fatalf("CanInline no nname %+v", fn)
+ }
+
+ var reason string // reason, if any, that the function was not inlined
+ if base.Flag.LowerM > 1 || logopt.Enabled() {
+ defer func() {
+ if reason != "" {
+ if base.Flag.LowerM > 1 {
+ fmt.Printf("%v: cannot inline %v: %s\n", ir.Line(fn), fn.Nname, reason)
+ }
+ if logopt.Enabled() {
+ logopt.LogOpt(fn.Pos(), "cannotInlineFunction", "inline", ir.FuncName(fn), reason)
+ }
+ }
+ }()
+ }
+
+ // If marked "go:noinline", don't inline
+ if fn.Pragma&ir.Noinline != 0 {
+ reason = "marked go:noinline"
+ return
+ }
+
+ // If marked "go:norace" and -race compilation, don't inline.
+ if base.Flag.Race && fn.Pragma&ir.Norace != 0 {
+ reason = "marked go:norace with -race compilation"
+ return
+ }
+
+ // If marked "go:nocheckptr" and -d checkptr compilation, don't inline.
+ if base.Debug.Checkptr != 0 && fn.Pragma&ir.NoCheckPtr != 0 {
+ reason = "marked go:nocheckptr"
+ return
+ }
+
+ // If marked "go:cgo_unsafe_args", don't inline, since the
+ // function makes assumptions about its argument frame layout.
+ if fn.Pragma&ir.CgoUnsafeArgs != 0 {
+ reason = "marked go:cgo_unsafe_args"
+ return
+ }
+
+ // If marked as "go:uintptrescapes", don't inline, since the
+ // escape information is lost during inlining.
+ if fn.Pragma&ir.UintptrEscapes != 0 {
+ reason = "marked as having an escaping uintptr argument"
+ return
+ }
+
+ // The nowritebarrierrec checker currently works at function
+ // granularity, so inlining yeswritebarrierrec functions can
+ // confuse it (#22342). As a workaround, disallow inlining
+ // them for now.
+ if fn.Pragma&ir.Yeswritebarrierrec != 0 {
+ reason = "marked go:yeswritebarrierrec"
+ return
+ }
+
+ // If fn has no body (is defined outside of Go), cannot inline it.
+ if len(fn.Body) == 0 {
+ reason = "no function body"
+ return
+ }
+
+ if fn.Typecheck() == 0 {
+ base.Fatalf("CanInline on non-typechecked function %v", fn)
+ }
+
+ n := fn.Nname
+ if n.Func.InlinabilityChecked() {
+ return
+ }
+ defer n.Func.SetInlinabilityChecked(true)
+
+ cc := int32(inlineExtraCallCost)
+ if base.Flag.LowerL == 4 {
+ cc = 1 // this appears to yield better performance than 0.
+ }
+
+ // At this point in the game the function we're looking at may
+ // have "stale" autos, vars that still appear in the Dcl list, but
+ // which no longer have any uses in the function body (due to
+ // elimination by deadcode). We'd like to exclude these dead vars
+ // when creating the "Inline.Dcl" field below; to accomplish this,
+ // the hairyVisitor below builds up a map of used/referenced
+ // locals, and we use this map to produce a pruned Inline.Dcl
+ // list. See issue 25249 for more context.
+
+ visitor := hairyVisitor{
+ budget: inlineMaxBudget,
+ extraCallCost: cc,
+ }
+ if visitor.tooHairy(fn) {
+ reason = visitor.reason
+ return
+ }
+
+ n.Func.Inl = &ir.Inline{
+ Cost: inlineMaxBudget - visitor.budget,
+ Dcl: pruneUnusedAutos(n.Defn.(*ir.Func).Dcl, &visitor),
+ Body: inlcopylist(fn.Body),
+ }
+
+ if base.Flag.LowerM > 1 {
+ fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, inlineMaxBudget-visitor.budget, fn.Type(), ir.Nodes(n.Func.Inl.Body))
+ } else if base.Flag.LowerM != 0 {
+ fmt.Printf("%v: can inline %v\n", ir.Line(fn), n)
+ }
+ if logopt.Enabled() {
+ logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", inlineMaxBudget-visitor.budget))
+ }
+}
+
+// Inline_Flood marks n's inline body for export and recursively ensures
+// all called functions are marked too.
+func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) {
+ if n == nil {
+ return
+ }
+ if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
+ base.Fatalf("Inline_Flood: unexpected %v, %v, %v", n, n.Op(), n.Class)
+ }
+ fn := n.Func
+ if fn == nil {
+ base.Fatalf("Inline_Flood: missing Func on %v", n)
+ }
+ if fn.Inl == nil {
+ return
+ }
+
+ if fn.ExportInline() {
+ return
+ }
+ fn.SetExportInline(true)
+
+ typecheck.ImportedBody(fn)
+
+ var doFlood func(n ir.Node)
+ doFlood = func(n ir.Node) {
+ switch n.Op() {
+ case ir.OMETHEXPR, ir.ODOTMETH:
+ Inline_Flood(ir.MethodExprName(n), exportsym)
+
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ switch n.Class {
+ case ir.PFUNC:
+ Inline_Flood(n, exportsym)
+ exportsym(n)
+ case ir.PEXTERN:
+ exportsym(n)
+ }
+
+ case ir.OCALLPART:
+ // Okay, because we don't yet inline indirect
+ // calls to method values.
+ case ir.OCLOSURE:
+ // VisitList doesn't visit closure bodies, so force a
+ // recursive call to VisitList on the body of the closure.
+ ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood)
+ }
+ }
+
+ // Recursively identify all referenced functions for
+ // reexport. We want to include even non-called functions,
+ // because after inlining they might be callable.
+ ir.VisitList(ir.Nodes(fn.Inl.Body), doFlood)
+}
+
+// hairyVisitor visits a function body to determine its inlining
+// hairiness and whether or not it can be inlined.
+type hairyVisitor struct {
+ budget int32
+ reason string
+ extraCallCost int32
+ usedLocals ir.NameSet
+ do func(ir.Node) bool
+}
+
+func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
+ v.do = v.doNode // cache closure
+ if ir.DoChildren(fn, v.do) {
+ return true
+ }
+ if v.budget < 0 {
+ v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-v.budget, inlineMaxBudget)
+ return true
+ }
+ return false
+}
+
+func (v *hairyVisitor) doNode(n ir.Node) bool {
+ if n == nil {
+ return false
+ }
+ switch n.Op() {
+ // Call is okay if inlinable and we have the budget for the body.
+ case ir.OCALLFUNC:
+ n := n.(*ir.CallExpr)
+ // Functions that call runtime.getcaller{pc,sp} can not be inlined
+ // because getcaller{pc,sp} expect a pointer to the caller's first argument.
+ //
+ // runtime.throw is a "cheap call" like panic in normal code.
+ if n.X.Op() == ir.ONAME {
+ name := n.X.(*ir.Name)
+ if name.Class == ir.PFUNC && types.IsRuntimePkg(name.Sym().Pkg) {
+ fn := name.Sym().Name
+ if fn == "getcallerpc" || fn == "getcallersp" {
+ v.reason = "call to " + fn
+ return true
+ }
+ if fn == "throw" {
+ v.budget -= inlineExtraThrowCost
+ break
+ }
+ }
+ }
+
+ if ir.IsIntrinsicCall(n) {
+ // Treat like any other node.
+ break
+ }
+
+ if fn := inlCallee(n.X); fn != nil && fn.Inl != nil {
+ v.budget -= fn.Inl.Cost
+ break
+ }
+
+ // Call cost for non-leaf inlining.
+ v.budget -= v.extraCallCost
+
+ // Call is okay if inlinable and we have the budget for the body.
+ case ir.OCALLMETH:
+ n := n.(*ir.CallExpr)
+ t := n.X.Type()
+ if t == nil {
+ base.Fatalf("no function type for [%p] %+v\n", n.X, n.X)
+ }
+ fn := ir.MethodExprName(n.X).Func
+ if types.IsRuntimePkg(fn.Sym().Pkg) && fn.Sym().Name == "heapBits.nextArena" {
+ // Special case: explicitly allow
+ // mid-stack inlining of
+ // runtime.heapBits.next even though
+ // it calls slow-path
+ // runtime.heapBits.nextArena.
+ break
+ }
+ if fn.Inl != nil {
+ v.budget -= fn.Inl.Cost
+ break
+ }
+ // Call cost for non-leaf inlining.
+ v.budget -= v.extraCallCost
+
+ // Things that are too hairy, irrespective of the budget
+ case ir.OCALL, ir.OCALLINTER:
+ // Call cost for non-leaf inlining.
+ v.budget -= v.extraCallCost
+
+ case ir.OPANIC:
+ n := n.(*ir.UnaryExpr)
+ if n.X.Op() == ir.OCONVIFACE && n.X.(*ir.ConvExpr).Implicit() {
+ // Hack to keep reflect.flag.mustBe inlinable for TestIntendedInlining.
+ // Before CL 284412, these conversions were introduced later in the
+ // compiler, so they didn't count against inlining budget.
+ v.budget++
+ }
+ v.budget -= inlineExtraPanicCost
+
+ case ir.ORECOVER:
+ // recover matches the argument frame pointer to find
+ // the right panic value, so it needs an argument frame.
+ v.reason = "call to recover"
+ return true
+
+ case ir.OCLOSURE:
+ // TODO(danscales) - fix some bugs when budget is lowered below 15
+ // Maybe make budget proportional to number of closure variables, e.g.:
+ //v.budget -= int32(len(n.(*ir.ClosureExpr).Func.ClosureVars) * 3)
+ v.budget -= 15
+ // Scan body of closure (which DoChildren doesn't automatically
+ // do) to check for disallowed ops in the body and include the
+ // body in the budget.
+ if doList(n.(*ir.ClosureExpr).Func.Body, v.do) {
+ return true
+ }
+
+ case ir.ORANGE,
+ ir.OSELECT,
+ ir.OGO,
+ ir.ODEFER,
+ ir.ODCLTYPE, // can't print yet
+ ir.OTAILCALL:
+ v.reason = "unhandled op " + n.Op().String()
+ return true
+
+ case ir.OAPPEND:
+ v.budget -= inlineExtraAppendCost
+
+ case ir.ODCLCONST, ir.OFALL:
+ // These nodes don't produce code; omit from inlining budget.
+ return false
+
+ case ir.OFOR, ir.OFORUNTIL:
+ n := n.(*ir.ForStmt)
+ if n.Label != nil {
+ v.reason = "labeled control"
+ return true
+ }
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+ if n.Label != nil {
+ v.reason = "labeled control"
+ return true
+ }
+ // case ir.ORANGE, ir.OSELECT in "unhandled" above
+
+ case ir.OBREAK, ir.OCONTINUE:
+ n := n.(*ir.BranchStmt)
+ if n.Label != nil {
+ // Should have short-circuited due to labeled control error above.
+ base.Fatalf("unexpected labeled break/continue: %v", n)
+ }
+
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ if ir.IsConst(n.Cond, constant.Bool) {
+ // This if and the condition cost nothing.
+ // TODO(rsc): It seems strange that we visit the dead branch.
+ return doList(n.Init(), v.do) ||
+ doList(n.Body, v.do) ||
+ doList(n.Else, v.do)
+ }
+
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ if n.Class == ir.PAUTO {
+ v.usedLocals.Add(n)
+ }
+
+ case ir.OBLOCK:
+ // The only OBLOCK we should see at this point is an empty one.
+ // In any event, let the visitList(n.List()) below take care of the statements,
+ // and don't charge for the OBLOCK itself. The ++ undoes the -- below.
+ v.budget++
+
+ case ir.OCALLPART, ir.OSLICELIT:
+ v.budget-- // Hack for toolstash -cmp.
+
+ case ir.OMETHEXPR:
+ v.budget++ // Hack for toolstash -cmp.
+ }
+
+ v.budget--
+
+ // When debugging, don't stop early, to get full cost of inlining this function
+ if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
+ v.reason = "too expensive"
+ return true
+ }
+
+ return ir.DoChildren(n, v.do)
+}
+
+func isBigFunc(fn *ir.Func) bool {
+ budget := inlineBigFunctionNodes
+ return ir.Any(fn, func(n ir.Node) bool {
+ budget--
+ return budget <= 0
+ })
+}
+
+// inlcopylist (together with inlcopy) recursively copies a list of nodes, except
+// that it keeps the same ONAME, OTYPE, and OLITERAL nodes. It is used for copying
+// the body and dcls of an inlineable function.
+func inlcopylist(ll []ir.Node) []ir.Node {
+ s := make([]ir.Node, len(ll))
+ for i, n := range ll {
+ s[i] = inlcopy(n)
+ }
+ return s
+}
+
+// inlcopy is like DeepCopy(), but does extra work to copy closures.
+func inlcopy(n ir.Node) ir.Node {
+ var edit func(ir.Node) ir.Node
+ edit = func(x ir.Node) ir.Node {
+ switch x.Op() {
+ case ir.ONAME, ir.OTYPE, ir.OLITERAL, ir.ONIL:
+ return x
+ }
+ m := ir.Copy(x)
+ ir.EditChildren(m, edit)
+ if x.Op() == ir.OCLOSURE {
+ x := x.(*ir.ClosureExpr)
+ // Need to save/duplicate x.Func.Nname,
+ // x.Func.Nname.Ntype, x.Func.Dcl, x.Func.ClosureVars, and
+ // x.Func.Body for iexport and local inlining.
+ oldfn := x.Func
+ newfn := ir.NewFunc(oldfn.Pos())
+ if oldfn.ClosureCalled() {
+ newfn.SetClosureCalled(true)
+ }
+ m.(*ir.ClosureExpr).Func = newfn
+ newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym())
+ // XXX OK to share fn.Type() ??
+ newfn.Nname.SetType(oldfn.Nname.Type())
+ newfn.Nname.Ntype = inlcopy(oldfn.Nname.Ntype).(ir.Ntype)
+ newfn.Body = inlcopylist(oldfn.Body)
+ // Make shallow copy of the Dcl and ClosureVar slices
+ newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...)
+ newfn.ClosureVars = append([]*ir.Name(nil), oldfn.ClosureVars...)
+ }
+ return m
+ }
+ return edit(n)
+}
+
+// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
+// calls made to inlineable functions. This is the external entry point.
+func InlineCalls(fn *ir.Func) {
+ savefn := ir.CurFunc
+ ir.CurFunc = fn
+ maxCost := int32(inlineMaxBudget)
+ if isBigFunc(fn) {
+ maxCost = inlineBigFunctionMaxCost
+ }
+ // Map to keep track of functions that have been inlined at a particular
+ // call site, in order to stop inlining when we reach the beginning of a
+ // recursion cycle again. We don't inline immediately recursive functions,
+ // but allow inlining if there is a recursion cycle of many functions.
+ // Most likely, the inlining will stop before we even hit the beginning of
+ // the cycle again, but the map catches the unusual case.
+ inlMap := make(map[*ir.Func]bool)
+ var edit func(ir.Node) ir.Node
+ edit = func(n ir.Node) ir.Node {
+ return inlnode(n, maxCost, inlMap, edit)
+ }
+ ir.EditChildren(fn, edit)
+ ir.CurFunc = savefn
+}
+
+// Turn an OINLCALL into a statement.
+func inlconv2stmt(inlcall *ir.InlinedCallExpr) ir.Node {
+ n := ir.NewBlockStmt(inlcall.Pos(), nil)
+ n.List = inlcall.Init()
+ n.List.Append(inlcall.Body.Take()...)
+ return n
+}
+
+// Turn an OINLCALL into a single valued expression.
+// The result of inlconv2expr MUST be assigned back to n, e.g.
+// n.Left = inlconv2expr(n.Left)
+func inlconv2expr(n *ir.InlinedCallExpr) ir.Node {
+ r := n.ReturnVars[0]
+ return ir.InitExpr(append(n.Init(), n.Body...), r)
+}
+
+// Turn the rlist (with the return values) of the OINLCALL in
+// n into an expression list lumping the ninit and body
+// containing the inlined statements on the first list element so
+// order will be preserved. Used in return, oas2func and call
+// statements.
+func inlconv2list(n *ir.InlinedCallExpr) []ir.Node {
+ if n.Op() != ir.OINLCALL || len(n.ReturnVars) == 0 {
+ base.Fatalf("inlconv2list %+v\n", n)
+ }
+
+ s := n.ReturnVars
+ s[0] = ir.InitExpr(append(n.Init(), n.Body...), s[0])
+ return s
+}
+
+// inlnode recurses over the tree to find inlineable calls, which will
+// be turned into OINLCALLs by mkinlcall. When the recursion comes
+// back up will examine left, right, list, rlist, ninit, ntest, nincr,
+// nbody and nelse and use one of the 4 inlconv/glue functions above
+// to turn the OINLCALL into an expression, a statement, or patch it
+// in to this nodes list or rlist as appropriate.
+// NOTE it makes no sense to pass the glue functions down the
+// recursion to the level where the OINLCALL gets created because they
+// have to edit /this/ n, so you'd have to push that one down as well,
+// but then you may as well do it here. so this is cleaner and
+// shorter and less complicated.
+// The result of inlnode MUST be assigned back to n, e.g.
+// n.Left = inlnode(n.Left)
+func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
+ if n == nil {
+ return n
+ }
+
+ switch n.Op() {
+ case ir.ODEFER, ir.OGO:
+ n := n.(*ir.GoDeferStmt)
+ switch call := n.Call; call.Op() {
+ case ir.OCALLFUNC, ir.OCALLMETH:
+ call := call.(*ir.CallExpr)
+ call.NoInline = true
+ }
+
+ // TODO do them here (or earlier),
+ // so escape analysis can avoid more heapmoves.
+ case ir.OCLOSURE:
+ return n
+ case ir.OCALLMETH:
+ // Prevent inlining some reflect.Value methods when using checkptr,
+ // even when package reflect was compiled without it (#35073).
+ n := n.(*ir.CallExpr)
+ if s := ir.MethodExprName(n.X).Sym(); base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
+ return n
+ }
+ }
+
+ lno := ir.SetPos(n)
+
+ ir.EditChildren(n, edit)
+
+ if as := n; as.Op() == ir.OAS2FUNC {
+ as := as.(*ir.AssignListStmt)
+ if as.Rhs[0].Op() == ir.OINLCALL {
+ as.Rhs = inlconv2list(as.Rhs[0].(*ir.InlinedCallExpr))
+ as.SetOp(ir.OAS2)
+ as.SetTypecheck(0)
+ n = typecheck.Stmt(as)
+ }
+ }
+
+ // with all the branches out of the way, it is now time to
+ // transmogrify this node itself unless inhibited by the
+ // switch at the top of this function.
+ switch n.Op() {
+ case ir.OCALLFUNC, ir.OCALLMETH:
+ n := n.(*ir.CallExpr)
+ if n.NoInline {
+ return n
+ }
+ }
+
+ var call *ir.CallExpr
+ switch n.Op() {
+ case ir.OCALLFUNC:
+ call = n.(*ir.CallExpr)
+ if base.Flag.LowerM > 3 {
+ fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X)
+ }
+ if ir.IsIntrinsicCall(call) {
+ break
+ }
+ if fn := inlCallee(call.X); fn != nil && fn.Inl != nil {
+ n = mkinlcall(call, fn, maxCost, inlMap, edit)
+ }
+
+ case ir.OCALLMETH:
+ call = n.(*ir.CallExpr)
+ if base.Flag.LowerM > 3 {
+ fmt.Printf("%v:call to meth %v\n", ir.Line(n), call.X.(*ir.SelectorExpr).Sel)
+ }
+
+ // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
+ if call.X.Type() == nil {
+ base.Fatalf("no function type for [%p] %+v\n", call.X, call.X)
+ }
+
+ n = mkinlcall(call, ir.MethodExprName(call.X).Func, maxCost, inlMap, edit)
+ }
+
+ base.Pos = lno
+
+ if n.Op() == ir.OINLCALL {
+ ic := n.(*ir.InlinedCallExpr)
+ switch call.Use {
+ default:
+ ir.Dump("call", call)
+ base.Fatalf("call missing use")
+ case ir.CallUseExpr:
+ n = inlconv2expr(ic)
+ case ir.CallUseStmt:
+ n = inlconv2stmt(ic)
+ case ir.CallUseList:
+ // leave for caller to convert
+ }
+ }
+
+ return n
+}
+
+// inlCallee takes a function-typed expression and returns the underlying function ONAME
+// that it refers to if statically known. Otherwise, it returns nil.
+func inlCallee(fn ir.Node) *ir.Func {
+ fn = ir.StaticValue(fn)
+ switch fn.Op() {
+ case ir.OMETHEXPR:
+ fn := fn.(*ir.SelectorExpr)
+ n := ir.MethodExprName(fn)
+ // Check that receiver type matches fn.X.
+ // TODO(mdempsky): Handle implicit dereference
+ // of pointer receiver argument?
+ if n == nil || !types.Identical(n.Type().Recv().Type, fn.X.Type()) {
+ return nil
+ }
+ return n.Func
+ case ir.ONAME:
+ fn := fn.(*ir.Name)
+ if fn.Class == ir.PFUNC {
+ return fn.Func
+ }
+ case ir.OCLOSURE:
+ fn := fn.(*ir.ClosureExpr)
+ c := fn.Func
+ CanInline(c)
+ return c
+ }
+ return nil
+}
+
+func inlParam(t *types.Field, as ir.InitNode, inlvars map[*ir.Name]*ir.Name) ir.Node {
+ if t.Nname == nil {
+ return ir.BlankNode
+ }
+ n := t.Nname.(*ir.Name)
+ if ir.IsBlank(n) {
+ return ir.BlankNode
+ }
+ inlvar := inlvars[n]
+ if inlvar == nil {
+ base.Fatalf("missing inlvar for %v", n)
+ }
+ as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, inlvar))
+ inlvar.Name().Defn = as
+ return inlvar
+}
+
+var inlgen int
+
+// SSADumpInline gives the SSA back end a chance to dump the function
+// when producing output for debugging the compiler itself.
+var SSADumpInline = func(*ir.Func) {}
+
+// If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a
+// function with an inlinable body, return an OINLCALL node that can replace n.
+// The returned node's Ninit has the parameter assignments, the Nbody is the
+// inlined function body, and (List, Rlist) contain the (input, output)
+// parameters.
+// The result of mkinlcall MUST be assigned back to n, e.g.
+// n.Left = mkinlcall(n.Left, fn, isddd)
+func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
+ if fn.Inl == nil {
+ if logopt.Enabled() {
+ logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
+ fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(fn)))
+ }
+ return n
+ }
+ if fn.Inl.Cost > maxCost {
+ // The inlined function body is too big. Typically we use this check to restrict
+ // inlining into very big functions. See issue 26546 and 17566.
+ if logopt.Enabled() {
+ logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
+ fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost))
+ }
+ return n
+ }
+
+ if fn == ir.CurFunc {
+ // Can't recursively inline a function into itself.
+ if logopt.Enabled() {
+ logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(ir.CurFunc)))
+ }
+ return n
+ }
+
+ if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) {
+ // Runtime package must not be instrumented.
+ // Instrument skips runtime package. However, some runtime code can be
+ // inlined into other packages and instrumented there. To avoid this,
+ // we disable inlining of runtime functions when instrumenting.
+ // The example that we observed is inlining of LockOSThread,
+ // which lead to false race reports on m contents.
+ return n
+ }
+
+ if inlMap[fn] {
+ if base.Flag.LowerM > 1 {
+ fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), fn, ir.FuncName(ir.CurFunc))
+ }
+ return n
+ }
+ inlMap[fn] = true
+ defer func() {
+ inlMap[fn] = false
+ }()
+ if base.Debug.TypecheckInl == 0 {
+ typecheck.ImportedBody(fn)
+ }
+
+ // We have a function node, and it has an inlineable body.
+ if base.Flag.LowerM > 1 {
+ fmt.Printf("%v: inlining call to %v %v { %v }\n", ir.Line(n), fn.Sym(), fn.Type(), ir.Nodes(fn.Inl.Body))
+ } else if base.Flag.LowerM != 0 {
+ fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
+ }
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
+ }
+
+ SSADumpInline(fn)
+
+ ninit := n.Init()
+
+ // For normal function calls, the function callee expression
+ // may contain side effects (e.g., added by addinit during
+ // inlconv2expr or inlconv2list). Make sure to preserve these,
+ // if necessary (#42703).
+ if n.Op() == ir.OCALLFUNC {
+ callee := n.X
+ for callee.Op() == ir.OCONVNOP {
+ conv := callee.(*ir.ConvExpr)
+ ninit.Append(ir.TakeInit(conv)...)
+ callee = conv.X
+ }
+ if callee.Op() != ir.ONAME && callee.Op() != ir.OCLOSURE && callee.Op() != ir.OMETHEXPR {
+ base.Fatalf("unexpected callee expression: %v", callee)
+ }
+ }
+
+ // Make temp names to use instead of the originals.
+ inlvars := make(map[*ir.Name]*ir.Name)
+
+ // record formals/locals for later post-processing
+ var inlfvars []*ir.Name
+
+ for _, ln := range fn.Inl.Dcl {
+ if ln.Op() != ir.ONAME {
+ continue
+ }
+ if ln.Class == ir.PPARAMOUT { // return values handled below.
+ continue
+ }
+ inlf := typecheck.Expr(inlvar(ln)).(*ir.Name)
+ inlvars[ln] = inlf
+ if base.Flag.GenDwarfInl > 0 {
+ if ln.Class == ir.PPARAM {
+ inlf.Name().SetInlFormal(true)
+ } else {
+ inlf.Name().SetInlLocal(true)
+ }
+ inlf.SetPos(ln.Pos())
+ inlfvars = append(inlfvars, inlf)
+ }
+ }
+
+ nreturns := 0
+ ir.VisitList(ir.Nodes(fn.Inl.Body), func(n ir.Node) {
+ if n != nil && n.Op() == ir.ORETURN {
+ nreturns++
+ }
+ })
+
+ // We can delay declaring+initializing result parameters if:
+ // (1) there's only one "return" statement in the inlined
+ // function, and (2) the result parameters aren't named.
+ delayretvars := nreturns == 1
+
+ // temporaries for return values.
+ var retvars []ir.Node
+ for i, t := range fn.Type().Results().Fields().Slice() {
+ var m *ir.Name
+ if nn := t.Nname; nn != nil && !ir.IsBlank(nn.(*ir.Name)) && !strings.HasPrefix(nn.Sym().Name, "~r") {
+ n := nn.(*ir.Name)
+ m = inlvar(n)
+ m = typecheck.Expr(m).(*ir.Name)
+ inlvars[n] = m
+ delayretvars = false // found a named result parameter
+ } else {
+ // anonymous return values, synthesize names for use in assignment that replaces return
+ m = retvar(t, i)
+ }
+
+ if base.Flag.GenDwarfInl > 0 {
+ // Don't update the src.Pos on a return variable if it
+ // was manufactured by the inliner (e.g. "~R2"); such vars
+ // were not part of the original callee.
+ if !strings.HasPrefix(m.Sym().Name, "~R") {
+ m.Name().SetInlFormal(true)
+ m.SetPos(t.Pos)
+ inlfvars = append(inlfvars, m)
+ }
+ }
+
+ retvars = append(retvars, m)
+ }
+
+ // Assign arguments to the parameters' temp names.
+ as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+ as.Def = true
+ if n.Op() == ir.OCALLMETH {
+ sel := n.X.(*ir.SelectorExpr)
+ if sel.X == nil {
+ base.Fatalf("method call without receiver: %+v", n)
+ }
+ as.Rhs.Append(sel.X)
+ }
+ as.Rhs.Append(n.Args...)
+
+ // For non-dotted calls to variadic functions, we assign the
+ // variadic parameter's temp name separately.
+ var vas *ir.AssignStmt
+
+ if recv := fn.Type().Recv(); recv != nil {
+ as.Lhs.Append(inlParam(recv, as, inlvars))
+ }
+ for _, param := range fn.Type().Params().Fields().Slice() {
+ // For ordinary parameters or variadic parameters in
+ // dotted calls, just add the variable to the
+ // assignment list, and we're done.
+ if !param.IsDDD() || n.IsDDD {
+ as.Lhs.Append(inlParam(param, as, inlvars))
+ continue
+ }
+
+ // Otherwise, we need to collect the remaining values
+ // to pass as a slice.
+
+ x := len(as.Lhs)
+ for len(as.Lhs) < len(as.Rhs) {
+ as.Lhs.Append(argvar(param.Type, len(as.Lhs)))
+ }
+ varargs := as.Lhs[x:]
+
+ vas = ir.NewAssignStmt(base.Pos, nil, nil)
+ vas.X = inlParam(param, vas, inlvars)
+ if len(varargs) == 0 {
+ vas.Y = typecheck.NodNil()
+ vas.Y.SetType(param.Type)
+ } else {
+ lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(param.Type), nil)
+ lit.List = varargs
+ vas.Y = lit
+ }
+ }
+
+ if len(as.Rhs) != 0 {
+ ninit.Append(typecheck.Stmt(as))
+ }
+
+ if vas != nil {
+ ninit.Append(typecheck.Stmt(vas))
+ }
+
+ if !delayretvars {
+ // Zero the return parameters.
+ for _, n := range retvars {
+ ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
+ ras := ir.NewAssignStmt(base.Pos, n, nil)
+ ninit.Append(typecheck.Stmt(ras))
+ }
+ }
+
+ retlabel := typecheck.AutoLabel(".i")
+
+ inlgen++
+
+ parent := -1
+ if b := base.Ctxt.PosTable.Pos(n.Pos()).Base(); b != nil {
+ parent = b.InliningIndex()
+ }
+
+ sym := fn.Linksym()
+ newIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym)
+
+ // Add an inline mark just before the inlined body.
+ // This mark is inline in the code so that it's a reasonable spot
+ // to put a breakpoint. Not sure if that's really necessary or not
+ // (in which case it could go at the end of the function instead).
+ // Note issue 28603.
+ inlMark := ir.NewInlineMarkStmt(base.Pos, types.BADWIDTH)
+ inlMark.SetPos(n.Pos().WithIsStmt())
+ inlMark.Index = int64(newIndex)
+ ninit.Append(inlMark)
+
+ if base.Flag.GenDwarfInl > 0 {
+ if !sym.WasInlined() {
+ base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
+ sym.Set(obj.AttrWasInlined, true)
+ }
+ }
+
+ subst := inlsubst{
+ retlabel: retlabel,
+ retvars: retvars,
+ delayretvars: delayretvars,
+ inlvars: inlvars,
+ bases: make(map[*src.PosBase]*src.PosBase),
+ newInlIndex: newIndex,
+ fn: fn,
+ }
+ subst.edit = subst.node
+
+ body := subst.list(ir.Nodes(fn.Inl.Body))
+
+ lab := ir.NewLabelStmt(base.Pos, retlabel)
+ body = append(body, lab)
+
+ typecheck.Stmts(body)
+
+ if base.Flag.GenDwarfInl > 0 {
+ for _, v := range inlfvars {
+ v.SetPos(subst.updatedPos(v.Pos()))
+ }
+ }
+
+ //dumplist("ninit post", ninit);
+
+ call := ir.NewInlinedCallExpr(base.Pos, nil, nil)
+ *call.PtrInit() = ninit
+ call.Body = body
+ call.ReturnVars = retvars
+ call.SetType(n.Type())
+ call.SetTypecheck(1)
+
+ // transitive inlining
+ // might be nice to do this before exporting the body,
+ // but can't emit the body with inlining expanded.
+ // instead we emit the things that the body needs
+ // and each use must redo the inlining.
+ // luckily these are small.
+ ir.EditChildren(call, edit)
+
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v: After inlining %+v\n\n", ir.Line(call), call)
+ }
+
+ return call
+}
+
+// Every time we expand a function we generate a new set of tmpnames,
+// PAUTO's in the calling functions, and link them off of the
+// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
+func inlvar(var_ *ir.Name) *ir.Name {
+ if base.Flag.LowerM > 3 {
+ fmt.Printf("inlvar %+v\n", var_)
+ }
+
+ n := typecheck.NewName(var_.Sym())
+ n.SetType(var_.Type())
+ n.Class = ir.PAUTO
+ n.SetUsed(true)
+ n.Curfn = ir.CurFunc // the calling function, not the called one
+ n.SetAddrtaken(var_.Addrtaken())
+
+ ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
+ return n
+}
+
+// Synthesize a variable to store the inlined function's results in.
+func retvar(t *types.Field, i int) *ir.Name {
+ n := typecheck.NewName(typecheck.LookupNum("~R", i))
+ n.SetType(t.Type)
+ n.Class = ir.PAUTO
+ n.SetUsed(true)
+ n.Curfn = ir.CurFunc // the calling function, not the called one
+ ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
+ return n
+}
+
+// Synthesize a variable to store the inlined function's arguments
+// when they come from a multiple return call.
+func argvar(t *types.Type, i int) ir.Node {
+ n := typecheck.NewName(typecheck.LookupNum("~arg", i))
+ n.SetType(t.Elem())
+ n.Class = ir.PAUTO
+ n.SetUsed(true)
+ n.Curfn = ir.CurFunc // the calling function, not the called one
+ ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
+ return n
+}
+
+// The inlsubst type implements the actual inlining of a single
+// function call.
+type inlsubst struct {
+ // Target of the goto substituted in place of a return.
+ retlabel *types.Sym
+
+ // Temporary result variables.
+ retvars []ir.Node
+
+ // Whether result variables should be initialized at the
+ // "return" statement.
+ delayretvars bool
+
+ inlvars map[*ir.Name]*ir.Name
+
+ // bases maps from original PosBase to PosBase with an extra
+ // inlined call frame.
+ bases map[*src.PosBase]*src.PosBase
+
+ // newInlIndex is the index of the inlined call frame to
+ // insert for inlined nodes.
+ newInlIndex int
+
+ edit func(ir.Node) ir.Node // cached copy of subst.node method value closure
+
+ // If non-nil, we are inside a closure inside the inlined function, and
+ // newclofn is the Func of the new inlined closure.
+ newclofn *ir.Func
+
+ fn *ir.Func // For debug -- the func that is being inlined
+}
+
+// list inlines a list of nodes.
+func (subst *inlsubst) list(ll ir.Nodes) []ir.Node {
+ s := make([]ir.Node, 0, len(ll))
+ for _, n := range ll {
+ s = append(s, subst.node(n))
+ }
+ return s
+}
+
+// fields returns a list of the fields of a struct type representing receiver,
+// params, or results, after duplicating the field nodes and substituting the
+// Nname nodes inside the field nodes.
+func (subst *inlsubst) fields(oldt *types.Type) []*types.Field {
+ oldfields := oldt.FieldSlice()
+ newfields := make([]*types.Field, len(oldfields))
+ for i := range oldfields {
+ newfields[i] = oldfields[i].Copy()
+ if oldfields[i].Nname != nil {
+ newfields[i].Nname = subst.node(oldfields[i].Nname.(*ir.Name))
+ }
+ }
+ return newfields
+}
+
+// clovar creates a new ONAME node for a local variable or param of a closure
+// inside a function being inlined.
+func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
+ // TODO(danscales): want to get rid of this shallow copy, with code like the
+ // following, but it is hard to copy all the necessary flags in a maintainable way.
+ // m := ir.NewNameAt(n.Pos(), n.Sym())
+ // m.Class = n.Class
+ // m.SetType(n.Type())
+ // m.SetTypecheck(1)
+ //if n.IsClosureVar() {
+ // m.SetIsClosureVar(true)
+ //}
+ m := &ir.Name{}
+ *m = *n
+ m.Curfn = subst.newclofn
+ if n.Defn != nil && n.Defn.Op() == ir.ONAME {
+ if !n.IsClosureVar() {
+ base.FatalfAt(n.Pos(), "want closure variable, got: %+v", n)
+ }
+ if n.Sym().Pkg != types.LocalPkg {
+ // If the closure came from inlining a function from
+ // another package, must change package of captured
+ // variable to localpkg, so that the fields of the closure
+ // struct are local package and can be accessed even if
+ // name is not exported. If you disable this code, you can
+ // reproduce the problem by running 'go test
+ // go/internal/srcimporter'. TODO(mdempsky) - maybe change
+ // how we create closure structs?
+ m.SetSym(types.LocalPkg.Lookup(n.Sym().Name))
+ }
+ // Make sure any inlvar which is the Defn
+ // of an ONAME closure var is rewritten
+ // during inlining. Don't substitute
+ // if Defn node is outside inlined function.
+ if subst.inlvars[n.Defn.(*ir.Name)] != nil {
+ m.Defn = subst.node(n.Defn)
+ }
+ }
+ if n.Outer != nil {
+ // Either the outer variable is defined in function being inlined,
+ // and we will replace it with the substituted variable, or it is
+ // defined outside the function being inlined, and we should just
+ // skip the outer variable (the closure variable of the function
+ // being inlined).
+ s := subst.node(n.Outer).(*ir.Name)
+ if s == n.Outer {
+ s = n.Outer.Outer
+ }
+ m.Outer = s
+ }
+ return m
+}
+
+// closure does the necessary substitions for a ClosureExpr n and returns the new
+// closure node.
+func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
+ m := ir.Copy(n)
+ m.SetPos(subst.updatedPos(m.Pos()))
+ ir.EditChildren(m, subst.edit)
+
+ //fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc))
+
+ // The following is similar to funcLit
+ oldfn := n.Func
+ newfn := ir.NewFunc(oldfn.Pos())
+ // These three lines are not strictly necessary, but just to be clear
+ // that new function needs to redo typechecking and inlinability.
+ newfn.SetTypecheck(0)
+ newfn.SetInlinabilityChecked(false)
+ newfn.Inl = nil
+ newfn.SetIsHiddenClosure(true)
+ newfn.Nname = ir.NewNameAt(n.Pos(), ir.BlankNode.Sym())
+ newfn.Nname.Func = newfn
+ newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype)
+ newfn.Nname.Defn = newfn
+
+ m.(*ir.ClosureExpr).Func = newfn
+ newfn.OClosure = m.(*ir.ClosureExpr)
+
+ if subst.newclofn != nil {
+ //fmt.Printf("Inlining a closure with a nested closure\n")
+ }
+ prevxfunc := subst.newclofn
+
+ // Mark that we are now substituting within a closure (within the
+ // inlined function), and create new nodes for all the local
+ // vars/params inside this closure.
+ subst.newclofn = newfn
+ newfn.Dcl = nil
+ newfn.ClosureVars = nil
+ for _, oldv := range oldfn.Dcl {
+ newv := subst.clovar(oldv)
+ subst.inlvars[oldv] = newv
+ newfn.Dcl = append(newfn.Dcl, newv)
+ }
+ for _, oldv := range oldfn.ClosureVars {
+ newv := subst.clovar(oldv)
+ subst.inlvars[oldv] = newv
+ newfn.ClosureVars = append(newfn.ClosureVars, newv)
+ }
+
+ // Need to replace ONAME nodes in
+ // newfn.Type().FuncType().Receiver/Params/Results.FieldSlice().Nname
+ oldt := oldfn.Type()
+ newrecvs := subst.fields(oldt.Recvs())
+ var newrecv *types.Field
+ if len(newrecvs) > 0 {
+ newrecv = newrecvs[0]
+ }
+ newt := types.NewSignature(oldt.Pkg(), newrecv,
+ subst.fields(oldt.Params()), subst.fields(oldt.Results()))
+
+ newfn.Nname.SetType(newt)
+ newfn.Body = subst.list(oldfn.Body)
+
+ // Remove the nodes for the current closure from subst.inlvars
+ for _, oldv := range oldfn.Dcl {
+ delete(subst.inlvars, oldv)
+ }
+ for _, oldv := range oldfn.ClosureVars {
+ delete(subst.inlvars, oldv)
+ }
+ // Go back to previous closure func
+ subst.newclofn = prevxfunc
+
+ // Actually create the named function for the closure, now that
+ // the closure is inlined in a specific function.
+ m.SetTypecheck(0)
+ if oldfn.ClosureCalled() {
+ typecheck.Callee(m)
+ } else {
+ typecheck.Expr(m)
+ }
+ return m
+}
+
+// node recursively copies a node from the saved pristine body of the
+// inlined function, substituting references to input/output
+// parameters with ones to the tmpnames, and substituting returns with
+// assignments to the output.
+func (subst *inlsubst) node(n ir.Node) ir.Node {
+ if n == nil {
+ return nil
+ }
+
+ switch n.Op() {
+ case ir.ONAME:
+ n := n.(*ir.Name)
+
+ // Handle captured variables when inlining closures.
+ if n.IsClosureVar() && subst.newclofn == nil {
+ o := n.Outer
+
+ // Deal with case where sequence of closures are inlined.
+ // TODO(danscales) - write test case to see if we need to
+ // go up multiple levels.
+ if o.Curfn != ir.CurFunc {
+ o = o.Outer
+ }
+
+ // make sure the outer param matches the inlining location
+ if o == nil || o.Curfn != ir.CurFunc {
+ base.Fatalf("%v: unresolvable capture %v\n", ir.Line(n), n)
+ }
+
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("substituting captured name %+v -> %+v\n", n, o)
+ }
+ return o
+ }
+
+ if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
+ }
+ return inlvar
+ }
+
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("not substituting name %+v\n", n)
+ }
+ return n
+
+ case ir.OMETHEXPR:
+ n := n.(*ir.SelectorExpr)
+ return n
+
+ case ir.OLITERAL, ir.ONIL, ir.OTYPE:
+ // If n is a named constant or type, we can continue
+ // using it in the inline copy. Otherwise, make a copy
+ // so we can update the line number.
+ if n.Sym() != nil {
+ return n
+ }
+
+ case ir.ORETURN:
+ if subst.newclofn != nil {
+ // Don't do special substitutions if inside a closure
+ break
+ }
+ // Since we don't handle bodies with closures,
+ // this return is guaranteed to belong to the current inlined function.
+ n := n.(*ir.ReturnStmt)
+ init := subst.list(n.Init())
+ if len(subst.retvars) != 0 && len(n.Results) != 0 {
+ as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+
+ // Make a shallow copy of retvars.
+ // Otherwise OINLCALL.Rlist will be the same list,
+ // and later walk and typecheck may clobber it.
+ for _, n := range subst.retvars {
+ as.Lhs.Append(n)
+ }
+ as.Rhs = subst.list(n.Results)
+
+ if subst.delayretvars {
+ for _, n := range as.Lhs {
+ as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
+ n.Name().Defn = as
+ }
+ }
+
+ init = append(init, typecheck.Stmt(as))
+ }
+ init = append(init, ir.NewBranchStmt(base.Pos, ir.OGOTO, subst.retlabel))
+ typecheck.Stmts(init)
+ return ir.NewBlockStmt(base.Pos, init)
+
+ case ir.OGOTO:
+ n := n.(*ir.BranchStmt)
+ m := ir.Copy(n).(*ir.BranchStmt)
+ m.SetPos(subst.updatedPos(m.Pos()))
+ *m.PtrInit() = nil
+ p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
+ m.Label = typecheck.Lookup(p)
+ return m
+
+ case ir.OLABEL:
+ if subst.newclofn != nil {
+ // Don't do special substitutions if inside a closure
+ break
+ }
+ n := n.(*ir.LabelStmt)
+ m := ir.Copy(n).(*ir.LabelStmt)
+ m.SetPos(subst.updatedPos(m.Pos()))
+ *m.PtrInit() = nil
+ p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
+ m.Label = typecheck.Lookup(p)
+ return m
+
+ case ir.OCLOSURE:
+ return subst.closure(n.(*ir.ClosureExpr))
+
+ }
+
+ m := ir.Copy(n)
+ m.SetPos(subst.updatedPos(m.Pos()))
+ ir.EditChildren(m, subst.edit)
+ return m
+}
+
+func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
+ pos := base.Ctxt.PosTable.Pos(xpos)
+ oldbase := pos.Base() // can be nil
+ newbase := subst.bases[oldbase]
+ if newbase == nil {
+ newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
+ subst.bases[oldbase] = newbase
+ }
+ pos.SetBase(newbase)
+ return base.Ctxt.PosTable.XPos(pos)
+}
+
+func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
+ s := make([]*ir.Name, 0, len(ll))
+ for _, n := range ll {
+ if n.Class == ir.PAUTO {
+ if !vis.usedLocals.Has(n) {
+ continue
+ }
+ }
+ s = append(s, n)
+ }
+ return s
+}
+
+// numNonClosures returns the number of functions in list which are not closures.
+func numNonClosures(list []*ir.Func) int {
+ count := 0
+ for _, fn := range list {
+ if fn.OClosure == nil {
+ count++
+ }
+ }
+ return count
+}
+
+func doList(list []ir.Node, do func(ir.Node) bool) bool {
+ for _, x := range list {
+ if x != nil {
+ if do(x) {
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/gc/bitset.go b/src/cmd/compile/internal/ir/bitset.go
similarity index 79%
rename from src/cmd/compile/internal/gc/bitset.go
rename to src/cmd/compile/internal/ir/bitset.go
index ed5eea0a11be9..0c7bd542f60bb 100644
--- a/src/cmd/compile/internal/gc/bitset.go
+++ b/src/cmd/compile/internal/ir/bitset.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.
-package gc
+package ir
type bitset8 uint8
@@ -14,6 +14,18 @@ func (f *bitset8) set(mask uint8, b bool) {
}
}
+func (f bitset8) get2(shift uint8) uint8 {
+ return uint8(f>>shift) & 3
+}
+
+// set2 sets two bits in f using the bottom two bits of b.
+func (f *bitset8) set2(shift uint8, b uint8) {
+ // Clear old bits.
+ *(*uint8)(f) &^= 3 << shift
+ // Set new bits.
+ *(*uint8)(f) |= uint8(b&3) << shift
+}
+
type bitset16 uint16
func (f *bitset16) set(mask uint16, b bool) {
diff --git a/src/cmd/compile/internal/ir/cfg.go b/src/cmd/compile/internal/ir/cfg.go
new file mode 100644
index 0000000000000..d986ac3a1e974
--- /dev/null
+++ b/src/cmd/compile/internal/ir/cfg.go
@@ -0,0 +1,26 @@
+// Copyright 2009 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 ir
+
+var (
+ // maximum size variable which we will allocate on the stack.
+ // This limit is for explicit variable declarations like "var x T" or "x := ...".
+ // Note: the flag smallframes can update this value.
+ MaxStackVarSize = int64(10 * 1024 * 1024)
+
+ // maximum size of implicit variables that we will allocate on the stack.
+ // p := new(T) allocating T on the stack
+ // p := &T{} allocating T on the stack
+ // s := make([]T, n) allocating [n]T on the stack
+ // s := []byte("...") allocating [n]byte on the stack
+ // Note: the flag smallframes can update this value.
+ MaxImplicitStackVarSize = int64(64 * 1024)
+
+ // MaxSmallArraySize is the maximum size of an array which is considered small.
+ // Small arrays will be initialized directly with a sequence of constant stores.
+ // Large arrays will be initialized by copying from a static temp.
+ // 256 bytes was chosen to minimize generated code + statictmp size.
+ MaxSmallArraySize = int64(256)
+)
diff --git a/src/cmd/compile/internal/gc/class_string.go b/src/cmd/compile/internal/ir/class_string.go
similarity index 89%
rename from src/cmd/compile/internal/gc/class_string.go
rename to src/cmd/compile/internal/ir/class_string.go
index a4084a7535209..13b9bd4812cce 100644
--- a/src/cmd/compile/internal/gc/class_string.go
+++ b/src/cmd/compile/internal/ir/class_string.go
@@ -1,6 +1,6 @@
-// Code generated by "stringer -type=Class"; DO NOT EDIT.
+// Code generated by "stringer -type=Class name.go"; DO NOT EDIT.
-package gc
+package ir
import "strconv"
diff --git a/src/cmd/compile/internal/ir/const.go b/src/cmd/compile/internal/ir/const.go
new file mode 100644
index 0000000000000..eaa4d5b6b15ca
--- /dev/null
+++ b/src/cmd/compile/internal/ir/const.go
@@ -0,0 +1,99 @@
+// Copyright 2009 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 ir
+
+import (
+ "go/constant"
+ "math"
+ "math/big"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+)
+
+func NewBool(b bool) Node {
+ return NewLiteral(constant.MakeBool(b))
+}
+
+func NewInt(v int64) Node {
+ return NewLiteral(constant.MakeInt64(v))
+}
+
+func NewString(s string) Node {
+ return NewLiteral(constant.MakeString(s))
+}
+
+const (
+ // Maximum size in bits for big.Ints before signalling
+ // overflow and also mantissa precision for big.Floats.
+ ConstPrec = 512
+)
+
+func BigFloat(v constant.Value) *big.Float {
+ f := new(big.Float)
+ f.SetPrec(ConstPrec)
+ switch u := constant.Val(v).(type) {
+ case int64:
+ f.SetInt64(u)
+ case *big.Int:
+ f.SetInt(u)
+ case *big.Float:
+ f.Set(u)
+ case *big.Rat:
+ f.SetRat(u)
+ default:
+ base.Fatalf("unexpected: %v", u)
+ }
+ return f
+}
+
+// ConstOverflow reports whether constant value v is too large
+// to represent with type t.
+func ConstOverflow(v constant.Value, t *types.Type) bool {
+ switch {
+ case t.IsInteger():
+ bits := uint(8 * t.Size())
+ if t.IsUnsigned() {
+ x, ok := constant.Uint64Val(v)
+ return !ok || x>>bits != 0
+ }
+ x, ok := constant.Int64Val(v)
+ if x < 0 {
+ x = ^x
+ }
+ return !ok || x>>(bits-1) != 0
+ case t.IsFloat():
+ switch t.Size() {
+ case 4:
+ f, _ := constant.Float32Val(v)
+ return math.IsInf(float64(f), 0)
+ case 8:
+ f, _ := constant.Float64Val(v)
+ return math.IsInf(f, 0)
+ }
+ case t.IsComplex():
+ ft := types.FloatForComplex(t)
+ return ConstOverflow(constant.Real(v), ft) || ConstOverflow(constant.Imag(v), ft)
+ }
+ base.Fatalf("ConstOverflow: %v, %v", v, t)
+ panic("unreachable")
+}
+
+// IsConstNode reports whether n is a Go language constant (as opposed to a
+// compile-time constant).
+//
+// Expressions derived from nil, like string([]byte(nil)), while they
+// may be known at compile time, are not Go language constants.
+func IsConstNode(n Node) bool {
+ return n.Op() == OLITERAL
+}
+
+func IsSmallIntConst(n Node) bool {
+ if n.Op() == OLITERAL {
+ v, ok := constant.Int64Val(n.Val())
+ return ok && int64(int32(v)) == v
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/ir/copy.go b/src/cmd/compile/internal/ir/copy.go
new file mode 100644
index 0000000000000..7da9b24940ff9
--- /dev/null
+++ b/src/cmd/compile/internal/ir/copy.go
@@ -0,0 +1,102 @@
+// 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 ir
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/internal/src"
+)
+
+// A Node may implement the Orig and SetOrig method to
+// maintain a pointer to the "unrewritten" form of a Node.
+// If a Node does not implement OrigNode, it is its own Orig.
+//
+// Note that both SepCopy and Copy have definitions compatible
+// with a Node that does not implement OrigNode: such a Node
+// is its own Orig, and in that case, that's what both want to return
+// anyway (SepCopy unconditionally, and Copy only when the input
+// is its own Orig as well, but if the output does not implement
+// OrigNode, then neither does the input, making the condition true).
+type OrigNode interface {
+ Node
+ Orig() Node
+ SetOrig(Node)
+}
+
+// origNode may be embedded into a Node to make it implement OrigNode.
+type origNode struct {
+ orig Node `mknode:"-"`
+}
+
+func (n *origNode) Orig() Node { return n.orig }
+func (n *origNode) SetOrig(o Node) { n.orig = o }
+
+// Orig returns the “original” node for n.
+// If n implements OrigNode, Orig returns n.Orig().
+// Otherwise Orig returns n itself.
+func Orig(n Node) Node {
+ if n, ok := n.(OrigNode); ok {
+ o := n.Orig()
+ if o == nil {
+ Dump("Orig nil", n)
+ base.Fatalf("Orig returned nil")
+ }
+ return o
+ }
+ return n
+}
+
+// SepCopy returns a separate shallow copy of n,
+// breaking any Orig link to any other nodes.
+func SepCopy(n Node) Node {
+ n = n.copy()
+ if n, ok := n.(OrigNode); ok {
+ n.SetOrig(n)
+ }
+ return n
+}
+
+// Copy returns a shallow copy of n.
+// If Orig(n) == n, then Orig(Copy(n)) == the copy.
+// Otherwise the Orig link is preserved as well.
+//
+// The specific semantics surrounding Orig are subtle but right for most uses.
+// See issues #26855 and #27765 for pitfalls.
+func Copy(n Node) Node {
+ c := n.copy()
+ if n, ok := n.(OrigNode); ok && n.Orig() == n {
+ c.(OrigNode).SetOrig(c)
+ }
+ return c
+}
+
+// DeepCopy returns a “deep” copy of n, with its entire structure copied
+// (except for shared nodes like ONAME, ONONAME, OLITERAL, and OTYPE).
+// If pos.IsKnown(), it sets the source position of newly allocated Nodes to pos.
+func DeepCopy(pos src.XPos, n Node) Node {
+ var edit func(Node) Node
+ edit = func(x Node) Node {
+ switch x.Op() {
+ case OPACK, ONAME, ONONAME, OLITERAL, ONIL, OTYPE:
+ return x
+ }
+ x = Copy(x)
+ if pos.IsKnown() {
+ x.SetPos(pos)
+ }
+ EditChildren(x, edit)
+ return x
+ }
+ return edit(n)
+}
+
+// DeepCopyList returns a list of deep copies (using DeepCopy) of the nodes in list.
+func DeepCopyList(pos src.XPos, list []Node) []Node {
+ var out []Node
+ for _, n := range list {
+ out = append(out, DeepCopy(pos, n))
+ }
+ return out
+}
diff --git a/src/cmd/compile/internal/gc/dump.go b/src/cmd/compile/internal/ir/dump.go
similarity index 91%
rename from src/cmd/compile/internal/gc/dump.go
rename to src/cmd/compile/internal/ir/dump.go
index 29eb1c1e48b6c..fc995cee6202f 100644
--- a/src/cmd/compile/internal/gc/dump.go
+++ b/src/cmd/compile/internal/ir/dump.go
@@ -6,21 +6,23 @@
// for debugging purposes. The code is customized for Node graphs
// and may be used for an alternative view of the node structure.
-package gc
+package ir
import (
- "cmd/compile/internal/types"
- "cmd/internal/src"
"fmt"
"io"
"os"
"reflect"
"regexp"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
)
// dump is like fdump but prints to stderr.
-func dump(root interface{}, filter string, depth int) {
- fdump(os.Stderr, root, filter, depth)
+func DumpAny(root interface{}, filter string, depth int) {
+ FDumpAny(os.Stderr, root, filter, depth)
}
// fdump prints the structure of a rooted data structure
@@ -40,7 +42,7 @@ func dump(root interface{}, filter string, depth int) {
// rather than their type; struct fields with zero values or
// non-matching field names are omitted, and "…" means recursion
// depth has been reached or struct fields have been omitted.
-func fdump(w io.Writer, root interface{}, filter string, depth int) {
+func FDumpAny(w io.Writer, root interface{}, filter string, depth int) {
if root == nil {
fmt.Fprintln(w, "nil")
return
@@ -138,19 +140,9 @@ func (p *dumper) dump(x reflect.Value, depth int) {
return
}
- // special cases
- switch v := x.Interface().(type) {
- case Nodes:
- // unpack Nodes since reflect cannot look inside
- // due to the unexported field in its struct
- x = reflect.ValueOf(v.Slice())
-
- case src.XPos:
- p.printf("%s", linestr(v))
+ if pos, ok := x.Interface().(src.XPos); ok {
+ p.printf("%s", base.FmtPos(pos))
return
-
- case *types.Node:
- x = reflect.ValueOf(asNode(v))
}
switch x.Kind() {
@@ -203,7 +195,7 @@ func (p *dumper) dump(x reflect.Value, depth int) {
isNode := false
if n, ok := x.Interface().(Node); ok {
isNode = true
- p.printf("%s %s {", n.Op.String(), p.addr(x))
+ p.printf("%s %s {", n.Op().String(), p.addr(x))
} else {
p.printf("%s {", typ)
}
@@ -230,7 +222,7 @@ func (p *dumper) dump(x reflect.Value, depth int) {
omitted = true
continue // exclude zero-valued fields
}
- if n, ok := x.Interface().(Nodes); ok && n.Len() == 0 {
+ if n, ok := x.Interface().(Nodes); ok && len(n) == 0 {
omitted = true
continue // exclude empty Nodes slices
}
diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go
new file mode 100644
index 0000000000000..b32ed71260d3e
--- /dev/null
+++ b/src/cmd/compile/internal/ir/expr.go
@@ -0,0 +1,1057 @@
+// 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 ir
+
+import (
+ "bytes"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+ "fmt"
+ "go/constant"
+ "go/token"
+)
+
+// An Expr is a Node that can appear as an expression.
+type Expr interface {
+ Node
+ isExpr()
+}
+
+// A miniExpr is a miniNode with extra fields common to expressions.
+// TODO(rsc): Once we are sure about the contents, compact the bools
+// into a bit field and leave extra bits available for implementations
+// embedding miniExpr. Right now there are ~60 unused bits sitting here.
+type miniExpr struct {
+ miniNode
+ typ *types.Type
+ init Nodes // TODO(rsc): Don't require every Node to have an init
+ flags bitset8
+}
+
+const (
+ miniExprNonNil = 1 << iota
+ miniExprTransient
+ miniExprBounded
+ miniExprImplicit // for use by implementations; not supported by every Expr
+ miniExprCheckPtr
+)
+
+func (*miniExpr) isExpr() {}
+
+func (n *miniExpr) Type() *types.Type { return n.typ }
+func (n *miniExpr) SetType(x *types.Type) { n.typ = x }
+func (n *miniExpr) NonNil() bool { return n.flags&miniExprNonNil != 0 }
+func (n *miniExpr) MarkNonNil() { n.flags |= miniExprNonNil }
+func (n *miniExpr) Transient() bool { return n.flags&miniExprTransient != 0 }
+func (n *miniExpr) SetTransient(b bool) { n.flags.set(miniExprTransient, b) }
+func (n *miniExpr) Bounded() bool { return n.flags&miniExprBounded != 0 }
+func (n *miniExpr) SetBounded(b bool) { n.flags.set(miniExprBounded, b) }
+func (n *miniExpr) Init() Nodes { return n.init }
+func (n *miniExpr) PtrInit() *Nodes { return &n.init }
+func (n *miniExpr) SetInit(x Nodes) { n.init = x }
+
+// An AddStringExpr is a string concatenation Expr[0] + Exprs[1] + ... + Expr[len(Expr)-1].
+type AddStringExpr struct {
+ miniExpr
+ List Nodes
+ Prealloc *Name
+}
+
+func NewAddStringExpr(pos src.XPos, list []Node) *AddStringExpr {
+ n := &AddStringExpr{}
+ n.pos = pos
+ n.op = OADDSTR
+ n.List = list
+ return n
+}
+
+// An AddrExpr is an address-of expression &X.
+// It may end up being a normal address-of or an allocation of a composite literal.
+type AddrExpr struct {
+ miniExpr
+ X Node
+ Prealloc *Name // preallocated storage if any
+}
+
+func NewAddrExpr(pos src.XPos, x Node) *AddrExpr {
+ n := &AddrExpr{X: x}
+ n.op = OADDR
+ n.pos = pos
+ return n
+}
+
+func (n *AddrExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
+func (n *AddrExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
+
+func (n *AddrExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OADDR, OPTRLIT:
+ n.op = op
+ }
+}
+
+// A BasicLit is a literal of basic type.
+type BasicLit struct {
+ miniExpr
+ val constant.Value
+}
+
+func NewBasicLit(pos src.XPos, val constant.Value) Node {
+ n := &BasicLit{val: val}
+ n.op = OLITERAL
+ n.pos = pos
+ if k := val.Kind(); k != constant.Unknown {
+ n.SetType(idealType(k))
+ }
+ return n
+}
+
+func (n *BasicLit) Val() constant.Value { return n.val }
+func (n *BasicLit) SetVal(val constant.Value) { n.val = val }
+
+// A BinaryExpr is a binary expression X Op Y,
+// or Op(X, Y) for builtin functions that do not become calls.
+type BinaryExpr struct {
+ miniExpr
+ X Node
+ Y Node
+}
+
+func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr {
+ n := &BinaryExpr{X: x, Y: y}
+ n.pos = pos
+ n.SetOp(op)
+ return n
+}
+
+func (n *BinaryExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OADD, OADDSTR, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE,
+ OLSH, OLT, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR,
+ OCOPY, OCOMPLEX,
+ OEFACE:
+ n.op = op
+ }
+}
+
+// A CallUse records how the result of the call is used:
+type CallUse byte
+
+const (
+ _ CallUse = iota
+
+ CallUseExpr // single expression result is used
+ CallUseList // list of results are used
+ CallUseStmt // results not used - call is a statement
+)
+
+// A CallExpr is a function call X(Args).
+type CallExpr struct {
+ miniExpr
+ origNode
+ X Node
+ Args Nodes
+ KeepAlive []*Name // vars to be kept alive until call returns
+ IsDDD bool
+ Use CallUse
+ NoInline bool
+}
+
+func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {
+ n := &CallExpr{X: fun}
+ n.pos = pos
+ n.orig = n
+ n.SetOp(op)
+ n.Args = args
+ return n
+}
+
+func (*CallExpr) isStmt() {}
+
+func (n *CallExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH,
+ OAPPEND, ODELETE, OGETG, OMAKE, OPRINT, OPRINTN, ORECOVER:
+ n.op = op
+ }
+}
+
+// A ClosureExpr is a function literal expression.
+type ClosureExpr struct {
+ miniExpr
+ Func *Func `mknode:"-"`
+ Prealloc *Name
+}
+
+func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr {
+ n := &ClosureExpr{Func: fn}
+ n.op = OCLOSURE
+ n.pos = pos
+ return n
+}
+
+// A CompLitExpr is a composite literal Type{Vals}.
+// Before type-checking, the type is Ntype.
+type CompLitExpr struct {
+ miniExpr
+ origNode
+ Ntype Ntype
+ List Nodes // initialized values
+ Prealloc *Name
+ Len int64 // backing array length for OSLICELIT
+}
+
+func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr {
+ n := &CompLitExpr{Ntype: typ}
+ n.pos = pos
+ n.SetOp(op)
+ n.List = list
+ n.orig = n
+ return n
+}
+
+func (n *CompLitExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
+func (n *CompLitExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
+
+func (n *CompLitExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OARRAYLIT, OCOMPLIT, OMAPLIT, OSTRUCTLIT, OSLICELIT:
+ n.op = op
+ }
+}
+
+type ConstExpr struct {
+ miniExpr
+ origNode
+ val constant.Value
+}
+
+func NewConstExpr(val constant.Value, orig Node) Node {
+ n := &ConstExpr{val: val}
+ n.op = OLITERAL
+ n.pos = orig.Pos()
+ n.orig = orig
+ n.SetType(orig.Type())
+ n.SetTypecheck(orig.Typecheck())
+ n.SetDiag(orig.Diag())
+ return n
+}
+
+func (n *ConstExpr) Sym() *types.Sym { return n.orig.Sym() }
+func (n *ConstExpr) Val() constant.Value { return n.val }
+
+// A ConvExpr is a conversion Type(X).
+// It may end up being a value or a type.
+type ConvExpr struct {
+ miniExpr
+ X Node
+}
+
+func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr {
+ n := &ConvExpr{X: x}
+ n.pos = pos
+ n.typ = typ
+ n.SetOp(op)
+ return n
+}
+
+func (n *ConvExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
+func (n *ConvExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
+func (n *ConvExpr) CheckPtr() bool { return n.flags&miniExprCheckPtr != 0 }
+func (n *ConvExpr) SetCheckPtr(b bool) { n.flags.set(miniExprCheckPtr, b) }
+
+func (n *ConvExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR:
+ n.op = op
+ }
+}
+
+// An IndexExpr is an index expression X[Y].
+type IndexExpr struct {
+ miniExpr
+ X Node
+ Index Node
+ Assigned bool
+}
+
+func NewIndexExpr(pos src.XPos, x, index Node) *IndexExpr {
+ n := &IndexExpr{X: x, Index: index}
+ n.pos = pos
+ n.op = OINDEX
+ return n
+}
+
+func (n *IndexExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OINDEX, OINDEXMAP:
+ n.op = op
+ }
+}
+
+// A KeyExpr is a Key: Value composite literal key.
+type KeyExpr struct {
+ miniExpr
+ Key Node
+ Value Node
+}
+
+func NewKeyExpr(pos src.XPos, key, value Node) *KeyExpr {
+ n := &KeyExpr{Key: key, Value: value}
+ n.pos = pos
+ n.op = OKEY
+ return n
+}
+
+// A StructKeyExpr is an Field: Value composite literal key.
+type StructKeyExpr struct {
+ miniExpr
+ Field *types.Sym
+ Value Node
+ Offset int64
+}
+
+func NewStructKeyExpr(pos src.XPos, field *types.Sym, value Node) *StructKeyExpr {
+ n := &StructKeyExpr{Field: field, Value: value}
+ n.pos = pos
+ n.op = OSTRUCTKEY
+ n.Offset = types.BADWIDTH
+ return n
+}
+
+func (n *StructKeyExpr) Sym() *types.Sym { return n.Field }
+
+// An InlinedCallExpr is an inlined function call.
+type InlinedCallExpr struct {
+ miniExpr
+ Body Nodes
+ ReturnVars Nodes
+}
+
+func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr {
+ n := &InlinedCallExpr{}
+ n.pos = pos
+ n.op = OINLCALL
+ n.Body = body
+ n.ReturnVars = retvars
+ return n
+}
+
+// A LogicalExpr is a expression X Op Y where Op is && or ||.
+// It is separate from BinaryExpr to make room for statements
+// that must be executed before Y but after X.
+type LogicalExpr struct {
+ miniExpr
+ X Node
+ Y Node
+}
+
+func NewLogicalExpr(pos src.XPos, op Op, x, y Node) *LogicalExpr {
+ n := &LogicalExpr{X: x, Y: y}
+ n.pos = pos
+ n.SetOp(op)
+ return n
+}
+
+func (n *LogicalExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OANDAND, OOROR:
+ n.op = op
+ }
+}
+
+// A MakeExpr is a make expression: make(Type[, Len[, Cap]]).
+// Op is OMAKECHAN, OMAKEMAP, OMAKESLICE, or OMAKESLICECOPY,
+// but *not* OMAKE (that's a pre-typechecking CallExpr).
+type MakeExpr struct {
+ miniExpr
+ Len Node
+ Cap Node
+}
+
+func NewMakeExpr(pos src.XPos, op Op, len, cap Node) *MakeExpr {
+ n := &MakeExpr{Len: len, Cap: cap}
+ n.pos = pos
+ n.SetOp(op)
+ return n
+}
+
+func (n *MakeExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OMAKECHAN, OMAKEMAP, OMAKESLICE, OMAKESLICECOPY:
+ n.op = op
+ }
+}
+
+// A NilExpr represents the predefined untyped constant nil.
+// (It may be copied and assigned a type, though.)
+type NilExpr struct {
+ miniExpr
+ Sym_ *types.Sym // TODO: Remove
+}
+
+func NewNilExpr(pos src.XPos) *NilExpr {
+ n := &NilExpr{}
+ n.pos = pos
+ n.op = ONIL
+ return n
+}
+
+func (n *NilExpr) Sym() *types.Sym { return n.Sym_ }
+func (n *NilExpr) SetSym(x *types.Sym) { n.Sym_ = x }
+
+// A ParenExpr is a parenthesized expression (X).
+// It may end up being a value or a type.
+type ParenExpr struct {
+ miniExpr
+ X Node
+}
+
+func NewParenExpr(pos src.XPos, x Node) *ParenExpr {
+ n := &ParenExpr{X: x}
+ n.op = OPAREN
+ n.pos = pos
+ return n
+}
+
+func (n *ParenExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
+func (n *ParenExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
+
+func (*ParenExpr) CanBeNtype() {}
+
+// SetOTYPE changes n to be an OTYPE node returning t,
+// like all the type nodes in type.go.
+func (n *ParenExpr) SetOTYPE(t *types.Type) {
+ n.op = OTYPE
+ n.typ = t
+ t.SetNod(n)
+}
+
+// A ResultExpr represents a direct access to a result slot on the stack frame.
+type ResultExpr struct {
+ miniExpr
+ Offset int64
+}
+
+func NewResultExpr(pos src.XPos, typ *types.Type, offset int64) *ResultExpr {
+ n := &ResultExpr{Offset: offset}
+ n.pos = pos
+ n.op = ORESULT
+ n.typ = typ
+ return n
+}
+
+// A LinksymOffsetExpr refers to an offset within a global variable.
+// It is like a SelectorExpr but without the field name.
+type LinksymOffsetExpr struct {
+ miniExpr
+ Linksym *obj.LSym
+ Offset_ int64
+}
+
+func NewLinksymOffsetExpr(pos src.XPos, lsym *obj.LSym, offset int64, typ *types.Type) *LinksymOffsetExpr {
+ n := &LinksymOffsetExpr{Linksym: lsym, Offset_: offset}
+ n.typ = typ
+ n.op = OLINKSYMOFFSET
+ return n
+}
+
+// NewLinksymExpr is NewLinksymOffsetExpr, but with offset fixed at 0.
+func NewLinksymExpr(pos src.XPos, lsym *obj.LSym, typ *types.Type) *LinksymOffsetExpr {
+ return NewLinksymOffsetExpr(pos, lsym, 0, typ)
+}
+
+// NewNameOffsetExpr is NewLinksymOffsetExpr, but taking a *Name
+// representing a global variable instead of an *obj.LSym directly.
+func NewNameOffsetExpr(pos src.XPos, name *Name, offset int64, typ *types.Type) *LinksymOffsetExpr {
+ if name == nil || IsBlank(name) || !(name.Op() == ONAME && name.Class == PEXTERN) {
+ base.FatalfAt(pos, "cannot take offset of nil, blank name or non-global variable: %v", name)
+ }
+ return NewLinksymOffsetExpr(pos, name.Linksym(), offset, typ)
+}
+
+// A SelectorExpr is a selector expression X.Sel.
+type SelectorExpr struct {
+ miniExpr
+ X Node
+ Sel *types.Sym
+ Selection *types.Field
+ Prealloc *Name // preallocated storage for OCALLPART, if any
+}
+
+func NewSelectorExpr(pos src.XPos, op Op, x Node, sel *types.Sym) *SelectorExpr {
+ n := &SelectorExpr{X: x, Sel: sel}
+ n.pos = pos
+ n.SetOp(op)
+ return n
+}
+
+func (n *SelectorExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OCALLPART, OMETHEXPR:
+ n.op = op
+ }
+}
+
+func (n *SelectorExpr) Sym() *types.Sym { return n.Sel }
+func (n *SelectorExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
+func (n *SelectorExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
+func (n *SelectorExpr) Offset() int64 { return n.Selection.Offset }
+
+func (n *SelectorExpr) FuncName() *Name {
+ if n.Op() != OMETHEXPR {
+ panic(n.no("FuncName"))
+ }
+ fn := NewNameAt(n.Selection.Pos, MethodSym(n.X.Type(), n.Sel))
+ fn.Class = PFUNC
+ fn.SetType(n.Type())
+ return fn
+}
+
+// Before type-checking, bytes.Buffer is a SelectorExpr.
+// After type-checking it becomes a Name.
+func (*SelectorExpr) CanBeNtype() {}
+
+// A SliceExpr is a slice expression X[Low:High] or X[Low:High:Max].
+type SliceExpr struct {
+ miniExpr
+ X Node
+ Low Node
+ High Node
+ Max Node
+}
+
+func NewSliceExpr(pos src.XPos, op Op, x, low, high, max Node) *SliceExpr {
+ n := &SliceExpr{X: x, Low: low, High: high, Max: max}
+ n.pos = pos
+ n.op = op
+ return n
+}
+
+func (n *SliceExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
+ n.op = op
+ }
+}
+
+// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).
+// o must be a slicing op.
+func (o Op) IsSlice3() bool {
+ switch o {
+ case OSLICE, OSLICEARR, OSLICESTR:
+ return false
+ case OSLICE3, OSLICE3ARR:
+ return true
+ }
+ base.Fatalf("IsSlice3 op %v", o)
+ return false
+}
+
+// A SliceHeader expression constructs a slice header from its parts.
+type SliceHeaderExpr struct {
+ miniExpr
+ Ptr Node
+ Len Node
+ Cap Node
+}
+
+func NewSliceHeaderExpr(pos src.XPos, typ *types.Type, ptr, len, cap Node) *SliceHeaderExpr {
+ n := &SliceHeaderExpr{Ptr: ptr, Len: len, Cap: cap}
+ n.pos = pos
+ n.op = OSLICEHEADER
+ n.typ = typ
+ return n
+}
+
+// A StarExpr is a dereference expression *X.
+// It may end up being a value or a type.
+type StarExpr struct {
+ miniExpr
+ X Node
+}
+
+func NewStarExpr(pos src.XPos, x Node) *StarExpr {
+ n := &StarExpr{X: x}
+ n.op = ODEREF
+ n.pos = pos
+ return n
+}
+
+func (n *StarExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
+func (n *StarExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
+
+func (*StarExpr) CanBeNtype() {}
+
+// SetOTYPE changes n to be an OTYPE node returning t,
+// like all the type nodes in type.go.
+func (n *StarExpr) SetOTYPE(t *types.Type) {
+ n.op = OTYPE
+ n.X = nil
+ n.typ = t
+ t.SetNod(n)
+}
+
+// A TypeAssertionExpr is a selector expression X.(Type).
+// Before type-checking, the type is Ntype.
+type TypeAssertExpr struct {
+ miniExpr
+ X Node
+ Ntype Ntype
+
+ // Runtime type information provided by walkDotType for
+ // assertions from non-empty interface to concrete type.
+ Itab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type
+}
+
+func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr {
+ n := &TypeAssertExpr{X: x, Ntype: typ}
+ n.pos = pos
+ n.op = ODOTTYPE
+ return n
+}
+
+func (n *TypeAssertExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case ODOTTYPE, ODOTTYPE2:
+ n.op = op
+ }
+}
+
+// A UnaryExpr is a unary expression Op X,
+// or Op(X) for a builtin function that does not end up being a call.
+type UnaryExpr struct {
+ miniExpr
+ X Node
+}
+
+func NewUnaryExpr(pos src.XPos, op Op, x Node) *UnaryExpr {
+ n := &UnaryExpr{X: x}
+ n.pos = pos
+ n.SetOp(op)
+ return n
+}
+
+func (n *UnaryExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OBITNOT, ONEG, ONOT, OPLUS, ORECV,
+ OALIGNOF, OCAP, OCLOSE, OIMAG, OLEN, ONEW,
+ OOFFSETOF, OPANIC, OREAL, OSIZEOF,
+ OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR, OVARDEF, OVARKILL, OVARLIVE:
+ n.op = op
+ }
+}
+
+func IsZero(n Node) bool {
+ switch n.Op() {
+ case ONIL:
+ return true
+
+ case OLITERAL:
+ switch u := n.Val(); u.Kind() {
+ case constant.String:
+ return constant.StringVal(u) == ""
+ case constant.Bool:
+ return !constant.BoolVal(u)
+ default:
+ return constant.Sign(u) == 0
+ }
+
+ case OARRAYLIT:
+ n := n.(*CompLitExpr)
+ for _, n1 := range n.List {
+ if n1.Op() == OKEY {
+ n1 = n1.(*KeyExpr).Value
+ }
+ if !IsZero(n1) {
+ return false
+ }
+ }
+ return true
+
+ case OSTRUCTLIT:
+ n := n.(*CompLitExpr)
+ for _, n1 := range n.List {
+ n1 := n1.(*StructKeyExpr)
+ if !IsZero(n1.Value) {
+ return false
+ }
+ }
+ return true
+ }
+
+ return false
+}
+
+// lvalue etc
+func IsAddressable(n Node) bool {
+ switch n.Op() {
+ case OINDEX:
+ n := n.(*IndexExpr)
+ if n.X.Type() != nil && n.X.Type().IsArray() {
+ return IsAddressable(n.X)
+ }
+ if n.X.Type() != nil && n.X.Type().IsString() {
+ return false
+ }
+ fallthrough
+ case ODEREF, ODOTPTR:
+ return true
+
+ case ODOT:
+ n := n.(*SelectorExpr)
+ return IsAddressable(n.X)
+
+ case ONAME:
+ n := n.(*Name)
+ if n.Class == PFUNC {
+ return false
+ }
+ return true
+
+ case OLINKSYMOFFSET:
+ return true
+ }
+
+ return false
+}
+
+func StaticValue(n Node) Node {
+ for {
+ if n.Op() == OCONVNOP {
+ n = n.(*ConvExpr).X
+ continue
+ }
+
+ n1 := staticValue1(n)
+ if n1 == nil {
+ return n
+ }
+ n = n1
+ }
+}
+
+// staticValue1 implements a simple SSA-like optimization. If n is a local variable
+// that is initialized and never reassigned, staticValue1 returns the initializer
+// expression. Otherwise, it returns nil.
+func staticValue1(nn Node) Node {
+ if nn.Op() != ONAME {
+ return nil
+ }
+ n := nn.(*Name)
+ if n.Class != PAUTO {
+ return nil
+ }
+
+ defn := n.Defn
+ if defn == nil {
+ return nil
+ }
+
+ var rhs Node
+FindRHS:
+ switch defn.Op() {
+ case OAS:
+ defn := defn.(*AssignStmt)
+ rhs = defn.Y
+ case OAS2:
+ defn := defn.(*AssignListStmt)
+ for i, lhs := range defn.Lhs {
+ if lhs == n {
+ rhs = defn.Rhs[i]
+ break FindRHS
+ }
+ }
+ base.Fatalf("%v missing from LHS of %v", n, defn)
+ default:
+ return nil
+ }
+ if rhs == nil {
+ base.Fatalf("RHS is nil: %v", defn)
+ }
+
+ if reassigned(n) {
+ return nil
+ }
+
+ return rhs
+}
+
+// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
+// indicating whether the name has any assignments other than its declaration.
+// The second return value is the first such assignment encountered in the walk, if any. It is mostly
+// useful for -m output documenting the reason for inhibited optimizations.
+// NB: global variables are always considered to be re-assigned.
+// TODO: handle initial declaration not including an assignment and followed by a single assignment?
+func reassigned(name *Name) bool {
+ if name.Op() != ONAME {
+ base.Fatalf("reassigned %v", name)
+ }
+ // no way to reliably check for no-reassignment of globals, assume it can be
+ if name.Curfn == nil {
+ return true
+ }
+
+ // TODO(mdempsky): This is inefficient and becoming increasingly
+ // unwieldy. Figure out a way to generalize escape analysis's
+ // reassignment detection for use by inlining and devirtualization.
+
+ // isName reports whether n is a reference to name.
+ isName := func(x Node) bool {
+ n, ok := x.(*Name)
+ return ok && n.Canonical() == name
+ }
+
+ var do func(n Node) bool
+ do = func(n Node) bool {
+ switch n.Op() {
+ case OAS:
+ n := n.(*AssignStmt)
+ if isName(n.X) && n != name.Defn {
+ return true
+ }
+ case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV, OSELRECV2:
+ n := n.(*AssignListStmt)
+ for _, p := range n.Lhs {
+ if isName(p) && n != name.Defn {
+ return true
+ }
+ }
+ case OADDR:
+ n := n.(*AddrExpr)
+ if isName(OuterValue(n.X)) {
+ return true
+ }
+ case OCLOSURE:
+ n := n.(*ClosureExpr)
+ if Any(n.Func, do) {
+ return true
+ }
+ }
+ return false
+ }
+ return Any(name.Curfn, do)
+}
+
+// IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation.
+var IsIntrinsicCall = func(*CallExpr) bool { return false }
+
+// SameSafeExpr checks whether it is safe to reuse one of l and r
+// instead of computing both. SameSafeExpr assumes that l and r are
+// used in the same statement or expression. In order for it to be
+// safe to reuse l or r, they must:
+// * be the same expression
+// * not have side-effects (no function calls, no channel ops);
+// however, panics are ok
+// * not cause inappropriate aliasing; e.g. two string to []byte
+// conversions, must result in two distinct slices
+//
+// The handling of OINDEXMAP is subtle. OINDEXMAP can occur both
+// as an lvalue (map assignment) and an rvalue (map access). This is
+// currently OK, since the only place SameSafeExpr gets used on an
+// lvalue expression is for OSLICE and OAPPEND optimizations, and it
+// is correct in those settings.
+func SameSafeExpr(l Node, r Node) bool {
+ if l.Op() != r.Op() || !types.Identical(l.Type(), r.Type()) {
+ return false
+ }
+
+ switch l.Op() {
+ case ONAME:
+ return l == r
+
+ case ODOT, ODOTPTR:
+ l := l.(*SelectorExpr)
+ r := r.(*SelectorExpr)
+ return l.Sel != nil && r.Sel != nil && l.Sel == r.Sel && SameSafeExpr(l.X, r.X)
+
+ case ODEREF:
+ l := l.(*StarExpr)
+ r := r.(*StarExpr)
+ return SameSafeExpr(l.X, r.X)
+
+ case ONOT, OBITNOT, OPLUS, ONEG:
+ l := l.(*UnaryExpr)
+ r := r.(*UnaryExpr)
+ return SameSafeExpr(l.X, r.X)
+
+ case OCONVNOP:
+ l := l.(*ConvExpr)
+ r := r.(*ConvExpr)
+ return SameSafeExpr(l.X, r.X)
+
+ case OCONV:
+ l := l.(*ConvExpr)
+ r := r.(*ConvExpr)
+ // Some conversions can't be reused, such as []byte(str).
+ // Allow only numeric-ish types. This is a bit conservative.
+ return types.IsSimple[l.Type().Kind()] && SameSafeExpr(l.X, r.X)
+
+ case OINDEX, OINDEXMAP:
+ l := l.(*IndexExpr)
+ r := r.(*IndexExpr)
+ return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Index, r.Index)
+
+ case OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
+ l := l.(*BinaryExpr)
+ r := r.(*BinaryExpr)
+ return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Y, r.Y)
+
+ case OLITERAL:
+ return constant.Compare(l.Val(), token.EQL, r.Val())
+
+ case ONIL:
+ return true
+ }
+
+ return false
+}
+
+// ShouldCheckPtr reports whether pointer checking should be enabled for
+// function fn at a given level. See debugHelpFooter for defined
+// levels.
+func ShouldCheckPtr(fn *Func, level int) bool {
+ return base.Debug.Checkptr >= level && fn.Pragma&NoCheckPtr == 0
+}
+
+// IsReflectHeaderDataField reports whether l is an expression p.Data
+// where p has type reflect.SliceHeader or reflect.StringHeader.
+func IsReflectHeaderDataField(l Node) bool {
+ if l.Type() != types.Types[types.TUINTPTR] {
+ return false
+ }
+
+ var tsym *types.Sym
+ switch l.Op() {
+ case ODOT:
+ l := l.(*SelectorExpr)
+ tsym = l.X.Type().Sym()
+ case ODOTPTR:
+ l := l.(*SelectorExpr)
+ tsym = l.X.Type().Elem().Sym()
+ default:
+ return false
+ }
+
+ if tsym == nil || l.Sym().Name != "Data" || tsym.Pkg.Path != "reflect" {
+ return false
+ }
+ return tsym.Name == "SliceHeader" || tsym.Name == "StringHeader"
+}
+
+func ParamNames(ft *types.Type) []Node {
+ args := make([]Node, ft.NumParams())
+ for i, f := range ft.Params().FieldSlice() {
+ args[i] = AsNode(f.Nname)
+ }
+ return args
+}
+
+// MethodSym returns the method symbol representing a method name
+// associated with a specific receiver type.
+//
+// Method symbols can be used to distinguish the same method appearing
+// in different method sets. For example, T.M and (*T).M have distinct
+// method symbols.
+//
+// The returned symbol will be marked as a function.
+func MethodSym(recv *types.Type, msym *types.Sym) *types.Sym {
+ sym := MethodSymSuffix(recv, msym, "")
+ sym.SetFunc(true)
+ return sym
+}
+
+// MethodSymSuffix is like methodsym, but allows attaching a
+// distinguisher suffix. To avoid collisions, the suffix must not
+// start with a letter, number, or period.
+func MethodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sym {
+ if msym.IsBlank() {
+ base.Fatalf("blank method name")
+ }
+
+ rsym := recv.Sym()
+ if recv.IsPtr() {
+ if rsym != nil {
+ base.Fatalf("declared pointer receiver type: %v", recv)
+ }
+ rsym = recv.Elem().Sym()
+ }
+
+ // Find the package the receiver type appeared in. For
+ // anonymous receiver types (i.e., anonymous structs with
+ // embedded fields), use the "go" pseudo-package instead.
+ rpkg := Pkgs.Go
+ if rsym != nil {
+ rpkg = rsym.Pkg
+ }
+
+ var b bytes.Buffer
+ if recv.IsPtr() {
+ // The parentheses aren't really necessary, but
+ // they're pretty traditional at this point.
+ fmt.Fprintf(&b, "(%-S)", recv)
+ } else {
+ fmt.Fprintf(&b, "%-S", recv)
+ }
+
+ // A particular receiver type may have multiple non-exported
+ // methods with the same name. To disambiguate them, include a
+ // package qualifier for names that came from a different
+ // package than the receiver type.
+ if !types.IsExported(msym.Name) && msym.Pkg != rpkg {
+ b.WriteString(".")
+ b.WriteString(msym.Pkg.Prefix)
+ }
+
+ b.WriteString(".")
+ b.WriteString(msym.Name)
+ b.WriteString(suffix)
+
+ return rpkg.LookupBytes(b.Bytes())
+}
+
+// MethodName returns the ONAME representing the method
+// referenced by expression n, which must be a method selector,
+// method expression, or method value.
+func MethodExprName(n Node) *Name {
+ name, _ := MethodExprFunc(n).Nname.(*Name)
+ return name
+}
+
+// MethodFunc is like MethodName, but returns the types.Field instead.
+func MethodExprFunc(n Node) *types.Field {
+ switch n.Op() {
+ case ODOTMETH, OMETHEXPR, OCALLPART:
+ return n.(*SelectorExpr).Selection
+ }
+ base.Fatalf("unexpected node: %v (%v)", n, n.Op())
+ panic("unreachable")
+}
diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go
new file mode 100644
index 0000000000000..1a05079dac8ee
--- /dev/null
+++ b/src/cmd/compile/internal/ir/fmt.go
@@ -0,0 +1,1331 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ir
+
+import (
+ "bytes"
+ "fmt"
+ "go/constant"
+ "io"
+ "math"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strings"
+
+ "unicode/utf8"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// Op
+
+var OpNames = []string{
+ OADDR: "&",
+ OADD: "+",
+ OADDSTR: "+",
+ OALIGNOF: "unsafe.Alignof",
+ OANDAND: "&&",
+ OANDNOT: "&^",
+ OAND: "&",
+ OAPPEND: "append",
+ OAS: "=",
+ OAS2: "=",
+ OBREAK: "break",
+ OCALL: "function call", // not actual syntax
+ OCAP: "cap",
+ OCASE: "case",
+ OCLOSE: "close",
+ OCOMPLEX: "complex",
+ OBITNOT: "^",
+ OCONTINUE: "continue",
+ OCOPY: "copy",
+ ODELETE: "delete",
+ ODEFER: "defer",
+ ODIV: "/",
+ OEQ: "==",
+ OFALL: "fallthrough",
+ OFOR: "for",
+ OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892
+ OGE: ">=",
+ OGOTO: "goto",
+ OGT: ">",
+ OIF: "if",
+ OIMAG: "imag",
+ OINLMARK: "inlmark",
+ ODEREF: "*",
+ OLEN: "len",
+ OLE: "<=",
+ OLSH: "<<",
+ OLT: "<",
+ OMAKE: "make",
+ ONEG: "-",
+ OMOD: "%",
+ OMUL: "*",
+ ONEW: "new",
+ ONE: "!=",
+ ONOT: "!",
+ OOFFSETOF: "unsafe.Offsetof",
+ OOROR: "||",
+ OOR: "|",
+ OPANIC: "panic",
+ OPLUS: "+",
+ OPRINTN: "println",
+ OPRINT: "print",
+ ORANGE: "range",
+ OREAL: "real",
+ ORECV: "<-",
+ ORECOVER: "recover",
+ ORETURN: "return",
+ ORSH: ">>",
+ OSELECT: "select",
+ OSEND: "<-",
+ OSIZEOF: "unsafe.Sizeof",
+ OSUB: "-",
+ OSWITCH: "switch",
+ OXOR: "^",
+}
+
+// GoString returns the Go syntax for the Op, or else its name.
+func (o Op) GoString() string {
+ if int(o) < len(OpNames) && OpNames[o] != "" {
+ return OpNames[o]
+ }
+ return o.String()
+}
+
+// Format implements formatting for an Op.
+// The valid formats are:
+//
+// %v Go syntax ("+", "<-", "print")
+// %+v Debug syntax ("ADD", "RECV", "PRINT")
+//
+func (o Op) Format(s fmt.State, verb rune) {
+ switch verb {
+ default:
+ fmt.Fprintf(s, "%%!%c(Op=%d)", verb, int(o))
+ case 'v':
+ if s.Flag('+') {
+ // %+v is OMUL instead of "*"
+ io.WriteString(s, o.String())
+ return
+ }
+ io.WriteString(s, o.GoString())
+ }
+}
+
+// Node
+
+// FmtNode implements formatting for a Node n.
+// Every Node implementation must define a Format method that calls FmtNode.
+// The valid formats are:
+//
+// %v Go syntax
+// %L Go syntax followed by " (type T)" if type is known.
+// %+v Debug syntax, as in Dump.
+//
+func fmtNode(n Node, s fmt.State, verb rune) {
+ // %+v prints Dump.
+ // Otherwise we print Go syntax.
+ if s.Flag('+') && verb == 'v' {
+ dumpNode(s, n, 1)
+ return
+ }
+
+ if verb != 'v' && verb != 'S' && verb != 'L' {
+ fmt.Fprintf(s, "%%!%c(*Node=%p)", verb, n)
+ return
+ }
+
+ if n == nil {
+ fmt.Fprint(s, "")
+ return
+ }
+
+ t := n.Type()
+ if verb == 'L' && t != nil {
+ if t.Kind() == types.TNIL {
+ fmt.Fprint(s, "nil")
+ } else if n.Op() == ONAME && n.Name().AutoTemp() {
+ fmt.Fprintf(s, "%v value", t)
+ } else {
+ fmt.Fprintf(s, "%v (type %v)", n, t)
+ }
+ return
+ }
+
+ // TODO inlining produces expressions with ninits. we can't print these yet.
+
+ if OpPrec[n.Op()] < 0 {
+ stmtFmt(n, s)
+ return
+ }
+
+ exprFmt(n, s, 0)
+}
+
+var OpPrec = []int{
+ OALIGNOF: 8,
+ OAPPEND: 8,
+ OBYTES2STR: 8,
+ OARRAYLIT: 8,
+ OSLICELIT: 8,
+ ORUNES2STR: 8,
+ OCALLFUNC: 8,
+ OCALLINTER: 8,
+ OCALLMETH: 8,
+ OCALL: 8,
+ OCAP: 8,
+ OCLOSE: 8,
+ OCOMPLIT: 8,
+ OCONVIFACE: 8,
+ OCONVNOP: 8,
+ OCONV: 8,
+ OCOPY: 8,
+ ODELETE: 8,
+ OGETG: 8,
+ OLEN: 8,
+ OLITERAL: 8,
+ OMAKESLICE: 8,
+ OMAKESLICECOPY: 8,
+ OMAKE: 8,
+ OMAPLIT: 8,
+ ONAME: 8,
+ ONEW: 8,
+ ONIL: 8,
+ ONONAME: 8,
+ OOFFSETOF: 8,
+ OPACK: 8,
+ OPANIC: 8,
+ OPAREN: 8,
+ OPRINTN: 8,
+ OPRINT: 8,
+ ORUNESTR: 8,
+ OSIZEOF: 8,
+ OSTR2BYTES: 8,
+ OSTR2RUNES: 8,
+ OSTRUCTLIT: 8,
+ OTARRAY: 8,
+ OTSLICE: 8,
+ OTCHAN: 8,
+ OTFUNC: 8,
+ OTINTER: 8,
+ OTMAP: 8,
+ OTSTRUCT: 8,
+ OTYPE: 8,
+ OINDEXMAP: 8,
+ OINDEX: 8,
+ OSLICE: 8,
+ OSLICESTR: 8,
+ OSLICEARR: 8,
+ OSLICE3: 8,
+ OSLICE3ARR: 8,
+ OSLICEHEADER: 8,
+ ODOTINTER: 8,
+ ODOTMETH: 8,
+ ODOTPTR: 8,
+ ODOTTYPE2: 8,
+ ODOTTYPE: 8,
+ ODOT: 8,
+ OXDOT: 8,
+ OCALLPART: 8,
+ OMETHEXPR: 8,
+ OPLUS: 7,
+ ONOT: 7,
+ OBITNOT: 7,
+ ONEG: 7,
+ OADDR: 7,
+ ODEREF: 7,
+ ORECV: 7,
+ OMUL: 6,
+ ODIV: 6,
+ OMOD: 6,
+ OLSH: 6,
+ ORSH: 6,
+ OAND: 6,
+ OANDNOT: 6,
+ OADD: 5,
+ OSUB: 5,
+ OOR: 5,
+ OXOR: 5,
+ OEQ: 4,
+ OLT: 4,
+ OLE: 4,
+ OGE: 4,
+ OGT: 4,
+ ONE: 4,
+ OSEND: 3,
+ OANDAND: 2,
+ OOROR: 1,
+
+ // Statements handled by stmtfmt
+ OAS: -1,
+ OAS2: -1,
+ OAS2DOTTYPE: -1,
+ OAS2FUNC: -1,
+ OAS2MAPR: -1,
+ OAS2RECV: -1,
+ OASOP: -1,
+ OBLOCK: -1,
+ OBREAK: -1,
+ OCASE: -1,
+ OCONTINUE: -1,
+ ODCL: -1,
+ ODEFER: -1,
+ OFALL: -1,
+ OFOR: -1,
+ OFORUNTIL: -1,
+ OGOTO: -1,
+ OIF: -1,
+ OLABEL: -1,
+ OGO: -1,
+ ORANGE: -1,
+ ORETURN: -1,
+ OSELECT: -1,
+ OSWITCH: -1,
+
+ OEND: 0,
+}
+
+// StmtWithInit reports whether op is a statement with an explicit init list.
+func StmtWithInit(op Op) bool {
+ switch op {
+ case OIF, OFOR, OFORUNTIL, OSWITCH:
+ return true
+ }
+ return false
+}
+
+func stmtFmt(n Node, s fmt.State) {
+ // NOTE(rsc): This code used to support the text-based
+ // which was more aggressive about printing full Go syntax
+ // (for example, an actual loop instead of "for loop").
+ // The code is preserved for now in case we want to expand
+ // any of those shortenings later. Or maybe we will delete
+ // the code. But for now, keep it.
+ const exportFormat = false
+
+ // some statements allow for an init, but at most one,
+ // but we may have an arbitrary number added, eg by typecheck
+ // and inlining. If it doesn't fit the syntax, emit an enclosing
+ // block starting with the init statements.
+
+ // if we can just say "for" n->ninit; ... then do so
+ simpleinit := len(n.Init()) == 1 && len(n.Init()[0].Init()) == 0 && StmtWithInit(n.Op())
+
+ // otherwise, print the inits as separate statements
+ complexinit := len(n.Init()) != 0 && !simpleinit && exportFormat
+
+ // but if it was for if/for/switch, put in an extra surrounding block to limit the scope
+ extrablock := complexinit && StmtWithInit(n.Op())
+
+ if extrablock {
+ fmt.Fprint(s, "{")
+ }
+
+ if complexinit {
+ fmt.Fprintf(s, " %v; ", n.Init())
+ }
+
+ switch n.Op() {
+ case ODCL:
+ n := n.(*Decl)
+ fmt.Fprintf(s, "var %v %v", n.X.Sym(), n.X.Type())
+
+ // Don't export "v = " initializing statements, hope they're always
+ // preceded by the DCL which will be re-parsed and typechecked to reproduce
+ // the "v = " again.
+ case OAS:
+ n := n.(*AssignStmt)
+ if n.Def && !complexinit {
+ fmt.Fprintf(s, "%v := %v", n.X, n.Y)
+ } else {
+ fmt.Fprintf(s, "%v = %v", n.X, n.Y)
+ }
+
+ case OASOP:
+ n := n.(*AssignOpStmt)
+ if n.IncDec {
+ if n.AsOp == OADD {
+ fmt.Fprintf(s, "%v++", n.X)
+ } else {
+ fmt.Fprintf(s, "%v--", n.X)
+ }
+ break
+ }
+
+ fmt.Fprintf(s, "%v %v= %v", n.X, n.AsOp, n.Y)
+
+ case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+ n := n.(*AssignListStmt)
+ if n.Def && !complexinit {
+ fmt.Fprintf(s, "%.v := %.v", n.Lhs, n.Rhs)
+ } else {
+ fmt.Fprintf(s, "%.v = %.v", n.Lhs, n.Rhs)
+ }
+
+ case OBLOCK:
+ n := n.(*BlockStmt)
+ if len(n.List) != 0 {
+ fmt.Fprintf(s, "%v", n.List)
+ }
+
+ case ORETURN:
+ n := n.(*ReturnStmt)
+ fmt.Fprintf(s, "return %.v", n.Results)
+
+ case OTAILCALL:
+ n := n.(*TailCallStmt)
+ fmt.Fprintf(s, "tailcall %v", n.Target)
+
+ case OINLMARK:
+ n := n.(*InlineMarkStmt)
+ fmt.Fprintf(s, "inlmark %d", n.Index)
+
+ case OGO:
+ n := n.(*GoDeferStmt)
+ fmt.Fprintf(s, "go %v", n.Call)
+
+ case ODEFER:
+ n := n.(*GoDeferStmt)
+ fmt.Fprintf(s, "defer %v", n.Call)
+
+ case OIF:
+ n := n.(*IfStmt)
+ if simpleinit {
+ fmt.Fprintf(s, "if %v; %v { %v }", n.Init()[0], n.Cond, n.Body)
+ } else {
+ fmt.Fprintf(s, "if %v { %v }", n.Cond, n.Body)
+ }
+ if len(n.Else) != 0 {
+ fmt.Fprintf(s, " else { %v }", n.Else)
+ }
+
+ case OFOR, OFORUNTIL:
+ n := n.(*ForStmt)
+ opname := "for"
+ if n.Op() == OFORUNTIL {
+ opname = "foruntil"
+ }
+ if !exportFormat { // TODO maybe only if FmtShort, same below
+ fmt.Fprintf(s, "%s loop", opname)
+ break
+ }
+
+ fmt.Fprint(s, opname)
+ if simpleinit {
+ fmt.Fprintf(s, " %v;", n.Init()[0])
+ } else if n.Post != nil {
+ fmt.Fprint(s, " ;")
+ }
+
+ if n.Cond != nil {
+ fmt.Fprintf(s, " %v", n.Cond)
+ }
+
+ if n.Post != nil {
+ fmt.Fprintf(s, "; %v", n.Post)
+ } else if simpleinit {
+ fmt.Fprint(s, ";")
+ }
+
+ if n.Op() == OFORUNTIL && len(n.Late) != 0 {
+ fmt.Fprintf(s, "; %v", n.Late)
+ }
+
+ fmt.Fprintf(s, " { %v }", n.Body)
+
+ case ORANGE:
+ n := n.(*RangeStmt)
+ if !exportFormat {
+ fmt.Fprint(s, "for loop")
+ break
+ }
+
+ fmt.Fprint(s, "for")
+ if n.Key != nil {
+ fmt.Fprintf(s, " %v", n.Key)
+ if n.Value != nil {
+ fmt.Fprintf(s, ", %v", n.Value)
+ }
+ fmt.Fprint(s, " =")
+ }
+ fmt.Fprintf(s, " range %v { %v }", n.X, n.Body)
+
+ case OSELECT:
+ n := n.(*SelectStmt)
+ if !exportFormat {
+ fmt.Fprintf(s, "%v statement", n.Op())
+ break
+ }
+ fmt.Fprintf(s, "select { %v }", n.Cases)
+
+ case OSWITCH:
+ n := n.(*SwitchStmt)
+ if !exportFormat {
+ fmt.Fprintf(s, "%v statement", n.Op())
+ break
+ }
+ fmt.Fprintf(s, "switch")
+ if simpleinit {
+ fmt.Fprintf(s, " %v;", n.Init()[0])
+ }
+ if n.Tag != nil {
+ fmt.Fprintf(s, " %v ", n.Tag)
+ }
+ fmt.Fprintf(s, " { %v }", n.Cases)
+
+ case OCASE:
+ n := n.(*CaseClause)
+ if len(n.List) != 0 {
+ fmt.Fprintf(s, "case %.v", n.List)
+ } else {
+ fmt.Fprint(s, "default")
+ }
+ fmt.Fprintf(s, ": %v", n.Body)
+
+ case OBREAK, OCONTINUE, OGOTO, OFALL:
+ n := n.(*BranchStmt)
+ if n.Label != nil {
+ fmt.Fprintf(s, "%v %v", n.Op(), n.Label)
+ } else {
+ fmt.Fprintf(s, "%v", n.Op())
+ }
+
+ case OLABEL:
+ n := n.(*LabelStmt)
+ fmt.Fprintf(s, "%v: ", n.Label)
+ }
+
+ if extrablock {
+ fmt.Fprint(s, "}")
+ }
+}
+
+func exprFmt(n Node, s fmt.State, prec int) {
+ // NOTE(rsc): This code used to support the text-based
+ // which was more aggressive about printing full Go syntax
+ // (for example, an actual loop instead of "for loop").
+ // The code is preserved for now in case we want to expand
+ // any of those shortenings later. Or maybe we will delete
+ // the code. But for now, keep it.
+ const exportFormat = false
+
+ for {
+ if n == nil {
+ fmt.Fprint(s, "")
+ return
+ }
+
+ // We always want the original, if any.
+ if o := Orig(n); o != n {
+ n = o
+ continue
+ }
+
+ // Skip implicit operations introduced during typechecking.
+ switch nn := n; nn.Op() {
+ case OADDR:
+ nn := nn.(*AddrExpr)
+ if nn.Implicit() {
+ n = nn.X
+ continue
+ }
+ case ODEREF:
+ nn := nn.(*StarExpr)
+ if nn.Implicit() {
+ n = nn.X
+ continue
+ }
+ case OCONV, OCONVNOP, OCONVIFACE:
+ nn := nn.(*ConvExpr)
+ if nn.Implicit() {
+ n = nn.X
+ continue
+ }
+ }
+
+ break
+ }
+
+ nprec := OpPrec[n.Op()]
+ if n.Op() == OTYPE && n.Type().IsPtr() {
+ nprec = OpPrec[ODEREF]
+ }
+
+ if prec > nprec {
+ fmt.Fprintf(s, "(%v)", n)
+ return
+ }
+
+ switch n.Op() {
+ case OPAREN:
+ n := n.(*ParenExpr)
+ fmt.Fprintf(s, "(%v)", n.X)
+
+ case ONIL:
+ fmt.Fprint(s, "nil")
+
+ case OLITERAL: // this is a bit of a mess
+ if !exportFormat && n.Sym() != nil {
+ fmt.Fprint(s, n.Sym())
+ return
+ }
+
+ needUnparen := false
+ if n.Type() != nil && !n.Type().IsUntyped() {
+ // Need parens when type begins with what might
+ // be misinterpreted as a unary operator: * or <-.
+ if n.Type().IsPtr() || (n.Type().IsChan() && n.Type().ChanDir() == types.Crecv) {
+ fmt.Fprintf(s, "(%v)(", n.Type())
+ } else {
+ fmt.Fprintf(s, "%v(", n.Type())
+ }
+ needUnparen = true
+ }
+
+ if n.Type() == types.UntypedRune {
+ switch x, ok := constant.Uint64Val(n.Val()); {
+ case !ok:
+ fallthrough
+ default:
+ fmt.Fprintf(s, "('\\x00' + %v)", n.Val())
+
+ case x < utf8.RuneSelf:
+ fmt.Fprintf(s, "%q", x)
+
+ case x < 1<<16:
+ fmt.Fprintf(s, "'\\u%04x'", x)
+
+ case x <= utf8.MaxRune:
+ fmt.Fprintf(s, "'\\U%08x'", x)
+ }
+ } else {
+ fmt.Fprint(s, types.FmtConst(n.Val(), s.Flag('#')))
+ }
+
+ if needUnparen {
+ fmt.Fprintf(s, ")")
+ }
+
+ case ODCLFUNC:
+ n := n.(*Func)
+ if sym := n.Sym(); sym != nil {
+ fmt.Fprint(s, sym)
+ return
+ }
+ fmt.Fprintf(s, "")
+
+ case ONAME:
+ n := n.(*Name)
+ // Special case: name used as local variable in export.
+ // _ becomes ~b%d internally; print as _ for export
+ if !exportFormat && n.Sym() != nil && n.Sym().Name[0] == '~' && n.Sym().Name[1] == 'b' {
+ fmt.Fprint(s, "_")
+ return
+ }
+ fallthrough
+ case OPACK, ONONAME:
+ fmt.Fprint(s, n.Sym())
+
+ case OLINKSYMOFFSET:
+ n := n.(*LinksymOffsetExpr)
+ fmt.Fprintf(s, "(%v)(%s@%d)", n.Type(), n.Linksym.Name, n.Offset_)
+
+ case OTYPE:
+ if n.Type() == nil && n.Sym() != nil {
+ fmt.Fprint(s, n.Sym())
+ return
+ }
+ fmt.Fprintf(s, "%v", n.Type())
+
+ case OTSLICE:
+ n := n.(*SliceType)
+ if n.DDD {
+ fmt.Fprintf(s, "...%v", n.Elem)
+ } else {
+ fmt.Fprintf(s, "[]%v", n.Elem) // happens before typecheck
+ }
+
+ case OTARRAY:
+ n := n.(*ArrayType)
+ if n.Len == nil {
+ fmt.Fprintf(s, "[...]%v", n.Elem)
+ } else {
+ fmt.Fprintf(s, "[%v]%v", n.Len, n.Elem)
+ }
+
+ case OTMAP:
+ n := n.(*MapType)
+ fmt.Fprintf(s, "map[%v]%v", n.Key, n.Elem)
+
+ case OTCHAN:
+ n := n.(*ChanType)
+ switch n.Dir {
+ case types.Crecv:
+ fmt.Fprintf(s, "<-chan %v", n.Elem)
+
+ case types.Csend:
+ fmt.Fprintf(s, "chan<- %v", n.Elem)
+
+ default:
+ if n.Elem != nil && n.Elem.Op() == OTCHAN && n.Elem.(*ChanType).Dir == types.Crecv {
+ fmt.Fprintf(s, "chan (%v)", n.Elem)
+ } else {
+ fmt.Fprintf(s, "chan %v", n.Elem)
+ }
+ }
+
+ case OTSTRUCT:
+ fmt.Fprint(s, "")
+
+ case OTINTER:
+ fmt.Fprint(s, "")
+
+ case OTFUNC:
+ fmt.Fprint(s, "")
+
+ case OCLOSURE:
+ n := n.(*ClosureExpr)
+ if !exportFormat {
+ fmt.Fprint(s, "func literal")
+ return
+ }
+ fmt.Fprintf(s, "%v { %v }", n.Type(), n.Func.Body)
+
+ case OCOMPLIT:
+ n := n.(*CompLitExpr)
+ if !exportFormat {
+ if n.Implicit() {
+ fmt.Fprintf(s, "... argument")
+ return
+ }
+ if n.Ntype != nil {
+ fmt.Fprintf(s, "%v{%s}", n.Ntype, ellipsisIf(len(n.List) != 0))
+ return
+ }
+
+ fmt.Fprint(s, "composite literal")
+ return
+ }
+ fmt.Fprintf(s, "(%v{ %.v })", n.Ntype, n.List)
+
+ case OPTRLIT:
+ n := n.(*AddrExpr)
+ fmt.Fprintf(s, "&%v", n.X)
+
+ case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
+ n := n.(*CompLitExpr)
+ if !exportFormat {
+ fmt.Fprintf(s, "%v{%s}", n.Type(), ellipsisIf(len(n.List) != 0))
+ return
+ }
+ fmt.Fprintf(s, "(%v{ %.v })", n.Type(), n.List)
+
+ case OKEY:
+ n := n.(*KeyExpr)
+ if n.Key != nil && n.Value != nil {
+ fmt.Fprintf(s, "%v:%v", n.Key, n.Value)
+ return
+ }
+
+ if n.Key == nil && n.Value != nil {
+ fmt.Fprintf(s, ":%v", n.Value)
+ return
+ }
+ if n.Key != nil && n.Value == nil {
+ fmt.Fprintf(s, "%v:", n.Key)
+ return
+ }
+ fmt.Fprint(s, ":")
+
+ case OSTRUCTKEY:
+ n := n.(*StructKeyExpr)
+ fmt.Fprintf(s, "%v:%v", n.Field, n.Value)
+
+ case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OCALLPART, OMETHEXPR:
+ n := n.(*SelectorExpr)
+ exprFmt(n.X, s, nprec)
+ if n.Sel == nil {
+ fmt.Fprint(s, ".")
+ return
+ }
+ fmt.Fprintf(s, ".%s", n.Sel.Name)
+
+ case ODOTTYPE, ODOTTYPE2:
+ n := n.(*TypeAssertExpr)
+ exprFmt(n.X, s, nprec)
+ if n.Ntype != nil {
+ fmt.Fprintf(s, ".(%v)", n.Ntype)
+ return
+ }
+ fmt.Fprintf(s, ".(%v)", n.Type())
+
+ case OINDEX, OINDEXMAP:
+ n := n.(*IndexExpr)
+ exprFmt(n.X, s, nprec)
+ fmt.Fprintf(s, "[%v]", n.Index)
+
+ case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
+ n := n.(*SliceExpr)
+ exprFmt(n.X, s, nprec)
+ fmt.Fprint(s, "[")
+ if n.Low != nil {
+ fmt.Fprint(s, n.Low)
+ }
+ fmt.Fprint(s, ":")
+ if n.High != nil {
+ fmt.Fprint(s, n.High)
+ }
+ if n.Op().IsSlice3() {
+ fmt.Fprint(s, ":")
+ if n.Max != nil {
+ fmt.Fprint(s, n.Max)
+ }
+ }
+ fmt.Fprint(s, "]")
+
+ case OSLICEHEADER:
+ n := n.(*SliceHeaderExpr)
+ fmt.Fprintf(s, "sliceheader{%v,%v,%v}", n.Ptr, n.Len, n.Cap)
+
+ case OCOMPLEX, OCOPY:
+ n := n.(*BinaryExpr)
+ fmt.Fprintf(s, "%v(%v, %v)", n.Op(), n.X, n.Y)
+
+ case OCONV,
+ OCONVIFACE,
+ OCONVNOP,
+ OBYTES2STR,
+ ORUNES2STR,
+ OSTR2BYTES,
+ OSTR2RUNES,
+ ORUNESTR:
+ n := n.(*ConvExpr)
+ if n.Type() == nil || n.Type().Sym() == nil {
+ fmt.Fprintf(s, "(%v)", n.Type())
+ } else {
+ fmt.Fprintf(s, "%v", n.Type())
+ }
+ fmt.Fprintf(s, "(%v)", n.X)
+
+ case OREAL,
+ OIMAG,
+ OCAP,
+ OCLOSE,
+ OLEN,
+ ONEW,
+ OPANIC,
+ OALIGNOF,
+ OOFFSETOF,
+ OSIZEOF:
+ n := n.(*UnaryExpr)
+ fmt.Fprintf(s, "%v(%v)", n.Op(), n.X)
+
+ case OAPPEND,
+ ODELETE,
+ OMAKE,
+ ORECOVER,
+ OPRINT,
+ OPRINTN:
+ n := n.(*CallExpr)
+ if n.IsDDD {
+ fmt.Fprintf(s, "%v(%.v...)", n.Op(), n.Args)
+ return
+ }
+ fmt.Fprintf(s, "%v(%.v)", n.Op(), n.Args)
+
+ case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG:
+ n := n.(*CallExpr)
+ exprFmt(n.X, s, nprec)
+ if n.IsDDD {
+ fmt.Fprintf(s, "(%.v...)", n.Args)
+ return
+ }
+ fmt.Fprintf(s, "(%.v)", n.Args)
+
+ case OMAKEMAP, OMAKECHAN, OMAKESLICE:
+ n := n.(*MakeExpr)
+ if n.Cap != nil {
+ fmt.Fprintf(s, "make(%v, %v, %v)", n.Type(), n.Len, n.Cap)
+ return
+ }
+ if n.Len != nil && (n.Op() == OMAKESLICE || !n.Len.Type().IsUntyped()) {
+ fmt.Fprintf(s, "make(%v, %v)", n.Type(), n.Len)
+ return
+ }
+ fmt.Fprintf(s, "make(%v)", n.Type())
+
+ case OMAKESLICECOPY:
+ n := n.(*MakeExpr)
+ fmt.Fprintf(s, "makeslicecopy(%v, %v, %v)", n.Type(), n.Len, n.Cap)
+
+ case OPLUS, ONEG, OBITNOT, ONOT, ORECV:
+ // Unary
+ n := n.(*UnaryExpr)
+ fmt.Fprintf(s, "%v", n.Op())
+ if n.X != nil && n.X.Op() == n.Op() {
+ fmt.Fprint(s, " ")
+ }
+ exprFmt(n.X, s, nprec+1)
+
+ case OADDR:
+ n := n.(*AddrExpr)
+ fmt.Fprintf(s, "%v", n.Op())
+ if n.X != nil && n.X.Op() == n.Op() {
+ fmt.Fprint(s, " ")
+ }
+ exprFmt(n.X, s, nprec+1)
+
+ case ODEREF:
+ n := n.(*StarExpr)
+ fmt.Fprintf(s, "%v", n.Op())
+ exprFmt(n.X, s, nprec+1)
+
+ // Binary
+ case OADD,
+ OAND,
+ OANDNOT,
+ ODIV,
+ OEQ,
+ OGE,
+ OGT,
+ OLE,
+ OLT,
+ OLSH,
+ OMOD,
+ OMUL,
+ ONE,
+ OOR,
+ ORSH,
+ OSUB,
+ OXOR:
+ n := n.(*BinaryExpr)
+ exprFmt(n.X, s, nprec)
+ fmt.Fprintf(s, " %v ", n.Op())
+ exprFmt(n.Y, s, nprec+1)
+
+ case OANDAND,
+ OOROR:
+ n := n.(*LogicalExpr)
+ exprFmt(n.X, s, nprec)
+ fmt.Fprintf(s, " %v ", n.Op())
+ exprFmt(n.Y, s, nprec+1)
+
+ case OSEND:
+ n := n.(*SendStmt)
+ exprFmt(n.Chan, s, nprec)
+ fmt.Fprintf(s, " <- ")
+ exprFmt(n.Value, s, nprec+1)
+
+ case OADDSTR:
+ n := n.(*AddStringExpr)
+ for i, n1 := range n.List {
+ if i != 0 {
+ fmt.Fprint(s, " + ")
+ }
+ exprFmt(n1, s, nprec)
+ }
+ default:
+ fmt.Fprintf(s, "", n.Op())
+ }
+}
+
+func ellipsisIf(b bool) string {
+ if b {
+ return "..."
+ }
+ return ""
+}
+
+// Nodes
+
+// Format implements formatting for a Nodes.
+// The valid formats are:
+//
+// %v Go syntax, semicolon-separated
+// %.v Go syntax, comma-separated
+// %+v Debug syntax, as in DumpList.
+//
+func (l Nodes) Format(s fmt.State, verb rune) {
+ if s.Flag('+') && verb == 'v' {
+ // %+v is DumpList output
+ dumpNodes(s, l, 1)
+ return
+ }
+
+ if verb != 'v' {
+ fmt.Fprintf(s, "%%!%c(Nodes)", verb)
+ return
+ }
+
+ sep := "; "
+ if _, ok := s.Precision(); ok { // %.v is expr list
+ sep = ", "
+ }
+
+ for i, n := range l {
+ fmt.Fprint(s, n)
+ if i+1 < len(l) {
+ fmt.Fprint(s, sep)
+ }
+ }
+}
+
+// Dump
+
+// Dump prints the message s followed by a debug dump of n.
+func Dump(s string, n Node) {
+ fmt.Printf("%s [%p]%+v\n", s, n, n)
+}
+
+// DumpList prints the message s followed by a debug dump of each node in the list.
+func DumpList(s string, list Nodes) {
+ var buf bytes.Buffer
+ FDumpList(&buf, s, list)
+ os.Stdout.Write(buf.Bytes())
+}
+
+// FDumpList prints to w the message s followed by a debug dump of each node in the list.
+func FDumpList(w io.Writer, s string, list Nodes) {
+ io.WriteString(w, s)
+ dumpNodes(w, list, 1)
+ io.WriteString(w, "\n")
+}
+
+// indent prints indentation to w.
+func indent(w io.Writer, depth int) {
+ fmt.Fprint(w, "\n")
+ for i := 0; i < depth; i++ {
+ fmt.Fprint(w, ". ")
+ }
+}
+
+// EscFmt is set by the escape analysis code to add escape analysis details to the node print.
+var EscFmt func(n Node) string
+
+// dumpNodeHeader prints the debug-format node header line to w.
+func dumpNodeHeader(w io.Writer, n Node) {
+ // Useful to see which nodes in an AST printout are actually identical
+ if base.Debug.DumpPtrs != 0 {
+ fmt.Fprintf(w, " p(%p)", n)
+ }
+
+ if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Defn != nil {
+ // Useful to see where Defn is set and what node it points to
+ fmt.Fprintf(w, " defn(%p)", n.Name().Defn)
+ }
+
+ if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Curfn != nil {
+ // Useful to see where Defn is set and what node it points to
+ fmt.Fprintf(w, " curfn(%p)", n.Name().Curfn)
+ }
+ if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Outer != nil {
+ // Useful to see where Defn is set and what node it points to
+ fmt.Fprintf(w, " outer(%p)", n.Name().Outer)
+ }
+
+ if EscFmt != nil {
+ if esc := EscFmt(n); esc != "" {
+ fmt.Fprintf(w, " %s", esc)
+ }
+ }
+
+ if n.Typecheck() != 0 {
+ fmt.Fprintf(w, " tc(%d)", n.Typecheck())
+ }
+
+ // Print Node-specific fields of basic type in header line.
+ v := reflect.ValueOf(n).Elem()
+ t := v.Type()
+ nf := t.NumField()
+ for i := 0; i < nf; i++ {
+ tf := t.Field(i)
+ if tf.PkgPath != "" {
+ // skip unexported field - Interface will fail
+ continue
+ }
+ k := tf.Type.Kind()
+ if reflect.Bool <= k && k <= reflect.Complex128 {
+ name := strings.TrimSuffix(tf.Name, "_")
+ vf := v.Field(i)
+ vfi := vf.Interface()
+ if name == "Offset" && vfi == types.BADWIDTH || name != "Offset" && isZero(vf) {
+ continue
+ }
+ if vfi == true {
+ fmt.Fprintf(w, " %s", name)
+ } else {
+ fmt.Fprintf(w, " %s:%+v", name, vf.Interface())
+ }
+ }
+ }
+
+ // Print Node-specific booleans by looking for methods.
+ // Different v, t from above - want *Struct not Struct, for methods.
+ v = reflect.ValueOf(n)
+ t = v.Type()
+ nm := t.NumMethod()
+ for i := 0; i < nm; i++ {
+ tm := t.Method(i)
+ if tm.PkgPath != "" {
+ // skip unexported method - call will fail
+ continue
+ }
+ m := v.Method(i)
+ mt := m.Type()
+ if mt.NumIn() == 0 && mt.NumOut() == 1 && mt.Out(0).Kind() == reflect.Bool {
+ // TODO(rsc): Remove the func/defer/recover wrapping,
+ // which is guarding against panics in miniExpr,
+ // once we get down to the simpler state in which
+ // nodes have no getter methods that aren't allowed to be called.
+ func() {
+ defer func() { recover() }()
+ if m.Call(nil)[0].Bool() {
+ name := strings.TrimSuffix(tm.Name, "_")
+ fmt.Fprintf(w, " %s", name)
+ }
+ }()
+ }
+ }
+
+ if n.Op() == OCLOSURE {
+ n := n.(*ClosureExpr)
+ if fn := n.Func; fn != nil && fn.Nname.Sym() != nil {
+ fmt.Fprintf(w, " fnName(%+v)", fn.Nname.Sym())
+ }
+ }
+
+ if n.Type() != nil {
+ if n.Op() == OTYPE {
+ fmt.Fprintf(w, " type")
+ }
+ fmt.Fprintf(w, " %+v", n.Type())
+ }
+
+ if n.Pos().IsKnown() {
+ pfx := ""
+ switch n.Pos().IsStmt() {
+ case src.PosNotStmt:
+ pfx = "_" // "-" would be confusing
+ case src.PosIsStmt:
+ pfx = "+"
+ }
+ pos := base.Ctxt.PosTable.Pos(n.Pos())
+ file := filepath.Base(pos.Filename())
+ fmt.Fprintf(w, " # %s%s:%d", pfx, file, pos.Line())
+ }
+}
+
+func dumpNode(w io.Writer, n Node, depth int) {
+ indent(w, depth)
+ if depth > 40 {
+ fmt.Fprint(w, "...")
+ return
+ }
+
+ if n == nil {
+ fmt.Fprint(w, "NilIrNode")
+ return
+ }
+
+ if len(n.Init()) != 0 {
+ fmt.Fprintf(w, "%+v-init", n.Op())
+ dumpNodes(w, n.Init(), depth+1)
+ indent(w, depth)
+ }
+
+ switch n.Op() {
+ default:
+ fmt.Fprintf(w, "%+v", n.Op())
+ dumpNodeHeader(w, n)
+
+ case OLITERAL:
+ fmt.Fprintf(w, "%+v-%v", n.Op(), n.Val())
+ dumpNodeHeader(w, n)
+ return
+
+ case ONAME, ONONAME:
+ if n.Sym() != nil {
+ fmt.Fprintf(w, "%+v-%+v", n.Op(), n.Sym())
+ } else {
+ fmt.Fprintf(w, "%+v", n.Op())
+ }
+ dumpNodeHeader(w, n)
+ if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-ntype", n.Op())
+ dumpNode(w, n.Name().Ntype, depth+1)
+ }
+ return
+
+ case OASOP:
+ n := n.(*AssignOpStmt)
+ fmt.Fprintf(w, "%+v-%+v", n.Op(), n.AsOp)
+ dumpNodeHeader(w, n)
+
+ case OTYPE:
+ fmt.Fprintf(w, "%+v %+v", n.Op(), n.Sym())
+ dumpNodeHeader(w, n)
+ if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-ntype", n.Op())
+ dumpNode(w, n.Name().Ntype, depth+1)
+ }
+ return
+
+ case OCLOSURE:
+ fmt.Fprintf(w, "%+v", n.Op())
+ dumpNodeHeader(w, n)
+
+ case ODCLFUNC:
+ // Func has many fields we don't want to print.
+ // Bypass reflection and just print what we want.
+ n := n.(*Func)
+ fmt.Fprintf(w, "%+v", n.Op())
+ dumpNodeHeader(w, n)
+ fn := n
+ if len(fn.Dcl) > 0 {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-Dcl", n.Op())
+ for _, dcl := range n.Dcl {
+ dumpNode(w, dcl, depth+1)
+ }
+ }
+ if len(fn.ClosureVars) > 0 {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-ClosureVars", n.Op())
+ for _, cv := range fn.ClosureVars {
+ dumpNode(w, cv, depth+1)
+ }
+ }
+ if len(fn.Enter) > 0 {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-Enter", n.Op())
+ dumpNodes(w, fn.Enter, depth+1)
+ }
+ if len(fn.Body) > 0 {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-body", n.Op())
+ dumpNodes(w, fn.Body, depth+1)
+ }
+ return
+ }
+
+ if n.Sym() != nil {
+ fmt.Fprintf(w, " %+v", n.Sym())
+ }
+ if n.Type() != nil {
+ fmt.Fprintf(w, " %+v", n.Type())
+ }
+
+ v := reflect.ValueOf(n).Elem()
+ t := reflect.TypeOf(n).Elem()
+ nf := t.NumField()
+ for i := 0; i < nf; i++ {
+ tf := t.Field(i)
+ vf := v.Field(i)
+ if tf.PkgPath != "" {
+ // skip unexported field - Interface will fail
+ continue
+ }
+ switch tf.Type.Kind() {
+ case reflect.Interface, reflect.Ptr, reflect.Slice:
+ if vf.IsNil() {
+ continue
+ }
+ }
+ name := strings.TrimSuffix(tf.Name, "_")
+ // Do not bother with field name header lines for the
+ // most common positional arguments: unary, binary expr,
+ // index expr, send stmt, go and defer call expression.
+ switch name {
+ case "X", "Y", "Index", "Chan", "Value", "Call":
+ name = ""
+ }
+ switch val := vf.Interface().(type) {
+ case Node:
+ if name != "" {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-%s", n.Op(), name)
+ }
+ dumpNode(w, val, depth+1)
+ case Nodes:
+ if len(val) == 0 {
+ continue
+ }
+ if name != "" {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-%s", n.Op(), name)
+ }
+ dumpNodes(w, val, depth+1)
+ default:
+ if vf.Kind() == reflect.Slice && vf.Type().Elem().Implements(nodeType) {
+ if vf.Len() == 0 {
+ continue
+ }
+ if name != "" {
+ indent(w, depth)
+ fmt.Fprintf(w, "%+v-%s", n.Op(), name)
+ }
+ for i, n := 0, vf.Len(); i < n; i++ {
+ dumpNode(w, vf.Index(i).Interface().(Node), depth+1)
+ }
+ }
+ }
+ }
+}
+
+var nodeType = reflect.TypeOf((*Node)(nil)).Elem()
+
+func dumpNodes(w io.Writer, list Nodes, depth int) {
+ if len(list) == 0 {
+ fmt.Fprintf(w, " ")
+ return
+ }
+
+ for _, n := range list {
+ dumpNode(w, n, depth)
+ }
+}
+
+// reflect.IsZero is not available in Go 1.4 (added in Go 1.13), so we use this copy instead.
+func isZero(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return math.Float64bits(v.Float()) == 0
+ case reflect.Complex64, reflect.Complex128:
+ c := v.Complex()
+ return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
+ case reflect.Array:
+ for i := 0; i < v.Len(); i++ {
+ if !isZero(v.Index(i)) {
+ return false
+ }
+ }
+ return true
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
+ return v.IsNil()
+ case reflect.String:
+ return v.Len() == 0
+ case reflect.Struct:
+ for i := 0; i < v.NumField(); i++ {
+ if !isZero(v.Field(i)) {
+ return false
+ }
+ }
+ return true
+ default:
+ return false
+ }
+}
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go
new file mode 100644
index 0000000000000..0a9db92d96713
--- /dev/null
+++ b/src/cmd/compile/internal/ir/func.go
@@ -0,0 +1,284 @@
+// 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 ir
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+// A Func corresponds to a single function in a Go program
+// (and vice versa: each function is denoted by exactly one *Func).
+//
+// There are multiple nodes that represent a Func in the IR.
+//
+// The ONAME node (Func.Nname) is used for plain references to it.
+// The ODCLFUNC node (the Func itself) is used for its declaration code.
+// The OCLOSURE node (Func.OClosure) is used for a reference to a
+// function literal.
+//
+// An imported function will have an ONAME node which points to a Func
+// with an empty body.
+// A declared function or method has an ODCLFUNC (the Func itself) and an ONAME.
+// A function literal is represented directly by an OCLOSURE, but it also
+// has an ODCLFUNC (and a matching ONAME) representing the compiled
+// underlying form of the closure, which accesses the captured variables
+// using a special data structure passed in a register.
+//
+// A method declaration is represented like functions, except f.Sym
+// will be the qualified method name (e.g., "T.m") and
+// f.Func.Shortname is the bare method name (e.g., "m").
+//
+// A method expression (T.M) is represented as an OMETHEXPR node,
+// in which n.Left and n.Right point to the type and method, respectively.
+// Each distinct mention of a method expression in the source code
+// constructs a fresh node.
+//
+// A method value (t.M) is represented by ODOTMETH/ODOTINTER
+// when it is called directly and by OCALLPART otherwise.
+// These are like method expressions, except that for ODOTMETH/ODOTINTER,
+// the method name is stored in Sym instead of Right.
+// Each OCALLPART ends up being implemented as a new
+// function, a bit like a closure, with its own ODCLFUNC.
+// The OCALLPART uses n.Func to record the linkage to
+// the generated ODCLFUNC, but there is no
+// pointer from the Func back to the OCALLPART.
+type Func struct {
+ miniNode
+ Body Nodes
+ Iota int64
+
+ Nname *Name // ONAME node
+ OClosure *ClosureExpr // OCLOSURE node
+
+ Shortname *types.Sym
+
+ // Extra entry code for the function. For example, allocate and initialize
+ // memory for escaping parameters.
+ Enter Nodes
+ Exit Nodes
+
+ // ONAME nodes for all params/locals for this func/closure, does NOT
+ // include closurevars until transforming closures during walk.
+ // 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
+
+ // 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
+ // own copy of the variables, which are used within its function
+ // body. They will also each have IsClosureVar set, and will have
+ // 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].
+ Parents []ScopeID
+
+ // Marks records scope boundary changes.
+ Marks []Mark
+
+ FieldTrack map[*obj.LSym]struct{}
+ DebugInfo interface{}
+ LSym *obj.LSym
+
+ Inl *Inline
+
+ // Closgen tracks how many closures have been generated within
+ // this function. Used by closurename for creating unique
+ // function names.
+ Closgen int32
+
+ Label int32 // largest auto-generated label in this function
+
+ Endlineno src.XPos
+ WBPos src.XPos // position of first write barrier; see SetWBPos
+
+ Pragma PragmaFlag // go:xxx function annotations
+
+ flags bitset16
+ NumDefers int32 // number of defer calls in the function
+ NumReturns int32 // number of explicit returns in the function
+
+ // nwbrCalls records the LSyms of functions called by this
+ // function for go:nowritebarrierrec analysis. Only filled in
+ // if nowritebarrierrecCheck != nil.
+ NWBRCalls *[]SymAndPos
+}
+
+func NewFunc(pos src.XPos) *Func {
+ f := new(Func)
+ f.pos = pos
+ f.op = ODCLFUNC
+ f.Iota = -1
+ return f
+}
+
+func (f *Func) isStmt() {}
+
+func (n *Func) copy() Node { panic(n.no("copy")) }
+func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) }
+func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) }
+
+func (f *Func) Type() *types.Type { return f.Nname.Type() }
+func (f *Func) Sym() *types.Sym { return f.Nname.Sym() }
+func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() }
+func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) }
+
+// An Inline holds fields used for function bodies that can be inlined.
+type Inline struct {
+ Cost int32 // heuristic cost of inlining this function
+
+ // Copies of Func.Dcl and Nbody for use during inlining.
+ Dcl []*Name
+ Body []Node
+}
+
+// A Mark represents a scope boundary.
+type Mark struct {
+ // Pos is the position of the token that marks the scope
+ // change.
+ Pos src.XPos
+
+ // Scope identifies the innermost scope to the right of Pos.
+ Scope ScopeID
+}
+
+// A ScopeID represents a lexical scope within a function.
+type ScopeID int32
+
+const (
+ funcDupok = 1 << iota // duplicate definitions ok
+ funcWrapper // is method wrapper
+ funcNeedctxt // function uses context register (has closure variables)
+ funcReflectMethod // function calls reflect.Type.Method or MethodByName
+ // true if closure inside a function; false if a simple function or a
+ // closure in a global variable initialization
+ funcIsHiddenClosure
+ funcHasDefer // contains a defer statement
+ funcNilCheckDisabled // disable nil checks when compiling this function
+ funcInlinabilityChecked // inliner has already determined whether the function is inlinable
+ funcExportInline // include inline body in export data
+ funcInstrumentBody // add race/msan instrumentation during SSA construction
+ funcOpenCodedDeferDisallowed // can't do open-coded defers
+ funcClosureCalled // closure is only immediately called
+)
+
+type SymAndPos struct {
+ Sym *obj.LSym // LSym of callee
+ Pos src.XPos // line of call
+}
+
+func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
+func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 }
+func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 }
+func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 }
+func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 }
+func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 }
+func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
+func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
+func (f *Func) ExportInline() bool { return f.flags&funcExportInline != 0 }
+func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 }
+func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
+func (f *Func) ClosureCalled() bool { return f.flags&funcClosureCalled != 0 }
+
+func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
+func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
+func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) }
+func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) }
+func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) }
+func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) }
+func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
+func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
+func (f *Func) SetExportInline(b bool) { f.flags.set(funcExportInline, b) }
+func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) }
+func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
+func (f *Func) SetClosureCalled(b bool) { f.flags.set(funcClosureCalled, b) }
+
+func (f *Func) SetWBPos(pos src.XPos) {
+ if base.Debug.WB != 0 {
+ base.WarnfAt(pos, "write barrier")
+ }
+ if !f.WBPos.IsKnown() {
+ f.WBPos = pos
+ }
+}
+
+// funcname returns the name (without the package) of the function n.
+func FuncName(f *Func) string {
+ if f == nil || f.Nname == nil {
+ return ""
+ }
+ return f.Sym().Name
+}
+
+// pkgFuncName returns the name of the function referenced by n, with package prepended.
+// This differs from the compiler's internal convention where local functions lack a package
+// because the ultimate consumer of this is a human looking at an IDE; package is only empty
+// if the compilation package is actually the empty string.
+func PkgFuncName(f *Func) string {
+ if f == nil || f.Nname == nil {
+ return ""
+ }
+ s := f.Sym()
+ pkg := s.Pkg
+
+ p := base.Ctxt.Pkgpath
+ if pkg != nil && pkg.Path != "" {
+ p = pkg.Path
+ }
+ if p == "" {
+ return s.Name
+ }
+ return p + "." + s.Name
+}
+
+var CurFunc *Func
+
+func FuncSymName(s *types.Sym) string {
+ return s.Name + "·f"
+}
+
+// MarkFunc marks a node as a function.
+func MarkFunc(n *Name) {
+ if n.Op() != ONAME || n.Class != Pxxx {
+ base.Fatalf("expected ONAME/Pxxx node, got %v", n)
+ }
+
+ n.Class = PFUNC
+ n.Sym().SetFunc(true)
+}
+
+// ClosureDebugRuntimeCheck applies boilerplate checks for debug flags
+// and compiling runtime
+func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
+ if base.Debug.Closure > 0 {
+ if clo.Esc() == EscHeap {
+ base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
+ } else {
+ base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
+ }
+ }
+ if base.Flag.CompilingRuntime && clo.Esc() == EscHeap {
+ base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime")
+ }
+}
+
+// IsTrivialClosure reports whether closure clo has an
+// empty list of captured vars.
+func IsTrivialClosure(clo *ClosureExpr) bool {
+ return len(clo.Func.ClosureVars) == 0
+}
diff --git a/src/cmd/compile/internal/ir/ir.go b/src/cmd/compile/internal/ir/ir.go
new file mode 100644
index 0000000000000..82224ca2ed835
--- /dev/null
+++ b/src/cmd/compile/internal/ir/ir.go
@@ -0,0 +1,5 @@
+// Copyright 2009 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 ir
diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go
new file mode 100644
index 0000000000000..a7ff4ac9c77a1
--- /dev/null
+++ b/src/cmd/compile/internal/ir/mini.go
@@ -0,0 +1,92 @@
+// 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.
+
+//go:generate go run -mod=mod mknode.go
+
+package ir
+
+import (
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+ "fmt"
+ "go/constant"
+)
+
+// A miniNode is a minimal node implementation,
+// meant to be embedded as the first field in a larger node implementation,
+// at a cost of 8 bytes.
+//
+// A miniNode is NOT a valid Node by itself: the embedding struct
+// must at the least provide:
+//
+// func (n *MyNode) String() string { return fmt.Sprint(n) }
+// func (n *MyNode) rawCopy() Node { c := *n; return &c }
+// func (n *MyNode) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) }
+//
+// The embedding struct should also fill in n.op in its constructor,
+// for more useful panic messages when invalid methods are called,
+// instead of implementing Op itself.
+//
+type miniNode struct {
+ pos src.XPos // uint32
+ op Op // uint8
+ bits bitset8
+ esc uint16
+}
+
+// posOr returns pos if known, or else n.pos.
+// For use in DeepCopy.
+func (n *miniNode) posOr(pos src.XPos) src.XPos {
+ if pos.IsKnown() {
+ return pos
+ }
+ return n.pos
+}
+
+// op can be read, but not written.
+// An embedding implementation can provide a SetOp if desired.
+// (The panicking SetOp is with the other panics below.)
+func (n *miniNode) Op() Op { return n.op }
+func (n *miniNode) Pos() src.XPos { return n.pos }
+func (n *miniNode) SetPos(x src.XPos) { n.pos = x }
+func (n *miniNode) Esc() uint16 { return n.esc }
+func (n *miniNode) SetEsc(x uint16) { n.esc = x }
+
+const (
+ miniWalkdefShift = 0 // TODO(mdempsky): Move to Name.flags.
+ miniTypecheckShift = 2
+ miniDiag = 1 << 4
+ miniWalked = 1 << 5 // to prevent/catch re-walking
+)
+
+func (n *miniNode) Typecheck() uint8 { return n.bits.get2(miniTypecheckShift) }
+func (n *miniNode) SetTypecheck(x uint8) {
+ if x > 3 {
+ panic(fmt.Sprintf("cannot SetTypecheck %d", x))
+ }
+ n.bits.set2(miniTypecheckShift, x)
+}
+
+func (n *miniNode) Diag() bool { return n.bits&miniDiag != 0 }
+func (n *miniNode) SetDiag(x bool) { n.bits.set(miniDiag, x) }
+
+func (n *miniNode) Walked() bool { return n.bits&miniWalked != 0 }
+func (n *miniNode) SetWalked(x bool) { n.bits.set(miniWalked, x) }
+
+// Empty, immutable graph structure.
+
+func (n *miniNode) Init() Nodes { return Nodes{} }
+
+// Additional functionality unavailable.
+
+func (n *miniNode) no(name string) string { return "cannot " + name + " on " + n.op.String() }
+
+func (n *miniNode) Type() *types.Type { return nil }
+func (n *miniNode) SetType(*types.Type) { panic(n.no("SetType")) }
+func (n *miniNode) Name() *Name { return nil }
+func (n *miniNode) Sym() *types.Sym { return nil }
+func (n *miniNode) Val() constant.Value { panic(n.no("Val")) }
+func (n *miniNode) SetVal(v constant.Value) { panic(n.no("SetVal")) }
+func (n *miniNode) NonNil() bool { return false }
+func (n *miniNode) MarkNonNil() { panic(n.no("MarkNonNil")) }
diff --git a/src/cmd/compile/internal/ir/mknode.go b/src/cmd/compile/internal/ir/mknode.go
new file mode 100644
index 0000000000000..326f491a69d34
--- /dev/null
+++ b/src/cmd/compile/internal/ir/mknode.go
@@ -0,0 +1,228 @@
+// 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
+
+import (
+ "bytes"
+ "fmt"
+ "go/format"
+ "go/types"
+ "io/ioutil"
+ "log"
+ "reflect"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/packages"
+)
+
+var irPkg *types.Package
+var buf bytes.Buffer
+
+func main() {
+ cfg := &packages.Config{
+ Mode: packages.NeedSyntax | packages.NeedTypes,
+ }
+ pkgs, err := packages.Load(cfg, "cmd/compile/internal/ir")
+ if err != nil {
+ log.Fatal(err)
+ }
+ irPkg = pkgs[0].Types
+
+ fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, "package ir")
+ fmt.Fprintln(&buf)
+ fmt.Fprintln(&buf, `import "fmt"`)
+
+ scope := irPkg.Scope()
+ for _, name := range scope.Names() {
+ if strings.HasPrefix(name, "mini") {
+ continue
+ }
+
+ obj, ok := scope.Lookup(name).(*types.TypeName)
+ if !ok {
+ continue
+ }
+ typ := obj.Type().(*types.Named)
+ if !implementsNode(types.NewPointer(typ)) {
+ continue
+ }
+
+ fmt.Fprintf(&buf, "\n")
+ fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name)
+
+ switch name {
+ case "Name", "Func":
+ // Too specialized to automate.
+ continue
+ }
+
+ forNodeFields(typ,
+ "func (n *%[1]s) copy() Node { c := *n\n",
+ "",
+ "c.%[1]s = copy%[2]s(c.%[1]s)",
+ "return &c }\n")
+
+ forNodeFields(typ,
+ "func (n *%[1]s) doChildren(do func(Node) bool) bool {\n",
+ "if n.%[1]s != nil && do(n.%[1]s) { return true }",
+ "if do%[2]s(n.%[1]s, do) { return true }",
+ "return false }\n")
+
+ forNodeFields(typ,
+ "func (n *%[1]s) editChildren(edit func(Node) Node) {\n",
+ "if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }",
+ "edit%[2]s(n.%[1]s, edit)",
+ "}\n")
+ }
+
+ makeHelpers()
+
+ out, err := format.Source(buf.Bytes())
+ if err != nil {
+ // write out mangled source so we can see the bug.
+ out = buf.Bytes()
+ }
+
+ err = ioutil.WriteFile("node_gen.go", out, 0666)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+// needHelper maps needed slice helpers from their base name to their
+// respective slice-element type.
+var needHelper = map[string]string{}
+
+func makeHelpers() {
+ var names []string
+ for name := range needHelper {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+
+ for _, name := range names {
+ fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name])
+ }
+}
+
+const sliceHelperTmpl = `
+func copy%[1]s(list []%[2]s) []%[2]s {
+ if list == nil {
+ return nil
+ }
+ c := make([]%[2]s, len(list))
+ copy(c, list)
+ return c
+}
+func do%[1]s(list []%[2]s, do func(Node) bool) bool {
+ for _, x := range list {
+ if x != nil && do(x) {
+ return true
+ }
+ }
+ return false
+}
+func edit%[1]s(list []%[2]s, edit func(Node) Node) {
+ for i, x := range list {
+ if x != nil {
+ list[i] = edit(x).(%[2]s)
+ }
+ }
+}
+`
+
+func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) {
+ fmt.Fprintf(&buf, prologue, named.Obj().Name())
+
+ anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool {
+ if f.Embedded() {
+ return false
+ }
+ name, typ := f.Name(), f.Type()
+
+ slice, _ := typ.Underlying().(*types.Slice)
+ if slice != nil {
+ typ = slice.Elem()
+ }
+
+ tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg))
+ if implementsNode(typ) {
+ if slice != nil {
+ helper := strings.TrimPrefix(what, "*") + "s"
+ needHelper[helper] = what
+ tmpl, what = sliceTmpl, helper
+ }
+ } else if what == "*Field" {
+ // Special case for *Field.
+ tmpl = sliceTmpl
+ if slice != nil {
+ what = "Fields"
+ } else {
+ what = "Field"
+ }
+ } else {
+ return false
+ }
+
+ if tmpl == "" {
+ return false
+ }
+
+ // Allow template to not use all arguments without
+ // upsetting fmt.Printf.
+ s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what)
+ fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")])
+ return false
+ })
+
+ fmt.Fprintf(&buf, epilogue)
+}
+
+func implementsNode(typ types.Type) bool {
+ if _, ok := typ.Underlying().(*types.Interface); ok {
+ // TODO(mdempsky): Check the interface implements Node.
+ // Worst case, node_gen.go will fail to compile if we're wrong.
+ return true
+ }
+
+ if ptr, ok := typ.(*types.Pointer); ok {
+ if str, ok := ptr.Elem().Underlying().(*types.Struct); ok {
+ return anyField(str, func(f *types.Var) bool {
+ return f.Embedded() && f.Name() == "miniNode"
+ })
+ }
+ }
+
+ return false
+}
+
+func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool {
+ for i, n := 0, typ.NumFields(); i < n; i++ {
+ if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok {
+ if value != "-" {
+ panic(fmt.Sprintf("unexpected tag value: %q", value))
+ }
+ continue
+ }
+
+ f := typ.Field(i)
+ if pred(f) {
+ return true
+ }
+ if f.Embedded() {
+ if typ, ok := f.Type().Underlying().(*types.Struct); ok {
+ if anyField(typ, pred) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go
new file mode 100644
index 0000000000000..fa0639600ce0a
--- /dev/null
+++ b/src/cmd/compile/internal/ir/name.go
@@ -0,0 +1,512 @@
+// 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 ir
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+ "fmt"
+
+ "go/constant"
+)
+
+// An Ident is an identifier, possibly qualified.
+type Ident struct {
+ miniExpr
+ sym *types.Sym
+}
+
+func NewIdent(pos src.XPos, sym *types.Sym) *Ident {
+ n := new(Ident)
+ n.op = ONONAME
+ n.pos = pos
+ n.sym = sym
+ return n
+}
+
+func (n *Ident) Sym() *types.Sym { return n.sym }
+
+func (*Ident) CanBeNtype() {}
+
+// Name holds Node fields used only by named nodes (ONAME, OTYPE, some OLITERAL).
+type Name struct {
+ miniExpr
+ BuiltinOp Op // uint8
+ Class Class // uint8
+ pragma PragmaFlag // int16
+ flags bitset16
+ sym *types.Sym
+ Func *Func
+ Offset_ int64
+ val constant.Value
+ Opt interface{} // for use by escape analysis
+ Embed *[]Embed // list of embedded files, for ONAME var
+
+ PkgName *PkgName // real package for import . names
+ // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2).
+ // For a closure var, the ONAME node of the outer captured variable
+ Defn Node
+
+ // The function, method, or closure in which local variable or param is declared.
+ Curfn *Func
+
+ Ntype Ntype
+ Heapaddr *Name // temp holding heap address of param
+
+ // ONAME closure linkage
+ // Consider:
+ //
+ // func f() {
+ // x := 1 // x1
+ // func() {
+ // use(x) // x2
+ // func() {
+ // use(x) // x3
+ // --- parser is here ---
+ // }()
+ // }()
+ // }
+ //
+ // There is an original declaration of x and then a chain of mentions of x
+ // leading into the current function. Each time x is mentioned in a new closure,
+ // we create a variable representing x for use in that specific closure,
+ // since the way you get to x is different in each closure.
+ //
+ // Let's number the specific variables as shown in the code:
+ // x1 is the original x, x2 is when mentioned in the closure,
+ // and x3 is when mentioned in the closure in the closure.
+ //
+ // We keep these linked (assume N > 1):
+ //
+ // - x1.Defn = original declaration statement for x (like most variables)
+ // - x1.Innermost = current innermost closure x (in this case x3), or nil for none
+ // - x1.IsClosureVar() = false
+ //
+ // - xN.Defn = x1, N > 1
+ // - xN.IsClosureVar() = true, N > 1
+ // - x2.Outer = nil
+ // - xN.Outer = x(N-1), N > 2
+ //
+ //
+ // When we look up x in the symbol table, we always get x1.
+ // Then we can use x1.Innermost (if not nil) to get the x
+ // for the innermost known closure function,
+ // but the first reference in a closure will find either no x1.Innermost
+ // or an x1.Innermost with .Funcdepth < Funcdepth.
+ // In that case, a new xN must be created, linked in with:
+ //
+ // xN.Defn = x1
+ // xN.Outer = x1.Innermost
+ // x1.Innermost = xN
+ //
+ // When we finish the function, we'll process its closure variables
+ // and find xN and pop it off the list using:
+ //
+ // x1 := xN.Defn
+ // x1.Innermost = xN.Outer
+ //
+ // We leave x1.Innermost set so that we can still get to the original
+ // variable quickly. Not shown here, but once we're
+ // done parsing a function and no longer need xN.Outer for the
+ // lexical x reference links as described above, funcLit
+ // recomputes xN.Outer as the semantic x reference link tree,
+ // even filling in x in intermediate closures that might not
+ // have mentioned it along the way to inner closures that did.
+ // See funcLit for details.
+ //
+ // During the eventual compilation, then, for closure variables we have:
+ //
+ // xN.Defn = original variable
+ // xN.Outer = variable captured in next outward scope
+ // to make closure where xN appears
+ //
+ // Because of the sharding of pieces of the node, x.Defn means x.Name.Defn
+ // and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
+ Innermost *Name
+ Outer *Name
+}
+
+func (n *Name) isExpr() {}
+
+func (n *Name) copy() Node { panic(n.no("copy")) }
+func (n *Name) doChildren(do func(Node) bool) bool { return false }
+func (n *Name) editChildren(edit func(Node) Node) {}
+
+// TypeDefn returns the type definition for a named OTYPE.
+// That is, given "type T Defn", it returns Defn.
+// It is used by package types.
+func (n *Name) TypeDefn() *types.Type {
+ return n.Ntype.Type()
+}
+
+// RecordFrameOffset records the frame offset for the name.
+// It is used by package types when laying out function arguments.
+func (n *Name) RecordFrameOffset(offset int64) {
+ n.SetFrameOffset(offset)
+}
+
+// NewNameAt returns a new ONAME Node associated with symbol s at position pos.
+// The caller is responsible for setting Curfn.
+func NewNameAt(pos src.XPos, sym *types.Sym) *Name {
+ if sym == nil {
+ base.Fatalf("NewNameAt nil")
+ }
+ return newNameAt(pos, ONAME, sym)
+}
+
+// NewIota returns a new OIOTA Node.
+func NewIota(pos src.XPos, sym *types.Sym) *Name {
+ if sym == nil {
+ base.Fatalf("NewIota nil")
+ }
+ return newNameAt(pos, OIOTA, sym)
+}
+
+// NewDeclNameAt returns a new Name associated with symbol s at position pos.
+// The caller is responsible for setting Curfn.
+func NewDeclNameAt(pos src.XPos, op Op, sym *types.Sym) *Name {
+ if sym == nil {
+ base.Fatalf("NewDeclNameAt nil")
+ }
+ switch op {
+ case ONAME, OTYPE, OLITERAL:
+ // ok
+ default:
+ base.Fatalf("NewDeclNameAt op %v", op)
+ }
+ return newNameAt(pos, op, sym)
+}
+
+// NewConstAt returns a new OLITERAL Node associated with symbol s at position pos.
+func NewConstAt(pos src.XPos, sym *types.Sym, typ *types.Type, val constant.Value) *Name {
+ if sym == nil {
+ base.Fatalf("NewConstAt nil")
+ }
+ n := newNameAt(pos, OLITERAL, sym)
+ n.SetType(typ)
+ n.SetVal(val)
+ return n
+}
+
+// newNameAt is like NewNameAt but allows sym == nil.
+func newNameAt(pos src.XPos, op Op, sym *types.Sym) *Name {
+ n := new(Name)
+ n.op = op
+ n.pos = pos
+ n.sym = sym
+ return n
+}
+
+func (n *Name) Name() *Name { return n }
+func (n *Name) Sym() *types.Sym { return n.sym }
+func (n *Name) SetSym(x *types.Sym) { n.sym = x }
+func (n *Name) SubOp() Op { return n.BuiltinOp }
+func (n *Name) SetSubOp(x Op) { n.BuiltinOp = x }
+func (n *Name) SetFunc(x *Func) { n.Func = x }
+func (n *Name) Offset() int64 { panic("Name.Offset") }
+func (n *Name) SetOffset(x int64) {
+ if x != 0 {
+ panic("Name.SetOffset")
+ }
+}
+func (n *Name) FrameOffset() int64 { return n.Offset_ }
+func (n *Name) SetFrameOffset(x int64) { n.Offset_ = x }
+func (n *Name) Iota() int64 { return n.Offset_ }
+func (n *Name) SetIota(x int64) { n.Offset_ = x }
+func (n *Name) Walkdef() uint8 { return n.bits.get2(miniWalkdefShift) }
+func (n *Name) SetWalkdef(x uint8) {
+ if x > 3 {
+ panic(fmt.Sprintf("cannot SetWalkdef %d", x))
+ }
+ n.bits.set2(miniWalkdefShift, x)
+}
+
+func (n *Name) Linksym() *obj.LSym { return n.sym.Linksym() }
+func (n *Name) LinksymABI(abi obj.ABI) *obj.LSym { return n.sym.LinksymABI(abi) }
+
+func (*Name) CanBeNtype() {}
+func (*Name) CanBeAnSSASym() {}
+func (*Name) CanBeAnSSAAux() {}
+
+// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
+func (n *Name) Pragma() PragmaFlag { return n.pragma }
+
+// SetPragma sets the PragmaFlag for p, which must be for an OTYPE.
+func (n *Name) SetPragma(flag PragmaFlag) { n.pragma = flag }
+
+// Alias reports whether p, which must be for an OTYPE, is a type alias.
+func (n *Name) Alias() bool { return n.flags&nameAlias != 0 }
+
+// SetAlias sets whether p, which must be for an OTYPE, is a type alias.
+func (n *Name) SetAlias(alias bool) { n.flags.set(nameAlias, alias) }
+
+const (
+ nameReadonly = 1 << iota
+ nameByval // is the variable captured by value or by reference
+ 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 (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
+ nameInlLocal // PAUTO created by inliner, derived from callee local
+ nameOpenDeferSlot // if temporary var storing info for open-coded defers
+ nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section
+ nameAlias // is type name an alias
+)
+
+func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
+func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 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 }
+func (n *Name) IsOutputParamHeapAddr() bool { return n.flags&nameIsOutputParamHeapAddr != 0 }
+func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken != 0 }
+func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 }
+func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 }
+func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 }
+func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 }
+
+func (n *Name) setReadonly(b bool) { n.flags.set(nameReadonly, b) }
+func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, 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) }
+func (n *Name) SetIsOutputParamHeapAddr(b bool) { n.flags.set(nameIsOutputParamHeapAddr, b) }
+func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken, b) }
+func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) }
+func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) }
+func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) }
+func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) }
+
+// OnStack reports whether variable n may reside on the stack.
+func (n *Name) OnStack() bool {
+ if n.Op() == ONAME {
+ switch n.Class {
+ case PPARAM, PPARAMOUT, PAUTO:
+ return n.Esc() != EscHeap
+ case PEXTERN, PAUTOHEAP:
+ return false
+ }
+ }
+ // Note: fmt.go:dumpNodeHeader calls all "func() bool"-typed
+ // methods, but it can only recover from panics, not Fatalf.
+ panic(fmt.Sprintf("%v: not a variable: %v", base.FmtPos(n.Pos()), n))
+}
+
+// MarkReadonly indicates that n is an ONAME with readonly contents.
+func (n *Name) MarkReadonly() {
+ if n.Op() != ONAME {
+ base.Fatalf("Node.MarkReadonly %v", n.Op())
+ }
+ n.setReadonly(true)
+ // Mark the linksym as readonly immediately
+ // so that the SSA backend can use this information.
+ // It will be overridden later during dumpglobls.
+ n.Linksym().Type = objabi.SRODATA
+}
+
+// Val returns the constant.Value for the node.
+func (n *Name) Val() constant.Value {
+ if n.val == nil {
+ return constant.MakeUnknown()
+ }
+ return n.val
+}
+
+// SetVal sets the constant.Value for the node.
+func (n *Name) SetVal(v constant.Value) {
+ if n.op != OLITERAL {
+ panic(n.no("SetVal"))
+ }
+ AssertValidTypeForConst(n.Type(), v)
+ n.val = v
+}
+
+// Canonical returns the logical declaration that n represents. If n
+// is a closure variable, then Canonical returns the original Name as
+// it appears in the function that immediately contains the
+// declaration. Otherwise, Canonical simply returns n itself.
+func (n *Name) Canonical() *Name {
+ if n.IsClosureVar() && n.Defn != nil {
+ n = n.Defn.(*Name)
+ }
+ return n
+}
+
+func (n *Name) SetByval(b bool) {
+ if n.Canonical() != n {
+ base.Fatalf("SetByval called on non-canonical variable: %v", n)
+ }
+ n.flags.set(nameByval, b)
+}
+
+func (n *Name) Byval() bool {
+ // We require byval to be set on the canonical variable, but we
+ // allow it to be accessed from any instance.
+ 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.
+//
+// It exists to help incrementally migrate the compiler towards
+// allowing the introduction of IdentExpr (#42990). Once we have
+// IdentExpr, it will no longer be safe to directly compare Node
+// values to tell if they refer to the same Name. Instead, code will
+// need to explicitly get references to the underlying Name object(s),
+// and compare those instead.
+//
+// It will still be safe to compare Nodes directly for checking if two
+// nodes are syntactically the same. The SameSource function exists to
+// indicate code that intentionally compares Nodes for syntactic
+// equality as opposed to code that has yet to be updated in
+// preparation for IdentExpr.
+func SameSource(n1, n2 Node) bool {
+ return n1 == n2
+}
+
+// Uses reports whether expression x is a (direct) use of the given
+// variable.
+func Uses(x Node, v *Name) bool {
+ if v == nil || v.Op() != ONAME {
+ base.Fatalf("RefersTo bad Name: %v", v)
+ }
+ return x.Op() == ONAME && x.Name() == v
+}
+
+// DeclaredBy reports whether expression x refers (directly) to a
+// variable that was declared by the given statement.
+func DeclaredBy(x, stmt Node) bool {
+ if stmt == nil {
+ base.Fatalf("DeclaredBy nil")
+ }
+ return x.Op() == ONAME && SameSource(x.Name().Defn, stmt)
+}
+
+// The Class of a variable/function describes the "storage class"
+// of a variable or function. During parsing, storage classes are
+// called declaration contexts.
+type Class uint8
+
+//go:generate stringer -type=Class name.go
+const (
+ Pxxx Class = iota // no class; used during ssa conversion to indicate pseudo-variables
+ PEXTERN // global variables
+ PAUTO // local variables
+ PAUTOHEAP // local variables or parameters moved to heap
+ PPARAM // input arguments
+ PPARAMOUT // output results
+ PFUNC // global functions
+
+ // Careful: Class is stored in three bits in Node.flags.
+ _ = uint((1 << 3) - iota) // static assert for iota <= (1 << 3)
+)
+
+type Embed struct {
+ Pos src.XPos
+ Patterns []string
+}
+
+// A Pack is an identifier referring to an imported package.
+type PkgName struct {
+ miniNode
+ sym *types.Sym
+ Pkg *types.Pkg
+ Used bool
+}
+
+func (p *PkgName) Sym() *types.Sym { return p.sym }
+
+func (*PkgName) CanBeNtype() {}
+
+func NewPkgName(pos src.XPos, sym *types.Sym, pkg *types.Pkg) *PkgName {
+ p := &PkgName{sym: sym, Pkg: pkg}
+ p.op = OPACK
+ p.pos = pos
+ return p
+}
+
+var RegFP *Name
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
new file mode 100644
index 0000000000000..ffa7daf6b2be7
--- /dev/null
+++ b/src/cmd/compile/internal/ir/node.go
@@ -0,0 +1,591 @@
+// Copyright 2009 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.
+
+// “Abstract” syntax representation.
+
+package ir
+
+import (
+ "fmt"
+ "go/constant"
+ "sort"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// A Node is the abstract interface to an IR node.
+type Node interface {
+ // Formatting
+ Format(s fmt.State, verb rune)
+
+ // Source position.
+ Pos() src.XPos
+ SetPos(x src.XPos)
+
+ // For making copies. For Copy and SepCopy.
+ copy() Node
+
+ doChildren(func(Node) bool) bool
+ editChildren(func(Node) Node)
+
+ // Abstract graph structure, for generic traversals.
+ Op() Op
+ Init() Nodes
+
+ // Fields specific to certain Ops only.
+ Type() *types.Type
+ SetType(t *types.Type)
+ Name() *Name
+ Sym() *types.Sym
+ Val() constant.Value
+ SetVal(v constant.Value)
+
+ // Storage for analysis passes.
+ Esc() uint16
+ SetEsc(x uint16)
+ Diag() bool
+ SetDiag(x bool)
+ Typecheck() uint8
+ SetTypecheck(x uint8)
+ NonNil() bool
+ MarkNonNil()
+}
+
+// Line returns n's position as a string. If n has been inlined,
+// it uses the outermost position where n has been inlined.
+func Line(n Node) string {
+ return base.FmtPos(n.Pos())
+}
+
+func IsSynthetic(n Node) bool {
+ name := n.Sym().Name
+ return name[0] == '.' || name[0] == '~'
+}
+
+// IsAutoTmp indicates if n was created by the compiler as a temporary,
+// based on the setting of the .AutoTemp flag in n's Name.
+func IsAutoTmp(n Node) bool {
+ if n == nil || n.Op() != ONAME {
+ return false
+ }
+ return n.Name().AutoTemp()
+}
+
+// mayBeShared reports whether n may occur in multiple places in the AST.
+// Extra care must be taken when mutating such a node.
+func MayBeShared(n Node) bool {
+ switch n.Op() {
+ case ONAME, OLITERAL, ONIL, OTYPE:
+ return true
+ }
+ return false
+}
+
+type InitNode interface {
+ Node
+ PtrInit() *Nodes
+ SetInit(x Nodes)
+}
+
+func TakeInit(n Node) Nodes {
+ init := n.Init()
+ if len(init) != 0 {
+ n.(InitNode).SetInit(nil)
+ }
+ return init
+}
+
+//go:generate stringer -type=Op -trimprefix=O node.go
+
+type Op uint8
+
+// Node ops.
+const (
+ OXXX Op = iota
+
+ // names
+ ONAME // var or func name
+ // Unnamed arg or return value: f(int, string) (int, error) { etc }
+ // Also used for a qualified package identifier that hasn't been resolved yet.
+ ONONAME
+ OTYPE // type name
+ OPACK // import
+ OLITERAL // literal
+ ONIL // nil
+
+ // expressions
+ OADD // Left + Right
+ OSUB // Left - Right
+ OOR // Left | Right
+ OXOR // Left ^ Right
+ OADDSTR // +{List} (string addition, list elements are strings)
+ OADDR // &Left
+ OANDAND // Left && Right
+ OAPPEND // append(List); after walk, Left may contain elem type descriptor
+ OBYTES2STR // Type(Left) (Type is string, Left is a []byte)
+ OBYTES2STRTMP // Type(Left) (Type is string, Left is a []byte, ephemeral)
+ ORUNES2STR // Type(Left) (Type is string, Left is a []rune)
+ OSTR2BYTES // Type(Left) (Type is []byte, Left is a string)
+ OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral)
+ OSTR2RUNES // Type(Left) (Type is []rune, Left is a string)
+ // Left = Right or (if Colas=true) Left := Right
+ // If Colas, then Ninit includes a DCL node for Left.
+ OAS
+ // List = Rlist (x, y, z = a, b, c) or (if Colas=true) List := Rlist
+ // If Colas, then Ninit includes DCL nodes for List
+ OAS2
+ OAS2DOTTYPE // List = Right (x, ok = I.(int))
+ OAS2FUNC // List = Right (x, y = f())
+ OAS2MAPR // List = Right (x, ok = m["foo"])
+ OAS2RECV // List = Right (x, ok = <-c)
+ OASOP // Left Etype= Right (x += y)
+ OCALL // Left(List) (function call, method call or type conversion)
+
+ // OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
+ // 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))
+ OCALLINTER // Left(List/Rlist) (interface method call x.Method(args))
+ OCALLPART // Left.Right (method expression x.Method, not called)
+ OCAP // cap(Left)
+ OCLOSE // close(Left)
+ OCLOSURE // func Type { Func.Closure.Nbody } (func literal)
+ OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
+ OMAPLIT // Type{List} (composite literal, Type is map)
+ OSTRUCTLIT // Type{List} (composite literal, Type is struct)
+ OARRAYLIT // Type{List} (composite literal, Type is array)
+ OSLICELIT // Type{List} (composite literal, Type is slice) Right.Int64() = slice length.
+ OPTRLIT // &Left (left is composite literal)
+ OCONV // Type(Left) (type conversion)
+ OCONVIFACE // Type(Left) (type conversion, to interface)
+ OCONVNOP // Type(Left) (type conversion, no effect)
+ OCOPY // copy(Left, Right)
+ ODCL // var Left (declares Left of type Left.Type)
+
+ // Used during parsing but don't last.
+ ODCLFUNC // func f() or func (r) f()
+ ODCLCONST // const pi = 3.14
+ ODCLTYPE // type Int int or type Int = int
+
+ ODELETE // delete(List)
+ ODOT // Left.Sym (Left is of struct type)
+ ODOTPTR // Left.Sym (Left is of pointer to struct type)
+ ODOTMETH // Left.Sym (Left is non-interface, Right is method name)
+ ODOTINTER // Left.Sym (Left is interface, Right is method name)
+ OXDOT // Left.Sym (before rewrite to one of the preceding)
+ ODOTTYPE // Left.Right or Left.Type (.Right during parsing, .Type once resolved); after walk, .Right contains address of interface type descriptor and .Right.Right contains address of concrete type descriptor
+ ODOTTYPE2 // Left.Right or Left.Type (.Right during parsing, .Type once resolved; on rhs of OAS2DOTTYPE); after walk, .Right contains address of interface type descriptor
+ OEQ // Left == Right
+ ONE // Left != Right
+ OLT // Left < Right
+ OLE // Left <= Right
+ OGE // Left >= Right
+ OGT // Left > Right
+ ODEREF // *Left
+ OINDEX // Left[Right] (index of array or slice)
+ OINDEXMAP // Left[Right] (index of map)
+ OKEY // Left:Right (key:value in struct/array/map literal)
+ OSTRUCTKEY // Sym:Left (key:value in struct literal, after type checking)
+ OLEN // len(Left)
+ OMAKE // make(List) (before type checking converts to one of the following)
+ OMAKECHAN // make(Type, Left) (type is chan)
+ OMAKEMAP // make(Type, Left) (type is map)
+ OMAKESLICE // make(Type, Left, Right) (type is slice)
+ OMAKESLICECOPY // makeslicecopy(Type, Left, Right) (type is slice; Left is length and Right is the copied from slice)
+ // OMAKESLICECOPY is created by the order pass and corresponds to:
+ // s = make(Type, Left); copy(s, Right)
+ //
+ // Bounded can be set on the node when Left == len(Right) is known at compile time.
+ //
+ // This node is created so the walk pass can optimize this pattern which would
+ // otherwise be hard to detect after the order pass.
+ OMUL // Left * Right
+ ODIV // Left / Right
+ OMOD // Left % Right
+ OLSH // Left << Right
+ ORSH // Left >> Right
+ OAND // Left & Right
+ OANDNOT // Left &^ Right
+ ONEW // new(Left); corresponds to calls to new in source code
+ ONOT // !Left
+ OBITNOT // ^Left
+ OPLUS // +Left
+ ONEG // -Left
+ OOROR // Left || Right
+ OPANIC // panic(Left)
+ OPRINT // print(List)
+ OPRINTN // println(List)
+ OPAREN // (Left)
+ OSEND // Left <- Right
+ OSLICE // Left[List[0] : List[1]] (Left is untypechecked or slice)
+ OSLICEARR // Left[List[0] : List[1]] (Left is pointer to array)
+ OSLICESTR // Left[List[0] : List[1]] (Left is string)
+ OSLICE3 // Left[List[0] : List[1] : List[2]] (Left is untypedchecked or slice)
+ OSLICE3ARR // Left[List[0] : List[1] : List[2]] (Left is pointer to array)
+ OSLICEHEADER // sliceheader{Left, List[0], List[1]} (Left is unsafe.Pointer, List[0] is length, List[1] is capacity)
+ ORECOVER // recover()
+ ORECV // <-Left
+ ORUNESTR // Type(Left) (Type is string, Left is rune)
+ OSELRECV2 // like OAS2: List = Rlist where len(List)=2, len(Rlist)=1, Rlist[0].Op = ORECV (appears as .Left of OCASE)
+ OIOTA // iota
+ OREAL // real(Left)
+ OIMAG // imag(Left)
+ OCOMPLEX // complex(Left, Right) or complex(List[0]) where List[0] is a 2-result function call
+ OALIGNOF // unsafe.Alignof(Left)
+ OOFFSETOF // unsafe.Offsetof(Left)
+ OSIZEOF // unsafe.Sizeof(Left)
+ OMETHEXPR // method expression
+ OSTMTEXPR // statement expression (Init; Left)
+
+ // statements
+ OBLOCK // { List } (block of code)
+ OBREAK // break [Sym]
+ // OCASE: case List: Nbody (List==nil means default)
+ // For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL
+ // for nil), and, if a type-switch variable is specified, Rlist is an
+ // ONAME for the version of the type-switch variable with the specified
+ // type.
+ OCASE
+ OCONTINUE // continue [Sym]
+ ODEFER // defer Left (Left must be call)
+ OFALL // fallthrough
+ OFOR // for Ninit; Left; Right { Nbody }
+ // OFORUNTIL is like OFOR, but the test (Left) is applied after the body:
+ // Ninit
+ // top: { Nbody } // Execute the body at least once
+ // cont: Right
+ // if Left { // And then test the loop condition
+ // List // Before looping to top, execute List
+ // goto top
+ // }
+ // OFORUNTIL is created by walk. There's no way to write this in Go code.
+ OFORUNTIL
+ OGOTO // goto Sym
+ OIF // if Ninit; Left { Nbody } else { Rlist }
+ OLABEL // Sym:
+ OGO // go Left (Left must be call)
+ ORANGE // for List = range Right { Nbody }
+ ORETURN // return List
+ OSELECT // select { List } (List is list of OCASE)
+ OSWITCH // switch Ninit; Left { List } (List is a list of OCASE)
+ // OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH)
+ // Left is nil if there is no type-switch variable
+ OTYPESW
+
+ // types
+ OTCHAN // chan int
+ OTMAP // map[string]int
+ OTSTRUCT // struct{}
+ OTINTER // interface{}
+ // OTFUNC: func() - Left is receiver field, List is list of param fields, Rlist is
+ // list of result fields.
+ OTFUNC
+ OTARRAY // [8]int or [...]int
+ OTSLICE // []int
+
+ // misc
+ // intermediate representation of an inlined call. Uses Init (assignments
+ // for the captured variables, parameters, retvars, & INLMARK op),
+ // Body (body of the inlined function), and ReturnVars (list of
+ // return values)
+ 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.
+ OLINKSYMOFFSET // offset within a name
+
+ // arch-specific opcodes
+ OTAILCALL // tail call to another function
+ OGETG // runtime.getg() (read g pointer)
+
+ OEND
+)
+
+// Nodes is a pointer to a slice of *Node.
+// For fields that are not used in most nodes, this is used instead of
+// a slice to save space.
+type Nodes []Node
+
+// Append appends entries to Nodes.
+func (n *Nodes) Append(a ...Node) {
+ if len(a) == 0 {
+ return
+ }
+ *n = append(*n, a...)
+}
+
+// Prepend prepends entries to Nodes.
+// If a slice is passed in, this will take ownership of it.
+func (n *Nodes) Prepend(a ...Node) {
+ if len(a) == 0 {
+ return
+ }
+ *n = append(a, *n...)
+}
+
+// Take clears n, returning its former contents.
+func (n *Nodes) Take() []Node {
+ ret := *n
+ *n = nil
+ return ret
+}
+
+// Copy returns a copy of the content of the slice.
+func (n Nodes) Copy() Nodes {
+ if n == nil {
+ return nil
+ }
+ c := make(Nodes, len(n))
+ copy(c, n)
+ return c
+}
+
+// NameQueue is a FIFO queue of *Name. The zero value of NameQueue is
+// a ready-to-use empty queue.
+type NameQueue struct {
+ ring []*Name
+ head, tail int
+}
+
+// Empty reports whether q contains no Names.
+func (q *NameQueue) Empty() bool {
+ return q.head == q.tail
+}
+
+// PushRight appends n to the right of the queue.
+func (q *NameQueue) PushRight(n *Name) {
+ if len(q.ring) == 0 {
+ q.ring = make([]*Name, 16)
+ } else if q.head+len(q.ring) == q.tail {
+ // Grow the ring.
+ nring := make([]*Name, len(q.ring)*2)
+ // Copy the old elements.
+ part := q.ring[q.head%len(q.ring):]
+ if q.tail-q.head <= len(part) {
+ part = part[:q.tail-q.head]
+ copy(nring, part)
+ } else {
+ pos := copy(nring, part)
+ copy(nring[pos:], q.ring[:q.tail%len(q.ring)])
+ }
+ q.ring, q.head, q.tail = nring, 0, q.tail-q.head
+ }
+
+ q.ring[q.tail%len(q.ring)] = n
+ q.tail++
+}
+
+// PopLeft pops a Name from the left of the queue. It panics if q is
+// empty.
+func (q *NameQueue) PopLeft() *Name {
+ if q.Empty() {
+ panic("dequeue empty")
+ }
+ n := q.ring[q.head%len(q.ring)]
+ q.head++
+ return n
+}
+
+// NameSet is a set of Names.
+type NameSet map[*Name]struct{}
+
+// Has reports whether s contains n.
+func (s NameSet) Has(n *Name) bool {
+ _, isPresent := s[n]
+ return isPresent
+}
+
+// Add adds n to s.
+func (s *NameSet) Add(n *Name) {
+ if *s == nil {
+ *s = make(map[*Name]struct{})
+ }
+ (*s)[n] = struct{}{}
+}
+
+// Sorted returns s sorted according to less.
+func (s NameSet) Sorted(less func(*Name, *Name) bool) []*Name {
+ var res []*Name
+ for n := range s {
+ res = append(res, n)
+ }
+ sort.Slice(res, func(i, j int) bool { return less(res[i], res[j]) })
+ return res
+}
+
+type PragmaFlag int16
+
+const (
+ // Func pragmas.
+ Nointerface PragmaFlag = 1 << iota
+ Noescape // func parameters don't escape
+ Norace // func must not have race detector annotations
+ Nosplit // func should not execute on separate stack
+ Noinline // func should not be inlined
+ NoCheckPtr // func should not be instrumented by checkptr
+ CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
+ UintptrEscapes // pointers converted to uintptr escape
+
+ // Runtime-only func pragmas.
+ // See ../../../../runtime/README.md for detailed descriptions.
+ Systemstack // func must run on system stack
+ Nowritebarrier // emit compiler error instead of write barrier
+ Nowritebarrierrec // error on write barrier in this or recursive callees
+ Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
+
+ // Runtime and cgo type pragmas
+ NotInHeap // values of this type must not be heap allocated
+
+ // Go command pragmas
+ GoBuildPragma
+
+ RegisterParams // TODO remove after register abi is working
+
+)
+
+func AsNode(n types.Object) Node {
+ if n == nil {
+ return nil
+ }
+ return n.(Node)
+}
+
+var BlankNode Node
+
+func IsConst(n Node, ct constant.Kind) bool {
+ return ConstType(n) == ct
+}
+
+// isNil reports whether n represents the universal untyped zero value "nil".
+func IsNil(n Node) bool {
+ // Check n.Orig because constant propagation may produce typed nil constants,
+ // which don't exist in the Go spec.
+ return n != nil && Orig(n).Op() == ONIL
+}
+
+func IsBlank(n Node) bool {
+ if n == nil {
+ return false
+ }
+ return n.Sym().IsBlank()
+}
+
+// IsMethod reports whether n is a method.
+// n must be a function or a method.
+func IsMethod(n Node) bool {
+ return n.Type().Recv() != nil
+}
+
+func HasNamedResults(fn *Func) bool {
+ typ := fn.Type()
+ return typ.NumResults() > 0 && types.OrigSym(typ.Results().Field(0).Sym) != nil
+}
+
+// HasUniquePos reports whether n has a unique position that can be
+// used for reporting error messages.
+//
+// It's primarily used to distinguish references to named objects,
+// whose Pos will point back to their declaration position rather than
+// their usage position.
+func HasUniquePos(n Node) bool {
+ switch n.Op() {
+ case ONAME, OPACK:
+ return false
+ case OLITERAL, ONIL, OTYPE:
+ if n.Sym() != nil {
+ return false
+ }
+ }
+
+ if !n.Pos().IsKnown() {
+ if base.Flag.K != 0 {
+ base.Warn("setlineno: unknown position (line 0)")
+ }
+ return false
+ }
+
+ return true
+}
+
+func SetPos(n Node) src.XPos {
+ lno := base.Pos
+ if n != nil && HasUniquePos(n) {
+ base.Pos = n.Pos()
+ }
+ return lno
+}
+
+// The result of InitExpr MUST be assigned back to n, e.g.
+// n.Left = InitExpr(init, n.Left)
+func InitExpr(init []Node, expr Node) Node {
+ if len(init) == 0 {
+ return expr
+ }
+
+ n, ok := expr.(InitNode)
+ if !ok || MayBeShared(n) {
+ // Introduce OCONVNOP to hold init list.
+ n = NewConvExpr(base.Pos, OCONVNOP, nil, expr)
+ n.SetType(expr.Type())
+ n.SetTypecheck(1)
+ }
+
+ n.PtrInit().Prepend(init...)
+ return n
+}
+
+// what's the outer value that a write to n affects?
+// outer value means containing struct or array.
+func OuterValue(n Node) Node {
+ for {
+ switch nn := n; nn.Op() {
+ case OXDOT:
+ base.Fatalf("OXDOT in walk")
+ case ODOT:
+ nn := nn.(*SelectorExpr)
+ n = nn.X
+ continue
+ case OPAREN:
+ nn := nn.(*ParenExpr)
+ n = nn.X
+ continue
+ case OCONVNOP:
+ nn := nn.(*ConvExpr)
+ n = nn.X
+ continue
+ case OINDEX:
+ nn := nn.(*IndexExpr)
+ if nn.X.Type() == nil {
+ base.Fatalf("OuterValue needs type for %v", nn.X)
+ }
+ if nn.X.Type().IsArray() {
+ n = nn.X
+ continue
+ }
+ }
+
+ return n
+ }
+}
+
+const (
+ EscUnknown = iota
+ EscNone // Does not escape to heap, result, or parameters.
+ EscHeap // Reachable from the heap
+ EscNever // By construction will not escape.
+)
diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go
new file mode 100644
index 0000000000000..fe436867b2c3d
--- /dev/null
+++ b/src/cmd/compile/internal/ir/node_gen.go
@@ -0,0 +1,1425 @@
+// Code generated by mknode.go. DO NOT EDIT.
+
+package ir
+
+import "fmt"
+
+func (n *AddStringExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *AddStringExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.List = copyNodes(c.List)
+ return &c
+}
+func (n *AddStringExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if doNodes(n.List, do) {
+ return true
+ }
+ if n.Prealloc != nil && do(n.Prealloc) {
+ return true
+ }
+ return false
+}
+func (n *AddStringExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ editNodes(n.List, edit)
+ if n.Prealloc != nil {
+ n.Prealloc = edit(n.Prealloc).(*Name)
+ }
+}
+
+func (n *AddrExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *AddrExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *AddrExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Prealloc != nil && do(n.Prealloc) {
+ return true
+ }
+ return false
+}
+func (n *AddrExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Prealloc != nil {
+ n.Prealloc = edit(n.Prealloc).(*Name)
+ }
+}
+
+func (n *ArrayType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ArrayType) copy() Node {
+ c := *n
+ return &c
+}
+func (n *ArrayType) doChildren(do func(Node) bool) bool {
+ if n.Len != nil && do(n.Len) {
+ return true
+ }
+ if n.Elem != nil && do(n.Elem) {
+ return true
+ }
+ return false
+}
+func (n *ArrayType) editChildren(edit func(Node) Node) {
+ if n.Len != nil {
+ n.Len = edit(n.Len).(Node)
+ }
+ if n.Elem != nil {
+ n.Elem = edit(n.Elem).(Ntype)
+ }
+}
+
+func (n *AssignListStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *AssignListStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Lhs = copyNodes(c.Lhs)
+ c.Rhs = copyNodes(c.Rhs)
+ return &c
+}
+func (n *AssignListStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if doNodes(n.Lhs, do) {
+ return true
+ }
+ if doNodes(n.Rhs, do) {
+ return true
+ }
+ return false
+}
+func (n *AssignListStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ editNodes(n.Lhs, edit)
+ editNodes(n.Rhs, edit)
+}
+
+func (n *AssignOpStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *AssignOpStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *AssignOpStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Y != nil && do(n.Y) {
+ return true
+ }
+ return false
+}
+func (n *AssignOpStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Y != nil {
+ n.Y = edit(n.Y).(Node)
+ }
+}
+
+func (n *AssignStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *AssignStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *AssignStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Y != nil && do(n.Y) {
+ return true
+ }
+ return false
+}
+func (n *AssignStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Y != nil {
+ n.Y = edit(n.Y).(Node)
+ }
+}
+
+func (n *BasicLit) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *BasicLit) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *BasicLit) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *BasicLit) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *BinaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *BinaryExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *BinaryExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Y != nil && do(n.Y) {
+ return true
+ }
+ return false
+}
+func (n *BinaryExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Y != nil {
+ n.Y = edit(n.Y).(Node)
+ }
+}
+
+func (n *BlockStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *BlockStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.List = copyNodes(c.List)
+ return &c
+}
+func (n *BlockStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if doNodes(n.List, do) {
+ return true
+ }
+ return false
+}
+func (n *BlockStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ editNodes(n.List, edit)
+}
+
+func (n *BranchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *BranchStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *BranchStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *BranchStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *CallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *CallExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Args = copyNodes(c.Args)
+ c.KeepAlive = copyNames(c.KeepAlive)
+ return &c
+}
+func (n *CallExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if doNodes(n.Args, do) {
+ return true
+ }
+ if doNames(n.KeepAlive, do) {
+ return true
+ }
+ return false
+}
+func (n *CallExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ editNodes(n.Args, edit)
+ editNames(n.KeepAlive, edit)
+}
+
+func (n *CaseClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *CaseClause) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.List = copyNodes(c.List)
+ c.Body = copyNodes(c.Body)
+ return &c
+}
+func (n *CaseClause) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Var != nil && do(n.Var) {
+ return true
+ }
+ if doNodes(n.List, do) {
+ return true
+ }
+ if doNodes(n.Body, do) {
+ return true
+ }
+ return false
+}
+func (n *CaseClause) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Var != nil {
+ n.Var = edit(n.Var).(*Name)
+ }
+ editNodes(n.List, edit)
+ editNodes(n.Body, edit)
+}
+
+func (n *ChanType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ChanType) copy() Node {
+ c := *n
+ return &c
+}
+func (n *ChanType) doChildren(do func(Node) bool) bool {
+ if n.Elem != nil && do(n.Elem) {
+ return true
+ }
+ return false
+}
+func (n *ChanType) editChildren(edit func(Node) Node) {
+ if n.Elem != nil {
+ n.Elem = edit(n.Elem).(Ntype)
+ }
+}
+
+func (n *ClosureExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ClosureExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *ClosureExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Prealloc != nil && do(n.Prealloc) {
+ return true
+ }
+ return false
+}
+func (n *ClosureExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Prealloc != nil {
+ n.Prealloc = edit(n.Prealloc).(*Name)
+ }
+}
+
+func (n *CommClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *CommClause) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Body = copyNodes(c.Body)
+ return &c
+}
+func (n *CommClause) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Comm != nil && do(n.Comm) {
+ return true
+ }
+ if doNodes(n.Body, do) {
+ return true
+ }
+ return false
+}
+func (n *CommClause) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Comm != nil {
+ n.Comm = edit(n.Comm).(Node)
+ }
+ editNodes(n.Body, edit)
+}
+
+func (n *CompLitExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *CompLitExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.List = copyNodes(c.List)
+ return &c
+}
+func (n *CompLitExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Ntype != nil && do(n.Ntype) {
+ return true
+ }
+ if doNodes(n.List, do) {
+ return true
+ }
+ if n.Prealloc != nil && do(n.Prealloc) {
+ return true
+ }
+ return false
+}
+func (n *CompLitExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Ntype != nil {
+ n.Ntype = edit(n.Ntype).(Ntype)
+ }
+ editNodes(n.List, edit)
+ if n.Prealloc != nil {
+ n.Prealloc = edit(n.Prealloc).(*Name)
+ }
+}
+
+func (n *ConstExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ConstExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *ConstExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *ConstExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *ConvExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ConvExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *ConvExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ return false
+}
+func (n *ConvExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+}
+
+func (n *Decl) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *Decl) copy() Node {
+ c := *n
+ return &c
+}
+func (n *Decl) doChildren(do func(Node) bool) bool {
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ return false
+}
+func (n *Decl) editChildren(edit func(Node) Node) {
+ if n.X != nil {
+ n.X = edit(n.X).(*Name)
+ }
+}
+
+func (n *ForStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ForStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Late = copyNodes(c.Late)
+ c.Body = copyNodes(c.Body)
+ return &c
+}
+func (n *ForStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Cond != nil && do(n.Cond) {
+ return true
+ }
+ if doNodes(n.Late, do) {
+ return true
+ }
+ if n.Post != nil && do(n.Post) {
+ return true
+ }
+ if doNodes(n.Body, do) {
+ return true
+ }
+ return false
+}
+func (n *ForStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Cond != nil {
+ n.Cond = edit(n.Cond).(Node)
+ }
+ editNodes(n.Late, edit)
+ if n.Post != nil {
+ n.Post = edit(n.Post).(Node)
+ }
+ editNodes(n.Body, edit)
+}
+
+func (n *Func) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+
+func (n *FuncType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *FuncType) copy() Node {
+ c := *n
+ c.Recv = copyField(c.Recv)
+ c.Params = copyFields(c.Params)
+ c.Results = copyFields(c.Results)
+ return &c
+}
+func (n *FuncType) doChildren(do func(Node) bool) bool {
+ if doField(n.Recv, do) {
+ return true
+ }
+ if doFields(n.Params, do) {
+ return true
+ }
+ if doFields(n.Results, do) {
+ return true
+ }
+ return false
+}
+func (n *FuncType) editChildren(edit func(Node) Node) {
+ editField(n.Recv, edit)
+ editFields(n.Params, edit)
+ editFields(n.Results, edit)
+}
+
+func (n *GoDeferStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *GoDeferStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *GoDeferStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Call != nil && do(n.Call) {
+ return true
+ }
+ return false
+}
+func (n *GoDeferStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Call != nil {
+ n.Call = edit(n.Call).(Node)
+ }
+}
+
+func (n *Ident) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *Ident) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *Ident) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *Ident) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *IfStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *IfStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Body = copyNodes(c.Body)
+ c.Else = copyNodes(c.Else)
+ return &c
+}
+func (n *IfStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Cond != nil && do(n.Cond) {
+ return true
+ }
+ if doNodes(n.Body, do) {
+ return true
+ }
+ if doNodes(n.Else, do) {
+ return true
+ }
+ return false
+}
+func (n *IfStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Cond != nil {
+ n.Cond = edit(n.Cond).(Node)
+ }
+ editNodes(n.Body, edit)
+ editNodes(n.Else, edit)
+}
+
+func (n *IndexExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *IndexExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *IndexExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Index != nil && do(n.Index) {
+ return true
+ }
+ return false
+}
+func (n *IndexExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Index != nil {
+ n.Index = edit(n.Index).(Node)
+ }
+}
+
+func (n *InlineMarkStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *InlineMarkStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *InlineMarkStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *InlineMarkStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *InlinedCallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *InlinedCallExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Body = copyNodes(c.Body)
+ c.ReturnVars = copyNodes(c.ReturnVars)
+ return &c
+}
+func (n *InlinedCallExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if doNodes(n.Body, do) {
+ return true
+ }
+ if doNodes(n.ReturnVars, do) {
+ return true
+ }
+ return false
+}
+func (n *InlinedCallExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ editNodes(n.Body, edit)
+ editNodes(n.ReturnVars, edit)
+}
+
+func (n *InterfaceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *InterfaceType) copy() Node {
+ c := *n
+ c.Methods = copyFields(c.Methods)
+ return &c
+}
+func (n *InterfaceType) doChildren(do func(Node) bool) bool {
+ if doFields(n.Methods, do) {
+ return true
+ }
+ return false
+}
+func (n *InterfaceType) editChildren(edit func(Node) Node) {
+ editFields(n.Methods, edit)
+}
+
+func (n *KeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *KeyExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *KeyExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Key != nil && do(n.Key) {
+ return true
+ }
+ if n.Value != nil && do(n.Value) {
+ return true
+ }
+ return false
+}
+func (n *KeyExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Key != nil {
+ n.Key = edit(n.Key).(Node)
+ }
+ if n.Value != nil {
+ n.Value = edit(n.Value).(Node)
+ }
+}
+
+func (n *LabelStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *LabelStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *LabelStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *LabelStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *LinksymOffsetExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *LinksymOffsetExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *LinksymOffsetExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *LinksymOffsetExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *LogicalExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *LogicalExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *LogicalExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Y != nil && do(n.Y) {
+ return true
+ }
+ return false
+}
+func (n *LogicalExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Y != nil {
+ n.Y = edit(n.Y).(Node)
+ }
+}
+
+func (n *MakeExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *MakeExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *MakeExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Len != nil && do(n.Len) {
+ return true
+ }
+ if n.Cap != nil && do(n.Cap) {
+ return true
+ }
+ return false
+}
+func (n *MakeExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Len != nil {
+ n.Len = edit(n.Len).(Node)
+ }
+ if n.Cap != nil {
+ n.Cap = edit(n.Cap).(Node)
+ }
+}
+
+func (n *MapType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *MapType) copy() Node {
+ c := *n
+ return &c
+}
+func (n *MapType) doChildren(do func(Node) bool) bool {
+ if n.Key != nil && do(n.Key) {
+ return true
+ }
+ if n.Elem != nil && do(n.Elem) {
+ return true
+ }
+ return false
+}
+func (n *MapType) editChildren(edit func(Node) Node) {
+ if n.Key != nil {
+ n.Key = edit(n.Key).(Ntype)
+ }
+ if n.Elem != nil {
+ n.Elem = edit(n.Elem).(Ntype)
+ }
+}
+
+func (n *Name) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+
+func (n *NilExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *NilExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *NilExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *NilExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *ParenExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ParenExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *ParenExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ return false
+}
+func (n *ParenExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+}
+
+func (n *PkgName) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *PkgName) copy() Node {
+ c := *n
+ return &c
+}
+func (n *PkgName) doChildren(do func(Node) bool) bool {
+ return false
+}
+func (n *PkgName) editChildren(edit func(Node) Node) {
+}
+
+func (n *RangeStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *RangeStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Body = copyNodes(c.Body)
+ return &c
+}
+func (n *RangeStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Key != nil && do(n.Key) {
+ return true
+ }
+ if n.Value != nil && do(n.Value) {
+ return true
+ }
+ if doNodes(n.Body, do) {
+ return true
+ }
+ if n.Prealloc != nil && do(n.Prealloc) {
+ return true
+ }
+ return false
+}
+func (n *RangeStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Key != nil {
+ n.Key = edit(n.Key).(Node)
+ }
+ if n.Value != nil {
+ n.Value = edit(n.Value).(Node)
+ }
+ editNodes(n.Body, edit)
+ if n.Prealloc != nil {
+ n.Prealloc = edit(n.Prealloc).(*Name)
+ }
+}
+
+func (n *ResultExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ResultExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *ResultExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *ResultExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
+func (n *ReturnStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *ReturnStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Results = copyNodes(c.Results)
+ return &c
+}
+func (n *ReturnStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if doNodes(n.Results, do) {
+ return true
+ }
+ return false
+}
+func (n *ReturnStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ editNodes(n.Results, edit)
+}
+
+func (n *SelectStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *SelectStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Cases = copyCommClauses(c.Cases)
+ c.Compiled = copyNodes(c.Compiled)
+ return &c
+}
+func (n *SelectStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if doCommClauses(n.Cases, do) {
+ return true
+ }
+ if doNodes(n.Compiled, do) {
+ return true
+ }
+ return false
+}
+func (n *SelectStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ editCommClauses(n.Cases, edit)
+ editNodes(n.Compiled, edit)
+}
+
+func (n *SelectorExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *SelectorExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *SelectorExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Prealloc != nil && do(n.Prealloc) {
+ return true
+ }
+ return false
+}
+func (n *SelectorExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Prealloc != nil {
+ n.Prealloc = edit(n.Prealloc).(*Name)
+ }
+}
+
+func (n *SendStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *SendStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *SendStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Chan != nil && do(n.Chan) {
+ return true
+ }
+ if n.Value != nil && do(n.Value) {
+ return true
+ }
+ return false
+}
+func (n *SendStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Chan != nil {
+ n.Chan = edit(n.Chan).(Node)
+ }
+ if n.Value != nil {
+ n.Value = edit(n.Value).(Node)
+ }
+}
+
+func (n *SliceExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *SliceExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *SliceExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Low != nil && do(n.Low) {
+ return true
+ }
+ if n.High != nil && do(n.High) {
+ return true
+ }
+ if n.Max != nil && do(n.Max) {
+ return true
+ }
+ return false
+}
+func (n *SliceExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Low != nil {
+ n.Low = edit(n.Low).(Node)
+ }
+ if n.High != nil {
+ n.High = edit(n.High).(Node)
+ }
+ if n.Max != nil {
+ n.Max = edit(n.Max).(Node)
+ }
+}
+
+func (n *SliceHeaderExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *SliceHeaderExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *SliceHeaderExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Ptr != nil && do(n.Ptr) {
+ return true
+ }
+ if n.Len != nil && do(n.Len) {
+ return true
+ }
+ if n.Cap != nil && do(n.Cap) {
+ return true
+ }
+ return false
+}
+func (n *SliceHeaderExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Ptr != nil {
+ n.Ptr = edit(n.Ptr).(Node)
+ }
+ if n.Len != nil {
+ n.Len = edit(n.Len).(Node)
+ }
+ if n.Cap != nil {
+ n.Cap = edit(n.Cap).(Node)
+ }
+}
+
+func (n *SliceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *SliceType) copy() Node {
+ c := *n
+ return &c
+}
+func (n *SliceType) doChildren(do func(Node) bool) bool {
+ if n.Elem != nil && do(n.Elem) {
+ return true
+ }
+ return false
+}
+func (n *SliceType) editChildren(edit func(Node) Node) {
+ if n.Elem != nil {
+ n.Elem = edit(n.Elem).(Ntype)
+ }
+}
+
+func (n *StarExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *StarExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *StarExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ return false
+}
+func (n *StarExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+}
+
+func (n *StructKeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *StructKeyExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *StructKeyExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Value != nil && do(n.Value) {
+ return true
+ }
+ return false
+}
+func (n *StructKeyExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Value != nil {
+ n.Value = edit(n.Value).(Node)
+ }
+}
+
+func (n *StructType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *StructType) copy() Node {
+ c := *n
+ c.Fields = copyFields(c.Fields)
+ return &c
+}
+func (n *StructType) doChildren(do func(Node) bool) bool {
+ if doFields(n.Fields, do) {
+ return true
+ }
+ return false
+}
+func (n *StructType) editChildren(edit func(Node) Node) {
+ editFields(n.Fields, edit)
+}
+
+func (n *SwitchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *SwitchStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ c.Cases = copyCaseClauses(c.Cases)
+ c.Compiled = copyNodes(c.Compiled)
+ return &c
+}
+func (n *SwitchStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Tag != nil && do(n.Tag) {
+ return true
+ }
+ if doCaseClauses(n.Cases, do) {
+ return true
+ }
+ if doNodes(n.Compiled, do) {
+ return true
+ }
+ return false
+}
+func (n *SwitchStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Tag != nil {
+ n.Tag = edit(n.Tag).(Node)
+ }
+ editCaseClauses(n.Cases, edit)
+ editNodes(n.Compiled, edit)
+}
+
+func (n *TailCallStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *TailCallStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *TailCallStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Target != nil && do(n.Target) {
+ return true
+ }
+ return false
+}
+func (n *TailCallStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Target != nil {
+ n.Target = edit(n.Target).(*Name)
+ }
+}
+
+func (n *TypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *TypeAssertExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *TypeAssertExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.Ntype != nil && do(n.Ntype) {
+ return true
+ }
+ return false
+}
+func (n *TypeAssertExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.Ntype != nil {
+ n.Ntype = edit(n.Ntype).(Ntype)
+ }
+}
+
+func (n *TypeSwitchGuard) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *TypeSwitchGuard) copy() Node {
+ c := *n
+ return &c
+}
+func (n *TypeSwitchGuard) doChildren(do func(Node) bool) bool {
+ if n.Tag != nil && do(n.Tag) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ return false
+}
+func (n *TypeSwitchGuard) editChildren(edit func(Node) Node) {
+ if n.Tag != nil {
+ n.Tag = edit(n.Tag).(*Ident)
+ }
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+}
+
+func (n *UnaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *UnaryExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *UnaryExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ return false
+}
+func (n *UnaryExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+}
+
+func (n *typeNode) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *typeNode) copy() Node {
+ c := *n
+ return &c
+}
+func (n *typeNode) doChildren(do func(Node) bool) bool {
+ return false
+}
+func (n *typeNode) editChildren(edit func(Node) Node) {
+}
+
+func copyCaseClauses(list []*CaseClause) []*CaseClause {
+ if list == nil {
+ return nil
+ }
+ c := make([]*CaseClause, len(list))
+ copy(c, list)
+ return c
+}
+func doCaseClauses(list []*CaseClause, do func(Node) bool) bool {
+ for _, x := range list {
+ if x != nil && do(x) {
+ return true
+ }
+ }
+ return false
+}
+func editCaseClauses(list []*CaseClause, edit func(Node) Node) {
+ for i, x := range list {
+ if x != nil {
+ list[i] = edit(x).(*CaseClause)
+ }
+ }
+}
+
+func copyCommClauses(list []*CommClause) []*CommClause {
+ if list == nil {
+ return nil
+ }
+ c := make([]*CommClause, len(list))
+ copy(c, list)
+ return c
+}
+func doCommClauses(list []*CommClause, do func(Node) bool) bool {
+ for _, x := range list {
+ if x != nil && do(x) {
+ return true
+ }
+ }
+ return false
+}
+func editCommClauses(list []*CommClause, edit func(Node) Node) {
+ for i, x := range list {
+ if x != nil {
+ list[i] = edit(x).(*CommClause)
+ }
+ }
+}
+
+func copyNames(list []*Name) []*Name {
+ if list == nil {
+ return nil
+ }
+ c := make([]*Name, len(list))
+ copy(c, list)
+ return c
+}
+func doNames(list []*Name, do func(Node) bool) bool {
+ for _, x := range list {
+ if x != nil && do(x) {
+ return true
+ }
+ }
+ return false
+}
+func editNames(list []*Name, edit func(Node) Node) {
+ for i, x := range list {
+ if x != nil {
+ list[i] = edit(x).(*Name)
+ }
+ }
+}
+
+func copyNodes(list []Node) []Node {
+ if list == nil {
+ return nil
+ }
+ c := make([]Node, len(list))
+ copy(c, list)
+ return c
+}
+func doNodes(list []Node, do func(Node) bool) bool {
+ for _, x := range list {
+ if x != nil && do(x) {
+ return true
+ }
+ }
+ return false
+}
+func editNodes(list []Node, edit func(Node) Node) {
+ for i, x := range list {
+ if x != nil {
+ list[i] = edit(x).(Node)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go
new file mode 100644
index 0000000000000..15c60baf449d9
--- /dev/null
+++ b/src/cmd/compile/internal/ir/op_string.go
@@ -0,0 +1,174 @@
+// Code generated by "stringer -type=Op -trimprefix=O node.go"; DO NOT EDIT.
+
+package ir
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[OXXX-0]
+ _ = x[ONAME-1]
+ _ = x[ONONAME-2]
+ _ = x[OTYPE-3]
+ _ = x[OPACK-4]
+ _ = x[OLITERAL-5]
+ _ = x[ONIL-6]
+ _ = x[OADD-7]
+ _ = x[OSUB-8]
+ _ = x[OOR-9]
+ _ = x[OXOR-10]
+ _ = x[OADDSTR-11]
+ _ = x[OADDR-12]
+ _ = x[OANDAND-13]
+ _ = x[OAPPEND-14]
+ _ = x[OBYTES2STR-15]
+ _ = x[OBYTES2STRTMP-16]
+ _ = x[ORUNES2STR-17]
+ _ = x[OSTR2BYTES-18]
+ _ = x[OSTR2BYTESTMP-19]
+ _ = x[OSTR2RUNES-20]
+ _ = x[OAS-21]
+ _ = x[OAS2-22]
+ _ = x[OAS2DOTTYPE-23]
+ _ = x[OAS2FUNC-24]
+ _ = x[OAS2MAPR-25]
+ _ = x[OAS2RECV-26]
+ _ = x[OASOP-27]
+ _ = x[OCALL-28]
+ _ = x[OCALLFUNC-29]
+ _ = x[OCALLMETH-30]
+ _ = x[OCALLINTER-31]
+ _ = x[OCALLPART-32]
+ _ = x[OCAP-33]
+ _ = x[OCLOSE-34]
+ _ = x[OCLOSURE-35]
+ _ = x[OCOMPLIT-36]
+ _ = x[OMAPLIT-37]
+ _ = x[OSTRUCTLIT-38]
+ _ = x[OARRAYLIT-39]
+ _ = x[OSLICELIT-40]
+ _ = x[OPTRLIT-41]
+ _ = x[OCONV-42]
+ _ = x[OCONVIFACE-43]
+ _ = x[OCONVNOP-44]
+ _ = x[OCOPY-45]
+ _ = x[ODCL-46]
+ _ = x[ODCLFUNC-47]
+ _ = x[ODCLCONST-48]
+ _ = x[ODCLTYPE-49]
+ _ = x[ODELETE-50]
+ _ = x[ODOT-51]
+ _ = x[ODOTPTR-52]
+ _ = x[ODOTMETH-53]
+ _ = x[ODOTINTER-54]
+ _ = x[OXDOT-55]
+ _ = x[ODOTTYPE-56]
+ _ = x[ODOTTYPE2-57]
+ _ = x[OEQ-58]
+ _ = x[ONE-59]
+ _ = x[OLT-60]
+ _ = x[OLE-61]
+ _ = x[OGE-62]
+ _ = x[OGT-63]
+ _ = x[ODEREF-64]
+ _ = x[OINDEX-65]
+ _ = x[OINDEXMAP-66]
+ _ = x[OKEY-67]
+ _ = x[OSTRUCTKEY-68]
+ _ = x[OLEN-69]
+ _ = x[OMAKE-70]
+ _ = x[OMAKECHAN-71]
+ _ = x[OMAKEMAP-72]
+ _ = x[OMAKESLICE-73]
+ _ = x[OMAKESLICECOPY-74]
+ _ = x[OMUL-75]
+ _ = x[ODIV-76]
+ _ = x[OMOD-77]
+ _ = x[OLSH-78]
+ _ = x[ORSH-79]
+ _ = x[OAND-80]
+ _ = x[OANDNOT-81]
+ _ = x[ONEW-82]
+ _ = x[ONOT-83]
+ _ = x[OBITNOT-84]
+ _ = x[OPLUS-85]
+ _ = x[ONEG-86]
+ _ = x[OOROR-87]
+ _ = x[OPANIC-88]
+ _ = x[OPRINT-89]
+ _ = x[OPRINTN-90]
+ _ = x[OPAREN-91]
+ _ = x[OSEND-92]
+ _ = x[OSLICE-93]
+ _ = x[OSLICEARR-94]
+ _ = x[OSLICESTR-95]
+ _ = x[OSLICE3-96]
+ _ = x[OSLICE3ARR-97]
+ _ = x[OSLICEHEADER-98]
+ _ = x[ORECOVER-99]
+ _ = x[ORECV-100]
+ _ = x[ORUNESTR-101]
+ _ = x[OSELRECV2-102]
+ _ = x[OIOTA-103]
+ _ = x[OREAL-104]
+ _ = x[OIMAG-105]
+ _ = x[OCOMPLEX-106]
+ _ = x[OALIGNOF-107]
+ _ = x[OOFFSETOF-108]
+ _ = x[OSIZEOF-109]
+ _ = x[OMETHEXPR-110]
+ _ = x[OSTMTEXPR-111]
+ _ = x[OBLOCK-112]
+ _ = x[OBREAK-113]
+ _ = x[OCASE-114]
+ _ = x[OCONTINUE-115]
+ _ = x[ODEFER-116]
+ _ = x[OFALL-117]
+ _ = x[OFOR-118]
+ _ = x[OFORUNTIL-119]
+ _ = x[OGOTO-120]
+ _ = x[OIF-121]
+ _ = x[OLABEL-122]
+ _ = x[OGO-123]
+ _ = x[ORANGE-124]
+ _ = x[ORETURN-125]
+ _ = x[OSELECT-126]
+ _ = x[OSWITCH-127]
+ _ = x[OTYPESW-128]
+ _ = x[OTCHAN-129]
+ _ = x[OTMAP-130]
+ _ = x[OTSTRUCT-131]
+ _ = x[OTINTER-132]
+ _ = x[OTFUNC-133]
+ _ = x[OTARRAY-134]
+ _ = x[OTSLICE-135]
+ _ = x[OINLCALL-136]
+ _ = x[OEFACE-137]
+ _ = x[OITAB-138]
+ _ = x[OIDATA-139]
+ _ = x[OSPTR-140]
+ _ = x[OCFUNC-141]
+ _ = x[OCHECKNIL-142]
+ _ = x[OVARDEF-143]
+ _ = x[OVARKILL-144]
+ _ = x[OVARLIVE-145]
+ _ = x[ORESULT-146]
+ _ = x[OINLMARK-147]
+ _ = x[OLINKSYMOFFSET-148]
+ _ = x[OTAILCALL-149]
+ _ = x[OGETG-150]
+ _ = x[OEND-151]
+}
+
+const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFMETHEXPRSTMTEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND"
+
+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, 474, 480, 484, 487, 491, 496, 501, 507, 512, 516, 521, 529, 537, 543, 552, 563, 570, 574, 581, 589, 593, 597, 601, 608, 615, 623, 629, 637, 645, 650, 655, 659, 667, 672, 676, 679, 687, 691, 693, 698, 700, 705, 711, 717, 723, 729, 734, 738, 745, 751, 756, 762, 768, 775, 780, 784, 789, 793, 798, 806, 812, 819, 826, 832, 839, 852, 860, 864, 867}
+
+func (i Op) String() string {
+ if i >= Op(len(_Op_index)-1) {
+ return "Op(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Op_name[_Op_index[i]:_Op_index[i+1]]
+}
diff --git a/src/cmd/compile/internal/ir/package.go b/src/cmd/compile/internal/ir/package.go
new file mode 100644
index 0000000000000..3896e2b91b117
--- /dev/null
+++ b/src/cmd/compile/internal/ir/package.go
@@ -0,0 +1,35 @@
+// 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 ir
+
+import "cmd/compile/internal/types"
+
+// A Package holds information about the package being compiled.
+type Package struct {
+ // Imports, listed in source order.
+ // See golang.org/issue/31636.
+ Imports []*types.Pkg
+
+ // Init functions, listed in source order.
+ Inits []*Func
+
+ // Top-level declarations.
+ Decls []Node
+
+ // Extern (package global) declarations.
+ Externs []Node
+
+ // Assembly function declarations.
+ Asms []*Name
+
+ // Cgo directives.
+ CgoPragmas [][]string
+
+ // Variables with //go:embed lines.
+ Embeds []*Name
+
+ // Exported (or re-exported) symbols.
+ Exports []*Name
+}
diff --git a/src/cmd/compile/internal/gc/scc.go b/src/cmd/compile/internal/ir/scc.go
similarity index 74%
rename from src/cmd/compile/internal/gc/scc.go
rename to src/cmd/compile/internal/ir/scc.go
index 5c7935aa87674..83c6074170b31 100644
--- a/src/cmd/compile/internal/gc/scc.go
+++ b/src/cmd/compile/internal/ir/scc.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.
-package gc
+package ir
// Strongly connected components.
//
@@ -30,13 +30,13 @@ package gc
// when analyzing a set of mutually recursive functions.
type bottomUpVisitor struct {
- analyze func([]*Node, bool)
+ analyze func([]*Func, bool)
visitgen uint32
- nodeID map[*Node]uint32
- stack []*Node
+ nodeID map[*Func]uint32
+ stack []*Func
}
-// visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
+// VisitFuncsBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
// It calls analyze with successive groups of functions, working from
// the bottom of the call graph upward. Each time analyze is called with
// a list of functions, every function on that list only calls other functions
@@ -49,18 +49,21 @@ type bottomUpVisitor struct {
// If recursive is false, the list consists of only a single function and its closures.
// If recursive is true, the list may still contain only a single function,
// if that function is itself recursive.
-func visitBottomUp(list []*Node, analyze func(list []*Node, recursive bool)) {
+func VisitFuncsBottomUp(list []Node, analyze func(list []*Func, recursive bool)) {
var v bottomUpVisitor
v.analyze = analyze
- v.nodeID = make(map[*Node]uint32)
+ v.nodeID = make(map[*Func]uint32)
for _, n := range list {
- if n.Op == ODCLFUNC && !n.Func.IsHiddenClosure() {
- v.visit(n)
+ if n.Op() == ODCLFUNC {
+ n := n.(*Func)
+ if !n.IsHiddenClosure() {
+ v.visit(n)
+ }
}
}
}
-func (v *bottomUpVisitor) visit(n *Node) uint32 {
+func (v *bottomUpVisitor) visit(n *Func) uint32 {
if id := v.nodeID[n]; id > 0 {
// already visited
return id
@@ -73,42 +76,31 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 {
min := v.visitgen
v.stack = append(v.stack, n)
- inspectList(n.Nbody, func(n *Node) bool {
- switch n.Op {
- case ONAME:
- if n.Class() == PFUNC {
- if n.isMethodExpression() {
- n = asNode(n.Type.Nname())
- }
- if n != nil && n.Name.Defn != nil {
- if m := v.visit(n.Name.Defn); m < min {
- min = m
- }
- }
+ do := func(defn Node) {
+ if defn != nil {
+ if m := v.visit(defn.(*Func)); m < min {
+ min = m
}
- case ODOTMETH:
- fn := asNode(n.Type.Nname())
- if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
- if m := v.visit(fn.Name.Defn); m < min {
- min = m
- }
+ }
+ }
+
+ Visit(n, func(n Node) {
+ switch n.Op() {
+ case ONAME:
+ if n := n.(*Name); n.Class == PFUNC {
+ do(n.Defn)
}
- case OCALLPART:
- fn := asNode(callpartMethod(n).Type.Nname())
- if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
- if m := v.visit(fn.Name.Defn); m < min {
- min = m
- }
+ case ODOTMETH, OCALLPART, OMETHEXPR:
+ if fn := MethodExprName(n); fn != nil {
+ do(fn.Defn)
}
case OCLOSURE:
- if m := v.visit(n.Func.Closure); m < min {
- min = m
- }
+ n := n.(*ClosureExpr)
+ do(n.Func)
}
- return true
})
- if (min == id || min == id+1) && !n.Func.IsHiddenClosure() {
+ if (min == id || min == id+1) && !n.IsHiddenClosure() {
// This node is the root of a strongly connected component.
// The original min passed to visitcodelist was v.nodeID[n]+1.
diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/ir/sizeof_test.go
similarity index 88%
rename from src/cmd/compile/internal/gc/sizeof_test.go
rename to src/cmd/compile/internal/ir/sizeof_test.go
index ce4a216c2e2da..d8c1518b90f34 100644
--- a/src/cmd/compile/internal/gc/sizeof_test.go
+++ b/src/cmd/compile/internal/ir/sizeof_test.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.
-package gc
+package ir
import (
"reflect"
@@ -20,10 +20,8 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {Func{}, 124, 224},
- {Name{}, 32, 56},
- {Param{}, 24, 48},
- {Node{}, 76, 128},
+ {Func{}, 188, 328},
+ {Name{}, 112, 200},
}
for _, tt := range tests {
diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go
new file mode 100644
index 0000000000000..c304867e1d9a9
--- /dev/null
+++ b/src/cmd/compile/internal/ir/stmt.go
@@ -0,0 +1,414 @@
+// 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 ir
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// A Decl is a declaration of a const, type, or var. (A declared func is a Func.)
+type Decl struct {
+ miniNode
+ X *Name // the thing being declared
+}
+
+func NewDecl(pos src.XPos, op Op, x *Name) *Decl {
+ n := &Decl{X: x}
+ n.pos = pos
+ switch op {
+ default:
+ panic("invalid Decl op " + op.String())
+ case ODCL, ODCLCONST, ODCLTYPE:
+ n.op = op
+ }
+ return n
+}
+
+func (*Decl) isStmt() {}
+
+// A Stmt is a Node that can appear as a statement.
+// This includes statement-like expressions such as f().
+//
+// (It's possible it should include <-c, but that would require
+// splitting ORECV out of UnaryExpr, which hasn't yet been
+// necessary. Maybe instead we will introduce ExprStmt at
+// some point.)
+type Stmt interface {
+ Node
+ isStmt()
+}
+
+// A miniStmt is a miniNode with extra fields common to statements.
+type miniStmt struct {
+ miniNode
+ init Nodes
+}
+
+func (*miniStmt) isStmt() {}
+
+func (n *miniStmt) Init() Nodes { return n.init }
+func (n *miniStmt) SetInit(x Nodes) { n.init = x }
+func (n *miniStmt) PtrInit() *Nodes { return &n.init }
+
+// An AssignListStmt is an assignment statement with
+// more than one item on at least one side: Lhs = Rhs.
+// If Def is true, the assignment is a :=.
+type AssignListStmt struct {
+ miniStmt
+ Lhs Nodes
+ Def bool
+ Rhs Nodes
+}
+
+func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt {
+ n := &AssignListStmt{}
+ n.pos = pos
+ n.SetOp(op)
+ n.Lhs = lhs
+ n.Rhs = rhs
+ return n
+}
+
+func (n *AssignListStmt) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2:
+ n.op = op
+ }
+}
+
+// An AssignStmt is a simple assignment statement: X = Y.
+// If Def is true, the assignment is a :=.
+type AssignStmt struct {
+ miniStmt
+ X Node
+ Def bool
+ Y Node
+}
+
+func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt {
+ n := &AssignStmt{X: x, Y: y}
+ n.pos = pos
+ n.op = OAS
+ return n
+}
+
+func (n *AssignStmt) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case OAS:
+ n.op = op
+ }
+}
+
+// An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y.
+type AssignOpStmt struct {
+ miniStmt
+ X Node
+ AsOp Op // OADD etc
+ Y Node
+ IncDec bool // actually ++ or --
+}
+
+func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt {
+ n := &AssignOpStmt{AsOp: asOp, X: x, Y: y}
+ n.pos = pos
+ n.op = OASOP
+ return n
+}
+
+// A BlockStmt is a block: { List }.
+type BlockStmt struct {
+ miniStmt
+ List Nodes
+}
+
+func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt {
+ n := &BlockStmt{}
+ n.pos = pos
+ if !pos.IsKnown() {
+ n.pos = base.Pos
+ if len(list) > 0 {
+ n.pos = list[0].Pos()
+ }
+ }
+ n.op = OBLOCK
+ n.List = list
+ return n
+}
+
+// A BranchStmt is a break, continue, fallthrough, or goto statement.
+type BranchStmt struct {
+ miniStmt
+ Label *types.Sym // label if present
+}
+
+func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt {
+ switch op {
+ case OBREAK, OCONTINUE, OFALL, OGOTO:
+ // ok
+ default:
+ panic("NewBranch " + op.String())
+ }
+ n := &BranchStmt{Label: label}
+ n.pos = pos
+ n.op = op
+ return n
+}
+
+func (n *BranchStmt) Sym() *types.Sym { return n.Label }
+
+// A CaseClause is a case statement in a switch or select: case List: Body.
+type CaseClause struct {
+ miniStmt
+ Var *Name // declared variable for this case in type switch
+ List Nodes // list of expressions for switch, early select
+ Body Nodes
+}
+
+func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause {
+ n := &CaseClause{List: list, Body: body}
+ n.pos = pos
+ n.op = OCASE
+ return n
+}
+
+type CommClause struct {
+ miniStmt
+ Comm Node // communication case
+ Body Nodes
+}
+
+func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause {
+ n := &CommClause{Comm: comm, Body: body}
+ n.pos = pos
+ n.op = OCASE
+ return n
+}
+
+// A ForStmt is a non-range for loop: for Init; Cond; Post { Body }
+// Op can be OFOR or OFORUNTIL (!Cond).
+type ForStmt struct {
+ miniStmt
+ Label *types.Sym
+ Cond Node
+ Late Nodes
+ Post Node
+ Body Nodes
+ HasBreak bool
+}
+
+func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt {
+ n := &ForStmt{Cond: cond, Post: post}
+ n.pos = pos
+ n.op = OFOR
+ if init != nil {
+ n.init = []Node{init}
+ }
+ n.Body = body
+ return n
+}
+
+func (n *ForStmt) SetOp(op Op) {
+ if op != OFOR && op != OFORUNTIL {
+ panic(n.no("SetOp " + op.String()))
+ }
+ n.op = op
+}
+
+// A GoDeferStmt is a go or defer statement: go Call / defer Call.
+//
+// The two opcodes use a signle syntax because the implementations
+// are very similar: both are concerned with saving Call and running it
+// in a different context (a separate goroutine or a later time).
+type GoDeferStmt struct {
+ miniStmt
+ Call Node
+}
+
+func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
+ n := &GoDeferStmt{Call: call}
+ n.pos = pos
+ switch op {
+ case ODEFER, OGO:
+ n.op = op
+ default:
+ panic("NewGoDeferStmt " + op.String())
+ }
+ return n
+}
+
+// A IfStmt is a return statement: if Init; Cond { Then } else { Else }.
+type IfStmt struct {
+ miniStmt
+ Cond Node
+ Body Nodes
+ Else Nodes
+ Likely bool // code layout hint
+}
+
+func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt {
+ n := &IfStmt{Cond: cond}
+ n.pos = pos
+ n.op = OIF
+ n.Body = body
+ n.Else = els
+ return n
+}
+
+// An InlineMarkStmt is a marker placed just before an inlined body.
+type InlineMarkStmt struct {
+ miniStmt
+ Index int64
+}
+
+func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt {
+ n := &InlineMarkStmt{Index: index}
+ n.pos = pos
+ n.op = OINLMARK
+ return n
+}
+
+func (n *InlineMarkStmt) Offset() int64 { return n.Index }
+func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x }
+
+// A LabelStmt is a label statement (just the label, not including the statement it labels).
+type LabelStmt struct {
+ miniStmt
+ Label *types.Sym // "Label:"
+}
+
+func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt {
+ n := &LabelStmt{Label: label}
+ n.pos = pos
+ n.op = OLABEL
+ return n
+}
+
+func (n *LabelStmt) Sym() *types.Sym { return n.Label }
+
+// A RangeStmt is a range loop: for Key, Value = range X { Body }
+type RangeStmt struct {
+ miniStmt
+ Label *types.Sym
+ Def bool
+ X Node
+ Key Node
+ Value Node
+ Body Nodes
+ HasBreak bool
+ Prealloc *Name
+}
+
+func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
+ n := &RangeStmt{X: x, Key: key, Value: value}
+ n.pos = pos
+ n.op = ORANGE
+ n.Body = body
+ return n
+}
+
+// A ReturnStmt is a return statement.
+type ReturnStmt struct {
+ miniStmt
+ origNode // for typecheckargs rewrite
+ Results Nodes // return list
+}
+
+func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
+ n := &ReturnStmt{}
+ n.pos = pos
+ n.op = ORETURN
+ n.orig = n
+ n.Results = results
+ return n
+}
+
+// A SelectStmt is a block: { Cases }.
+type SelectStmt struct {
+ miniStmt
+ Label *types.Sym
+ Cases []*CommClause
+ HasBreak bool
+
+ // TODO(rsc): Instead of recording here, replace with a block?
+ Compiled Nodes // compiled form, after walkSwitch
+}
+
+func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt {
+ n := &SelectStmt{Cases: cases}
+ n.pos = pos
+ n.op = OSELECT
+ return n
+}
+
+// A SendStmt is a send statement: X <- Y.
+type SendStmt struct {
+ miniStmt
+ Chan Node
+ Value Node
+}
+
+func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt {
+ n := &SendStmt{Chan: ch, Value: value}
+ n.pos = pos
+ n.op = OSEND
+ return n
+}
+
+// A SwitchStmt is a switch statement: switch Init; Expr { Cases }.
+type SwitchStmt struct {
+ miniStmt
+ Tag Node
+ Cases []*CaseClause
+ Label *types.Sym
+ HasBreak bool
+
+ // TODO(rsc): Instead of recording here, replace with a block?
+ Compiled Nodes // compiled form, after walkSwitch
+}
+
+func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
+ n := &SwitchStmt{Tag: tag, Cases: cases}
+ n.pos = pos
+ n.op = OSWITCH
+ return n
+}
+
+// A TailCallStmt is a tail call statement, which is used for back-end
+// code generation to jump directly to another function entirely.
+type TailCallStmt struct {
+ miniStmt
+ Target *Name
+}
+
+func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt {
+ if target.Op() != ONAME || target.Class != PFUNC {
+ base.FatalfAt(pos, "tail call to non-func %v", target)
+ }
+ n := &TailCallStmt{Target: target}
+ n.pos = pos
+ n.op = OTAILCALL
+ return n
+}
+
+// A TypeSwitchGuard is the [Name :=] X.(type) in a type switch.
+type TypeSwitchGuard struct {
+ miniNode
+ Tag *Ident
+ X Node
+ Used bool
+}
+
+func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard {
+ n := &TypeSwitchGuard{Tag: tag, X: x}
+ n.pos = pos
+ n.op = OTYPESW
+ return n
+}
diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go
new file mode 100644
index 0000000000000..61727fb1c4b00
--- /dev/null
+++ b/src/cmd/compile/internal/ir/symtab.go
@@ -0,0 +1,72 @@
+// Copyright 2009 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 ir
+
+import (
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+// Syms holds known symbols.
+var Syms struct {
+ AssertE2I *obj.LSym
+ AssertE2I2 *obj.LSym
+ AssertI2I *obj.LSym
+ AssertI2I2 *obj.LSym
+ Deferproc *obj.LSym
+ DeferprocStack *obj.LSym
+ Deferreturn *obj.LSym
+ Duffcopy *obj.LSym
+ Duffzero *obj.LSym
+ GCWriteBarrier *obj.LSym
+ Goschedguarded *obj.LSym
+ Growslice *obj.LSym
+ Msanread *obj.LSym
+ Msanwrite *obj.LSym
+ Msanmove *obj.LSym
+ Newobject *obj.LSym
+ Newproc *obj.LSym
+ Panicdivide *obj.LSym
+ Panicshift *obj.LSym
+ PanicdottypeE *obj.LSym
+ PanicdottypeI *obj.LSym
+ Panicnildottype *obj.LSym
+ Panicoverflow *obj.LSym
+ Raceread *obj.LSym
+ Racereadrange *obj.LSym
+ Racewrite *obj.LSym
+ Racewriterange *obj.LSym
+ // Wasm
+ SigPanic *obj.LSym
+ Staticuint64s *obj.LSym
+ Typedmemclr *obj.LSym
+ Typedmemmove *obj.LSym
+ Udiv *obj.LSym
+ WriteBarrier *obj.LSym
+ Zerobase *obj.LSym
+ ARM64HasATOMICS *obj.LSym
+ ARMHasVFPv4 *obj.LSym
+ X86HasFMA *obj.LSym
+ X86HasPOPCNT *obj.LSym
+ X86HasSSE41 *obj.LSym
+ // Wasm
+ WasmDiv *obj.LSym
+ // Wasm
+ WasmMove *obj.LSym
+ // Wasm
+ WasmZero *obj.LSym
+ // Wasm
+ WasmTruncS *obj.LSym
+ // Wasm
+ WasmTruncU *obj.LSym
+}
+
+// Pkgs holds known packages.
+var Pkgs struct {
+ Go *types.Pkg
+ Itab *types.Pkg
+ Runtime *types.Pkg
+ Unsafe *types.Pkg
+}
diff --git a/src/cmd/compile/internal/ir/type.go b/src/cmd/compile/internal/ir/type.go
new file mode 100644
index 0000000000000..a903ea8cd4554
--- /dev/null
+++ b/src/cmd/compile/internal/ir/type.go
@@ -0,0 +1,310 @@
+// Copyright 2009 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 ir
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+ "fmt"
+)
+
+// Nodes that represent the syntax of a type before type-checking.
+// After type-checking, they serve only as shells around a *types.Type.
+// Calling TypeNode converts a *types.Type to a Node shell.
+
+// An Ntype is a Node that syntactically looks like a type.
+// It can be the raw syntax for a type before typechecking,
+// or it can be an OTYPE with Type() set to a *types.Type.
+// Note that syntax doesn't guarantee it's a type: an expression
+// like *fmt is an Ntype (we don't know whether names are types yet),
+// but at least 1+1 is not an Ntype.
+type Ntype interface {
+ Node
+ CanBeNtype()
+}
+
+// A miniType is a minimal type syntax Node implementation,
+// to be embedded as the first field in a larger node implementation.
+type miniType struct {
+ miniNode
+ typ *types.Type
+}
+
+func (*miniType) CanBeNtype() {}
+
+func (n *miniType) Type() *types.Type { return n.typ }
+
+// setOTYPE changes n to be an OTYPE node returning t.
+// Rewriting the node in place this way should not be strictly
+// necessary (we should be able to update the uses with
+// proper OTYPE nodes), but it's mostly harmless and easy
+// to keep doing for now.
+//
+// setOTYPE also records t.Nod = self if t.Nod is not already set.
+// (Some types are shared by multiple OTYPE nodes, so only
+// the first such node is used as t.Nod.)
+func (n *miniType) setOTYPE(t *types.Type, self Ntype) {
+ if n.typ != nil {
+ panic(n.op.String() + " SetType: type already set")
+ }
+ n.op = OTYPE
+ n.typ = t
+ t.SetNod(self)
+}
+
+func (n *miniType) Sym() *types.Sym { return nil } // for Format OTYPE
+func (n *miniType) Implicit() bool { return false } // for Format OTYPE
+
+// A ChanType represents a chan Elem syntax with the direction Dir.
+type ChanType struct {
+ miniType
+ Elem Ntype
+ Dir types.ChanDir
+}
+
+func NewChanType(pos src.XPos, elem Ntype, dir types.ChanDir) *ChanType {
+ n := &ChanType{Elem: elem, Dir: dir}
+ n.op = OTCHAN
+ n.pos = pos
+ return n
+}
+
+func (n *ChanType) SetOTYPE(t *types.Type) {
+ n.setOTYPE(t, n)
+ n.Elem = nil
+}
+
+// A MapType represents a map[Key]Value type syntax.
+type MapType struct {
+ miniType
+ Key Ntype
+ Elem Ntype
+}
+
+func NewMapType(pos src.XPos, key, elem Ntype) *MapType {
+ n := &MapType{Key: key, Elem: elem}
+ n.op = OTMAP
+ n.pos = pos
+ return n
+}
+
+func (n *MapType) SetOTYPE(t *types.Type) {
+ n.setOTYPE(t, n)
+ n.Key = nil
+ n.Elem = nil
+}
+
+// A StructType represents a struct { ... } type syntax.
+type StructType struct {
+ miniType
+ Fields []*Field
+}
+
+func NewStructType(pos src.XPos, fields []*Field) *StructType {
+ n := &StructType{Fields: fields}
+ n.op = OTSTRUCT
+ n.pos = pos
+ return n
+}
+
+func (n *StructType) SetOTYPE(t *types.Type) {
+ n.setOTYPE(t, n)
+ n.Fields = nil
+}
+
+// An InterfaceType represents a struct { ... } type syntax.
+type InterfaceType struct {
+ miniType
+ Methods []*Field
+}
+
+func NewInterfaceType(pos src.XPos, methods []*Field) *InterfaceType {
+ n := &InterfaceType{Methods: methods}
+ n.op = OTINTER
+ n.pos = pos
+ return n
+}
+
+func (n *InterfaceType) SetOTYPE(t *types.Type) {
+ n.setOTYPE(t, n)
+ n.Methods = nil
+}
+
+// A FuncType represents a func(Args) Results type syntax.
+type FuncType struct {
+ miniType
+ Recv *Field
+ Params []*Field
+ Results []*Field
+}
+
+func NewFuncType(pos src.XPos, rcvr *Field, args, results []*Field) *FuncType {
+ n := &FuncType{Recv: rcvr, Params: args, Results: results}
+ n.op = OTFUNC
+ n.pos = pos
+ return n
+}
+
+func (n *FuncType) SetOTYPE(t *types.Type) {
+ n.setOTYPE(t, n)
+ n.Recv = nil
+ n.Params = nil
+ n.Results = nil
+}
+
+// A Field is a declared struct field, interface method, or function argument.
+// It is not a Node.
+type Field struct {
+ Pos src.XPos
+ Sym *types.Sym
+ Ntype Ntype
+ Type *types.Type
+ Embedded bool
+ IsDDD bool
+ Note string
+ Decl *Name
+}
+
+func NewField(pos src.XPos, sym *types.Sym, ntyp Ntype, typ *types.Type) *Field {
+ return &Field{Pos: pos, Sym: sym, Ntype: ntyp, Type: typ}
+}
+
+func (f *Field) String() string {
+ var typ string
+ if f.Type != nil {
+ typ = fmt.Sprint(f.Type)
+ } else {
+ typ = fmt.Sprint(f.Ntype)
+ }
+ if f.Sym != nil {
+ return fmt.Sprintf("%v %v", f.Sym, typ)
+ }
+ return typ
+}
+
+// TODO(mdempsky): Make Field a Node again so these can be generated?
+// Fields are Nodes in go/ast and cmd/compile/internal/syntax.
+
+func copyField(f *Field) *Field {
+ if f == nil {
+ return nil
+ }
+ c := *f
+ return &c
+}
+func doField(f *Field, do func(Node) bool) bool {
+ if f == nil {
+ return false
+ }
+ if f.Decl != nil && do(f.Decl) {
+ return true
+ }
+ if f.Ntype != nil && do(f.Ntype) {
+ return true
+ }
+ return false
+}
+func editField(f *Field, edit func(Node) Node) {
+ if f == nil {
+ return
+ }
+ if f.Decl != nil {
+ f.Decl = edit(f.Decl).(*Name)
+ }
+ if f.Ntype != nil {
+ f.Ntype = edit(f.Ntype).(Ntype)
+ }
+}
+
+func copyFields(list []*Field) []*Field {
+ out := make([]*Field, len(list))
+ for i, f := range list {
+ out[i] = copyField(f)
+ }
+ return out
+}
+func doFields(list []*Field, do func(Node) bool) bool {
+ for _, x := range list {
+ if doField(x, do) {
+ return true
+ }
+ }
+ return false
+}
+func editFields(list []*Field, edit func(Node) Node) {
+ for _, f := range list {
+ editField(f, edit)
+ }
+}
+
+// A SliceType represents a []Elem type syntax.
+// If DDD is true, it's the ...Elem at the end of a function list.
+type SliceType struct {
+ miniType
+ Elem Ntype
+ DDD bool
+}
+
+func NewSliceType(pos src.XPos, elem Ntype) *SliceType {
+ n := &SliceType{Elem: elem}
+ n.op = OTSLICE
+ n.pos = pos
+ return n
+}
+
+func (n *SliceType) SetOTYPE(t *types.Type) {
+ n.setOTYPE(t, n)
+ n.Elem = nil
+}
+
+// An ArrayType represents a [Len]Elem type syntax.
+// If Len is nil, the type is a [...]Elem in an array literal.
+type ArrayType struct {
+ miniType
+ Len Node
+ Elem Ntype
+}
+
+func NewArrayType(pos src.XPos, len Node, elem Ntype) *ArrayType {
+ n := &ArrayType{Len: len, Elem: elem}
+ n.op = OTARRAY
+ n.pos = pos
+ return n
+}
+
+func (n *ArrayType) SetOTYPE(t *types.Type) {
+ n.setOTYPE(t, n)
+ n.Len = nil
+ n.Elem = nil
+}
+
+// A typeNode is a Node wrapper for type t.
+type typeNode struct {
+ miniNode
+ typ *types.Type
+}
+
+func newTypeNode(pos src.XPos, typ *types.Type) *typeNode {
+ n := &typeNode{typ: typ}
+ n.pos = pos
+ n.op = OTYPE
+ return n
+}
+
+func (n *typeNode) Type() *types.Type { return n.typ }
+func (n *typeNode) Sym() *types.Sym { return n.typ.Sym() }
+func (n *typeNode) CanBeNtype() {}
+
+// TypeNode returns the Node representing the type t.
+func TypeNode(t *types.Type) Ntype {
+ if n := t.Obj(); n != nil {
+ if n.Type() != t {
+ base.Fatalf("type skew: %v has type %v, but expected %v", n, n.Type(), t)
+ }
+ return n.(Ntype)
+ }
+ return newTypeNode(src.NoXPos, t)
+}
diff --git a/src/cmd/compile/internal/ir/val.go b/src/cmd/compile/internal/ir/val.go
new file mode 100644
index 0000000000000..ff45f31074e1c
--- /dev/null
+++ b/src/cmd/compile/internal/ir/val.go
@@ -0,0 +1,171 @@
+// Copyright 2009 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 ir
+
+import (
+ "go/constant"
+ "math"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/types"
+)
+
+func ConstType(n Node) constant.Kind {
+ if n == nil || n.Op() != OLITERAL {
+ return constant.Unknown
+ }
+ return n.Val().Kind()
+}
+
+// ValueInterface returns the constant value stored in n as an interface{}.
+// It returns int64s for ints and runes, float64s for floats,
+// and complex128s for complex values.
+func ConstValue(n Node) interface{} {
+ switch v := n.Val(); v.Kind() {
+ default:
+ base.Fatalf("unexpected constant: %v", v)
+ panic("unreachable")
+ case constant.Bool:
+ return constant.BoolVal(v)
+ case constant.String:
+ return constant.StringVal(v)
+ case constant.Int:
+ return IntVal(n.Type(), v)
+ case constant.Float:
+ return Float64Val(v)
+ case constant.Complex:
+ return complex(Float64Val(constant.Real(v)), Float64Val(constant.Imag(v)))
+ }
+}
+
+// int64Val returns v converted to int64.
+// Note: if t is uint64, very large values will be converted to negative int64.
+func IntVal(t *types.Type, v constant.Value) int64 {
+ if t.IsUnsigned() {
+ if x, ok := constant.Uint64Val(v); ok {
+ return int64(x)
+ }
+ } else {
+ if x, ok := constant.Int64Val(v); ok {
+ return x
+ }
+ }
+ base.Fatalf("%v out of range for %v", v, t)
+ panic("unreachable")
+}
+
+func Float64Val(v constant.Value) float64 {
+ if x, _ := constant.Float64Val(v); !math.IsInf(x, 0) {
+ return x + 0 // avoid -0 (should not be needed, but be conservative)
+ }
+ base.Fatalf("bad float64 value: %v", v)
+ panic("unreachable")
+}
+
+func AssertValidTypeForConst(t *types.Type, v constant.Value) {
+ if !ValidTypeForConst(t, v) {
+ base.Fatalf("%v does not represent %v", t, v)
+ }
+}
+
+func ValidTypeForConst(t *types.Type, v constant.Value) bool {
+ switch v.Kind() {
+ case constant.Unknown:
+ return OKForConst[t.Kind()]
+ case constant.Bool:
+ return t.IsBoolean()
+ case constant.String:
+ return t.IsString()
+ case constant.Int:
+ return t.IsInteger()
+ case constant.Float:
+ return t.IsFloat()
+ case constant.Complex:
+ return t.IsComplex()
+ }
+
+ base.Fatalf("unexpected constant kind: %v", v)
+ panic("unreachable")
+}
+
+// nodlit returns a new untyped constant with value v.
+func NewLiteral(v constant.Value) Node {
+ return NewBasicLit(base.Pos, v)
+}
+
+func idealType(ct constant.Kind) *types.Type {
+ switch ct {
+ case constant.String:
+ return types.UntypedString
+ case constant.Bool:
+ return types.UntypedBool
+ case constant.Int:
+ return types.UntypedInt
+ case constant.Float:
+ return types.UntypedFloat
+ case constant.Complex:
+ return types.UntypedComplex
+ }
+ base.Fatalf("unexpected Ctype: %v", ct)
+ return nil
+}
+
+var OKForConst [types.NTYPE]bool
+
+// CanInt64 reports whether it is safe to call Int64Val() on n.
+func CanInt64(n Node) bool {
+ if !IsConst(n, constant.Int) {
+ return false
+ }
+
+ // if the value inside n cannot be represented as an int64, the
+ // return value of Int64 is undefined
+ _, ok := constant.Int64Val(n.Val())
+ return ok
+}
+
+// Int64Val returns n as an int64.
+// n must be an integer or rune constant.
+func Int64Val(n Node) int64 {
+ if !IsConst(n, constant.Int) {
+ base.Fatalf("Int64Val(%v)", n)
+ }
+ x, ok := constant.Int64Val(n.Val())
+ if !ok {
+ base.Fatalf("Int64Val(%v)", n)
+ }
+ return x
+}
+
+// Uint64Val returns n as an uint64.
+// n must be an integer or rune constant.
+func Uint64Val(n Node) uint64 {
+ if !IsConst(n, constant.Int) {
+ base.Fatalf("Uint64Val(%v)", n)
+ }
+ x, ok := constant.Uint64Val(n.Val())
+ if !ok {
+ base.Fatalf("Uint64Val(%v)", n)
+ }
+ return x
+}
+
+// BoolVal returns n as a bool.
+// n must be a boolean constant.
+func BoolVal(n Node) bool {
+ if !IsConst(n, constant.Bool) {
+ base.Fatalf("BoolVal(%v)", n)
+ }
+ return constant.BoolVal(n.Val())
+}
+
+// StringVal returns the value of a literal string Node as a string.
+// n must be a string constant.
+func StringVal(n Node) string {
+ if !IsConst(n, constant.String) {
+ base.Fatalf("StringVal(%v)", n)
+ }
+ return constant.StringVal(n.Val())
+}
diff --git a/src/cmd/compile/internal/ir/visit.go b/src/cmd/compile/internal/ir/visit.go
new file mode 100644
index 0000000000000..c1b3d4ed9508c
--- /dev/null
+++ b/src/cmd/compile/internal/ir/visit.go
@@ -0,0 +1,186 @@
+// 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.
+
+// IR visitors for walking the IR tree.
+//
+// The lowest level helpers are DoChildren and EditChildren, which
+// nodes help implement and provide control over whether and when
+// recursion happens during the walk of the IR.
+//
+// Although these are both useful directly, two simpler patterns
+// are fairly common and also provided: Visit and Any.
+
+package ir
+
+// DoChildren calls do(x) on each of n's non-nil child nodes x.
+// If any call returns true, DoChildren stops and returns true.
+// Otherwise, DoChildren returns false.
+//
+// Note that DoChildren(n, do) only calls do(x) for n's immediate children.
+// If x's children should be processed, then do(x) must call DoChildren(x, do).
+//
+// DoChildren allows constructing general traversals of the IR graph
+// that can stop early if needed. The most general usage is:
+//
+// var do func(ir.Node) bool
+// do = func(x ir.Node) bool {
+// ... processing BEFORE visting children ...
+// if ... should visit children ... {
+// ir.DoChildren(x, do)
+// ... processing AFTER visting children ...
+// }
+// if ... should stop parent DoChildren call from visiting siblings ... {
+// return true
+// }
+// return false
+// }
+// do(root)
+//
+// Since DoChildren does not return true itself, if the do function
+// never wants to stop the traversal, it can assume that DoChildren
+// itself will always return false, simplifying to:
+//
+// var do func(ir.Node) bool
+// do = func(x ir.Node) bool {
+// ... processing BEFORE visting children ...
+// if ... should visit children ... {
+// ir.DoChildren(x, do)
+// }
+// ... processing AFTER visting children ...
+// return false
+// }
+// do(root)
+//
+// The Visit function illustrates a further simplification of the pattern,
+// only processing before visiting children and never stopping:
+//
+// func Visit(n ir.Node, visit func(ir.Node)) {
+// if n == nil {
+// return
+// }
+// var do func(ir.Node) bool
+// do = func(x ir.Node) bool {
+// visit(x)
+// return ir.DoChildren(x, do)
+// }
+// do(n)
+// }
+//
+// The Any function illustrates a different simplification of the pattern,
+// visiting each node and then its children, recursively, until finding
+// a node x for which cond(x) returns true, at which point the entire
+// traversal stops and returns true.
+//
+// func Any(n ir.Node, cond(ir.Node) bool) bool {
+// if n == nil {
+// return false
+// }
+// var do func(ir.Node) bool
+// do = func(x ir.Node) bool {
+// return cond(x) || ir.DoChildren(x, do)
+// }
+// return do(n)
+// }
+//
+// Visit and Any are presented above as examples of how to use
+// DoChildren effectively, but of course, usage that fits within the
+// simplifications captured by Visit or Any will be best served
+// by directly calling the ones provided by this package.
+func DoChildren(n Node, do func(Node) bool) bool {
+ if n == nil {
+ return false
+ }
+ return n.doChildren(do)
+}
+
+// Visit visits each non-nil node x in the IR tree rooted at n
+// in a depth-first preorder traversal, calling visit on each node visited.
+func Visit(n Node, visit func(Node)) {
+ if n == nil {
+ return
+ }
+ var do func(Node) bool
+ do = func(x Node) bool {
+ visit(x)
+ return DoChildren(x, do)
+ }
+ do(n)
+}
+
+// VisitList calls Visit(x, visit) for each node x in the list.
+func VisitList(list Nodes, visit func(Node)) {
+ for _, x := range list {
+ Visit(x, visit)
+ }
+}
+
+// Any looks for a non-nil node x in the IR tree rooted at n
+// for which cond(x) returns true.
+// Any considers nodes in a depth-first, preorder traversal.
+// When Any finds a node x such that cond(x) is true,
+// Any ends the traversal and returns true immediately.
+// Otherwise Any returns false after completing the entire traversal.
+func Any(n Node, cond func(Node) bool) bool {
+ if n == nil {
+ return false
+ }
+ var do func(Node) bool
+ do = func(x Node) bool {
+ return cond(x) || DoChildren(x, do)
+ }
+ return do(n)
+}
+
+// AnyList calls Any(x, cond) for each node x in the list, in order.
+// If any call returns true, AnyList stops and returns true.
+// Otherwise, AnyList returns false after calling Any(x, cond)
+// for every x in the list.
+func AnyList(list Nodes, cond func(Node) bool) bool {
+ for _, x := range list {
+ if Any(x, cond) {
+ return true
+ }
+ }
+ return false
+}
+
+// EditChildren edits the child nodes of n, replacing each child x with edit(x).
+//
+// Note that EditChildren(n, edit) only calls edit(x) for n's immediate children.
+// If x's children should be processed, then edit(x) must call EditChildren(x, edit).
+//
+// EditChildren allows constructing general editing passes of the IR graph.
+// The most general usage is:
+//
+// var edit func(ir.Node) ir.Node
+// edit = func(x ir.Node) ir.Node {
+// ... processing BEFORE editing children ...
+// if ... should edit children ... {
+// EditChildren(x, edit)
+// ... processing AFTER editing children ...
+// }
+// ... return x ...
+// }
+// n = edit(n)
+//
+// EditChildren edits the node in place. To edit a copy, call Copy first.
+// As an example, a simple deep copy implementation would be:
+//
+// func deepCopy(n ir.Node) ir.Node {
+// var edit func(ir.Node) ir.Node
+// edit = func(x ir.Node) ir.Node {
+// x = ir.Copy(x)
+// ir.EditChildren(x, edit)
+// return x
+// }
+// return edit(n)
+// }
+//
+// Of course, in this case it is better to call ir.DeepCopy than to build one anew.
+func EditChildren(n Node, edit func(Node) Node) {
+ if n == nil {
+ return
+ }
+ n.editChildren(edit)
+}
diff --git a/src/cmd/compile/internal/liveness/bvset.go b/src/cmd/compile/internal/liveness/bvset.go
new file mode 100644
index 0000000000000..3431f54ede84e
--- /dev/null
+++ b/src/cmd/compile/internal/liveness/bvset.go
@@ -0,0 +1,97 @@
+// Copyright 2013 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 liveness
+
+import "cmd/compile/internal/bitvec"
+
+// FNV-1 hash function constants.
+const (
+ h0 = 2166136261
+ hp = 16777619
+)
+
+// bvecSet is a set of bvecs, in initial insertion order.
+type bvecSet struct {
+ index []int // hash -> uniq index. -1 indicates empty slot.
+ uniq []bitvec.BitVec // unique bvecs, in insertion order
+}
+
+func (m *bvecSet) grow() {
+ // Allocate new index.
+ n := len(m.index) * 2
+ if n == 0 {
+ n = 32
+ }
+ newIndex := make([]int, n)
+ for i := range newIndex {
+ newIndex[i] = -1
+ }
+
+ // Rehash into newIndex.
+ for i, bv := range m.uniq {
+ h := hashbitmap(h0, bv) % uint32(len(newIndex))
+ for {
+ j := newIndex[h]
+ if j < 0 {
+ newIndex[h] = i
+ break
+ }
+ h++
+ if h == uint32(len(newIndex)) {
+ h = 0
+ }
+ }
+ }
+ m.index = newIndex
+}
+
+// add adds bv to the set and returns its index in m.extractUnique.
+// The caller must not modify bv after this.
+func (m *bvecSet) add(bv bitvec.BitVec) int {
+ if len(m.uniq)*4 >= len(m.index) {
+ m.grow()
+ }
+
+ index := m.index
+ h := hashbitmap(h0, bv) % uint32(len(index))
+ for {
+ j := index[h]
+ if j < 0 {
+ // New bvec.
+ index[h] = len(m.uniq)
+ m.uniq = append(m.uniq, bv)
+ return len(m.uniq) - 1
+ }
+ jlive := m.uniq[j]
+ if bv.Eq(jlive) {
+ // Existing bvec.
+ return j
+ }
+
+ h++
+ if h == uint32(len(index)) {
+ h = 0
+ }
+ }
+}
+
+// extractUnique returns this slice of unique bit vectors in m, as
+// indexed by the result of bvecSet.add.
+func (m *bvecSet) extractUnique() []bitvec.BitVec {
+ return m.uniq
+}
+
+func hashbitmap(h uint32, bv bitvec.BitVec) uint32 {
+ n := int((bv.N + 31) / 32)
+ for i := 0; i < n; i++ {
+ w := bv.B[i]
+ h = (h * hp) ^ (w & 0xff)
+ h = (h * hp) ^ ((w >> 8) & 0xff)
+ h = (h * hp) ^ ((w >> 16) & 0xff)
+ h = (h * hp) ^ ((w >> 24) & 0xff)
+ }
+
+ return h
+}
diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/liveness/plive.go
similarity index 71%
rename from src/cmd/compile/internal/gc/plive.go
rename to src/cmd/compile/internal/liveness/plive.go
index a48173e0d65bf..53ae797fce18f 100644
--- a/src/cmd/compile/internal/gc/plive.go
+++ b/src/cmd/compile/internal/liveness/plive.go
@@ -12,16 +12,24 @@
//
// Each level includes the earlier output as well.
-package gc
+package liveness
import (
+ "crypto/md5"
+ "fmt"
+ "sort"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/bitvec"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/typebits"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
- "crypto/md5"
- "fmt"
- "strings"
)
// OpVarDef is an annotation for the liveness analysis, marking a place
@@ -79,117 +87,87 @@ import (
// so the compiler can allocate two temps to the same location. Here it's now
// useless, since the implementation of stack objects.
-// BlockEffects summarizes the liveness effects on an SSA block.
-type BlockEffects struct {
+// blockEffects summarizes the liveness effects on an SSA block.
+type blockEffects struct {
// Computed during Liveness.prologue using only the content of
// individual blocks:
//
// uevar: upward exposed variables (used before set in block)
// varkill: killed variables (set in block)
- uevar bvec
- varkill bvec
+ uevar bitvec.BitVec
+ varkill bitvec.BitVec
// Computed during Liveness.solve using control flow information:
//
// livein: variables live at block entry
// liveout: variables live at block exit
- livein bvec
- liveout bvec
+ livein bitvec.BitVec
+ liveout bitvec.BitVec
}
// A collection of global state used by liveness analysis.
-type Liveness struct {
- fn *Node
+type liveness struct {
+ fn *ir.Func
f *ssa.Func
- vars []*Node
- idx map[*Node]int32
+ vars []*ir.Name
+ idx map[*ir.Name]int32
stkptrsize int64
- be []BlockEffects
+ be []blockEffects
// allUnsafe indicates that all points in this function are
// unsafe-points.
allUnsafe bool
// unsafePoints bit i is set if Value ID i is an unsafe-point
// (preemption is not allowed). Only valid if !allUnsafe.
- unsafePoints bvec
+ unsafePoints bitvec.BitVec
// An array with a bit vector for each safe point in the
// current Block during Liveness.epilogue. Indexed in Value
// order for that block. Additionally, for the entry block
// livevars[0] is the entry bitmap. Liveness.compact moves
// these to stackMaps.
- livevars []bvec
+ livevars []bitvec.BitVec
// livenessMap maps from safe points (i.e., CALLs) to their
// liveness map indexes.
- livenessMap LivenessMap
+ livenessMap Map
stackMapSet bvecSet
- stackMaps []bvec
+ stackMaps []bitvec.BitVec
cache progeffectscache
}
-// LivenessMap maps from *ssa.Value to LivenessIndex.
-type LivenessMap struct {
- vals map[ssa.ID]LivenessIndex
- // The set of live, pointer-containing variables at the deferreturn
+// Map maps from *ssa.Value to LivenessIndex.
+type Map struct {
+ Vals map[ssa.ID]objw.LivenessIndex
+ // The set of live, pointer-containing variables at the DeferReturn
// call (only set when open-coded defers are used).
- deferreturn LivenessIndex
+ DeferReturn objw.LivenessIndex
}
-func (m *LivenessMap) reset() {
- if m.vals == nil {
- m.vals = make(map[ssa.ID]LivenessIndex)
+func (m *Map) reset() {
+ if m.Vals == nil {
+ m.Vals = make(map[ssa.ID]objw.LivenessIndex)
} else {
- for k := range m.vals {
- delete(m.vals, k)
+ for k := range m.Vals {
+ delete(m.Vals, k)
}
}
- m.deferreturn = LivenessDontCare
+ m.DeferReturn = objw.LivenessDontCare
}
-func (m *LivenessMap) set(v *ssa.Value, i LivenessIndex) {
- m.vals[v.ID] = i
+func (m *Map) set(v *ssa.Value, i objw.LivenessIndex) {
+ m.Vals[v.ID] = i
}
-func (m LivenessMap) Get(v *ssa.Value) LivenessIndex {
+func (m Map) Get(v *ssa.Value) objw.LivenessIndex {
// If v isn't in the map, then it's a "don't care" and not an
// unsafe-point.
- if idx, ok := m.vals[v.ID]; ok {
+ if idx, ok := m.Vals[v.ID]; ok {
return idx
}
- return LivenessIndex{StackMapDontCare, false}
-}
-
-// LivenessIndex stores the liveness map information for a Value.
-type LivenessIndex struct {
- stackMapIndex int
-
- // isUnsafePoint indicates that this is an unsafe-point.
- //
- // Note that it's possible for a call Value to have a stack
- // map while also being an unsafe-point. This means it cannot
- // be preempted at this instruction, but that a preemption or
- // stack growth may happen in the called function.
- isUnsafePoint bool
-}
-
-// LivenessDontCare indicates that the liveness information doesn't
-// matter. Currently it is used in deferreturn liveness when we don't
-// actually need it. It should never be emitted to the PCDATA stream.
-var LivenessDontCare = LivenessIndex{StackMapDontCare, true}
-
-// StackMapDontCare indicates that the stack map index at a Value
-// doesn't matter.
-//
-// This is a sentinel value that should never be emitted to the PCDATA
-// stream. We use -1000 because that's obviously never a valid stack
-// index (but -1 is).
-const StackMapDontCare = -1000
-
-func (idx LivenessIndex) StackMapValid() bool {
- return idx.stackMapIndex != StackMapDontCare
+ return objw.LivenessIndex{StackMapIndex: objw.StackMapDontCare, IsUnsafePoint: false}
}
type progeffectscache struct {
@@ -198,42 +176,42 @@ type progeffectscache struct {
initialized bool
}
-// livenessShouldTrack reports whether the liveness analysis
+// shouldTrack reports whether the liveness analysis
// should track the variable n.
// We don't care about variables that have no pointers,
// nor do we care about non-local variables,
// nor do we care about empty structs (handled by the pointer check),
// nor do we care about the fake PAUTOHEAP variables.
-func livenessShouldTrack(n *Node) bool {
- return n.Op == ONAME && (n.Class() == PAUTO || n.Class() == PPARAM || n.Class() == PPARAMOUT) && n.Type.HasPointers()
+func shouldTrack(n *ir.Name) bool {
+ return (n.Class == ir.PAUTO && n.Esc() != ir.EscHeap || n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT) && n.Type().HasPointers()
}
// getvariables returns the list of on-stack variables that we need to track
// and a map for looking up indices by *Node.
-func getvariables(fn *Node) ([]*Node, map[*Node]int32) {
- var vars []*Node
- for _, n := range fn.Func.Dcl {
- if livenessShouldTrack(n) {
+func getvariables(fn *ir.Func) ([]*ir.Name, map[*ir.Name]int32) {
+ var vars []*ir.Name
+ for _, n := range fn.Dcl {
+ if shouldTrack(n) {
vars = append(vars, n)
}
}
- idx := make(map[*Node]int32, len(vars))
+ idx := make(map[*ir.Name]int32, len(vars))
for i, n := range vars {
idx[n] = int32(i)
}
return vars, idx
}
-func (lv *Liveness) initcache() {
+func (lv *liveness) initcache() {
if lv.cache.initialized {
- Fatalf("liveness cache initialized twice")
+ base.Fatalf("liveness cache initialized twice")
return
}
lv.cache.initialized = true
for i, node := range lv.vars {
- switch node.Class() {
- case PPARAM:
+ switch node.Class {
+ case ir.PPARAM:
// A return instruction with a p.to is a tail return, which brings
// the stack pointer back up (if it ever went down) and then jumps
// to a new function entirely. That form of instruction must read
@@ -242,7 +220,7 @@ func (lv *Liveness) initcache() {
// function runs.
lv.cache.tailuevar = append(lv.cache.tailuevar, int32(i))
- case PPARAMOUT:
+ case ir.PPARAMOUT:
// All results are live at every return point.
// Note that this point is after escaping return values
// are copied back to the stack using their PAUTOHEAP references.
@@ -268,19 +246,18 @@ const (
// valueEffects returns the index of a variable in lv.vars and the
// liveness effects v has on that variable.
// If v does not affect any tracked variables, it returns -1, 0.
-func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
- n, e := affectedNode(v)
- if e == 0 || n == nil || n.Op != ONAME { // cheapest checks first
+func (lv *liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
+ n, e := affectedVar(v)
+ if e == 0 || n == nil { // cheapest checks first
return -1, 0
}
-
// AllocFrame has dropped unused variables from
// lv.fn.Func.Dcl, but they might still be referenced by
// OpVarFoo pseudo-ops. Ignore them to prevent "lost track of
// variable" ICEs (issue 19632).
switch v.Op {
case ssa.OpVarDef, ssa.OpVarKill, ssa.OpVarLive, ssa.OpKeepAlive:
- if !n.Name.Used() {
+ if !n.Used() {
return -1, 0
}
}
@@ -295,7 +272,7 @@ func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
if e&(ssa.SymRead|ssa.SymAddr) != 0 {
effect |= uevar
}
- if e&ssa.SymWrite != 0 && (!isfat(n.Type) || v.Op == ssa.OpVarDef) {
+ if e&ssa.SymWrite != 0 && (!isfat(n.Type()) || v.Op == ssa.OpVarDef) {
effect |= varkill
}
@@ -309,23 +286,23 @@ func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
return -1, 0
}
-// affectedNode returns the *Node affected by v
-func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
+// affectedVar returns the *ir.Name node affected by v
+func affectedVar(v *ssa.Value) (*ir.Name, ssa.SymEffect) {
// Special cases.
switch v.Op {
case ssa.OpLoadReg:
- n, _ := AutoVar(v.Args[0])
+ n, _ := ssa.AutoVar(v.Args[0])
return n, ssa.SymRead
case ssa.OpStoreReg:
- n, _ := AutoVar(v)
+ n, _ := ssa.AutoVar(v)
return n, ssa.SymWrite
case ssa.OpVarLive:
- return v.Aux.(*Node), ssa.SymRead
+ return v.Aux.(*ir.Name), ssa.SymRead
case ssa.OpVarDef, ssa.OpVarKill:
- return v.Aux.(*Node), ssa.SymWrite
+ return v.Aux.(*ir.Name), ssa.SymWrite
case ssa.OpKeepAlive:
- n, _ := AutoVar(v.Args[0])
+ n, _ := ssa.AutoVar(v.Args[0])
return n, ssa.SymRead
}
@@ -338,24 +315,24 @@ func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
case nil, *obj.LSym:
// ok, but no node
return nil, e
- case *Node:
+ case *ir.Name:
return a, e
default:
- Fatalf("weird aux: %s", v.LongString())
+ base.Fatalf("weird aux: %s", v.LongString())
return nil, e
}
}
type livenessFuncCache struct {
- be []BlockEffects
- livenessMap LivenessMap
+ be []blockEffects
+ livenessMap Map
}
// Constructs a new liveness structure used to hold the global state of the
// liveness computation. The cfg argument is a slice of *BasicBlocks and the
// vars argument is a slice of *Nodes.
-func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkptrsize int64) *Liveness {
- lv := &Liveness{
+func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *liveness {
+ lv := &liveness{
fn: fn,
f: f,
vars: vars,
@@ -373,23 +350,23 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
if cap(lc.be) >= f.NumBlocks() {
lv.be = lc.be[:f.NumBlocks()]
}
- lv.livenessMap = LivenessMap{vals: lc.livenessMap.vals, deferreturn: LivenessDontCare}
- lc.livenessMap.vals = nil
+ lv.livenessMap = Map{Vals: lc.livenessMap.Vals, DeferReturn: objw.LivenessDontCare}
+ lc.livenessMap.Vals = nil
}
if lv.be == nil {
- lv.be = make([]BlockEffects, f.NumBlocks())
+ lv.be = make([]blockEffects, f.NumBlocks())
}
nblocks := int32(len(f.Blocks))
nvars := int32(len(vars))
- bulk := bvbulkalloc(nvars, nblocks*7)
+ bulk := bitvec.NewBulk(nvars, nblocks*7)
for _, b := range f.Blocks {
be := lv.blockEffects(b)
- be.uevar = bulk.next()
- be.varkill = bulk.next()
- be.livein = bulk.next()
- be.liveout = bulk.next()
+ be.uevar = bulk.Next()
+ be.varkill = bulk.Next()
+ be.livein = bulk.Next()
+ be.liveout = bulk.Next()
}
lv.livenessMap.reset()
@@ -397,109 +374,33 @@ func newliveness(fn *Node, f *ssa.Func, vars []*Node, idx map[*Node]int32, stkpt
return lv
}
-func (lv *Liveness) blockEffects(b *ssa.Block) *BlockEffects {
+func (lv *liveness) blockEffects(b *ssa.Block) *blockEffects {
return &lv.be[b.ID]
}
-// NOTE: The bitmap for a specific type t could be cached in t after
-// the first run and then simply copied into bv at the correct offset
-// on future calls with the same type t.
-func onebitwalktype1(t *types.Type, off int64, bv bvec) {
- if t.Align > 0 && off&int64(t.Align-1) != 0 {
- Fatalf("onebitwalktype1: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
- }
- if !t.HasPointers() {
- // Note: this case ensures that pointers to go:notinheap types
- // are not considered pointers by garbage collection and stack copying.
- return
- }
-
- switch t.Etype {
- case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
- if off&int64(Widthptr-1) != 0 {
- Fatalf("onebitwalktype1: invalid alignment, %v", t)
- }
- bv.Set(int32(off / int64(Widthptr))) // pointer
-
- case TSTRING:
- // struct { byte *str; intgo len; }
- if off&int64(Widthptr-1) != 0 {
- Fatalf("onebitwalktype1: invalid alignment, %v", t)
- }
- bv.Set(int32(off / int64(Widthptr))) //pointer in first slot
-
- case TINTER:
- // struct { Itab *tab; void *data; }
- // or, when isnilinter(t)==true:
- // struct { Type *type; void *data; }
- if off&int64(Widthptr-1) != 0 {
- Fatalf("onebitwalktype1: invalid alignment, %v", t)
- }
- // The first word of an interface is a pointer, but we don't
- // treat it as such.
- // 1. If it is a non-empty interface, the pointer points to an itab
- // which is always in persistentalloc space.
- // 2. If it is an empty interface, the pointer points to a _type.
- // a. If it is a compile-time-allocated type, it points into
- // the read-only data section.
- // b. If it is a reflect-allocated type, it points into the Go heap.
- // Reflect is responsible for keeping a reference to
- // the underlying type so it won't be GCd.
- // If we ever have a moving GC, we need to change this for 2b (as
- // well as scan itabs to update their itab._type fields).
- bv.Set(int32(off/int64(Widthptr) + 1)) // pointer in second slot
-
- case TSLICE:
- // struct { byte *array; uintgo len; uintgo cap; }
- if off&int64(Widthptr-1) != 0 {
- Fatalf("onebitwalktype1: invalid TARRAY alignment, %v", t)
- }
- bv.Set(int32(off / int64(Widthptr))) // pointer in first slot (BitsPointer)
-
- case TARRAY:
- elt := t.Elem()
- if elt.Width == 0 {
- // Short-circuit for #20739.
- break
- }
- for i := int64(0); i < t.NumElem(); i++ {
- onebitwalktype1(elt, off, bv)
- off += elt.Width
- }
-
- case TSTRUCT:
- for _, f := range t.Fields().Slice() {
- onebitwalktype1(f.Type, off+f.Offset, bv)
- }
-
- default:
- Fatalf("onebitwalktype1: unexpected type, %v", t)
- }
-}
-
// Generates live pointer value maps for arguments and local variables. The
// this argument and the in arguments are always assumed live. The vars
// argument is a slice of *Nodes.
-func (lv *Liveness) pointerMap(liveout bvec, vars []*Node, args, locals bvec) {
+func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) {
for i := int32(0); ; i++ {
i = liveout.Next(i)
if i < 0 {
break
}
node := vars[i]
- switch node.Class() {
- case PAUTO:
- onebitwalktype1(node.Type, node.Xoffset+lv.stkptrsize, locals)
+ switch node.Class {
+ case ir.PAUTO:
+ typebits.Set(node.Type(), node.FrameOffset()+lv.stkptrsize, locals)
- case PPARAM, PPARAMOUT:
- onebitwalktype1(node.Type, node.Xoffset, args)
+ case ir.PPARAM, ir.PPARAMOUT:
+ typebits.Set(node.Type(), node.FrameOffset(), args)
}
}
}
-// allUnsafe indicates that all points in this function are
+// IsUnsafe indicates that all points in this function are
// unsafe-points.
-func allUnsafe(f *ssa.Func) bool {
+func IsUnsafe(f *ssa.Func) bool {
// The runtime assumes the only safe-points are function
// prologues (because that's how it used to be). We could and
// should improve that, but for now keep consider all points
@@ -509,18 +410,18 @@ func allUnsafe(f *ssa.Func) bool {
// go:nosplit functions are similar. Since safe points used to
// be coupled with stack checks, go:nosplit often actually
// means "no safe points in this function".
- return compiling_runtime || f.NoSplit
+ return base.Flag.CompilingRuntime || f.NoSplit
}
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
-func (lv *Liveness) markUnsafePoints() {
- if allUnsafe(lv.f) {
+func (lv *liveness) markUnsafePoints() {
+ if IsUnsafe(lv.f) {
// No complex analysis necessary.
lv.allUnsafe = true
return
}
- lv.unsafePoints = bvalloc(int32(lv.f.NumValues()))
+ lv.unsafePoints = bitvec.New(int32(lv.f.NumValues()))
// Mark architecture-specific unsafe points.
for _, b := range lv.f.Blocks {
@@ -564,7 +465,7 @@ func (lv *Liveness) markUnsafePoints() {
var load *ssa.Value
v := wbBlock.Controls[0]
for {
- if sym, ok := v.Aux.(*obj.LSym); ok && sym == writeBarrier {
+ if sym, ok := v.Aux.(*obj.LSym); ok && sym == ir.Syms.WriteBarrier {
load = v
break
}
@@ -631,11 +532,11 @@ func (lv *Liveness) markUnsafePoints() {
// nice to only flood as far as the unsafe.Pointer -> uintptr
// conversion, but it's hard to know which argument of an Add
// or Sub to follow.
- var flooded bvec
+ var flooded bitvec.BitVec
var flood func(b *ssa.Block, vi int)
flood = func(b *ssa.Block, vi int) {
- if flooded.n == 0 {
- flooded = bvalloc(int32(lv.f.NumBlocks()))
+ if flooded.N == 0 {
+ flooded = bitvec.New(int32(lv.f.NumBlocks()))
}
if flooded.Get(int32(b.ID)) {
return
@@ -676,14 +577,14 @@ func (lv *Liveness) markUnsafePoints() {
// This does not necessarily mean the instruction is a safe-point. In
// particular, call Values can have a stack map in case the callee
// grows the stack, but not themselves be a safe-point.
-func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
+func (lv *liveness) hasStackMap(v *ssa.Value) bool {
if !v.Op.IsCall() {
return false
}
// typedmemclr and typedmemmove are write barriers and
// deeply non-preemptible. They are unsafe points and
// hence should not have liveness maps.
- if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == typedmemclr || sym.Fn == typedmemmove) {
+ if sym, ok := v.Aux.(*ssa.AuxCall); ok && (sym.Fn == ir.Syms.Typedmemclr || sym.Fn == ir.Syms.Typedmemmove) {
return false
}
return true
@@ -692,7 +593,7 @@ func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
// Initializes the sets for solving the live variables. Visits all the
// instructions in each basic block to summarizes the information at each basic
// block
-func (lv *Liveness) prologue() {
+func (lv *liveness) prologue() {
lv.initcache()
for _, b := range lv.f.Blocks {
@@ -714,12 +615,12 @@ func (lv *Liveness) prologue() {
}
// Solve the liveness dataflow equations.
-func (lv *Liveness) solve() {
+func (lv *liveness) solve() {
// These temporary bitvectors exist to avoid successive allocations and
// frees within the loop.
nvars := int32(len(lv.vars))
- newlivein := bvalloc(nvars)
- newliveout := bvalloc(nvars)
+ newlivein := bitvec.New(nvars)
+ newliveout := bitvec.New(nvars)
// Walk blocks in postorder ordering. This improves convergence.
po := lv.f.Postorder()
@@ -774,10 +675,10 @@ func (lv *Liveness) solve() {
// Visits all instructions in a basic block and computes a bit vector of live
// variables at each safe point locations.
-func (lv *Liveness) epilogue() {
+func (lv *liveness) epilogue() {
nvars := int32(len(lv.vars))
- liveout := bvalloc(nvars)
- livedefer := bvalloc(nvars) // always-live variables
+ liveout := bitvec.New(nvars)
+ livedefer := bitvec.New(nvars) // always-live variables
// If there is a defer (that could recover), then all output
// parameters are live all the time. In addition, any locals
@@ -786,14 +687,14 @@ func (lv *Liveness) epilogue() {
// pointers to copy values back to the stack).
// TODO: if the output parameter is heap-allocated, then we
// don't need to keep the stack copy live?
- if lv.fn.Func.HasDefer() {
+ if lv.fn.HasDefer() {
for i, n := range lv.vars {
- if n.Class() == PPARAMOUT {
- if n.Name.IsOutputParamHeapAddr() {
+ if n.Class == ir.PPARAMOUT {
+ if n.IsOutputParamHeapAddr() {
// Just to be paranoid. Heap addresses are PAUTOs.
- Fatalf("variable %v both output param and heap output param", n)
+ base.Fatalf("variable %v both output param and heap output param", n)
}
- if n.Name.Param.Heapaddr != nil {
+ if n.Heapaddr != nil {
// If this variable moved to the heap, then
// its stack copy is not live.
continue
@@ -801,22 +702,22 @@ func (lv *Liveness) epilogue() {
// Note: zeroing is handled by zeroResults in walk.go.
livedefer.Set(int32(i))
}
- if n.Name.IsOutputParamHeapAddr() {
+ if n.IsOutputParamHeapAddr() {
// This variable will be overwritten early in the function
// prologue (from the result of a mallocgc) but we need to
// zero it in case that malloc causes a stack scan.
- n.Name.SetNeedzero(true)
+ n.SetNeedzero(true)
livedefer.Set(int32(i))
}
- if n.Name.OpenDeferSlot() {
+ if n.OpenDeferSlot() {
// Open-coded defer args slots must be live
// everywhere in a function, since a panic can
// occur (almost) anywhere. Because it is live
// everywhere, it must be zeroed on entry.
livedefer.Set(int32(i))
// It was already marked as Needzero when created.
- if !n.Name.Needzero() {
- Fatalf("all pointer-containing defer arg slots should have Needzero set")
+ if !n.Needzero() {
+ base.Fatalf("all pointer-containing defer arg slots should have Needzero set")
}
}
}
@@ -831,7 +732,7 @@ func (lv *Liveness) epilogue() {
{
// Reserve an entry for function entry.
- live := bvalloc(nvars)
+ live := bitvec.New(nvars)
lv.livevars = append(lv.livevars, live)
}
@@ -845,7 +746,7 @@ func (lv *Liveness) epilogue() {
continue
}
- live := bvalloc(nvars)
+ live := bitvec.New(nvars)
lv.livevars = append(lv.livevars, live)
}
@@ -878,7 +779,7 @@ func (lv *Liveness) epilogue() {
if b == lv.f.Entry {
if index != 0 {
- Fatalf("bad index for entry point: %v", index)
+ base.Fatalf("bad index for entry point: %v", index)
}
// Check to make sure only input variables are live.
@@ -886,10 +787,10 @@ func (lv *Liveness) epilogue() {
if !liveout.Get(int32(i)) {
continue
}
- if n.Class() == PPARAM {
+ if n.Class == ir.PPARAM {
continue // ok
}
- Fatalf("bad live variable at entry of %v: %L", lv.fn.Func.Nname, n)
+ base.FatalfAt(n.Pos(), "bad live variable at entry of %v: %L", lv.fn.Nname, n)
}
// Record live variables.
@@ -902,25 +803,25 @@ func (lv *Liveness) epilogue() {
}
// If we have an open-coded deferreturn call, make a liveness map for it.
- if lv.fn.Func.OpenCodedDeferDisallowed() {
- lv.livenessMap.deferreturn = LivenessDontCare
+ if lv.fn.OpenCodedDeferDisallowed() {
+ lv.livenessMap.DeferReturn = objw.LivenessDontCare
} else {
- lv.livenessMap.deferreturn = LivenessIndex{
- stackMapIndex: lv.stackMapSet.add(livedefer),
- isUnsafePoint: false,
+ lv.livenessMap.DeferReturn = objw.LivenessIndex{
+ StackMapIndex: lv.stackMapSet.add(livedefer),
+ IsUnsafePoint: false,
}
}
// Done compacting. Throw out the stack map set.
- lv.stackMaps = lv.stackMapSet.extractUniqe()
+ lv.stackMaps = lv.stackMapSet.extractUnique()
lv.stackMapSet = bvecSet{}
// Useful sanity check: on entry to the function,
// the only things that can possibly be live are the
// input parameters.
for j, n := range lv.vars {
- if n.Class() != PPARAM && lv.stackMaps[0].Get(int32(j)) {
- lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Func.Nname, n)
+ if n.Class != ir.PPARAM && lv.stackMaps[0].Get(int32(j)) {
+ lv.f.Fatalf("%v %L recorded as live on entry", lv.fn.Nname, n)
}
}
}
@@ -941,7 +842,7 @@ func (lv *Liveness) epilogue() {
// is actually a net loss: we save about 50k of argument bitmaps but the new
// PCDATA tables cost about 100k. So for now we keep using a single index for
// both bitmap lists.
-func (lv *Liveness) compact(b *ssa.Block) {
+func (lv *liveness) compact(b *ssa.Block) {
pos := 0
if b == lv.f.Entry {
// Handle entry stack map.
@@ -951,9 +852,9 @@ func (lv *Liveness) compact(b *ssa.Block) {
for _, v := range b.Values {
hasStackMap := lv.hasStackMap(v)
isUnsafePoint := lv.allUnsafe || lv.unsafePoints.Get(int32(v.ID))
- idx := LivenessIndex{StackMapDontCare, isUnsafePoint}
+ idx := objw.LivenessIndex{StackMapIndex: objw.StackMapDontCare, IsUnsafePoint: isUnsafePoint}
if hasStackMap {
- idx.stackMapIndex = lv.stackMapSet.add(lv.livevars[pos])
+ idx.StackMapIndex = lv.stackMapSet.add(lv.livevars[pos])
pos++
}
if hasStackMap || isUnsafePoint {
@@ -965,8 +866,8 @@ func (lv *Liveness) compact(b *ssa.Block) {
lv.livevars = lv.livevars[:0]
}
-func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
- if debuglive == 0 || lv.fn.funcname() == "init" || strings.HasPrefix(lv.fn.funcname(), ".") {
+func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
+ if base.Flag.Live == 0 || ir.FuncName(lv.fn) == "init" || strings.HasPrefix(ir.FuncName(lv.fn), ".") {
return
}
if !(v == nil || v.Op.IsCall()) {
@@ -978,14 +879,14 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
return
}
- pos := lv.fn.Func.Nname.Pos
+ pos := lv.fn.Nname.Pos()
if v != nil {
pos = v.Pos
}
s := "live at "
if v == nil {
- s += fmt.Sprintf("entry to %s:", lv.fn.funcname())
+ s += fmt.Sprintf("entry to %s:", ir.FuncName(lv.fn))
} else if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
fn := sym.Fn.Name
if pos := strings.Index(fn, "."); pos >= 0 {
@@ -1002,10 +903,10 @@ func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
}
}
- Warnl(pos, s)
+ base.WarnfAt(pos, s)
}
-func (lv *Liveness) printbvec(printed bool, name string, live bvec) bool {
+func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool {
if live.IsEmpty() {
return printed
}
@@ -1022,14 +923,14 @@ func (lv *Liveness) printbvec(printed bool, name string, live bvec) bool {
if !live.Get(int32(i)) {
continue
}
- fmt.Printf("%s%s", comma, n.Sym.Name)
+ fmt.Printf("%s%s", comma, n.Sym().Name)
comma = ","
}
return true
}
// printeffect is like printbvec, but for valueEffects.
-func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
+func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
if !x {
return printed
}
@@ -1040,7 +941,7 @@ func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bo
}
fmt.Printf("%s=", name)
if x {
- fmt.Printf("%s", lv.vars[pos].Sym.Name)
+ fmt.Printf("%s", lv.vars[pos].Sym().Name)
}
return true
@@ -1049,8 +950,8 @@ func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bo
// Prints the computed liveness information and inputs, for debugging.
// This format synthesizes the information used during the multiple passes
// into a single presentation.
-func (lv *Liveness) printDebug() {
- fmt.Printf("liveness: %s\n", lv.fn.funcname())
+func (lv *liveness) printDebug() {
+ fmt.Printf("liveness: %s\n", ir.FuncName(lv.fn))
for i, b := range lv.f.Blocks {
if i > 0 {
@@ -1088,7 +989,7 @@ func (lv *Liveness) printDebug() {
if b == lv.f.Entry {
live := lv.stackMaps[0]
- fmt.Printf("(%s) function entry\n", linestr(lv.fn.Func.Nname.Pos))
+ fmt.Printf("(%s) function entry\n", base.FmtPos(lv.fn.Nname.Pos()))
fmt.Printf("\tlive=")
printed = false
for j, n := range lv.vars {
@@ -1105,7 +1006,7 @@ func (lv *Liveness) printDebug() {
}
for _, v := range b.Values {
- fmt.Printf("(%s) %v\n", linestr(v.Pos), v.LongString())
+ fmt.Printf("(%s) %v\n", base.FmtPos(v.Pos), v.LongString())
pcdata := lv.livenessMap.Get(v)
@@ -1121,7 +1022,7 @@ func (lv *Liveness) printDebug() {
fmt.Printf("\tlive=")
printed = false
if pcdata.StackMapValid() {
- live := lv.stackMaps[pcdata.stackMapIndex]
+ live := lv.stackMaps[pcdata.StackMapIndex]
for j, n := range lv.vars {
if !live.Get(int32(j)) {
continue
@@ -1136,7 +1037,7 @@ func (lv *Liveness) printDebug() {
fmt.Printf("\n")
}
- if pcdata.isUnsafePoint {
+ if pcdata.IsUnsafePoint {
fmt.Printf("\tunsafe-point\n")
}
}
@@ -1158,15 +1059,15 @@ func (lv *Liveness) printDebug() {
// first word dumped is the total number of bitmaps. The second word is the
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
// remaining bytes are the raw bitmaps.
-func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
+func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
// Size args bitmaps to be just large enough to hold the largest pointer.
// First, find the largest Xoffset node we care about.
- // (Nodes without pointers aren't in lv.vars; see livenessShouldTrack.)
- var maxArgNode *Node
+ // (Nodes without pointers aren't in lv.vars; see ShouldTrack.)
+ var maxArgNode *ir.Name
for _, n := range lv.vars {
- switch n.Class() {
- case PPARAM, PPARAMOUT:
- if maxArgNode == nil || n.Xoffset > maxArgNode.Xoffset {
+ switch n.Class {
+ case ir.PPARAM, ir.PPARAMOUT:
+ if maxArgNode == nil || n.FrameOffset() > maxArgNode.FrameOffset() {
maxArgNode = n
}
}
@@ -1174,7 +1075,7 @@ func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
// Next, find the offset of the largest pointer in the largest node.
var maxArgs int64
if maxArgNode != nil {
- maxArgs = maxArgNode.Xoffset + typeptrdata(maxArgNode.Type)
+ maxArgs = maxArgNode.FrameOffset() + types.PtrDataSize(maxArgNode.Type())
}
// Size locals bitmaps to be stkptrsize sized.
@@ -1189,13 +1090,13 @@ func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
// Temporary symbols for encoding bitmaps.
var argsSymTmp, liveSymTmp obj.LSym
- args := bvalloc(int32(maxArgs / int64(Widthptr)))
- aoff := duint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
- aoff = duint32(&argsSymTmp, aoff, uint32(args.n)) // number of bits in each bitmap
+ args := bitvec.New(int32(maxArgs / int64(types.PtrSize)))
+ aoff := objw.Uint32(&argsSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
+ aoff = objw.Uint32(&argsSymTmp, aoff, uint32(args.N)) // number of bits in each bitmap
- locals := bvalloc(int32(maxLocals / int64(Widthptr)))
- loff := duint32(&liveSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
- loff = duint32(&liveSymTmp, loff, uint32(locals.n)) // number of bits in each bitmap
+ locals := bitvec.New(int32(maxLocals / int64(types.PtrSize)))
+ loff := objw.Uint32(&liveSymTmp, 0, uint32(len(lv.stackMaps))) // number of bitmaps
+ loff = objw.Uint32(&liveSymTmp, loff, uint32(locals.N)) // number of bits in each bitmap
for _, live := range lv.stackMaps {
args.Clear()
@@ -1203,8 +1104,8 @@ func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
lv.pointerMap(live, lv.vars, args, locals)
- aoff = dbvec(&argsSymTmp, aoff, args)
- loff = dbvec(&liveSymTmp, loff, locals)
+ aoff = objw.BitVec(&argsSymTmp, aoff, args)
+ loff = objw.BitVec(&liveSymTmp, loff, locals)
}
// Give these LSyms content-addressable names,
@@ -1214,7 +1115,7 @@ func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
// These symbols will be added to Ctxt.Data by addGCLocals
// after parallel compilation is done.
makeSym := func(tmpSym *obj.LSym) *obj.LSym {
- return Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) {
+ return base.Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) {
lsym.P = tmpSym.P
lsym.Set(obj.AttrContentAddressable, true)
})
@@ -1222,30 +1123,30 @@ func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
return makeSym(&argsSymTmp), makeSym(&liveSymTmp)
}
-// Entry pointer for liveness analysis. Solves for the liveness of
+// Entry pointer for Compute analysis. Solves for the Compute of
// pointer variables in the function and emits a runtime data
// structure read by the garbage collector.
// Returns a map from GC safe points to their corresponding stack map index.
-func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap {
+func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) Map {
// Construct the global liveness state.
- vars, idx := getvariables(e.curfn)
- lv := newliveness(e.curfn, f, vars, idx, e.stkptrsize)
+ vars, idx := getvariables(curfn)
+ lv := newliveness(curfn, f, vars, idx, stkptrsize)
// Run the dataflow framework.
lv.prologue()
lv.solve()
lv.epilogue()
- if debuglive > 0 {
+ if base.Flag.Live > 0 {
lv.showlive(nil, lv.stackMaps[0])
for _, b := range f.Blocks {
for _, val := range b.Values {
if idx := lv.livenessMap.Get(val); idx.StackMapValid() {
- lv.showlive(val, lv.stackMaps[idx.stackMapIndex])
+ lv.showlive(val, lv.stackMaps[idx.StackMapIndex])
}
}
}
}
- if debuglive >= 2 {
+ if base.Flag.Live >= 2 {
lv.printDebug()
}
@@ -1254,35 +1155,80 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap {
cache := f.Cache.Liveness.(*livenessFuncCache)
if cap(lv.be) < 2000 { // Threshold from ssa.Cache slices.
for i := range lv.be {
- lv.be[i] = BlockEffects{}
+ lv.be[i] = blockEffects{}
}
cache.be = lv.be
}
- if len(lv.livenessMap.vals) < 2000 {
+ if len(lv.livenessMap.Vals) < 2000 {
cache.livenessMap = lv.livenessMap
}
}
// Emit the live pointer map data structures
- ls := e.curfn.Func.lsym
+ ls := curfn.LSym
fninfo := ls.Func()
fninfo.GCArgs, fninfo.GCLocals = lv.emit()
p := pp.Prog(obj.AFUNCDATA)
- Addrconst(&p.From, objabi.FUNCDATA_ArgsPointerMaps)
+ p.From.SetConst(objabi.FUNCDATA_ArgsPointerMaps)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = fninfo.GCArgs
p = pp.Prog(obj.AFUNCDATA)
- Addrconst(&p.From, objabi.FUNCDATA_LocalsPointerMaps)
+ p.From.SetConst(objabi.FUNCDATA_LocalsPointerMaps)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = fninfo.GCLocals
+ if x := lv.emitStackObjects(); x != nil {
+ p := pp.Prog(obj.AFUNCDATA)
+ p.From.SetConst(objabi.FUNCDATA_StackObjects)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = x
+ }
+
return lv.livenessMap
}
+func (lv *liveness) emitStackObjects() *obj.LSym {
+ var vars []*ir.Name
+ for _, n := range lv.fn.Dcl {
+ if shouldTrack(n) && n.Addrtaken() && n.Esc() != ir.EscHeap {
+ vars = append(vars, n)
+ }
+ }
+ if len(vars) == 0 {
+ return nil
+ }
+
+ // Sort variables from lowest to highest address.
+ sort.Slice(vars, func(i, j int) bool { return vars[i].FrameOffset() < vars[j].FrameOffset() })
+
+ // Populate the stack object data.
+ // Format must match runtime/stack.go:stackObjectRecord.
+ x := base.Ctxt.Lookup(lv.fn.LSym.Name + ".stkobj")
+ lv.fn.LSym.Func().StackObjects = x
+ off := 0
+ off = objw.Uintptr(x, off, uint64(len(vars)))
+ for _, v := range vars {
+ // Note: arguments and return values have non-negative Xoffset,
+ // in which case the offset is relative to argp.
+ // Locals have a negative Xoffset, in which case the offset is relative to varp.
+ off = objw.Uintptr(x, off, uint64(v.FrameOffset()))
+ off = objw.SymPtr(x, off, reflectdata.TypeLinksym(v.Type()), 0)
+ }
+
+ if base.Flag.Live != 0 {
+ for _, v := range vars {
+ base.WarnfAt(v.Pos(), "stack object %v %v", v, v.Type())
+ }
+ }
+
+ return x
+}
+
// isfat reports whether a variable of type t needs multiple assignments to initialize.
// For example:
//
@@ -1298,17 +1244,17 @@ func liveness(e *ssafn, f *ssa.Func, pp *Progs) LivenessMap {
// to fully initialize t.
func isfat(t *types.Type) bool {
if t != nil {
- switch t.Etype {
- case TSLICE, TSTRING,
- TINTER: // maybe remove later
+ switch t.Kind() {
+ case types.TSLICE, types.TSTRING,
+ types.TINTER: // maybe remove later
return true
- case TARRAY:
+ case types.TARRAY:
// Array of 1 element, check if element is fat
if t.NumElem() == 1 {
return isfat(t.Elem())
}
return true
- case TSTRUCT:
+ case types.TSTRUCT:
// Struct with 1 field, check if field is fat
if t.NumFields() == 1 {
return isfat(t.Field(0).Type)
@@ -1319,3 +1265,34 @@ func isfat(t *types.Type) bool {
return false
}
+
+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)
+ nbitmap := 1
+ if fn.Type().NumResults() > 0 {
+ nbitmap = 2
+ }
+ off := objw.Uint32(lsym, 0, uint32(nbitmap))
+ off = objw.Uint32(lsym, off, uint32(bv.N))
+
+ if ir.IsMethod(fn) {
+ typebits.Set(fn.Type().Recvs(), 0, bv)
+ }
+ if fn.Type().NumParams() > 0 {
+ typebits.Set(fn.Type().Params(), 0, bv)
+ }
+ off = objw.BitVec(lsym, off, bv)
+
+ if fn.Type().NumResults() > 0 {
+ typebits.Set(fn.Type().Results(), 0, bv)
+ off = objw.BitVec(lsym, off, bv)
+ }
+
+ objw.Global(lsym, int32(off), obj.RODATA|obj.LOCAL)
+}
diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go
index e121c1abd2286..71976174b0351 100644
--- a/src/cmd/compile/internal/logopt/logopt_test.go
+++ b/src/cmd/compile/internal/logopt/logopt_test.go
@@ -132,7 +132,7 @@ func TestLogOpt(t *testing.T) {
// Check at both 1 and 8-byte alignments.
t.Run("Copy", func(t *testing.T) {
const copyCode = `package x
-func s128a1(x *[128]int8) [128]int8 {
+func s128a1(x *[128]int8) [128]int8 {
return *x
}
func s127a1(x *[127]int8) [127]int8 {
@@ -219,7 +219,7 @@ func s15a8(x *[15]int64) [15]int64 {
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u003cN\u003e (assign-pair)"},`+
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u0026y.b (assign-pair)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r2 = ~R0:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return (*int)(~R0) (return)"}]}`)
})
diff --git a/src/cmd/compile/internal/mips/galign.go b/src/cmd/compile/internal/mips/galign.go
index be40c16dde0b7..599163550bb29 100644
--- a/src/cmd/compile/internal/mips/galign.go
+++ b/src/cmd/compile/internal/mips/galign.go
@@ -5,13 +5,13 @@
package mips
import (
- "cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/mips"
"cmd/internal/objabi"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &mips.Linkmips
if objabi.GOARCH == "mipsle" {
arch.LinkArch = &mips.Linkmipsle
@@ -22,7 +22,7 @@ func Init(arch *gc.Arch) {
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
arch.Ginsnopdefer = ginsnop
- arch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+ arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
}
diff --git a/src/cmd/compile/internal/mips/ggen.go b/src/cmd/compile/internal/mips/ggen.go
index 5e867721c3fcf..1a5125207dd0e 100644
--- a/src/cmd/compile/internal/mips/ggen.go
+++ b/src/cmd/compile/internal/mips/ggen.go
@@ -5,20 +5,22 @@
package mips
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/mips"
)
// TODO(mips): implement DUFFZERO
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
if cnt == 0 {
return p
}
- if cnt < int64(4*gc.Widthptr) {
- for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
- p = pp.Appendpp(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, gc.Ctxt.FixedFrameSize()+off+i)
+ if cnt < int64(4*types.PtrSize) {
+ for i := int64(0); i < cnt; i += int64(types.PtrSize) {
+ p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.FixedFrameSize()+off+i)
}
} else {
//fmt.Printf("zerorange frame:%v, lo: %v, hi:%v \n", frame ,lo, hi)
@@ -28,22 +30,22 @@ func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
// MOVW R0, (Widthptr)r1
// ADD $Widthptr, r1
// BNE r1, r2, loop
- p = pp.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+off-4, obj.TYPE_REG, mips.REGRT1, 0)
+ p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-4, obj.TYPE_REG, mips.REGRT1, 0)
p.Reg = mips.REGSP
- p = pp.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
+ p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
p.Reg = mips.REGRT1
- p = pp.Appendpp(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(gc.Widthptr))
+ p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(types.PtrSize))
p1 := p
- p = pp.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, int64(gc.Widthptr), obj.TYPE_REG, mips.REGRT1, 0)
- p = pp.Appendpp(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
+ p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, mips.REGRT1, 0)
+ p = pp.Append(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
p.Reg = mips.REGRT2
- gc.Patch(p, p1)
+ p.To.SetTarget(p1)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
p := pp.Prog(mips.ANOR)
p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REG_R0
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
index 9d11c6bf53ae7..f1cdbd3241a65 100644
--- a/src/cmd/compile/internal/mips/ssa.go
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -7,9 +7,11 @@ package mips
import (
"math"
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/mips"
@@ -75,7 +77,7 @@ func storeByType(t *types.Type, r int16) obj.As {
panic("bad store type")
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy, ssa.OpMIPSMOVWreg:
t := v.Type
@@ -121,7 +123,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
r := v.Reg()
p := s.Prog(loadByType(v.Type, r))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = r
if isHILO(r) {
@@ -151,7 +153,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type, r))
p.From.Type = obj.TYPE_REG
p.From.Reg = r
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpMIPSADD,
ssa.OpMIPSSUB,
ssa.OpMIPSAND,
@@ -286,10 +288,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
- gc.AddAux(&p.From, v)
- case *gc.Node:
+ ssagen.AddAux(&p.From, v)
+ case *ir.Name:
wantreg = "SP"
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVW $off(SP), R
wantreg = "SP"
@@ -310,7 +312,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpMIPSMOVBstore,
@@ -323,7 +325,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpMIPSMOVBstorezero,
ssa.OpMIPSMOVHstorezero,
ssa.OpMIPSMOVWstorezero:
@@ -332,7 +334,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = mips.REGZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpMIPSMOVBreg,
ssa.OpMIPSMOVBUreg,
ssa.OpMIPSMOVHreg,
@@ -425,7 +427,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p4.From.Reg = v.Args[1].Reg()
p4.Reg = mips.REG_R1
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p2)
+ p4.To.SetTarget(p2)
case ssa.OpMIPSLoweredMove:
// SUBU $4, R1
// MOVW 4(R1), Rtmp
@@ -478,7 +480,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p6.From.Reg = v.Args[2].Reg()
p6.Reg = mips.REG_R1
p6.To.Type = obj.TYPE_BRANCH
- gc.Patch(p6, p2)
+ p6.To.SetTarget(p2)
case ssa.OpMIPSCALLstatic, ssa.OpMIPSCALLclosure, ssa.OpMIPSCALLinter:
s.Call(v)
case ssa.OpMIPSLoweredWB:
@@ -490,13 +492,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(8) // space used in callee args area by assembly stubs
case ssa.OpMIPSLoweredPanicExtendA, ssa.OpMIPSLoweredPanicExtendB, ssa.OpMIPSLoweredPanicExtendC:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.ExtendCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
s.UseArgs(12) // space used in callee args area by assembly stubs
case ssa.OpMIPSLoweredAtomicLoad8,
ssa.OpMIPSLoweredAtomicLoad32:
@@ -575,7 +577,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
case ssa.OpMIPSLoweredAtomicAdd:
@@ -611,7 +613,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
@@ -655,7 +657,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
@@ -699,7 +701,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
@@ -748,26 +750,26 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p5.From.Type = obj.TYPE_REG
p5.From.Reg = v.Reg0()
p5.To.Type = obj.TYPE_BRANCH
- gc.Patch(p5, p1)
+ p5.To.SetTarget(p1)
s.Prog(mips.ASYNC)
p6 := s.Prog(obj.ANOP)
- gc.Patch(p2, p6)
+ p2.To.SetTarget(p6)
case ssa.OpMIPSLoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(mips.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpMIPSFPFlagTrue,
ssa.OpMIPSFPFlagFalse:
@@ -791,12 +793,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpMIPSLoweredGetClosurePtr:
// Closure pointer is R22 (mips.REGCTXT).
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpMIPSLoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -824,13 +826,13 @@ var blockJump = map[ssa.BlockKind]struct {
ssa.BlockMIPSFPF: {mips.ABFPF, mips.ABFPT},
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
// defer returns in R1:
@@ -841,11 +843,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.From.Reg = mips.REGZERO
p.Reg = mips.REG_R1
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
diff --git a/src/cmd/compile/internal/mips64/galign.go b/src/cmd/compile/internal/mips64/galign.go
index 90c381a50b6df..fc0a34228c8f4 100644
--- a/src/cmd/compile/internal/mips64/galign.go
+++ b/src/cmd/compile/internal/mips64/galign.go
@@ -5,13 +5,13 @@
package mips64
import (
- "cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/mips"
"cmd/internal/objabi"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &mips.Linkmips64
if objabi.GOARCH == "mips64le" {
arch.LinkArch = &mips.Linkmips64le
@@ -23,7 +23,7 @@ func Init(arch *gc.Arch) {
arch.Ginsnop = ginsnop
arch.Ginsnopdefer = ginsnop
- arch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+ arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
}
diff --git a/src/cmd/compile/internal/mips64/ggen.go b/src/cmd/compile/internal/mips64/ggen.go
index 04e7a66e417b9..37bb871958bf4 100644
--- a/src/cmd/compile/internal/mips64/ggen.go
+++ b/src/cmd/compile/internal/mips64/ggen.go
@@ -5,26 +5,28 @@
package mips64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/mips"
)
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
if cnt == 0 {
return p
}
- if cnt < int64(4*gc.Widthptr) {
- for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
- p = pp.Appendpp(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, 8+off+i)
+ if cnt < int64(4*types.PtrSize) {
+ for i := int64(0); i < cnt; i += int64(types.PtrSize) {
+ p = pp.Append(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, 8+off+i)
}
- } else if cnt <= int64(128*gc.Widthptr) {
- p = pp.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, mips.REGRT1, 0)
+ } else if cnt <= int64(128*types.PtrSize) {
+ p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, mips.REGRT1, 0)
p.Reg = mips.REGSP
- p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+ p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
- p.To.Offset = 8 * (128 - cnt/int64(gc.Widthptr))
+ p.To.Sym = ir.Syms.Duffzero
+ p.To.Offset = 8 * (128 - cnt/int64(types.PtrSize))
} else {
// ADDV $(8+frame+lo-8), SP, r1
// ADDV $cnt, r1, r2
@@ -32,22 +34,22 @@ func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
// MOVV R0, (Widthptr)r1
// ADDV $Widthptr, r1
// BNE r1, r2, loop
- p = pp.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, mips.REGRT1, 0)
+ p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, mips.REGRT1, 0)
p.Reg = mips.REGSP
- p = pp.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
+ p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
p.Reg = mips.REGRT1
- p = pp.Appendpp(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(gc.Widthptr))
+ p = pp.Append(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(types.PtrSize))
p1 := p
- p = pp.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, int64(gc.Widthptr), obj.TYPE_REG, mips.REGRT1, 0)
- p = pp.Appendpp(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
+ p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, mips.REGRT1, 0)
+ p = pp.Append(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
p.Reg = mips.REGRT2
- gc.Patch(p, p1)
+ p.To.SetTarget(p1)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
p := pp.Prog(mips.ANOR)
p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REG_R0
diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go
index 2727c4d8a8340..14cf7af143703 100644
--- a/src/cmd/compile/internal/mips64/ssa.go
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -7,9 +7,11 @@ package mips64
import (
"math"
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/mips"
@@ -83,7 +85,7 @@ func storeByType(t *types.Type, r int16) obj.As {
panic("bad store type")
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy, ssa.OpMIPS64MOVVreg:
if v.Type.IsMemory() {
@@ -124,7 +126,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
r := v.Reg()
p := s.Prog(loadByType(v.Type, r))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = r
if isHILO(r) {
@@ -154,7 +156,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type, r))
p.From.Type = obj.TYPE_REG
p.From.Reg = r
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpMIPS64ADDV,
ssa.OpMIPS64SUBV,
ssa.OpMIPS64AND,
@@ -260,10 +262,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
- gc.AddAux(&p.From, v)
- case *gc.Node:
+ ssagen.AddAux(&p.From, v)
+ case *ir.Name:
wantreg = "SP"
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVV $off(SP), R
wantreg = "SP"
@@ -286,7 +288,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpMIPS64MOVBstore,
@@ -300,7 +302,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpMIPS64MOVBstorezero,
ssa.OpMIPS64MOVHstorezero,
ssa.OpMIPS64MOVWstorezero,
@@ -310,7 +312,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = mips.REGZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpMIPS64MOVBreg,
ssa.OpMIPS64MOVBUreg,
ssa.OpMIPS64MOVHreg,
@@ -381,7 +383,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p = s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
+ p.To.Sym = ir.Syms.Duffzero
p.To.Offset = v.AuxInt
case ssa.OpMIPS64LoweredZero:
// SUBV $8, R1
@@ -426,12 +428,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p4.From.Reg = v.Args[1].Reg()
p4.Reg = mips.REG_R1
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p2)
+ p4.To.SetTarget(p2)
case ssa.OpMIPS64DUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffcopy
+ p.To.Sym = ir.Syms.Duffcopy
p.To.Offset = v.AuxInt
case ssa.OpMIPS64LoweredMove:
// SUBV $8, R1
@@ -488,7 +490,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p6.From.Reg = v.Args[2].Reg()
p6.Reg = mips.REG_R1
p6.To.Type = obj.TYPE_BRANCH
- gc.Patch(p6, p2)
+ p6.To.SetTarget(p2)
case ssa.OpMIPS64CALLstatic, ssa.OpMIPS64CALLclosure, ssa.OpMIPS64CALLinter:
s.Call(v)
case ssa.OpMIPS64LoweredWB:
@@ -500,7 +502,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpMIPS64LoweredAtomicLoad8, ssa.OpMIPS64LoweredAtomicLoad32, ssa.OpMIPS64LoweredAtomicLoad64:
as := mips.AMOVV
@@ -577,7 +579,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
case ssa.OpMIPS64LoweredAtomicAdd32, ssa.OpMIPS64LoweredAtomicAdd64:
// SYNC
@@ -614,7 +616,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
p4 := s.Prog(mips.AADDVU)
p4.From.Type = obj.TYPE_REG
@@ -657,7 +659,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
p4 := s.Prog(mips.AADDVU)
p4.From.Type = obj.TYPE_CONST
@@ -710,22 +712,22 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p5.From.Type = obj.TYPE_REG
p5.From.Reg = v.Reg0()
p5.To.Type = obj.TYPE_BRANCH
- gc.Patch(p5, p1)
+ p5.To.SetTarget(p1)
p6 := s.Prog(mips.ASYNC)
- gc.Patch(p2, p6)
+ p2.To.SetTarget(p6)
case ssa.OpMIPS64LoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(mips.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpMIPS64FPFlagTrue,
ssa.OpMIPS64FPFlagFalse:
@@ -749,15 +751,15 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.To.Type = obj.TYPE_REG
p3.To.Reg = v.Reg()
p4 := s.Prog(obj.ANOP) // not a machine instruction, for branch to land
- gc.Patch(p2, p4)
+ p2.To.SetTarget(p4)
case ssa.OpMIPS64LoweredGetClosurePtr:
// Closure pointer is R22 (mips.REGCTXT).
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpMIPS64LoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(mips.AMOVV)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -785,13 +787,13 @@ var blockJump = map[ssa.BlockKind]struct {
ssa.BlockMIPS64FPF: {mips.ABFPF, mips.ABFPT},
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
// defer returns in R1:
@@ -802,11 +804,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.From.Reg = mips.REGZERO
p.Reg = mips.REG_R1
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
new file mode 100644
index 0000000000000..747c30e6ff103
--- /dev/null
+++ b/src/cmd/compile/internal/noder/import.go
@@ -0,0 +1,480 @@
+// Copyright 2009 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 noder
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ pathpkg "path"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/archive"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+func isDriveLetter(b byte) bool {
+ return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
+}
+
+// is this path a local name? begins with ./ or ../ or /
+func islocalname(name string) bool {
+ return strings.HasPrefix(name, "/") ||
+ runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
+ strings.HasPrefix(name, "./") || name == "." ||
+ strings.HasPrefix(name, "../") || name == ".."
+}
+
+func openPackage(path string) (*os.File, error) {
+ if islocalname(path) {
+ if base.Flag.NoLocalImports {
+ return nil, errors.New("local imports disallowed")
+ }
+
+ if base.Flag.Cfg.PackageFile != nil {
+ return os.Open(base.Flag.Cfg.PackageFile[path])
+ }
+
+ // try .a before .o. important for building libraries:
+ // if there is an array.o in the array.a library,
+ // want to find all of array.a, not just array.o.
+ if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
+ return file, nil
+ }
+ if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
+ return file, nil
+ }
+ return nil, errors.New("file not found")
+ }
+
+ // local imports should be canonicalized already.
+ // don't want to see "encoding/../encoding/base64"
+ // as different from "encoding/base64".
+ if q := pathpkg.Clean(path); q != path {
+ return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
+ }
+
+ if base.Flag.Cfg.PackageFile != nil {
+ return os.Open(base.Flag.Cfg.PackageFile[path])
+ }
+
+ for _, dir := range base.Flag.Cfg.ImportDirs {
+ if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
+ return file, nil
+ }
+ if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
+ return file, nil
+ }
+ }
+
+ if objabi.GOROOT != "" {
+ suffix := ""
+ if base.Flag.InstallSuffix != "" {
+ suffix = "_" + base.Flag.InstallSuffix
+ } else if base.Flag.Race {
+ suffix = "_race"
+ } else if base.Flag.MSan {
+ suffix = "_msan"
+ }
+
+ if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
+ return file, nil
+ }
+ if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
+ return file, nil
+ }
+ }
+ return nil, errors.New("file not found")
+}
+
+// myheight tracks the local package's height based on packages
+// imported so far.
+var myheight int
+
+// resolveImportPath resolves an import path as it appears in a Go
+// source file to the package's full path.
+func resolveImportPath(path string) (string, error) {
+ // The package name main is no longer reserved,
+ // but we reserve the import path "main" to identify
+ // the main package, just as we reserve the import
+ // path "math" to identify the standard math package.
+ if path == "main" {
+ return "", errors.New("cannot import \"main\"")
+ }
+
+ if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath {
+ return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
+ }
+
+ if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
+ path = mapped
+ }
+
+ if islocalname(path) {
+ if path[0] == '/' {
+ return "", errors.New("import path cannot be absolute path")
+ }
+
+ prefix := base.Flag.D
+ if prefix == "" {
+ // Questionable, but when -D isn't specified, historically we
+ // resolve local import paths relative to the directory the
+ // compiler's current directory, not the respective source
+ // file's directory.
+ prefix = base.Ctxt.Pathname
+ }
+ path = pathpkg.Join(prefix, path)
+
+ if err := checkImportPath(path, true); err != nil {
+ return "", err
+ }
+ }
+
+ return path, nil
+}
+
+// TODO(mdempsky): Return an error instead.
+func importfile(decl *syntax.ImportDecl) *types.Pkg {
+ if decl.Path.Kind != syntax.StringLit {
+ base.Errorf("import path must be a string")
+ return nil
+ }
+
+ path, err := strconv.Unquote(decl.Path.Value)
+ if err != nil {
+ base.Errorf("import path must be a string")
+ return nil
+ }
+
+ if err := checkImportPath(path, false); err != nil {
+ base.Errorf("%s", err.Error())
+ return nil
+ }
+
+ path, err = resolveImportPath(path)
+ if err != nil {
+ base.Errorf("%s", err)
+ return nil
+ }
+
+ importpkg := types.NewPkg(path, "")
+ if importpkg.Direct {
+ return importpkg // already fully loaded
+ }
+ importpkg.Direct = true
+ typecheck.Target.Imports = append(typecheck.Target.Imports, importpkg)
+
+ if path == "unsafe" {
+ return importpkg // initialized with universe
+ }
+
+ f, err := openPackage(path)
+ if err != nil {
+ base.Errorf("could not import %q: %v", path, err)
+ base.ErrorExit()
+ }
+ imp := bio.NewReader(f)
+ defer imp.Close()
+ file := f.Name()
+
+ // check object header
+ p, err := imp.ReadString('\n')
+ if err != nil {
+ base.Errorf("import %s: reading input: %v", file, err)
+ base.ErrorExit()
+ }
+
+ if p == "!\n" { // package archive
+ // package export block should be first
+ sz := archive.ReadHeader(imp.Reader, "__.PKGDEF")
+ if sz <= 0 {
+ base.Errorf("import %s: not a package file", file)
+ base.ErrorExit()
+ }
+ p, err = imp.ReadString('\n')
+ if err != nil {
+ base.Errorf("import %s: reading input: %v", file, err)
+ base.ErrorExit()
+ }
+ }
+
+ if !strings.HasPrefix(p, "go object ") {
+ base.Errorf("import %s: not a go object file: %s", file, p)
+ base.ErrorExit()
+ }
+ q := fmt.Sprintf("%s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring())
+ if p[10:] != q {
+ base.Errorf("import %s: object is [%s] expected [%s]", file, p[10:], q)
+ base.ErrorExit()
+ }
+
+ // process header lines
+ for {
+ p, err = imp.ReadString('\n')
+ if err != nil {
+ base.Errorf("import %s: reading input: %v", file, err)
+ base.ErrorExit()
+ }
+ if p == "\n" {
+ break // header ends with blank line
+ }
+ }
+
+ // Expect $$B\n to signal binary import format.
+
+ // look for $$
+ var c byte
+ for {
+ c, err = imp.ReadByte()
+ if err != nil {
+ break
+ }
+ if c == '$' {
+ c, err = imp.ReadByte()
+ if c == '$' || err != nil {
+ break
+ }
+ }
+ }
+
+ // get character after $$
+ if err == nil {
+ c, _ = imp.ReadByte()
+ }
+
+ var fingerprint goobj.FingerprintType
+ switch c {
+ case '\n':
+ base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path)
+ return nil
+
+ case 'B':
+ if base.Debug.Export != 0 {
+ fmt.Printf("importing %s (%s)\n", path, file)
+ }
+ imp.ReadByte() // skip \n after $$B
+
+ c, err = imp.ReadByte()
+ if err != nil {
+ base.Errorf("import %s: reading input: %v", file, err)
+ base.ErrorExit()
+ }
+
+ // Indexed format is distinguished by an 'i' byte,
+ // whereas previous export formats started with 'c', 'd', or 'v'.
+ if c != 'i' {
+ base.Errorf("import %s: unexpected package format byte: %v", file, c)
+ base.ErrorExit()
+ }
+ fingerprint = typecheck.ReadImports(importpkg, imp)
+
+ default:
+ base.Errorf("no import in %q", path)
+ base.ErrorExit()
+ }
+
+ // assume files move (get installed) so don't record the full path
+ if base.Flag.Cfg.PackageFile != nil {
+ // If using a packageFile map, assume path_ can be recorded directly.
+ base.Ctxt.AddImport(path, fingerprint)
+ } else {
+ // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
+ base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
+ }
+
+ if importpkg.Height >= myheight {
+ myheight = importpkg.Height + 1
+ }
+
+ return importpkg
+}
+
+// The linker uses the magic symbol prefixes "go." and "type."
+// Avoid potential confusion between import paths and symbols
+// by rejecting these reserved imports for now. Also, people
+// "can do weird things in GOPATH and we'd prefer they didn't
+// do _that_ weird thing" (per rsc). See also #4257.
+var reservedimports = []string{
+ "go",
+ "type",
+}
+
+func checkImportPath(path string, allowSpace bool) error {
+ if path == "" {
+ return errors.New("import path is empty")
+ }
+
+ if strings.Contains(path, "\x00") {
+ return errors.New("import path contains NUL")
+ }
+
+ for _, ri := range reservedimports {
+ if path == ri {
+ return fmt.Errorf("import path %q is reserved and cannot be used", path)
+ }
+ }
+
+ for _, r := range path {
+ switch {
+ case r == utf8.RuneError:
+ return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
+ case r < 0x20 || r == 0x7f:
+ return fmt.Errorf("import path contains control character: %q", path)
+ case r == '\\':
+ return fmt.Errorf("import path contains backslash; use slash: %q", path)
+ case !allowSpace && unicode.IsSpace(r):
+ return fmt.Errorf("import path contains space character: %q", path)
+ case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
+ return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
+ }
+ }
+
+ return nil
+}
+
+func pkgnotused(lineno src.XPos, path string, name string) {
+ // If the package was imported with a name other than the final
+ // import path element, show it explicitly in the error message.
+ // Note that this handles both renamed imports and imports of
+ // packages containing unconventional package declarations.
+ // Note that this uses / always, even on Windows, because Go import
+ // paths always use forward slashes.
+ elem := path
+ if i := strings.LastIndex(elem, "/"); i >= 0 {
+ elem = elem[i+1:]
+ }
+ if name == "" || elem == name {
+ base.ErrorfAt(lineno, "imported and not used: %q", path)
+ } else {
+ base.ErrorfAt(lineno, "imported and not used: %q as %s", path, name)
+ }
+}
+
+func mkpackage(pkgname string) {
+ if types.LocalPkg.Name == "" {
+ if pkgname == "_" {
+ base.Errorf("invalid package name _")
+ }
+ types.LocalPkg.Name = pkgname
+ } else {
+ if pkgname != types.LocalPkg.Name {
+ base.Errorf("package %s; expected %s", pkgname, types.LocalPkg.Name)
+ }
+ }
+}
+
+func clearImports() {
+ type importedPkg struct {
+ pos src.XPos
+ path string
+ name string
+ }
+ var unused []importedPkg
+
+ for _, s := range types.LocalPkg.Syms {
+ n := ir.AsNode(s.Def)
+ if n == nil {
+ continue
+ }
+ if n.Op() == ir.OPACK {
+ // throw away top-level package name left over
+ // from previous file.
+ // leave s->block set to cause redeclaration
+ // errors if a conflicting top-level name is
+ // introduced by a different file.
+ p := n.(*ir.PkgName)
+ if !p.Used && base.SyntaxErrors() == 0 {
+ unused = append(unused, importedPkg{p.Pos(), p.Pkg.Path, s.Name})
+ }
+ s.Def = nil
+ continue
+ }
+ if types.IsDotAlias(s) {
+ // throw away top-level name left over
+ // from previous import . "x"
+ // We'll report errors after type checking in CheckDotImports.
+ s.Def = nil
+ continue
+ }
+ }
+
+ sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) })
+ for _, pkg := range unused {
+ pkgnotused(pkg.pos, pkg.path, pkg.name)
+ }
+}
+
+// CheckDotImports reports errors for any unused dot imports.
+func CheckDotImports() {
+ for _, pack := range dotImports {
+ if !pack.Used {
+ base.ErrorfAt(pack.Pos(), "imported and not used: %q", pack.Pkg.Path)
+ }
+ }
+
+ // No longer needed; release memory.
+ dotImports = nil
+ typecheck.DotImportRefs = nil
+}
+
+// dotImports tracks all PkgNames that have been dot-imported.
+var dotImports []*ir.PkgName
+
+// find all the exported symbols in package referenced by PkgName,
+// and make them available in the current package
+func importDot(pack *ir.PkgName) {
+ if typecheck.DotImportRefs == nil {
+ typecheck.DotImportRefs = make(map[*ir.Ident]*ir.PkgName)
+ }
+
+ opkg := pack.Pkg
+ for _, s := range opkg.Syms {
+ if s.Def == nil {
+ if _, ok := typecheck.DeclImporter[s]; !ok {
+ continue
+ }
+ }
+ if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
+ continue
+ }
+ s1 := typecheck.Lookup(s.Name)
+ if s1.Def != nil {
+ pkgerror := fmt.Sprintf("during import %q", opkg.Path)
+ typecheck.Redeclared(base.Pos, s1, pkgerror)
+ continue
+ }
+
+ id := ir.NewIdent(src.NoXPos, s)
+ typecheck.DotImportRefs[id] = pack
+ s1.Def = id
+ s1.Block = 1
+ }
+
+ dotImports = append(dotImports, pack)
+}
+
+// importName is like oldname,
+// but it reports an error if sym is from another package and not exported.
+func importName(sym *types.Sym) ir.Node {
+ n := oldname(sym)
+ if !types.IsExported(sym.Name) && sym.Pkg != types.LocalPkg {
+ n.SetDiag(true)
+ base.Errorf("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
+ }
+ return n
+}
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/noder/lex.go
similarity index 67%
rename from src/cmd/compile/internal/gc/lex.go
rename to src/cmd/compile/internal/noder/lex.go
index 7cce371408ae8..cdca9e55f336c 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/noder/lex.go
@@ -2,23 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package noder
import (
- "cmd/compile/internal/syntax"
- "cmd/internal/objabi"
- "cmd/internal/src"
"fmt"
"strings"
-)
-
-// lineno is the source position at the start of the most recently lexed token.
-// TODO(gri) rename and eventually remove
-var lineno src.XPos
-func makePos(base *src.PosBase, line, col uint) src.XPos {
- return Ctxt.PosTable.XPos(src.MakePos(base, line, col))
-}
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/internal/objabi"
+)
func isSpace(c rune) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
@@ -28,78 +21,52 @@ func isQuoted(s string) bool {
return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
}
-type PragmaFlag int16
-
-const (
- // Func pragmas.
- Nointerface PragmaFlag = 1 << iota
- Noescape // func parameters don't escape
- Norace // func must not have race detector annotations
- Nosplit // func should not execute on separate stack
- Noinline // func should not be inlined
- NoCheckPtr // func should not be instrumented by checkptr
- CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
- UintptrEscapes // pointers converted to uintptr escape
-
- // Runtime-only func pragmas.
- // See ../../../../runtime/README.md for detailed descriptions.
- Systemstack // func must run on system stack
- Nowritebarrier // emit compiler error instead of write barrier
- Nowritebarrierrec // error on write barrier in this or recursive callees
- Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
-
- // Runtime and cgo type pragmas
- NotInHeap // values of this type must not be heap allocated
-
- // Go command pragmas
- GoBuildPragma
-)
-
const (
- FuncPragmas = Nointerface |
- Noescape |
- Norace |
- Nosplit |
- Noinline |
- NoCheckPtr |
- CgoUnsafeArgs |
- UintptrEscapes |
- Systemstack |
- Nowritebarrier |
- Nowritebarrierrec |
- Yeswritebarrierrec
-
- TypePragmas = NotInHeap
+ funcPragmas = ir.Nointerface |
+ ir.Noescape |
+ ir.Norace |
+ ir.Nosplit |
+ ir.Noinline |
+ ir.NoCheckPtr |
+ ir.RegisterParams | // TODO remove after register abi is working
+ ir.CgoUnsafeArgs |
+ ir.UintptrEscapes |
+ ir.Systemstack |
+ ir.Nowritebarrier |
+ ir.Nowritebarrierrec |
+ ir.Yeswritebarrierrec
+
+ typePragmas = ir.NotInHeap
)
-func pragmaFlag(verb string) PragmaFlag {
+func pragmaFlag(verb string) ir.PragmaFlag {
switch verb {
case "go:build":
- return GoBuildPragma
+ return ir.GoBuildPragma
case "go:nointerface":
if objabi.Fieldtrack_enabled != 0 {
- return Nointerface
+ return ir.Nointerface
}
case "go:noescape":
- return Noescape
+ return ir.Noescape
case "go:norace":
- return Norace
+ return ir.Norace
case "go:nosplit":
- return Nosplit | NoCheckPtr // implies NoCheckPtr (see #34972)
+ return ir.Nosplit | ir.NoCheckPtr // implies NoCheckPtr (see #34972)
case "go:noinline":
- return Noinline
+ return ir.Noinline
case "go:nocheckptr":
- return NoCheckPtr
+ return ir.NoCheckPtr
case "go:systemstack":
- return Systemstack
+ return ir.Systemstack
case "go:nowritebarrier":
- return Nowritebarrier
+ return ir.Nowritebarrier
case "go:nowritebarrierrec":
- return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
+ return ir.Nowritebarrierrec | ir.Nowritebarrier // implies Nowritebarrier
case "go:yeswritebarrierrec":
- return Yeswritebarrierrec
+ return ir.Yeswritebarrierrec
case "go:cgo_unsafe_args":
- return CgoUnsafeArgs | NoCheckPtr // implies NoCheckPtr (see #34968)
+ return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968)
case "go:uintptrescapes":
// For the next function declared in the file
// any uintptr arguments may be pointer values
@@ -112,9 +79,11 @@ func pragmaFlag(verb string) PragmaFlag {
// call. The conversion to uintptr must appear
// in the argument list.
// Used in syscall/dll_windows.go.
- return UintptrEscapes
+ return ir.UintptrEscapes
+ case "go:registerparams": // TODO remove after register abi is working
+ return ir.RegisterParams
case "go:notinheap":
- return NotInHeap
+ return ir.NotInHeap
}
return 0
}
diff --git a/src/cmd/compile/internal/gc/lex_test.go b/src/cmd/compile/internal/noder/lex_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/lex_test.go
rename to src/cmd/compile/internal/noder/lex_test.go
index b2081a1732bbb..85a3f06759ad7 100644
--- a/src/cmd/compile/internal/gc/lex_test.go
+++ b/src/cmd/compile/internal/noder/lex_test.go
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package noder
import (
- "cmd/compile/internal/syntax"
"reflect"
"runtime"
"testing"
+
+ "cmd/compile/internal/syntax"
)
func eq(a, b []string) bool {
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
new file mode 100644
index 0000000000000..5b5b09cb2df93
--- /dev/null
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -0,0 +1,1858 @@
+// 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.
+
+package noder
+
+import (
+ "fmt"
+ "go/constant"
+ "go/token"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/dwarfgen"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+func LoadPackage(filenames []string) {
+ base.Timer.Start("fe", "parse")
+
+ mode := syntax.CheckBranches
+
+ // Limit the number of simultaneously open files.
+ sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
+
+ noders := make([]*noder, len(filenames))
+ for i, filename := range filenames {
+ p := noder{
+ err: make(chan syntax.Error),
+ trackScopes: base.Flag.Dwarf,
+ }
+ noders[i] = &p
+
+ filename := filename
+ go func() {
+ sem <- struct{}{}
+ defer func() { <-sem }()
+ defer close(p.err)
+ fbase := syntax.NewFileBase(filename)
+
+ f, err := os.Open(filename)
+ if err != nil {
+ p.error(syntax.Error{Msg: err.Error()})
+ return
+ }
+ defer f.Close()
+
+ p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error
+ }()
+ }
+
+ var lines uint
+ for _, p := range noders {
+ for e := range p.err {
+ p.errorAt(e.Pos, "%s", e.Msg)
+ }
+ lines += p.file.Lines
+ }
+ base.Timer.AddEvent(int64(lines), "lines")
+
+ for _, p := range noders {
+ p.node()
+ p.file = nil // release memory
+ }
+
+ if base.SyntaxErrors() != 0 {
+ base.ErrorExit()
+ }
+ types.CheckDclstack()
+
+ for _, p := range noders {
+ p.processPragmas()
+ }
+
+ // Typecheck.
+ types.LocalPkg.Height = myheight
+ typecheck.DeclareUniverse()
+ typecheck.TypecheckAllowed = true
+
+ // Process top-level declarations in phases.
+
+ // Phase 1: const, type, and names and types of funcs.
+ // This will gather all the information about types
+ // and methods but doesn't depend on any of it.
+ //
+ // We also defer type alias declarations until phase 2
+ // to avoid cycles like #18640.
+ // TODO(gri) Remove this again once we have a fix for #25838.
+
+ // Don't use range--typecheck can add closures to Target.Decls.
+ base.Timer.Start("fe", "typecheck", "top1")
+ for i := 0; i < len(typecheck.Target.Decls); i++ {
+ n := typecheck.Target.Decls[i]
+ if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).X.Alias()) {
+ typecheck.Target.Decls[i] = typecheck.Stmt(n)
+ }
+ }
+
+ // Phase 2: Variable assignments.
+ // To check interface assignments, depends on phase 1.
+
+ // Don't use range--typecheck can add closures to Target.Decls.
+ base.Timer.Start("fe", "typecheck", "top2")
+ for i := 0; i < len(typecheck.Target.Decls); i++ {
+ n := typecheck.Target.Decls[i]
+ if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias() {
+ typecheck.Target.Decls[i] = typecheck.Stmt(n)
+ }
+ }
+
+ // Phase 3: Type check function bodies.
+ // Don't use range--typecheck can add closures to Target.Decls.
+ base.Timer.Start("fe", "typecheck", "func")
+ var fcount int64
+ for i := 0; i < len(typecheck.Target.Decls); i++ {
+ n := typecheck.Target.Decls[i]
+ if n.Op() == ir.ODCLFUNC {
+ if base.Flag.W > 1 {
+ s := fmt.Sprintf("\nbefore typecheck %v", n)
+ ir.Dump(s, n)
+ }
+ typecheck.FuncBody(n.(*ir.Func))
+ if base.Flag.W > 1 {
+ s := fmt.Sprintf("\nafter typecheck %v", n)
+ ir.Dump(s, n)
+ }
+ fcount++
+ }
+ }
+
+ // Phase 4: Check external declarations.
+ // TODO(mdempsky): This should be handled when type checking their
+ // corresponding ODCL nodes.
+ base.Timer.Start("fe", "typecheck", "externdcls")
+ for i, n := range typecheck.Target.Externs {
+ if n.Op() == ir.ONAME {
+ typecheck.Target.Externs[i] = typecheck.Expr(typecheck.Target.Externs[i])
+ }
+ }
+
+ // Phase 5: With all user code type-checked, it's now safe to verify map keys.
+ // With all user code typechecked, it's now safe to verify unused dot imports.
+ typecheck.CheckMapKeys()
+ CheckDotImports()
+ base.ExitIfErrors()
+}
+
+func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) {
+ base.ErrorfAt(p.makeXPos(pos), format, args...)
+}
+
+// TODO(gri) Can we eliminate fileh in favor of absFilename?
+func fileh(name string) string {
+ return objabi.AbsFile("", name, base.Flag.TrimPath)
+}
+
+func absFilename(name string) string {
+ return objabi.AbsFile(base.Ctxt.Pathname, name, base.Flag.TrimPath)
+}
+
+// noder transforms package syntax's AST into a Node tree.
+type noder struct {
+ posMap
+
+ file *syntax.File
+ linknames []linkname
+ pragcgobuf [][]string
+ err chan syntax.Error
+ importedUnsafe bool
+ importedEmbed bool
+ trackScopes bool
+
+ funcState *funcState
+}
+
+// funcState tracks all per-function state to make handling nested
+// functions easier.
+type funcState struct {
+ // scopeVars is a stack tracking the number of variables declared in
+ // the current function at the moment each open scope was opened.
+ scopeVars []int
+ marker dwarfgen.ScopeMarker
+
+ lastCloseScopePos syntax.Pos
+}
+
+func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) {
+ outerFuncState := p.funcState
+ p.funcState = new(funcState)
+ typecheck.StartFuncBody(fn)
+
+ if block != nil {
+ body := p.stmts(block.List)
+ if body == nil {
+ body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)}
+ }
+ fn.Body = body
+
+ base.Pos = p.makeXPos(block.Rbrace)
+ fn.Endlineno = base.Pos
+ }
+
+ typecheck.FinishFuncBody()
+ p.funcState.marker.WriteTo(fn)
+ p.funcState = outerFuncState
+}
+
+func (p *noder) openScope(pos syntax.Pos) {
+ fs := p.funcState
+ types.Markdcl()
+
+ if p.trackScopes {
+ fs.scopeVars = append(fs.scopeVars, len(ir.CurFunc.Dcl))
+ fs.marker.Push(p.makeXPos(pos))
+ }
+}
+
+func (p *noder) closeScope(pos syntax.Pos) {
+ fs := p.funcState
+ fs.lastCloseScopePos = pos
+ types.Popdcl()
+
+ if p.trackScopes {
+ scopeVars := fs.scopeVars[len(fs.scopeVars)-1]
+ fs.scopeVars = fs.scopeVars[:len(fs.scopeVars)-1]
+ if scopeVars == len(ir.CurFunc.Dcl) {
+ // no variables were declared in this scope, so we can retract it.
+ fs.marker.Unpush()
+ } else {
+ fs.marker.Pop(p.makeXPos(pos))
+ }
+ }
+}
+
+// closeAnotherScope is like closeScope, but it reuses the same mark
+// position as the last closeScope call. This is useful for "for" and
+// "if" statements, as their implicit blocks always end at the same
+// position as an explicit block.
+func (p *noder) closeAnotherScope() {
+ p.closeScope(p.funcState.lastCloseScopePos)
+}
+
+// linkname records a //go:linkname directive.
+type linkname struct {
+ pos syntax.Pos
+ local string
+ remote string
+}
+
+func (p *noder) node() {
+ p.importedUnsafe = false
+ p.importedEmbed = false
+
+ p.setlineno(p.file.PkgName)
+ mkpackage(p.file.PkgName.Value)
+
+ if pragma, ok := p.file.Pragma.(*pragmas); ok {
+ pragma.Flag &^= ir.GoBuildPragma
+ p.checkUnused(pragma)
+ }
+
+ typecheck.Target.Decls = append(typecheck.Target.Decls, p.decls(p.file.DeclList)...)
+
+ base.Pos = src.NoXPos
+ clearImports()
+}
+
+func (p *noder) processPragmas() {
+ for _, l := range p.linknames {
+ if !p.importedUnsafe {
+ p.errorAt(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
+ continue
+ }
+ n := ir.AsNode(typecheck.Lookup(l.local).Def)
+ if n == nil || n.Op() != ir.ONAME {
+ // TODO(mdempsky): Change to p.errorAt before Go 1.17 release.
+ // base.WarnfAt(p.makeXPos(l.pos), "//go:linkname must refer to declared function or variable (will be an error in Go 1.17)")
+ continue
+ }
+ if n.Sym().Linkname != "" {
+ p.errorAt(l.pos, "duplicate //go:linkname for %s", l.local)
+ continue
+ }
+ n.Sym().Linkname = l.remote
+ }
+ typecheck.Target.CgoPragmas = append(typecheck.Target.CgoPragmas, p.pragcgobuf...)
+}
+
+func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) {
+ var cs constState
+
+ for _, decl := range decls {
+ p.setlineno(decl)
+ switch decl := decl.(type) {
+ case *syntax.ImportDecl:
+ p.importDecl(decl)
+
+ case *syntax.VarDecl:
+ l = append(l, p.varDecl(decl)...)
+
+ case *syntax.ConstDecl:
+ l = append(l, p.constDecl(decl, &cs)...)
+
+ case *syntax.TypeDecl:
+ l = append(l, p.typeDecl(decl))
+
+ case *syntax.FuncDecl:
+ l = append(l, p.funcDecl(decl))
+
+ default:
+ panic("unhandled Decl")
+ }
+ }
+
+ return
+}
+
+func (p *noder) importDecl(imp *syntax.ImportDecl) {
+ if imp.Path == nil || imp.Path.Bad {
+ return // avoid follow-on errors if there was a syntax error
+ }
+
+ if pragma, ok := imp.Pragma.(*pragmas); ok {
+ p.checkUnused(pragma)
+ }
+
+ ipkg := importfile(imp)
+ if ipkg == nil {
+ if base.Errors() == 0 {
+ base.Fatalf("phase error in import")
+ }
+ return
+ }
+
+ if ipkg == ir.Pkgs.Unsafe {
+ p.importedUnsafe = true
+ }
+ if ipkg.Path == "embed" {
+ p.importedEmbed = true
+ }
+
+ var my *types.Sym
+ if imp.LocalPkgName != nil {
+ my = p.name(imp.LocalPkgName)
+ } else {
+ my = typecheck.Lookup(ipkg.Name)
+ }
+
+ pack := ir.NewPkgName(p.pos(imp), my, ipkg)
+
+ switch my.Name {
+ case ".":
+ importDot(pack)
+ return
+ case "init":
+ base.ErrorfAt(pack.Pos(), "cannot import package as init - init must be a func")
+ return
+ case "_":
+ return
+ }
+ if my.Def != nil {
+ typecheck.Redeclared(pack.Pos(), my, "as imported package name")
+ }
+ my.Def = pack
+ my.Lastlineno = pack.Pos()
+ my.Block = 1 // at top level
+}
+
+func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node {
+ names := p.declNames(ir.ONAME, decl.NameList)
+ typ := p.typeExprOrNil(decl.Type)
+ exprs := p.exprList(decl.Values)
+
+ if pragma, ok := decl.Pragma.(*pragmas); ok {
+ varEmbed(p.makeXPos, names[0], decl, pragma, p.importedEmbed)
+ p.checkUnused(pragma)
+ }
+
+ var init []ir.Node
+ p.setlineno(decl)
+
+ if len(names) > 1 && len(exprs) == 1 {
+ as2 := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, exprs)
+ for _, v := range names {
+ as2.Lhs.Append(v)
+ typecheck.Declare(v, typecheck.DeclContext)
+ v.Ntype = typ
+ v.Defn = as2
+ if ir.CurFunc != nil {
+ init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v))
+ }
+ }
+
+ return append(init, as2)
+ }
+
+ for i, v := range names {
+ var e ir.Node
+ if i < len(exprs) {
+ e = exprs[i]
+ }
+
+ typecheck.Declare(v, typecheck.DeclContext)
+ v.Ntype = typ
+
+ if ir.CurFunc != nil {
+ init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v))
+ }
+ as := ir.NewAssignStmt(base.Pos, v, e)
+ init = append(init, as)
+ if e != nil || ir.CurFunc == nil {
+ v.Defn = as
+ }
+ }
+
+ if len(exprs) != 0 && len(names) != len(exprs) {
+ base.Errorf("assignment mismatch: %d variables but %d values", len(names), len(exprs))
+ }
+
+ return init
+}
+
+// constState tracks state between constant specifiers within a
+// declaration group. This state is kept separate from noder so nested
+// constant declarations are handled correctly (e.g., issue 15550).
+type constState struct {
+ group *syntax.Group
+ typ ir.Ntype
+ values []ir.Node
+ iota int64
+}
+
+func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
+ if decl.Group == nil || decl.Group != cs.group {
+ *cs = constState{
+ group: decl.Group,
+ }
+ }
+
+ if pragma, ok := decl.Pragma.(*pragmas); ok {
+ p.checkUnused(pragma)
+ }
+
+ names := p.declNames(ir.OLITERAL, decl.NameList)
+ typ := p.typeExprOrNil(decl.Type)
+
+ var values []ir.Node
+ if decl.Values != nil {
+ values = p.exprList(decl.Values)
+ cs.typ, cs.values = typ, values
+ } else {
+ if typ != nil {
+ base.Errorf("const declaration cannot have type without expression")
+ }
+ typ, values = cs.typ, cs.values
+ }
+
+ nn := make([]ir.Node, 0, len(names))
+ for i, n := range names {
+ if i >= len(values) {
+ base.Errorf("missing value in const declaration")
+ break
+ }
+ v := values[i]
+ if decl.Values == nil {
+ v = ir.DeepCopy(n.Pos(), v)
+ }
+ typecheck.Declare(n, typecheck.DeclContext)
+
+ n.Ntype = typ
+ n.Defn = v
+ n.SetIota(cs.iota)
+
+ nn = append(nn, ir.NewDecl(p.pos(decl), ir.ODCLCONST, n))
+ }
+
+ if len(values) > len(names) {
+ base.Errorf("extra expression in const declaration")
+ }
+
+ cs.iota++
+
+ return nn
+}
+
+func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node {
+ n := p.declName(ir.OTYPE, decl.Name)
+ typecheck.Declare(n, typecheck.DeclContext)
+
+ // decl.Type may be nil but in that case we got a syntax error during parsing
+ typ := p.typeExprOrNil(decl.Type)
+
+ n.Ntype = typ
+ n.SetAlias(decl.Alias)
+ if pragma, ok := decl.Pragma.(*pragmas); ok {
+ if !decl.Alias {
+ n.SetPragma(pragma.Flag & typePragmas)
+ pragma.Flag &^= typePragmas
+ }
+ p.checkUnused(pragma)
+ }
+
+ nod := ir.NewDecl(p.pos(decl), ir.ODCLTYPE, n)
+ if n.Alias() && !types.AllowsGoVersion(types.LocalPkg, 1, 9) {
+ base.ErrorfAt(nod.Pos(), "type aliases only supported as of -lang=go1.9")
+ }
+ return nod
+}
+
+func (p *noder) declNames(op ir.Op, names []*syntax.Name) []*ir.Name {
+ nodes := make([]*ir.Name, 0, len(names))
+ for _, name := range names {
+ nodes = append(nodes, p.declName(op, name))
+ }
+ return nodes
+}
+
+func (p *noder) declName(op ir.Op, name *syntax.Name) *ir.Name {
+ return ir.NewDeclNameAt(p.pos(name), op, p.name(name))
+}
+
+func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node {
+ name := p.name(fun.Name)
+ t := p.signature(fun.Recv, fun.Type)
+ f := ir.NewFunc(p.pos(fun))
+
+ if fun.Recv == nil {
+ if name.Name == "init" {
+ name = renameinit()
+ if len(t.Params) > 0 || len(t.Results) > 0 {
+ base.ErrorfAt(f.Pos(), "func init must have no arguments and no return values")
+ }
+ typecheck.Target.Inits = append(typecheck.Target.Inits, f)
+ }
+
+ if types.LocalPkg.Name == "main" && name.Name == "main" {
+ if len(t.Params) > 0 || len(t.Results) > 0 {
+ base.ErrorfAt(f.Pos(), "func main must have no arguments and no return values")
+ }
+ }
+ } else {
+ f.Shortname = name
+ name = ir.BlankNode.Sym() // filled in by tcFunc
+ }
+
+ f.Nname = ir.NewNameAt(p.pos(fun.Name), name)
+ f.Nname.Func = f
+ f.Nname.Defn = f
+ f.Nname.Ntype = t
+
+ if pragma, ok := fun.Pragma.(*pragmas); ok {
+ f.Pragma = pragma.Flag & funcPragmas
+ if pragma.Flag&ir.Systemstack != 0 && pragma.Flag&ir.Nosplit != 0 {
+ base.ErrorfAt(f.Pos(), "go:nosplit and go:systemstack cannot be combined")
+ }
+ pragma.Flag &^= funcPragmas
+ p.checkUnused(pragma)
+ }
+
+ if fun.Recv == nil {
+ typecheck.Declare(f.Nname, ir.PFUNC)
+ }
+
+ p.funcBody(f, fun.Body)
+
+ if fun.Body != nil {
+ if f.Pragma&ir.Noescape != 0 {
+ base.ErrorfAt(f.Pos(), "can only use //go:noescape with external func implementations")
+ }
+ } else {
+ if base.Flag.Complete || strings.HasPrefix(ir.FuncName(f), "init.") {
+ // Linknamed functions are allowed to have no body. Hopefully
+ // the linkname target has a body. See issue 23311.
+ isLinknamed := false
+ for _, n := range p.linknames {
+ if ir.FuncName(f) == n.local {
+ isLinknamed = true
+ break
+ }
+ }
+ if !isLinknamed {
+ base.ErrorfAt(f.Pos(), "missing function body")
+ }
+ }
+ }
+
+ return f
+}
+
+func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *ir.FuncType {
+ var rcvr *ir.Field
+ if recv != nil {
+ rcvr = p.param(recv, false, false)
+ }
+ return ir.NewFuncType(p.pos(typ), rcvr,
+ p.params(typ.ParamList, true),
+ p.params(typ.ResultList, false))
+}
+
+func (p *noder) params(params []*syntax.Field, dddOk bool) []*ir.Field {
+ nodes := make([]*ir.Field, 0, len(params))
+ for i, param := range params {
+ p.setlineno(param)
+ nodes = append(nodes, p.param(param, dddOk, i+1 == len(params)))
+ }
+ return nodes
+}
+
+func (p *noder) param(param *syntax.Field, dddOk, final bool) *ir.Field {
+ var name *types.Sym
+ if param.Name != nil {
+ name = p.name(param.Name)
+ }
+
+ typ := p.typeExpr(param.Type)
+ n := ir.NewField(p.pos(param), name, typ, nil)
+
+ // rewrite ...T parameter
+ if typ, ok := typ.(*ir.SliceType); ok && typ.DDD {
+ if !dddOk {
+ // We mark these as syntax errors to get automatic elimination
+ // of multiple such errors per line (see ErrorfAt in subr.go).
+ base.Errorf("syntax error: cannot use ... in receiver or result parameter list")
+ } else if !final {
+ if param.Name == nil {
+ base.Errorf("syntax error: cannot use ... with non-final parameter")
+ } else {
+ p.errorAt(param.Name.Pos(), "syntax error: cannot use ... with non-final parameter %s", param.Name.Value)
+ }
+ }
+ typ.DDD = false
+ n.IsDDD = true
+ }
+
+ return n
+}
+
+func (p *noder) exprList(expr syntax.Expr) []ir.Node {
+ switch expr := expr.(type) {
+ case nil:
+ return nil
+ case *syntax.ListExpr:
+ return p.exprs(expr.ElemList)
+ default:
+ return []ir.Node{p.expr(expr)}
+ }
+}
+
+func (p *noder) exprs(exprs []syntax.Expr) []ir.Node {
+ nodes := make([]ir.Node, 0, len(exprs))
+ for _, expr := range exprs {
+ nodes = append(nodes, p.expr(expr))
+ }
+ return nodes
+}
+
+func (p *noder) expr(expr syntax.Expr) ir.Node {
+ p.setlineno(expr)
+ switch expr := expr.(type) {
+ case nil, *syntax.BadExpr:
+ return nil
+ case *syntax.Name:
+ return p.mkname(expr)
+ case *syntax.BasicLit:
+ n := ir.NewBasicLit(p.pos(expr), p.basicLit(expr))
+ if expr.Kind == syntax.RuneLit {
+ n.SetType(types.UntypedRune)
+ }
+ n.SetDiag(expr.Bad) // avoid follow-on errors if there was a syntax error
+ return n
+ case *syntax.CompositeLit:
+ n := ir.NewCompLitExpr(p.pos(expr), ir.OCOMPLIT, p.typeExpr(expr.Type), nil)
+ l := p.exprs(expr.ElemList)
+ for i, e := range l {
+ l[i] = p.wrapname(expr.ElemList[i], e)
+ }
+ n.List = l
+ base.Pos = p.makeXPos(expr.Rbrace)
+ return n
+ case *syntax.KeyValueExpr:
+ // use position of expr.Key rather than of expr (which has position of ':')
+ return ir.NewKeyExpr(p.pos(expr.Key), p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
+ case *syntax.FuncLit:
+ return p.funcLit(expr)
+ case *syntax.ParenExpr:
+ return ir.NewParenExpr(p.pos(expr), p.expr(expr.X))
+ case *syntax.SelectorExpr:
+ // parser.new_dotname
+ obj := p.expr(expr.X)
+ if obj.Op() == ir.OPACK {
+ pack := obj.(*ir.PkgName)
+ pack.Used = true
+ return importName(pack.Pkg.Lookup(expr.Sel.Value))
+ }
+ n := ir.NewSelectorExpr(base.Pos, ir.OXDOT, obj, p.name(expr.Sel))
+ n.SetPos(p.pos(expr)) // lineno may have been changed by p.expr(expr.X)
+ return n
+ case *syntax.IndexExpr:
+ return ir.NewIndexExpr(p.pos(expr), p.expr(expr.X), p.expr(expr.Index))
+ case *syntax.SliceExpr:
+ op := ir.OSLICE
+ if expr.Full {
+ op = ir.OSLICE3
+ }
+ x := p.expr(expr.X)
+ var index [3]ir.Node
+ for i, n := range &expr.Index {
+ if n != nil {
+ index[i] = p.expr(n)
+ }
+ }
+ return ir.NewSliceExpr(p.pos(expr), op, x, index[0], index[1], index[2])
+ case *syntax.AssertExpr:
+ return ir.NewTypeAssertExpr(p.pos(expr), p.expr(expr.X), p.typeExpr(expr.Type))
+ case *syntax.Operation:
+ if expr.Op == syntax.Add && expr.Y != nil {
+ return p.sum(expr)
+ }
+ x := p.expr(expr.X)
+ if expr.Y == nil {
+ pos, op := p.pos(expr), p.unOp(expr.Op)
+ switch op {
+ case ir.OADDR:
+ return typecheck.NodAddrAt(pos, x)
+ case ir.ODEREF:
+ return ir.NewStarExpr(pos, x)
+ }
+ return ir.NewUnaryExpr(pos, op, x)
+ }
+
+ pos, op, y := p.pos(expr), p.binOp(expr.Op), p.expr(expr.Y)
+ switch op {
+ case ir.OANDAND, ir.OOROR:
+ return ir.NewLogicalExpr(pos, op, x, y)
+ }
+ return ir.NewBinaryExpr(pos, op, x, y)
+ case *syntax.CallExpr:
+ n := ir.NewCallExpr(p.pos(expr), ir.OCALL, p.expr(expr.Fun), p.exprs(expr.ArgList))
+ n.IsDDD = expr.HasDots
+ return n
+
+ case *syntax.ArrayType:
+ var len ir.Node
+ if expr.Len != nil {
+ len = p.expr(expr.Len)
+ }
+ return ir.NewArrayType(p.pos(expr), len, p.typeExpr(expr.Elem))
+ case *syntax.SliceType:
+ return ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem))
+ case *syntax.DotsType:
+ t := ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem))
+ t.DDD = true
+ return t
+ case *syntax.StructType:
+ return p.structType(expr)
+ case *syntax.InterfaceType:
+ return p.interfaceType(expr)
+ case *syntax.FuncType:
+ return p.signature(nil, expr)
+ case *syntax.MapType:
+ return ir.NewMapType(p.pos(expr),
+ p.typeExpr(expr.Key), p.typeExpr(expr.Value))
+ case *syntax.ChanType:
+ return ir.NewChanType(p.pos(expr),
+ p.typeExpr(expr.Elem), p.chanDir(expr.Dir))
+
+ case *syntax.TypeSwitchGuard:
+ var tag *ir.Ident
+ if expr.Lhs != nil {
+ tag = ir.NewIdent(p.pos(expr.Lhs), p.name(expr.Lhs))
+ if ir.IsBlank(tag) {
+ base.Errorf("invalid variable name %v in type switch", tag)
+ }
+ }
+ return ir.NewTypeSwitchGuard(p.pos(expr), tag, p.expr(expr.X))
+ }
+ panic("unhandled Expr")
+}
+
+// sum efficiently handles very large summation expressions (such as
+// in issue #16394). In particular, it avoids left recursion and
+// collapses string literals.
+func (p *noder) sum(x syntax.Expr) ir.Node {
+ // While we need to handle long sums with asymptotic
+ // efficiency, the vast majority of sums are very small: ~95%
+ // have only 2 or 3 operands, and ~99% of string literals are
+ // never concatenated.
+
+ adds := make([]*syntax.Operation, 0, 2)
+ for {
+ add, ok := x.(*syntax.Operation)
+ if !ok || add.Op != syntax.Add || add.Y == nil {
+ break
+ }
+ adds = append(adds, add)
+ x = add.X
+ }
+
+ // nstr is the current rightmost string literal in the
+ // summation (if any), and chunks holds its accumulated
+ // substrings.
+ //
+ // Consider the expression x + "a" + "b" + "c" + y. When we
+ // reach the string literal "a", we assign nstr to point to
+ // its corresponding Node and initialize chunks to {"a"}.
+ // Visiting the subsequent string literals "b" and "c", we
+ // simply append their values to chunks. Finally, when we
+ // reach the non-constant operand y, we'll join chunks to form
+ // "abc" and reassign the "a" string literal's value.
+ //
+ // N.B., we need to be careful about named string constants
+ // (indicated by Sym != nil) because 1) we can't modify their
+ // value, as doing so would affect other uses of the string
+ // constant, and 2) they may have types, which we need to
+ // handle correctly. For now, we avoid these problems by
+ // treating named string constants the same as non-constant
+ // operands.
+ var nstr ir.Node
+ chunks := make([]string, 0, 1)
+
+ n := p.expr(x)
+ if ir.IsConst(n, constant.String) && n.Sym() == nil {
+ nstr = n
+ chunks = append(chunks, ir.StringVal(nstr))
+ }
+
+ for i := len(adds) - 1; i >= 0; i-- {
+ add := adds[i]
+
+ r := p.expr(add.Y)
+ if ir.IsConst(r, constant.String) && r.Sym() == nil {
+ if nstr != nil {
+ // Collapse r into nstr instead of adding to n.
+ chunks = append(chunks, ir.StringVal(r))
+ continue
+ }
+
+ nstr = r
+ chunks = append(chunks, ir.StringVal(nstr))
+ } else {
+ if len(chunks) > 1 {
+ nstr.SetVal(constant.MakeString(strings.Join(chunks, "")))
+ }
+ nstr = nil
+ chunks = chunks[:0]
+ }
+ n = ir.NewBinaryExpr(p.pos(add), ir.OADD, n, r)
+ }
+ if len(chunks) > 1 {
+ nstr.SetVal(constant.MakeString(strings.Join(chunks, "")))
+ }
+
+ return n
+}
+
+func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype {
+ // TODO(mdempsky): Be stricter? typecheck should handle errors anyway.
+ n := p.expr(typ)
+ if n == nil {
+ return nil
+ }
+ if _, ok := n.(ir.Ntype); !ok {
+ ir.Dump("NOT NTYPE", n)
+ }
+ return n.(ir.Ntype)
+}
+
+func (p *noder) typeExprOrNil(typ syntax.Expr) ir.Ntype {
+ if typ != nil {
+ return p.typeExpr(typ)
+ }
+ return nil
+}
+
+func (p *noder) chanDir(dir syntax.ChanDir) types.ChanDir {
+ switch dir {
+ case 0:
+ return types.Cboth
+ case syntax.SendOnly:
+ return types.Csend
+ case syntax.RecvOnly:
+ return types.Crecv
+ }
+ panic("unhandled ChanDir")
+}
+
+func (p *noder) structType(expr *syntax.StructType) ir.Node {
+ l := make([]*ir.Field, 0, len(expr.FieldList))
+ for i, field := range expr.FieldList {
+ p.setlineno(field)
+ var n *ir.Field
+ if field.Name == nil {
+ n = p.embedded(field.Type)
+ } else {
+ n = ir.NewField(p.pos(field), p.name(field.Name), p.typeExpr(field.Type), nil)
+ }
+ if i < len(expr.TagList) && expr.TagList[i] != nil {
+ n.Note = constant.StringVal(p.basicLit(expr.TagList[i]))
+ }
+ l = append(l, n)
+ }
+
+ p.setlineno(expr)
+ return ir.NewStructType(p.pos(expr), l)
+}
+
+func (p *noder) interfaceType(expr *syntax.InterfaceType) ir.Node {
+ l := make([]*ir.Field, 0, len(expr.MethodList))
+ for _, method := range expr.MethodList {
+ p.setlineno(method)
+ var n *ir.Field
+ if method.Name == nil {
+ n = ir.NewField(p.pos(method), nil, importName(p.packname(method.Type)).(ir.Ntype), nil)
+ } else {
+ mname := p.name(method.Name)
+ if mname.IsBlank() {
+ base.Errorf("methods must have a unique non-blank name")
+ continue
+ }
+ sig := p.typeExpr(method.Type).(*ir.FuncType)
+ sig.Recv = fakeRecv()
+ n = ir.NewField(p.pos(method), mname, sig, nil)
+ }
+ l = append(l, n)
+ }
+
+ return ir.NewInterfaceType(p.pos(expr), l)
+}
+
+func (p *noder) packname(expr syntax.Expr) *types.Sym {
+ switch expr := expr.(type) {
+ case *syntax.Name:
+ name := p.name(expr)
+ if n := oldname(name); n.Name() != nil && n.Name().PkgName != nil {
+ n.Name().PkgName.Used = true
+ }
+ return name
+ case *syntax.SelectorExpr:
+ name := p.name(expr.X.(*syntax.Name))
+ def := ir.AsNode(name.Def)
+ if def == nil {
+ base.Errorf("undefined: %v", name)
+ return name
+ }
+ var pkg *types.Pkg
+ if def.Op() != ir.OPACK {
+ base.Errorf("%v is not a package", name)
+ pkg = types.LocalPkg
+ } else {
+ def := def.(*ir.PkgName)
+ def.Used = true
+ pkg = def.Pkg
+ }
+ return pkg.Lookup(expr.Sel.Value)
+ }
+ panic(fmt.Sprintf("unexpected packname: %#v", expr))
+}
+
+func (p *noder) embedded(typ syntax.Expr) *ir.Field {
+ op, isStar := typ.(*syntax.Operation)
+ if isStar {
+ if op.Op != syntax.Mul || op.Y != nil {
+ panic("unexpected Operation")
+ }
+ typ = op.X
+ }
+
+ sym := p.packname(typ)
+ n := ir.NewField(p.pos(typ), typecheck.Lookup(sym.Name), importName(sym).(ir.Ntype), nil)
+ n.Embedded = true
+
+ if isStar {
+ n.Ntype = ir.NewStarExpr(p.pos(op), n.Ntype)
+ }
+ return n
+}
+
+func (p *noder) stmts(stmts []syntax.Stmt) []ir.Node {
+ return p.stmtsFall(stmts, false)
+}
+
+func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []ir.Node {
+ var nodes []ir.Node
+ for i, stmt := range stmts {
+ s := p.stmtFall(stmt, fallOK && i+1 == len(stmts))
+ if s == nil {
+ } else if s.Op() == ir.OBLOCK && len(s.(*ir.BlockStmt).List) > 0 {
+ // Inline non-empty block.
+ // Empty blocks must be preserved for CheckReturn.
+ nodes = append(nodes, s.(*ir.BlockStmt).List...)
+ } else {
+ nodes = append(nodes, s)
+ }
+ }
+ return nodes
+}
+
+func (p *noder) stmt(stmt syntax.Stmt) ir.Node {
+ return p.stmtFall(stmt, false)
+}
+
+func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node {
+ p.setlineno(stmt)
+ switch stmt := stmt.(type) {
+ case nil, *syntax.EmptyStmt:
+ return nil
+ case *syntax.LabeledStmt:
+ return p.labeledStmt(stmt, fallOK)
+ case *syntax.BlockStmt:
+ l := p.blockStmt(stmt)
+ if len(l) == 0 {
+ // TODO(mdempsky): Line number?
+ return ir.NewBlockStmt(base.Pos, nil)
+ }
+ return ir.NewBlockStmt(src.NoXPos, l)
+ case *syntax.ExprStmt:
+ return p.wrapname(stmt, p.expr(stmt.X))
+ case *syntax.SendStmt:
+ return ir.NewSendStmt(p.pos(stmt), p.expr(stmt.Chan), p.expr(stmt.Value))
+ case *syntax.DeclStmt:
+ return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList))
+ case *syntax.AssignStmt:
+ if stmt.Rhs == syntax.ImplicitOne {
+ one := constant.MakeInt64(1)
+ pos := p.pos(stmt)
+ n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one))
+ n.IncDec = true
+ return n
+ }
+
+ if stmt.Op != 0 && stmt.Op != syntax.Def {
+ n := ir.NewAssignOpStmt(p.pos(stmt), p.binOp(stmt.Op), p.expr(stmt.Lhs), p.expr(stmt.Rhs))
+ return n
+ }
+
+ rhs := p.exprList(stmt.Rhs)
+ if list, ok := stmt.Lhs.(*syntax.ListExpr); ok && len(list.ElemList) != 1 || len(rhs) != 1 {
+ n := ir.NewAssignListStmt(p.pos(stmt), ir.OAS2, nil, nil)
+ n.Def = stmt.Op == syntax.Def
+ n.Lhs = p.assignList(stmt.Lhs, n, n.Def)
+ n.Rhs = rhs
+ return n
+ }
+
+ n := ir.NewAssignStmt(p.pos(stmt), nil, nil)
+ n.Def = stmt.Op == syntax.Def
+ n.X = p.assignList(stmt.Lhs, n, n.Def)[0]
+ n.Y = rhs[0]
+ return n
+
+ case *syntax.BranchStmt:
+ var op ir.Op
+ switch stmt.Tok {
+ case syntax.Break:
+ op = ir.OBREAK
+ case syntax.Continue:
+ op = ir.OCONTINUE
+ case syntax.Fallthrough:
+ if !fallOK {
+ base.Errorf("fallthrough statement out of place")
+ }
+ op = ir.OFALL
+ case syntax.Goto:
+ op = ir.OGOTO
+ default:
+ panic("unhandled BranchStmt")
+ }
+ var sym *types.Sym
+ if stmt.Label != nil {
+ sym = p.name(stmt.Label)
+ }
+ return ir.NewBranchStmt(p.pos(stmt), op, sym)
+ case *syntax.CallStmt:
+ var op ir.Op
+ switch stmt.Tok {
+ case syntax.Defer:
+ op = ir.ODEFER
+ case syntax.Go:
+ op = ir.OGO
+ default:
+ panic("unhandled CallStmt")
+ }
+ return ir.NewGoDeferStmt(p.pos(stmt), op, p.expr(stmt.Call))
+ case *syntax.ReturnStmt:
+ n := ir.NewReturnStmt(p.pos(stmt), p.exprList(stmt.Results))
+ if len(n.Results) == 0 && ir.CurFunc != nil {
+ for _, ln := range ir.CurFunc.Dcl {
+ if ln.Class == ir.PPARAM {
+ continue
+ }
+ if ln.Class != ir.PPARAMOUT {
+ break
+ }
+ if ln.Sym().Def != ln {
+ base.Errorf("%s is shadowed during return", ln.Sym().Name)
+ }
+ }
+ }
+ return n
+ case *syntax.IfStmt:
+ return p.ifStmt(stmt)
+ case *syntax.ForStmt:
+ return p.forStmt(stmt)
+ case *syntax.SwitchStmt:
+ return p.switchStmt(stmt)
+ case *syntax.SelectStmt:
+ return p.selectStmt(stmt)
+ }
+ panic("unhandled Stmt")
+}
+
+func (p *noder) assignList(expr syntax.Expr, defn ir.InitNode, colas bool) []ir.Node {
+ if !colas {
+ return p.exprList(expr)
+ }
+
+ var exprs []syntax.Expr
+ if list, ok := expr.(*syntax.ListExpr); ok {
+ exprs = list.ElemList
+ } else {
+ exprs = []syntax.Expr{expr}
+ }
+
+ res := make([]ir.Node, len(exprs))
+ seen := make(map[*types.Sym]bool, len(exprs))
+
+ newOrErr := false
+ for i, expr := range exprs {
+ p.setlineno(expr)
+ res[i] = ir.BlankNode
+
+ name, ok := expr.(*syntax.Name)
+ if !ok {
+ p.errorAt(expr.Pos(), "non-name %v on left side of :=", p.expr(expr))
+ newOrErr = true
+ continue
+ }
+
+ sym := p.name(name)
+ if sym.IsBlank() {
+ continue
+ }
+
+ if seen[sym] {
+ p.errorAt(expr.Pos(), "%v repeated on left side of :=", sym)
+ newOrErr = true
+ continue
+ }
+ seen[sym] = true
+
+ if sym.Block == types.Block {
+ res[i] = oldname(sym)
+ continue
+ }
+
+ newOrErr = true
+ n := typecheck.NewName(sym)
+ typecheck.Declare(n, typecheck.DeclContext)
+ n.Defn = defn
+ defn.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n))
+ res[i] = n
+ }
+
+ if !newOrErr {
+ base.ErrorfAt(defn.Pos(), "no new variables on left side of :=")
+ }
+ return res
+}
+
+func (p *noder) blockStmt(stmt *syntax.BlockStmt) []ir.Node {
+ p.openScope(stmt.Pos())
+ nodes := p.stmts(stmt.List)
+ p.closeScope(stmt.Rbrace)
+ return nodes
+}
+
+func (p *noder) ifStmt(stmt *syntax.IfStmt) ir.Node {
+ p.openScope(stmt.Pos())
+ init := p.stmt(stmt.Init)
+ n := ir.NewIfStmt(p.pos(stmt), p.expr(stmt.Cond), p.blockStmt(stmt.Then), nil)
+ if init != nil {
+ *n.PtrInit() = []ir.Node{init}
+ }
+ if stmt.Else != nil {
+ e := p.stmt(stmt.Else)
+ if e.Op() == ir.OBLOCK {
+ e := e.(*ir.BlockStmt)
+ n.Else = e.List
+ } else {
+ n.Else = []ir.Node{e}
+ }
+ }
+ p.closeAnotherScope()
+ return n
+}
+
+func (p *noder) forStmt(stmt *syntax.ForStmt) ir.Node {
+ p.openScope(stmt.Pos())
+ if r, ok := stmt.Init.(*syntax.RangeClause); ok {
+ if stmt.Cond != nil || stmt.Post != nil {
+ panic("unexpected RangeClause")
+ }
+
+ n := ir.NewRangeStmt(p.pos(r), nil, nil, p.expr(r.X), nil)
+ if r.Lhs != nil {
+ n.Def = r.Def
+ lhs := p.assignList(r.Lhs, n, n.Def)
+ n.Key = lhs[0]
+ if len(lhs) > 1 {
+ n.Value = lhs[1]
+ }
+ }
+ n.Body = p.blockStmt(stmt.Body)
+ p.closeAnotherScope()
+ return n
+ }
+
+ n := ir.NewForStmt(p.pos(stmt), p.stmt(stmt.Init), p.expr(stmt.Cond), p.stmt(stmt.Post), p.blockStmt(stmt.Body))
+ p.closeAnotherScope()
+ return n
+}
+
+func (p *noder) switchStmt(stmt *syntax.SwitchStmt) ir.Node {
+ p.openScope(stmt.Pos())
+
+ init := p.stmt(stmt.Init)
+ n := ir.NewSwitchStmt(p.pos(stmt), p.expr(stmt.Tag), nil)
+ if init != nil {
+ *n.PtrInit() = []ir.Node{init}
+ }
+
+ var tswitch *ir.TypeSwitchGuard
+ if l := n.Tag; l != nil && l.Op() == ir.OTYPESW {
+ tswitch = l.(*ir.TypeSwitchGuard)
+ }
+ n.Cases = p.caseClauses(stmt.Body, tswitch, stmt.Rbrace)
+
+ p.closeScope(stmt.Rbrace)
+ return n
+}
+
+func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *ir.TypeSwitchGuard, rbrace syntax.Pos) []*ir.CaseClause {
+ nodes := make([]*ir.CaseClause, 0, len(clauses))
+ for i, clause := range clauses {
+ p.setlineno(clause)
+ if i > 0 {
+ p.closeScope(clause.Pos())
+ }
+ p.openScope(clause.Pos())
+
+ n := ir.NewCaseStmt(p.pos(clause), p.exprList(clause.Cases), nil)
+ if tswitch != nil && tswitch.Tag != nil {
+ nn := typecheck.NewName(tswitch.Tag.Sym())
+ typecheck.Declare(nn, typecheck.DeclContext)
+ n.Var = nn
+ // keep track of the instances for reporting unused
+ nn.Defn = tswitch
+ }
+
+ // Trim trailing empty statements. We omit them from
+ // the Node AST anyway, and it's easier to identify
+ // out-of-place fallthrough statements without them.
+ body := clause.Body
+ for len(body) > 0 {
+ if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok {
+ break
+ }
+ body = body[:len(body)-1]
+ }
+
+ n.Body = p.stmtsFall(body, true)
+ if l := len(n.Body); l > 0 && n.Body[l-1].Op() == ir.OFALL {
+ if tswitch != nil {
+ base.Errorf("cannot fallthrough in type switch")
+ }
+ if i+1 == len(clauses) {
+ base.Errorf("cannot fallthrough final case in switch")
+ }
+ }
+
+ nodes = append(nodes, n)
+ }
+ if len(clauses) > 0 {
+ p.closeScope(rbrace)
+ }
+ return nodes
+}
+
+func (p *noder) selectStmt(stmt *syntax.SelectStmt) ir.Node {
+ return ir.NewSelectStmt(p.pos(stmt), p.commClauses(stmt.Body, stmt.Rbrace))
+}
+
+func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*ir.CommClause {
+ nodes := make([]*ir.CommClause, len(clauses))
+ for i, clause := range clauses {
+ p.setlineno(clause)
+ if i > 0 {
+ p.closeScope(clause.Pos())
+ }
+ p.openScope(clause.Pos())
+
+ nodes[i] = ir.NewCommStmt(p.pos(clause), p.stmt(clause.Comm), p.stmts(clause.Body))
+ }
+ if len(clauses) > 0 {
+ p.closeScope(rbrace)
+ }
+ return nodes
+}
+
+func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) ir.Node {
+ sym := p.name(label.Label)
+ lhs := ir.NewLabelStmt(p.pos(label), sym)
+
+ var ls ir.Node
+ if label.Stmt != nil { // TODO(mdempsky): Should always be present.
+ ls = p.stmtFall(label.Stmt, fallOK)
+ // Attach label directly to control statement too.
+ if ls != nil {
+ switch ls.Op() {
+ case ir.OFOR:
+ ls := ls.(*ir.ForStmt)
+ ls.Label = sym
+ case ir.ORANGE:
+ ls := ls.(*ir.RangeStmt)
+ ls.Label = sym
+ case ir.OSWITCH:
+ ls := ls.(*ir.SwitchStmt)
+ ls.Label = sym
+ case ir.OSELECT:
+ ls := ls.(*ir.SelectStmt)
+ ls.Label = sym
+ }
+ }
+ }
+
+ l := []ir.Node{lhs}
+ if ls != nil {
+ if ls.Op() == ir.OBLOCK {
+ ls := ls.(*ir.BlockStmt)
+ l = append(l, ls.List...)
+ } else {
+ l = append(l, ls)
+ }
+ }
+ return ir.NewBlockStmt(src.NoXPos, l)
+}
+
+var unOps = [...]ir.Op{
+ syntax.Recv: ir.ORECV,
+ syntax.Mul: ir.ODEREF,
+ syntax.And: ir.OADDR,
+
+ syntax.Not: ir.ONOT,
+ syntax.Xor: ir.OBITNOT,
+ syntax.Add: ir.OPLUS,
+ syntax.Sub: ir.ONEG,
+}
+
+func (p *noder) unOp(op syntax.Operator) ir.Op {
+ if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 {
+ panic("invalid Operator")
+ }
+ return unOps[op]
+}
+
+var binOps = [...]ir.Op{
+ syntax.OrOr: ir.OOROR,
+ syntax.AndAnd: ir.OANDAND,
+
+ syntax.Eql: ir.OEQ,
+ syntax.Neq: ir.ONE,
+ syntax.Lss: ir.OLT,
+ syntax.Leq: ir.OLE,
+ syntax.Gtr: ir.OGT,
+ syntax.Geq: ir.OGE,
+
+ syntax.Add: ir.OADD,
+ syntax.Sub: ir.OSUB,
+ syntax.Or: ir.OOR,
+ syntax.Xor: ir.OXOR,
+
+ syntax.Mul: ir.OMUL,
+ syntax.Div: ir.ODIV,
+ syntax.Rem: ir.OMOD,
+ syntax.And: ir.OAND,
+ syntax.AndNot: ir.OANDNOT,
+ syntax.Shl: ir.OLSH,
+ syntax.Shr: ir.ORSH,
+}
+
+func (p *noder) binOp(op syntax.Operator) ir.Op {
+ if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 {
+ panic("invalid Operator")
+ }
+ return binOps[op]
+}
+
+// checkLangCompat reports an error if the representation of a numeric
+// literal is not compatible with the current language version.
+func checkLangCompat(lit *syntax.BasicLit) {
+ s := lit.Value
+ if len(s) <= 2 || types.AllowsGoVersion(types.LocalPkg, 1, 13) {
+ return
+ }
+ // len(s) > 2
+ if strings.Contains(s, "_") {
+ base.ErrorfVers("go1.13", "underscores in numeric literals")
+ return
+ }
+ if s[0] != '0' {
+ return
+ }
+ radix := s[1]
+ if radix == 'b' || radix == 'B' {
+ base.ErrorfVers("go1.13", "binary literals")
+ return
+ }
+ if radix == 'o' || radix == 'O' {
+ base.ErrorfVers("go1.13", "0o/0O-style octal literals")
+ return
+ }
+ if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') {
+ base.ErrorfVers("go1.13", "hexadecimal floating-point literals")
+ }
+}
+
+func (p *noder) basicLit(lit *syntax.BasicLit) constant.Value {
+ // We don't use the errors of the conversion routines to determine
+ // if a literal string is valid because the conversion routines may
+ // accept a wider syntax than the language permits. Rely on lit.Bad
+ // instead.
+ if lit.Bad {
+ return constant.MakeUnknown()
+ }
+
+ switch lit.Kind {
+ case syntax.IntLit, syntax.FloatLit, syntax.ImagLit:
+ checkLangCompat(lit)
+ }
+
+ v := constant.MakeFromLiteral(lit.Value, tokenForLitKind[lit.Kind], 0)
+ if v.Kind() == constant.Unknown {
+ // TODO(mdempsky): Better error message?
+ p.errorAt(lit.Pos(), "malformed constant: %s", lit.Value)
+ }
+
+ return v
+}
+
+var tokenForLitKind = [...]token.Token{
+ syntax.IntLit: token.INT,
+ syntax.RuneLit: token.CHAR,
+ syntax.FloatLit: token.FLOAT,
+ syntax.ImagLit: token.IMAG,
+ syntax.StringLit: token.STRING,
+}
+
+func (p *noder) name(name *syntax.Name) *types.Sym {
+ return typecheck.Lookup(name.Value)
+}
+
+func (p *noder) mkname(name *syntax.Name) ir.Node {
+ // TODO(mdempsky): Set line number?
+ return mkname(p.name(name))
+}
+
+func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
+ // These nodes do not carry line numbers.
+ // Introduce a wrapper node to give them the correct line.
+ switch x.Op() {
+ case ir.OTYPE, ir.OLITERAL:
+ if x.Sym() == nil {
+ break
+ }
+ fallthrough
+ case ir.ONAME, ir.ONONAME, ir.OPACK:
+ p := ir.NewParenExpr(p.pos(n), x)
+ p.SetImplicit(true)
+ return p
+ }
+ return x
+}
+
+func (p *noder) setlineno(n syntax.Node) {
+ if n != nil {
+ base.Pos = p.pos(n)
+ }
+}
+
+// error is called concurrently if files are parsed concurrently.
+func (p *noder) error(err error) {
+ p.err <- err.(syntax.Error)
+}
+
+// pragmas that are allowed in the std lib, but don't have
+// a syntax.Pragma value (see lex.go) associated with them.
+var allowedStdPragmas = map[string]bool{
+ "go:cgo_export_static": true,
+ "go:cgo_export_dynamic": true,
+ "go:cgo_import_static": true,
+ "go:cgo_import_dynamic": true,
+ "go:cgo_ldflag": true,
+ "go:cgo_dynamic_linker": true,
+ "go:embed": true,
+ "go:generate": true,
+}
+
+// *pragmas is the value stored in a syntax.pragmas during parsing.
+type pragmas struct {
+ Flag ir.PragmaFlag // collected bits
+ Pos []pragmaPos // position of each individual flag
+ Embeds []pragmaEmbed
+}
+
+type pragmaPos struct {
+ Flag ir.PragmaFlag
+ Pos syntax.Pos
+}
+
+type pragmaEmbed struct {
+ Pos syntax.Pos
+ Patterns []string
+}
+
+func (p *noder) checkUnused(pragma *pragmas) {
+ for _, pos := range pragma.Pos {
+ if pos.Flag&pragma.Flag != 0 {
+ p.errorAt(pos.Pos, "misplaced compiler directive")
+ }
+ }
+ if len(pragma.Embeds) > 0 {
+ for _, e := range pragma.Embeds {
+ p.errorAt(e.Pos, "misplaced go:embed directive")
+ }
+ }
+}
+
+func (p *noder) checkUnusedDuringParse(pragma *pragmas) {
+ for _, pos := range pragma.Pos {
+ if pos.Flag&pragma.Flag != 0 {
+ p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"})
+ }
+ }
+ if len(pragma.Embeds) > 0 {
+ for _, e := range pragma.Embeds {
+ p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"})
+ }
+ }
+}
+
+// pragma is called concurrently if files are parsed concurrently.
+func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma {
+ pragma, _ := old.(*pragmas)
+ if pragma == nil {
+ pragma = new(pragmas)
+ }
+
+ if text == "" {
+ // unused pragma; only called with old != nil.
+ p.checkUnusedDuringParse(pragma)
+ return nil
+ }
+
+ if strings.HasPrefix(text, "line ") {
+ // line directives are handled by syntax package
+ panic("unreachable")
+ }
+
+ if !blankLine {
+ // directive must be on line by itself
+ p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"})
+ return pragma
+ }
+
+ switch {
+ case strings.HasPrefix(text, "go:linkname "):
+ f := strings.Fields(text)
+ if !(2 <= len(f) && len(f) <= 3) {
+ p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"})
+ break
+ }
+ // The second argument is optional. If omitted, we use
+ // the default object symbol name for this and
+ // linkname only serves to mark this symbol as
+ // something that may be referenced via the object
+ // symbol name from another package.
+ var target string
+ if len(f) == 3 {
+ target = f[2]
+ } else if base.Ctxt.Pkgpath != "" {
+ // Use the default object symbol name if the
+ // user didn't provide one.
+ target = objabi.PathToPrefix(base.Ctxt.Pkgpath) + "." + f[1]
+ } else {
+ p.error(syntax.Error{Pos: pos, Msg: "//go:linkname requires linkname argument or -p compiler flag"})
+ break
+ }
+ p.linknames = append(p.linknames, linkname{pos, f[1], target})
+
+ case text == "go:embed", strings.HasPrefix(text, "go:embed "):
+ args, err := parseGoEmbed(text[len("go:embed"):])
+ if err != nil {
+ p.error(syntax.Error{Pos: pos, Msg: err.Error()})
+ }
+ if len(args) == 0 {
+ p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."})
+ break
+ }
+ pragma.Embeds = append(pragma.Embeds, pragmaEmbed{pos, args})
+
+ case strings.HasPrefix(text, "go:cgo_import_dynamic "):
+ // This is permitted for general use because Solaris
+ // code relies on it in golang.org/x/sys/unix and others.
+ fields := pragmaFields(text)
+ if len(fields) >= 4 {
+ lib := strings.Trim(fields[3], `"`)
+ if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)})
+ }
+ p.pragcgo(pos, text)
+ pragma.Flag |= pragmaFlag("go:cgo_import_dynamic")
+ break
+ }
+ fallthrough
+ case strings.HasPrefix(text, "go:cgo_"):
+ // For security, we disallow //go:cgo_* directives other
+ // than cgo_import_dynamic outside cgo-generated files.
+ // Exception: they are allowed in the standard library, for runtime and syscall.
+ if !isCgoGeneratedFile(pos) && !base.Flag.Std {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)})
+ }
+ p.pragcgo(pos, text)
+ fallthrough // because of //go:cgo_unsafe_args
+ default:
+ verb := text
+ if i := strings.Index(text, " "); i >= 0 {
+ verb = verb[:i]
+ }
+ flag := pragmaFlag(verb)
+ const runtimePragmas = ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec
+ if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)})
+ }
+ if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)})
+ }
+ pragma.Flag |= flag
+ pragma.Pos = append(pragma.Pos, pragmaPos{flag, pos})
+ }
+
+ return pragma
+}
+
+// isCgoGeneratedFile reports whether pos is in a file
+// generated by cgo, which is to say a file with name
+// beginning with "_cgo_". Such files are allowed to
+// contain cgo directives, and for security reasons
+// (primarily misuse of linker flags), other files are not.
+// See golang.org/issue/23672.
+func isCgoGeneratedFile(pos syntax.Pos) bool {
+ return strings.HasPrefix(filepath.Base(filepath.Clean(fileh(pos.Base().Filename()))), "_cgo_")
+}
+
+// safeArg reports whether arg is a "safe" command-line argument,
+// meaning that when it appears in a command-line, it probably
+// doesn't have some special meaning other than its own name.
+// This is copied from SafeArg in cmd/go/internal/load/pkg.go.
+func safeArg(name string) bool {
+ if name == "" {
+ return false
+ }
+ c := name[0]
+ return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
+}
+
+func mkname(sym *types.Sym) ir.Node {
+ n := oldname(sym)
+ if n.Name() != nil && n.Name().PkgName != nil {
+ n.Name().PkgName.Used = true
+ }
+ return n
+}
+
+// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns.
+// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings.
+// go/build/read.go also processes these strings and contains similar logic.
+func parseGoEmbed(args string) ([]string, error) {
+ var list []string
+ for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) {
+ var path string
+ Switch:
+ switch args[0] {
+ default:
+ i := len(args)
+ for j, c := range args {
+ if unicode.IsSpace(c) {
+ i = j
+ break
+ }
+ }
+ path = args[:i]
+ args = args[i:]
+
+ case '`':
+ i := strings.Index(args[1:], "`")
+ if i < 0 {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ path = args[1 : 1+i]
+ args = args[1+i+1:]
+
+ case '"':
+ i := 1
+ for ; i < len(args); i++ {
+ if args[i] == '\\' {
+ i++
+ continue
+ }
+ if args[i] == '"' {
+ q, err := strconv.Unquote(args[:i+1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1])
+ }
+ path = q
+ args = args[i+1:]
+ break Switch
+ }
+ }
+ if i >= len(args) {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ }
+
+ if args != "" {
+ r, _ := utf8.DecodeRuneInString(args)
+ if !unicode.IsSpace(r) {
+ return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args)
+ }
+ }
+ list = append(list, path)
+ }
+ return list, nil
+}
+
+func fakeRecv() *ir.Field {
+ return ir.NewField(base.Pos, nil, nil, types.FakeRecvType())
+}
+
+func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
+ xtype := p.typeExpr(expr.Type)
+
+ fn := ir.NewFunc(p.pos(expr))
+ fn.SetIsHiddenClosure(ir.CurFunc != nil)
+
+ fn.Nname = ir.NewNameAt(p.pos(expr), ir.BlankNode.Sym()) // filled in by tcClosure
+ fn.Nname.Func = fn
+ fn.Nname.Ntype = xtype
+ fn.Nname.Defn = fn
+
+ clo := ir.NewClosureExpr(p.pos(expr), fn)
+ fn.OClosure = clo
+
+ p.funcBody(fn, expr.Body)
+
+ ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn)
+
+ return clo
+}
+
+// A function named init is a special case.
+// It is called by the initialization before main is run.
+// To make it unique within a package and also uncallable,
+// the name, normally "pkg.init", is altered to "pkg.init.0".
+var renameinitgen int
+
+func renameinit() *types.Sym {
+ s := typecheck.LookupNum("init.", renameinitgen)
+ renameinitgen++
+ return s
+}
+
+// oldname returns the Node that declares symbol s in the current scope.
+// If no such Node currently exists, an ONONAME Node is returned instead.
+// Automatically creates a new closure variable if the referenced symbol was
+// declared in a different (containing) function.
+func oldname(s *types.Sym) ir.Node {
+ if s.Pkg != types.LocalPkg {
+ return ir.NewIdent(base.Pos, s)
+ }
+
+ n := ir.AsNode(s.Def)
+ if n == nil {
+ // Maybe a top-level declaration will come along later to
+ // define s. resolve will check s.Def again once all input
+ // source has been processed.
+ return ir.NewIdent(base.Pos, s)
+ }
+
+ 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.
+ return ir.CaptureName(base.Pos, ir.CurFunc, n)
+ }
+
+ return n
+}
+
+func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) {
+ if pragma.Embeds == nil {
+ return
+ }
+
+ pragmaEmbeds := pragma.Embeds
+ pragma.Embeds = nil
+ pos := makeXPos(pragmaEmbeds[0].Pos)
+
+ if !haveEmbed {
+ base.ErrorfAt(pos, "go:embed only allowed in Go files that import \"embed\"")
+ return
+ }
+ if len(decl.NameList) > 1 {
+ base.ErrorfAt(pos, "go:embed cannot apply to multiple vars")
+ return
+ }
+ if decl.Values != nil {
+ base.ErrorfAt(pos, "go:embed cannot apply to var with initializer")
+ return
+ }
+ if decl.Type == nil {
+ // Should not happen, since Values == nil now.
+ base.ErrorfAt(pos, "go:embed cannot apply to var without type")
+ return
+ }
+ if typecheck.DeclContext != ir.PEXTERN {
+ base.ErrorfAt(pos, "go:embed cannot apply to var inside func")
+ return
+ }
+
+ var embeds []ir.Embed
+ for _, e := range pragmaEmbeds {
+ embeds = append(embeds, ir.Embed{Pos: makeXPos(e.Pos), Patterns: e.Patterns})
+ }
+ typecheck.Target.Embeds = append(typecheck.Target.Embeds, name)
+ name.Embed = &embeds
+}
diff --git a/src/cmd/compile/internal/noder/posmap.go b/src/cmd/compile/internal/noder/posmap.go
new file mode 100644
index 0000000000000..a6d3e2d7ef4d9
--- /dev/null
+++ b/src/cmd/compile/internal/noder/posmap.go
@@ -0,0 +1,83 @@
+// 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 noder
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/syntax"
+ "cmd/internal/src"
+)
+
+// A posMap handles mapping from syntax.Pos to src.XPos.
+type posMap struct {
+ bases map[*syntax.PosBase]*src.PosBase
+ cache struct {
+ last *syntax.PosBase
+ base *src.PosBase
+ }
+}
+
+type poser interface{ Pos() syntax.Pos }
+type ender interface{ End() syntax.Pos }
+
+func (m *posMap) pos(p poser) src.XPos { return m.makeXPos(p.Pos()) }
+func (m *posMap) end(p ender) src.XPos { return m.makeXPos(p.End()) }
+
+func (m *posMap) makeXPos(pos syntax.Pos) src.XPos {
+ if !pos.IsKnown() {
+ // TODO(mdempsky): Investigate restoring base.Fatalf.
+ return src.NoXPos
+ }
+
+ posBase := m.makeSrcPosBase(pos.Base())
+ return base.Ctxt.PosTable.XPos(src.MakePos(posBase, pos.Line(), pos.Col()))
+}
+
+// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
+func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
+ // fast path: most likely PosBase hasn't changed
+ if m.cache.last == b0 {
+ return m.cache.base
+ }
+
+ b1, ok := m.bases[b0]
+ if !ok {
+ fn := b0.Filename()
+ if b0.IsFileBase() {
+ b1 = src.NewFileBase(fn, absFilename(fn))
+ } else {
+ // line directive base
+ p0 := b0.Pos()
+ p0b := p0.Base()
+ if p0b == b0 {
+ panic("infinite recursion in makeSrcPosBase")
+ }
+ p1 := src.MakePos(m.makeSrcPosBase(p0b), p0.Line(), p0.Col())
+ b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col())
+ }
+ if m.bases == nil {
+ m.bases = make(map[*syntax.PosBase]*src.PosBase)
+ }
+ m.bases[b0] = b1
+ }
+
+ // update cache
+ m.cache.last = b0
+ m.cache.base = b1
+
+ return b1
+}
+
+func (m *posMap) join(other *posMap) {
+ if m.bases == nil {
+ m.bases = make(map[*syntax.PosBase]*src.PosBase)
+ }
+ for k, v := range other.bases {
+ if m.bases[k] != nil {
+ base.Fatalf("duplicate posmap bases")
+ }
+ m.bases[k] = v
+ }
+}
diff --git a/src/cmd/compile/internal/objw/objw.go b/src/cmd/compile/internal/objw/objw.go
new file mode 100644
index 0000000000000..dfbcf5155655c
--- /dev/null
+++ b/src/cmd/compile/internal/objw/objw.go
@@ -0,0 +1,72 @@
+// Copyright 2009 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 objw
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/bitvec"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+func Uint8(s *obj.LSym, off int, v uint8) int {
+ return UintN(s, off, uint64(v), 1)
+}
+
+func Uint16(s *obj.LSym, off int, v uint16) int {
+ return UintN(s, off, uint64(v), 2)
+}
+
+func Uint32(s *obj.LSym, off int, v uint32) int {
+ return UintN(s, off, uint64(v), 4)
+}
+
+func Uintptr(s *obj.LSym, off int, v uint64) int {
+ return UintN(s, off, v, types.PtrSize)
+}
+
+func UintN(s *obj.LSym, off int, v uint64, wid int) int {
+ if off&(wid-1) != 0 {
+ base.Fatalf("duintxxLSym: misaligned: v=%d wid=%d off=%d", v, wid, off)
+ }
+ s.WriteInt(base.Ctxt, int64(off), wid, int64(v))
+ return off + wid
+}
+
+func SymPtr(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
+ off = int(types.Rnd(int64(off), int64(types.PtrSize)))
+ s.WriteAddr(base.Ctxt, int64(off), types.PtrSize, x, int64(xoff))
+ off += types.PtrSize
+ return off
+}
+
+func SymPtrOff(s *obj.LSym, off int, x *obj.LSym) int {
+ s.WriteOff(base.Ctxt, int64(off), x, 0)
+ off += 4
+ return off
+}
+
+func SymPtrWeakOff(s *obj.LSym, off int, x *obj.LSym) int {
+ s.WriteWeakOff(base.Ctxt, int64(off), x, 0)
+ off += 4
+ return off
+}
+
+func Global(s *obj.LSym, width int32, flags int16) {
+ if flags&obj.LOCAL != 0 {
+ s.Set(obj.AttrLocal, true)
+ flags &^= obj.LOCAL
+ }
+ base.Ctxt.Globl(s, int64(width), int(flags))
+}
+
+func BitVec(s *obj.LSym, off int, bv bitvec.BitVec) int {
+ // Runtime reads the bitmaps as byte arrays. Oblige.
+ for j := 0; int32(j) < bv.N; j += 8 {
+ word := bv.B[j/32]
+ off = Uint8(s, off, uint8(word>>(uint(j)%32)))
+ }
+ return off
+}
diff --git a/src/cmd/compile/internal/objw/prog.go b/src/cmd/compile/internal/objw/prog.go
new file mode 100644
index 0000000000000..b5ac4dda1eb97
--- /dev/null
+++ b/src/cmd/compile/internal/objw/prog.go
@@ -0,0 +1,226 @@
+// Derived from Inferno utils/6c/txt.c
+// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6c/txt.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package objw
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+var sharedProgArray = new([10000]obj.Prog) // *T instead of T to work around issue 19839
+
+// NewProgs returns a new Progs for fn.
+// worker indicates which of the backend workers will use the Progs.
+func NewProgs(fn *ir.Func, worker int) *Progs {
+ pp := new(Progs)
+ if base.Ctxt.CanReuseProgs() {
+ sz := len(sharedProgArray) / base.Flag.LowerC
+ pp.Cache = sharedProgArray[sz*worker : sz*(worker+1)]
+ }
+ pp.CurFunc = fn
+
+ // prime the pump
+ pp.Next = pp.NewProg()
+ pp.Clear(pp.Next)
+
+ pp.Pos = fn.Pos()
+ pp.SetText(fn)
+ // PCDATA tables implicitly start with index -1.
+ pp.PrevLive = LivenessIndex{-1, false}
+ pp.NextLive = pp.PrevLive
+ return pp
+}
+
+// Progs accumulates Progs for a function and converts them into machine code.
+type Progs struct {
+ Text *obj.Prog // ATEXT Prog for this function
+ Next *obj.Prog // next Prog
+ PC int64 // virtual PC; count of Progs
+ Pos src.XPos // position to use for new Progs
+ CurFunc *ir.Func // fn these Progs are for
+ Cache []obj.Prog // local progcache
+ CacheIndex int // first free element of progcache
+
+ NextLive LivenessIndex // liveness index for the next Prog
+ PrevLive LivenessIndex // last emitted liveness index
+}
+
+// LivenessIndex stores the liveness map information for a Value.
+type LivenessIndex struct {
+ StackMapIndex int
+
+ // IsUnsafePoint indicates that this is an unsafe-point.
+ //
+ // Note that it's possible for a call Value to have a stack
+ // map while also being an unsafe-point. This means it cannot
+ // be preempted at this instruction, but that a preemption or
+ // stack growth may happen in the called function.
+ IsUnsafePoint bool
+}
+
+// StackMapDontCare indicates that the stack map index at a Value
+// doesn't matter.
+//
+// This is a sentinel value that should never be emitted to the PCDATA
+// stream. We use -1000 because that's obviously never a valid stack
+// index (but -1 is).
+const StackMapDontCare = -1000
+
+// LivenessDontCare indicates that the liveness information doesn't
+// matter. Currently it is used in deferreturn liveness when we don't
+// actually need it. It should never be emitted to the PCDATA stream.
+var LivenessDontCare = LivenessIndex{StackMapDontCare, true}
+
+func (idx LivenessIndex) StackMapValid() bool {
+ return idx.StackMapIndex != StackMapDontCare
+}
+
+func (pp *Progs) NewProg() *obj.Prog {
+ var p *obj.Prog
+ if pp.CacheIndex < len(pp.Cache) {
+ p = &pp.Cache[pp.CacheIndex]
+ pp.CacheIndex++
+ } else {
+ p = new(obj.Prog)
+ }
+ p.Ctxt = base.Ctxt
+ return p
+}
+
+// Flush converts from pp to machine code.
+func (pp *Progs) Flush() {
+ plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.CurFunc}
+ obj.Flushplist(base.Ctxt, plist, pp.NewProg, base.Ctxt.Pkgpath)
+}
+
+// Free clears pp and any associated resources.
+func (pp *Progs) Free() {
+ if base.Ctxt.CanReuseProgs() {
+ // Clear progs to enable GC and avoid abuse.
+ s := pp.Cache[:pp.CacheIndex]
+ for i := range s {
+ s[i] = obj.Prog{}
+ }
+ }
+ // Clear pp to avoid abuse.
+ *pp = Progs{}
+}
+
+// Prog adds a Prog with instruction As to pp.
+func (pp *Progs) Prog(as obj.As) *obj.Prog {
+ if pp.NextLive.StackMapValid() && pp.NextLive.StackMapIndex != pp.PrevLive.StackMapIndex {
+ // Emit stack map index change.
+ idx := pp.NextLive.StackMapIndex
+ pp.PrevLive.StackMapIndex = idx
+ p := pp.Prog(obj.APCDATA)
+ p.From.SetConst(objabi.PCDATA_StackMapIndex)
+ p.To.SetConst(int64(idx))
+ }
+ if pp.NextLive.IsUnsafePoint != pp.PrevLive.IsUnsafePoint {
+ // Emit unsafe-point marker.
+ pp.PrevLive.IsUnsafePoint = pp.NextLive.IsUnsafePoint
+ p := pp.Prog(obj.APCDATA)
+ p.From.SetConst(objabi.PCDATA_UnsafePoint)
+ if pp.NextLive.IsUnsafePoint {
+ p.To.SetConst(objabi.PCDATA_UnsafePointUnsafe)
+ } else {
+ p.To.SetConst(objabi.PCDATA_UnsafePointSafe)
+ }
+ }
+
+ p := pp.Next
+ pp.Next = pp.NewProg()
+ pp.Clear(pp.Next)
+ p.Link = pp.Next
+
+ if !pp.Pos.IsKnown() && base.Flag.K != 0 {
+ base.Warn("prog: unknown position (line 0)")
+ }
+
+ p.As = as
+ p.Pos = pp.Pos
+ if pp.Pos.IsStmt() == src.PosIsStmt {
+ // Clear IsStmt for later Progs at this pos provided that as can be marked as a stmt
+ if LosesStmtMark(as) {
+ return p
+ }
+ pp.Pos = pp.Pos.WithNotStmt()
+ }
+ return p
+}
+
+func (pp *Progs) Clear(p *obj.Prog) {
+ obj.Nopout(p)
+ p.As = obj.AEND
+ p.Pc = pp.PC
+ pp.PC++
+}
+
+func (pp *Progs) Append(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
+ q := pp.NewProg()
+ pp.Clear(q)
+ q.As = as
+ q.Pos = p.Pos
+ q.From.Type = ftype
+ q.From.Reg = freg
+ q.From.Offset = foffset
+ q.To.Type = ttype
+ q.To.Reg = treg
+ q.To.Offset = toffset
+ q.Link = p.Link
+ p.Link = q
+ return q
+}
+
+func (pp *Progs) SetText(fn *ir.Func) {
+ if pp.Text != nil {
+ base.Fatalf("Progs.SetText called twice")
+ }
+ ptxt := pp.Prog(obj.ATEXT)
+ pp.Text = ptxt
+
+ fn.LSym.Func().Text = ptxt
+ ptxt.From.Type = obj.TYPE_MEM
+ ptxt.From.Name = obj.NAME_EXTERN
+ ptxt.From.Sym = fn.LSym
+}
+
+// LosesStmtMark reports whether a prog with op as loses its statement mark on the way to DWARF.
+// The attributes from some opcodes are lost in translation.
+// TODO: this is an artifact of how funcpctab combines information for instructions at a single PC.
+// Should try to fix it there.
+func LosesStmtMark(as obj.As) bool {
+ // is_stmt does not work for these; it DOES for ANOP even though that generates no code.
+ return as == obj.APCDATA || as == obj.AFUNCDATA
+}
diff --git a/src/cmd/compile/internal/pkginit/init.go b/src/cmd/compile/internal/pkginit/init.go
new file mode 100644
index 0000000000000..7cad2622146d0
--- /dev/null
+++ b/src/cmd/compile/internal/pkginit/init.go
@@ -0,0 +1,109 @@
+// Copyright 2009 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 pkginit
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/deadcode"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+// Task makes and returns an initialization record for the package.
+// See runtime/proc.go:initTask for its layout.
+// The 3 tasks for initialization are:
+// 1) Initialize all of the packages the current package depends on.
+// 2) Initialize all the variables that have initializers.
+// 3) Run any init functions.
+func Task() *ir.Name {
+ nf := initOrder(typecheck.Target.Decls)
+
+ var deps []*obj.LSym // initTask records for packages the current package depends on
+ var fns []*obj.LSym // functions to call for package initialization
+
+ // Find imported packages with init tasks.
+ for _, pkg := range typecheck.Target.Imports {
+ n := typecheck.Resolve(ir.NewIdent(base.Pos, pkg.Lookup(".inittask")))
+ if n.Op() == ir.ONONAME {
+ continue
+ }
+ if n.Op() != ir.ONAME || n.(*ir.Name).Class != ir.PEXTERN {
+ base.Fatalf("bad inittask: %v", n)
+ }
+ deps = append(deps, n.(*ir.Name).Linksym())
+ }
+
+ // Make a function that contains all the initialization statements.
+ if len(nf) > 0 {
+ base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt
+ initializers := typecheck.Lookup("init")
+ fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil))
+ for _, dcl := range typecheck.InitTodoFunc.Dcl {
+ dcl.Curfn = fn
+ }
+ fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
+ typecheck.InitTodoFunc.Dcl = nil
+
+ fn.Body = nf
+ typecheck.FinishFuncBody()
+
+ typecheck.Func(fn)
+ ir.CurFunc = fn
+ typecheck.Stmts(nf)
+ ir.CurFunc = nil
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+ fns = append(fns, fn.Linksym())
+ }
+ if typecheck.InitTodoFunc.Dcl != nil {
+ // We only generate temps using InitTodoFunc if there
+ // are package-scope initialization statements, so
+ // something's weird if we get here.
+ base.Fatalf("InitTodoFunc still has declarations")
+ }
+ typecheck.InitTodoFunc = nil
+
+ // Record user init functions.
+ for _, fn := range typecheck.Target.Inits {
+ // Must happen after initOrder; see #43444.
+ deadcode.Func(fn)
+
+ // Skip init functions with empty bodies.
+ if len(fn.Body) == 1 {
+ if stmt := fn.Body[0]; stmt.Op() == ir.OBLOCK && len(stmt.(*ir.BlockStmt).List) == 0 {
+ continue
+ }
+ }
+ fns = append(fns, fn.Nname.Linksym())
+ }
+
+ if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Name != "main" && types.LocalPkg.Name != "runtime" {
+ return nil // nothing to initialize
+ }
+
+ // Make an .inittask structure.
+ sym := typecheck.Lookup(".inittask")
+ task := typecheck.NewName(sym)
+ task.SetType(types.Types[types.TUINT8]) // fake type
+ task.Class = ir.PEXTERN
+ sym.Def = task
+ lsym := task.Linksym()
+ ot := 0
+ ot = objw.Uintptr(lsym, ot, 0) // state: not initialized yet
+ ot = objw.Uintptr(lsym, ot, uint64(len(deps)))
+ ot = objw.Uintptr(lsym, ot, uint64(len(fns)))
+ for _, d := range deps {
+ ot = objw.SymPtr(lsym, ot, d, 0)
+ }
+ for _, f := range fns {
+ ot = objw.SymPtr(lsym, ot, f, 0)
+ }
+ // An initTask has pointers, but none into the Go heap.
+ // It's not quite read only, the state field must be modifiable.
+ objw.Global(lsym, int32(ot), obj.NOPTR)
+ return task
+}
diff --git a/src/cmd/compile/internal/gc/initorder.go b/src/cmd/compile/internal/pkginit/initorder.go
similarity index 64%
rename from src/cmd/compile/internal/gc/initorder.go
rename to src/cmd/compile/internal/pkginit/initorder.go
index e2084fd03862c..97d69629fbae9 100644
--- a/src/cmd/compile/internal/gc/initorder.go
+++ b/src/cmd/compile/internal/pkginit/initorder.go
@@ -2,12 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package pkginit
import (
"bytes"
"container/heap"
"fmt"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/staticinit"
)
// Package initialization
@@ -60,56 +64,57 @@ const (
type InitOrder struct {
// blocking maps initialization assignments to the assignments
// that depend on it.
- blocking map[*Node][]*Node
+ blocking map[ir.Node][]ir.Node
// ready is the queue of Pending initialization assignments
// that are ready for initialization.
ready declOrder
+
+ order map[ir.Node]int
}
// initOrder computes initialization order for a list l of
// package-level declarations (in declaration order) and outputs the
// corresponding list of statements to include in the init() function
// body.
-func initOrder(l []*Node) []*Node {
- s := InitSchedule{
- initplans: make(map[*Node]*InitPlan),
- inittemps: make(map[*Node]*Node),
+func initOrder(l []ir.Node) []ir.Node {
+ s := staticinit.Schedule{
+ Plans: make(map[ir.Node]*staticinit.Plan),
+ Temps: make(map[ir.Node]*ir.Name),
}
o := InitOrder{
- blocking: make(map[*Node][]*Node),
+ blocking: make(map[ir.Node][]ir.Node),
+ order: make(map[ir.Node]int),
}
// Process all package-level assignment in declaration order.
for _, n := range l {
- switch n.Op {
- case OAS, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+ switch n.Op() {
+ case ir.OAS, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
o.processAssign(n)
- o.flushReady(s.staticInit)
- case ODCLCONST, ODCLFUNC, ODCLTYPE:
+ o.flushReady(s.StaticInit)
+ case ir.ODCLCONST, ir.ODCLFUNC, ir.ODCLTYPE:
// nop
default:
- Fatalf("unexpected package-level statement: %v", n)
+ base.Fatalf("unexpected package-level statement: %v", n)
}
}
// Check that all assignments are now Done; if not, there must
// have been a dependency cycle.
for _, n := range l {
- switch n.Op {
- case OAS, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- if n.Initorder() != InitDone {
+ switch n.Op() {
+ case ir.OAS, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
+ if o.order[n] != orderDone {
// If there have already been errors
// printed, those errors may have
// confused us and there might not be
// a loop. Let the user fix those
// first.
- if nerrors > 0 {
- errorexit()
- }
+ base.ExitIfErrors()
- findInitLoopAndExit(firstLHS(n), new([]*Node), make(map[*Node]bool))
- Fatalf("initialization unfinished, but failed to identify loop")
+ o.findInitLoopAndExit(firstLHS(n), new([]*ir.Name), new(ir.NameSet))
+ base.Fatalf("initialization unfinished, but failed to identify loop")
}
}
}
@@ -117,58 +122,56 @@ func initOrder(l []*Node) []*Node {
// Invariant consistency check. If this is non-zero, then we
// should have found a cycle above.
if len(o.blocking) != 0 {
- Fatalf("expected empty map: %v", o.blocking)
+ base.Fatalf("expected empty map: %v", o.blocking)
}
- return s.out
+ return s.Out
}
-func (o *InitOrder) processAssign(n *Node) {
- if n.Initorder() != InitNotStarted || n.Xoffset != BADWIDTH {
- Fatalf("unexpected state: %v, %v, %v", n, n.Initorder(), n.Xoffset)
+func (o *InitOrder) processAssign(n ir.Node) {
+ if _, ok := o.order[n]; ok {
+ base.Fatalf("unexpected state: %v, %v", n, o.order[n])
}
-
- n.SetInitorder(InitPending)
- n.Xoffset = 0
+ o.order[n] = 0
// Compute number of variable dependencies and build the
// inverse dependency ("blocking") graph.
for dep := range collectDeps(n, true) {
- defn := dep.Name.Defn
+ defn := dep.Defn
// Skip dependencies on functions (PFUNC) and
// variables already initialized (InitDone).
- if dep.Class() != PEXTERN || defn.Initorder() == InitDone {
+ if dep.Class != ir.PEXTERN || o.order[defn] == orderDone {
continue
}
- n.Xoffset++
+ o.order[n]++
o.blocking[defn] = append(o.blocking[defn], n)
}
- if n.Xoffset == 0 {
+ if o.order[n] == 0 {
heap.Push(&o.ready, n)
}
}
+const orderDone = -1000
+
// flushReady repeatedly applies initialize to the earliest (in
// declaration order) assignment ready for initialization and updates
// the inverse dependency ("blocking") graph.
-func (o *InitOrder) flushReady(initialize func(*Node)) {
+func (o *InitOrder) flushReady(initialize func(ir.Node)) {
for o.ready.Len() != 0 {
- n := heap.Pop(&o.ready).(*Node)
- if n.Initorder() != InitPending || n.Xoffset != 0 {
- Fatalf("unexpected state: %v, %v, %v", n, n.Initorder(), n.Xoffset)
+ n := heap.Pop(&o.ready).(ir.Node)
+ if order, ok := o.order[n]; !ok || order != 0 {
+ base.Fatalf("unexpected state: %v, %v, %v", n, ok, order)
}
initialize(n)
- n.SetInitorder(InitDone)
- n.Xoffset = BADWIDTH
+ o.order[n] = orderDone
blocked := o.blocking[n]
delete(o.blocking, n)
for _, m := range blocked {
- m.Xoffset--
- if m.Xoffset == 0 {
+ if o.order[m]--; o.order[m] == 0 {
heap.Push(&o.ready, m)
}
}
@@ -181,7 +184,7 @@ func (o *InitOrder) flushReady(initialize func(*Node)) {
// path points to a slice used for tracking the sequence of
// variables/functions visited. Using a pointer to a slice allows the
// slice capacity to grow and limit reallocations.
-func findInitLoopAndExit(n *Node, path *[]*Node, ok map[*Node]bool) {
+func (o *InitOrder) findInitLoopAndExit(n *ir.Name, path *[]*ir.Name, ok *ir.NameSet) {
for i, x := range *path {
if x == n {
reportInitLoopAndExit((*path)[i:])
@@ -191,24 +194,25 @@ func findInitLoopAndExit(n *Node, path *[]*Node, ok map[*Node]bool) {
// There might be multiple loops involving n; by sorting
// references, we deterministically pick the one reported.
- refers := collectDeps(n.Name.Defn, false).Sorted(func(ni, nj *Node) bool {
- return ni.Pos.Before(nj.Pos)
+ refers := collectDeps(n.Defn, false).Sorted(func(ni, nj *ir.Name) bool {
+ return ni.Pos().Before(nj.Pos())
})
*path = append(*path, n)
for _, ref := range refers {
// Short-circuit variables that were initialized.
- if ref.Class() == PEXTERN && ref.Name.Defn.Initorder() == InitDone || ok[ref] {
+ if ref.Class == ir.PEXTERN && o.order[ref.Defn] == orderDone || ok.Has(ref) {
continue
}
- findInitLoopAndExit(ref, path, ok)
+
+ o.findInitLoopAndExit(ref, path, ok)
}
// n is not involved in a cycle.
// Record that fact to avoid checking it again when reached another way,
// or else this traversal will take exponential time traversing all paths
// through the part of the package's call graph implicated in the cycle.
- ok[n] = true
+ ok.Add(n)
*path = (*path)[:len(*path)-1]
}
@@ -216,12 +220,12 @@ func findInitLoopAndExit(n *Node, path *[]*Node, ok map[*Node]bool) {
// reportInitLoopAndExit reports and initialization loop as an error
// and exits. However, if l is not actually an initialization loop, it
// simply returns instead.
-func reportInitLoopAndExit(l []*Node) {
+func reportInitLoopAndExit(l []*ir.Name) {
// Rotate loop so that the earliest variable declaration is at
// the start.
i := -1
for j, n := range l {
- if n.Class() == PEXTERN && (i == -1 || n.Pos.Before(l[i].Pos)) {
+ if n.Class == ir.PEXTERN && (i == -1 || n.Pos().Before(l[i].Pos())) {
i = j
}
}
@@ -239,69 +243,75 @@ func reportInitLoopAndExit(l []*Node) {
var msg bytes.Buffer
fmt.Fprintf(&msg, "initialization loop:\n")
for _, n := range l {
- fmt.Fprintf(&msg, "\t%v: %v refers to\n", n.Line(), n)
+ fmt.Fprintf(&msg, "\t%v: %v refers to\n", ir.Line(n), n)
}
- fmt.Fprintf(&msg, "\t%v: %v", l[0].Line(), l[0])
+ fmt.Fprintf(&msg, "\t%v: %v", ir.Line(l[0]), l[0])
- yyerrorl(l[0].Pos, msg.String())
- errorexit()
+ base.ErrorfAt(l[0].Pos(), msg.String())
+ base.ErrorExit()
}
// collectDeps returns all of the package-level functions and
// variables that declaration n depends on. If transitive is true,
// then it also includes the transitive dependencies of any depended
// upon functions (but not variables).
-func collectDeps(n *Node, transitive bool) NodeSet {
+func collectDeps(n ir.Node, transitive bool) ir.NameSet {
d := initDeps{transitive: transitive}
- switch n.Op {
- case OAS:
- d.inspect(n.Right)
- case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- d.inspect(n.Right)
- case ODCLFUNC:
- d.inspectList(n.Nbody)
+ switch n.Op() {
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ d.inspect(n.Y)
+ case ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
+ n := n.(*ir.AssignListStmt)
+ d.inspect(n.Rhs[0])
+ case ir.ODCLFUNC:
+ n := n.(*ir.Func)
+ d.inspectList(n.Body)
default:
- Fatalf("unexpected Op: %v", n.Op)
+ base.Fatalf("unexpected Op: %v", n.Op())
}
return d.seen
}
type initDeps struct {
transitive bool
- seen NodeSet
+ seen ir.NameSet
+ cvisit func(ir.Node)
+}
+
+func (d *initDeps) cachedVisit() func(ir.Node) {
+ if d.cvisit == nil {
+ d.cvisit = d.visit // cache closure
+ }
+ return d.cvisit
}
-func (d *initDeps) inspect(n *Node) { inspect(n, d.visit) }
-func (d *initDeps) inspectList(l Nodes) { inspectList(l, d.visit) }
+func (d *initDeps) inspect(n ir.Node) { ir.Visit(n, d.cachedVisit()) }
+func (d *initDeps) inspectList(l ir.Nodes) { ir.VisitList(l, d.cachedVisit()) }
// visit calls foundDep on any package-level functions or variables
// referenced by n, if any.
-func (d *initDeps) visit(n *Node) bool {
- switch n.Op {
- case ONAME:
- if n.isMethodExpression() {
- d.foundDep(asNode(n.Type.FuncType().Nname))
- return false
- }
-
- switch n.Class() {
- case PEXTERN, PFUNC:
+func (d *initDeps) visit(n ir.Node) {
+ switch n.Op() {
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ switch n.Class {
+ case ir.PEXTERN, ir.PFUNC:
d.foundDep(n)
}
- case OCLOSURE:
- d.inspectList(n.Func.Closure.Nbody)
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ d.inspectList(n.Func.Body)
- case ODOTMETH, OCALLPART:
- d.foundDep(asNode(n.Type.FuncType().Nname))
+ case ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR:
+ d.foundDep(ir.MethodExprName(n))
}
-
- return true
}
// foundDep records that we've found a dependency on n by adding it to
// seen.
-func (d *initDeps) foundDep(n *Node) {
+func (d *initDeps) foundDep(n *ir.Name) {
// Can happen with method expressions involving interface
// types; e.g., fixedbugs/issue4495.go.
if n == nil {
@@ -310,7 +320,7 @@ func (d *initDeps) foundDep(n *Node) {
// Names without definitions aren't interesting as far as
// initialization ordering goes.
- if n.Name.Defn == nil {
+ if n.Defn == nil {
return
}
@@ -318,8 +328,8 @@ func (d *initDeps) foundDep(n *Node) {
return
}
d.seen.Add(n)
- if d.transitive && n.Class() == PFUNC {
- d.inspectList(n.Name.Defn.Nbody)
+ if d.transitive && n.Class == ir.PFUNC {
+ d.inspectList(n.Defn.(*ir.Func).Body)
}
}
@@ -330,13 +340,15 @@ func (d *initDeps) foundDep(n *Node) {
// an OAS node's Pos may not be unique. For example, given the
// declaration "var a, b = f(), g()", "a" must be ordered before "b",
// but both OAS nodes use the "=" token's position as their Pos.
-type declOrder []*Node
+type declOrder []ir.Node
-func (s declOrder) Len() int { return len(s) }
-func (s declOrder) Less(i, j int) bool { return firstLHS(s[i]).Pos.Before(firstLHS(s[j]).Pos) }
-func (s declOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s declOrder) Len() int { return len(s) }
+func (s declOrder) Less(i, j int) bool {
+ return firstLHS(s[i]).Pos().Before(firstLHS(s[j]).Pos())
+}
+func (s declOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s *declOrder) Push(x interface{}) { *s = append(*s, x.(*Node)) }
+func (s *declOrder) Push(x interface{}) { *s = append(*s, x.(ir.Node)) }
func (s *declOrder) Pop() interface{} {
n := (*s)[len(*s)-1]
*s = (*s)[:len(*s)-1]
@@ -345,14 +357,16 @@ func (s *declOrder) Pop() interface{} {
// firstLHS returns the first expression on the left-hand side of
// assignment n.
-func firstLHS(n *Node) *Node {
- switch n.Op {
- case OAS:
- return n.Left
- case OAS2DOTTYPE, OAS2FUNC, OAS2RECV, OAS2MAPR:
- return n.List.First()
+func firstLHS(n ir.Node) *ir.Name {
+ switch n.Op() {
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ return n.X.Name()
+ case ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2RECV, ir.OAS2MAPR:
+ n := n.(*ir.AssignListStmt)
+ return n.Lhs[0].Name()
}
- Fatalf("unexpected Op: %v", n.Op)
+ base.Fatalf("unexpected Op: %v", n.Op())
return nil
}
diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go
index c8ef567dc3fd0..c72d1aa8348c4 100644
--- a/src/cmd/compile/internal/ppc64/galign.go
+++ b/src/cmd/compile/internal/ppc64/galign.go
@@ -5,12 +5,12 @@
package ppc64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/ppc64"
"cmd/internal/objabi"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &ppc64.Linkppc64
if objabi.GOARCH == "ppc64le" {
arch.LinkArch = &ppc64.Linkppc64le
diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go
index a5a772b49179e..c76962cfb8113 100644
--- a/src/cmd/compile/internal/ppc64/ggen.go
+++ b/src/cmd/compile/internal/ppc64/ggen.go
@@ -5,44 +5,47 @@
package ppc64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
)
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
if cnt == 0 {
return p
}
- if cnt < int64(4*gc.Widthptr) {
- for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
- p = pp.Appendpp(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, gc.Ctxt.FixedFrameSize()+off+i)
+ if cnt < int64(4*types.PtrSize) {
+ for i := int64(0); i < cnt; i += int64(types.PtrSize) {
+ p = pp.Append(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, base.Ctxt.FixedFrameSize()+off+i)
}
- } else if cnt <= int64(128*gc.Widthptr) {
- p = pp.Appendpp(p, ppc64.AADD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGRT1, 0)
+ } else if cnt <= int64(128*types.PtrSize) {
+ p = pp.Append(p, ppc64.AADD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGRT1, 0)
p.Reg = ppc64.REGSP
- p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+ p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
- p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr))
+ p.To.Sym = ir.Syms.Duffzero
+ p.To.Offset = 4 * (128 - cnt/int64(types.PtrSize))
} else {
- p = pp.Appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGTMP, 0)
- p = pp.Appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0)
+ p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGTMP, 0)
+ p = pp.Append(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0)
p.Reg = ppc64.REGSP
- p = pp.Appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0)
- p = pp.Appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
+ p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0)
+ p = pp.Append(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
p.Reg = ppc64.REGRT1
- p = pp.Appendpp(p, ppc64.AMOVDU, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGRT1, int64(gc.Widthptr))
+ p = pp.Append(p, ppc64.AMOVDU, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGRT1, int64(types.PtrSize))
p1 := p
- p = pp.Appendpp(p, ppc64.ACMP, obj.TYPE_REG, ppc64.REGRT1, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
- p = pp.Appendpp(p, ppc64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
- gc.Patch(p, p1)
+ p = pp.Append(p, ppc64.ACMP, obj.TYPE_REG, ppc64.REGRT1, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
+ p = pp.Append(p, ppc64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+ p.To.SetTarget(p1)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
p := pp.Prog(ppc64.AOR)
p.From.Type = obj.TYPE_REG
p.From.Reg = ppc64.REG_R0
@@ -51,7 +54,7 @@ func ginsnop(pp *gc.Progs) *obj.Prog {
return p
}
-func ginsnopdefer(pp *gc.Progs) *obj.Prog {
+func ginsnopdefer(pp *objw.Progs) *obj.Prog {
// On PPC64 two nops are required in the defer case.
//
// (see gc/cgen.go, gc/plive.go -- copy of comment below)
@@ -66,7 +69,7 @@ func ginsnopdefer(pp *gc.Progs) *obj.Prog {
// on ppc64 in both shared and non-shared modes.
ginsnop(pp)
- if gc.Ctxt.Flag_shared {
+ if base.Ctxt.Flag_shared {
p := pp.Prog(ppc64.AMOVD)
p.From.Type = obj.TYPE_MEM
p.From.Offset = 24
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index 3e20c44a4c72b..c85e110ed3584 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -5,9 +5,11 @@
package ppc64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
@@ -17,7 +19,7 @@ import (
)
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
-func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
// flive := b.FlagsLiveAtEnd
// if b.Control != nil && b.Control.Type.IsFlags() {
// flive = true
@@ -99,7 +101,7 @@ func storeByType(t *types.Type) obj.As {
panic("bad store type")
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy:
t := v.Type
@@ -208,7 +210,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// BNE retry
p3 := s.Prog(ppc64.ABNE)
p3.To.Type = obj.TYPE_BRANCH
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
case ssa.OpPPC64LoweredAtomicAdd32,
ssa.OpPPC64LoweredAtomicAdd64:
@@ -252,7 +254,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// BNE retry
p4 := s.Prog(ppc64.ABNE)
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p)
+ p4.To.SetTarget(p)
// Ensure a 32 bit result
if v.Op == ssa.OpPPC64LoweredAtomicAdd32 {
@@ -298,7 +300,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// BNE retry
p2 := s.Prog(ppc64.ABNE)
p2.To.Type = obj.TYPE_BRANCH
- gc.Patch(p2, p)
+ p2.To.SetTarget(p)
// ISYNC
pisync := s.Prog(ppc64.AISYNC)
pisync.To.Type = obj.TYPE_NONE
@@ -346,7 +348,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// ISYNC
pisync := s.Prog(ppc64.AISYNC)
pisync.To.Type = obj.TYPE_NONE
- gc.Patch(p2, pisync)
+ p2.To.SetTarget(pisync)
case ssa.OpPPC64LoweredAtomicStore8,
ssa.OpPPC64LoweredAtomicStore32,
@@ -437,7 +439,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// BNE retry
p4 := s.Prog(ppc64.ABNE)
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p)
+ p4.To.SetTarget(p)
// LWSYNC - Assuming shared data not write-through-required nor
// caching-inhibited. See Appendix B.2.1.1 in the ISA 2.07b.
// If the operation is a CAS-Release, then synchronization is not necessary.
@@ -460,20 +462,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p7.From.Offset = 0
p7.To.Type = obj.TYPE_REG
p7.To.Reg = out
- gc.Patch(p2, p7)
+ p2.To.SetTarget(p7)
// done (label)
p8 := s.Prog(obj.ANOP)
- gc.Patch(p6, p8)
+ p6.To.SetTarget(p8)
case ssa.OpPPC64LoweredGetClosurePtr:
// Closure pointer is R11 (already)
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpPPC64LoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(ppc64.AMOVD)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -489,7 +491,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpLoadReg:
loadOp := loadByType(v.Type)
p := s.Prog(loadOp)
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -498,7 +500,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeOp)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpPPC64DIVD:
// For now,
@@ -537,10 +539,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = r
p.From.Type = obj.TYPE_REG
p.From.Reg = r0
- gc.Patch(pbahead, p)
+ pbahead.To.SetTarget(p)
p = s.Prog(obj.ANOP)
- gc.Patch(pbover, p)
+ pbover.To.SetTarget(p)
case ssa.OpPPC64DIVW:
// word-width version of above
@@ -572,10 +574,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = r
p.From.Type = obj.TYPE_REG
p.From.Reg = r0
- gc.Patch(pbahead, p)
+ pbahead.To.SetTarget(p)
p = s.Prog(obj.ANOP)
- gc.Patch(pbover, p)
+ pbover.To.SetTarget(p)
case ssa.OpPPC64CLRLSLWI:
r := v.Reg()
@@ -750,13 +752,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Reg()
}
- case *obj.LSym, *gc.Node:
+ case *obj.LSym, ir.Node:
p := s.Prog(ppc64.AMOVD)
p.From.Type = obj.TYPE_ADDR
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
}
@@ -817,7 +819,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(ppc64.AMOVD)
p.From.Type = obj.TYPE_ADDR
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
// Load go.string using 0 offset
@@ -835,7 +837,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -869,7 +871,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = ppc64.REGZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpPPC64MOVDstore, ssa.OpPPC64MOVWstore, ssa.OpPPC64MOVHstore, ssa.OpPPC64MOVBstore, ssa.OpPPC64FMOVDstore, ssa.OpPPC64FMOVSstore:
p := s.Prog(v.Op.Asm())
@@ -877,7 +879,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpPPC64MOVDstoreidx, ssa.OpPPC64MOVWstoreidx, ssa.OpPPC64MOVHstoreidx, ssa.OpPPC64MOVBstoreidx,
ssa.OpPPC64FMOVDstoreidx, ssa.OpPPC64FMOVSstoreidx, ssa.OpPPC64MOVDBRstoreidx, ssa.OpPPC64MOVWBRstoreidx,
@@ -1026,7 +1028,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = ppc64.BO_BCTR
p.Reg = ppc64.REG_R0
p.To.Type = obj.TYPE_BRANCH
- gc.Patch(p, top)
+ p.To.SetTarget(top)
}
// When ctr == 1 the loop was not generated but
// there are at least 64 bytes to clear, so add
@@ -1226,7 +1228,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = ppc64.BO_BCTR
p.Reg = ppc64.REG_R0
p.To.Type = obj.TYPE_BRANCH
- gc.Patch(p, top)
+ p.To.SetTarget(top)
}
// when ctr == 1 the loop was not generated but
@@ -1405,7 +1407,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = ppc64.BO_BCTR
p.Reg = ppc64.REG_R0
p.To.Type = obj.TYPE_BRANCH
- gc.Patch(p, top)
+ p.To.SetTarget(top)
// srcReg and dstReg were incremented in the loop, so
// later instructions start with offset 0.
@@ -1652,7 +1654,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = ppc64.BO_BCTR
p.Reg = ppc64.REG_R0
p.To.Type = obj.TYPE_BRANCH
- gc.Patch(p, top)
+ p.To.SetTarget(top)
// srcReg and dstReg were incremented in the loop, so
// later instructions start with offset 0.
@@ -1784,7 +1786,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// Insert a hint this is not a subroutine return.
pp.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: 1})
- if gc.Ctxt.Flag_shared {
+ if base.Ctxt.Flag_shared {
// When compiling Go into PIC, the function we just
// called via pointer might have been implemented in
// a separate module and so overwritten the TOC
@@ -1807,7 +1809,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpPPC64LoweredNilCheck:
@@ -1838,22 +1840,22 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// NOP (so the BNE has somewhere to land)
nop := s.Prog(obj.ANOP)
- gc.Patch(p2, nop)
+ p2.To.SetTarget(nop)
} else {
// Issue a load which will fault if arg is nil.
p := s.Prog(ppc64.AMOVBZ)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = ppc64.REGTMP
}
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
// These should be resolved by rules and not make it here.
@@ -1891,7 +1893,7 @@ var blockJump = [...]struct {
ssa.BlockPPC64FGT: {ppc64.ABGT, ppc64.ABLE, false, false},
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockDefer:
// defer returns in R3:
@@ -1905,18 +1907,18 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p = s.Prog(ppc64.ABNE)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
diff --git a/src/cmd/compile/internal/reflectdata/alg.go b/src/cmd/compile/internal/reflectdata/alg.go
new file mode 100644
index 0000000000000..fcd824f164837
--- /dev/null
+++ b/src/cmd/compile/internal/reflectdata/alg.go
@@ -0,0 +1,788 @@
+// 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.
+
+package reflectdata
+
+import (
+ "fmt"
+ "sort"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+// isRegularMemory reports whether t can be compared/hashed as regular memory.
+func isRegularMemory(t *types.Type) bool {
+ a, _ := types.AlgType(t)
+ return a == types.AMEM
+}
+
+// eqCanPanic reports whether == on type t could panic (has an interface somewhere).
+// t must be comparable.
+func eqCanPanic(t *types.Type) bool {
+ switch t.Kind() {
+ default:
+ return false
+ case types.TINTER:
+ return true
+ case types.TARRAY:
+ return eqCanPanic(t.Elem())
+ case types.TSTRUCT:
+ for _, f := range t.FieldSlice() {
+ if !f.Sym.IsBlank() && eqCanPanic(f.Type) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+// 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 {
+ switch t.Width {
+ case 0:
+ return types.AMEM0
+ case 1:
+ return types.AMEM8
+ case 2:
+ return types.AMEM16
+ case 4:
+ return types.AMEM32
+ case 8:
+ return types.AMEM64
+ case 16:
+ return types.AMEM128
+ }
+ }
+
+ return a
+}
+
+// genhash returns a symbol which is the closure used to compute
+// the hash of a value of type t.
+// Note: the generated function must match runtime.typehash exactly.
+func genhash(t *types.Type) *obj.LSym {
+ switch AlgType(t) {
+ default:
+ // genhash is only called for types that have equality
+ base.Fatalf("genhash %v", t)
+ case types.AMEM0:
+ return sysClosure("memhash0")
+ case types.AMEM8:
+ return sysClosure("memhash8")
+ case types.AMEM16:
+ return sysClosure("memhash16")
+ case types.AMEM32:
+ return sysClosure("memhash32")
+ case types.AMEM64:
+ return sysClosure("memhash64")
+ case types.AMEM128:
+ return sysClosure("memhash128")
+ case types.ASTRING:
+ return sysClosure("strhash")
+ case types.AINTER:
+ return sysClosure("interhash")
+ case types.ANILINTER:
+ return sysClosure("nilinterhash")
+ case types.AFLOAT32:
+ return sysClosure("f32hash")
+ case types.AFLOAT64:
+ return sysClosure("f64hash")
+ case types.ACPLX64:
+ return sysClosure("c64hash")
+ case types.ACPLX128:
+ return sysClosure("c128hash")
+ case types.AMEM:
+ // For other sizes of plain memory, we build a closure
+ // that calls memhash_varlen. The size of the memory is
+ // encoded in the first slot of the closure.
+ closure := TypeLinksymLookup(fmt.Sprintf(".hashfunc%d", t.Width))
+ if len(closure.P) > 0 { // already generated
+ return closure
+ }
+ if memhashvarlen == nil {
+ memhashvarlen = typecheck.LookupRuntimeFunc("memhash_varlen")
+ }
+ ot := 0
+ ot = objw.SymPtr(closure, ot, memhashvarlen, 0)
+ ot = objw.Uintptr(closure, ot, uint64(t.Width)) // size encoded in closure
+ objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
+ return closure
+ case types.ASPECIAL:
+ break
+ }
+
+ closure := TypeLinksymPrefix(".hashfunc", t)
+ if len(closure.P) > 0 { // already generated
+ return closure
+ }
+
+ // Generate hash functions for subtypes.
+ // There are cases where we might not use these hashes,
+ // but in that case they will get dead-code eliminated.
+ // (And the closure generated by genhash will also get
+ // dead-code eliminated, as we call the subtype hashers
+ // directly.)
+ switch t.Kind() {
+ case types.TARRAY:
+ genhash(t.Elem())
+ case types.TSTRUCT:
+ for _, f := range t.FieldSlice() {
+ genhash(f.Type)
+ }
+ }
+
+ sym := TypeSymPrefix(".hash", t)
+ if base.Flag.LowerR != 0 {
+ fmt.Printf("genhash %v %v %v\n", closure, sym, t)
+ }
+
+ base.Pos = base.AutogeneratedPos // less confusing than end of input
+ typecheck.DeclContext = ir.PEXTERN
+
+ // func sym(p *T, h uintptr) uintptr
+ args := []*ir.Field{
+ ir.NewField(base.Pos, typecheck.Lookup("p"), nil, types.NewPtr(t)),
+ ir.NewField(base.Pos, typecheck.Lookup("h"), nil, types.Types[types.TUINTPTR]),
+ }
+ results := []*ir.Field{ir.NewField(base.Pos, nil, nil, types.Types[types.TUINTPTR])}
+ tfn := ir.NewFuncType(base.Pos, nil, args, results)
+
+ fn := typecheck.DeclFunc(sym, tfn)
+ np := ir.AsNode(tfn.Type().Params().Field(0).Nname)
+ nh := ir.AsNode(tfn.Type().Params().Field(1).Nname)
+
+ switch t.Kind() {
+ case types.TARRAY:
+ // An array of pure memory would be handled by the
+ // standard algorithm, so the element type must not be
+ // pure memory.
+ hashel := hashfor(t.Elem())
+
+ // for i := 0; i < nelem; i++
+ ni := typecheck.Temp(types.Types[types.TINT])
+ init := ir.NewAssignStmt(base.Pos, ni, ir.NewInt(0))
+ cond := ir.NewBinaryExpr(base.Pos, ir.OLT, ni, ir.NewInt(t.NumElem()))
+ post := ir.NewAssignStmt(base.Pos, ni, ir.NewBinaryExpr(base.Pos, ir.OADD, ni, ir.NewInt(1)))
+ loop := ir.NewForStmt(base.Pos, nil, cond, post, nil)
+ loop.PtrInit().Append(init)
+
+ // h = hashel(&p[i], h)
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
+
+ nx := ir.NewIndexExpr(base.Pos, np, ni)
+ nx.SetBounded(true)
+ na := typecheck.NodAddr(nx)
+ call.Args.Append(na)
+ call.Args.Append(nh)
+ loop.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))
+
+ fn.Body.Append(loop)
+
+ case types.TSTRUCT:
+ // Walk the struct using memhash for runs of AMEM
+ // and calling specific hash functions for the others.
+ for i, fields := 0, t.FieldSlice(); i < len(fields); {
+ f := fields[i]
+
+ // Skip blank fields.
+ if f.Sym.IsBlank() {
+ i++
+ continue
+ }
+
+ // Hash non-memory fields with appropriate hash function.
+ if !isRegularMemory(f.Type) {
+ hashel := hashfor(f.Type)
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
+ nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages?
+ na := typecheck.NodAddr(nx)
+ call.Args.Append(na)
+ call.Args.Append(nh)
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))
+ i++
+ continue
+ }
+
+ // Otherwise, hash a maximal length run of raw memory.
+ size, next := memrun(t, i)
+
+ // h = hashel(&p.first, size, h)
+ hashel := hashmem(f.Type)
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
+ nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages?
+ na := typecheck.NodAddr(nx)
+ call.Args.Append(na)
+ call.Args.Append(nh)
+ call.Args.Append(ir.NewInt(size))
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))
+
+ i = next
+ }
+ }
+
+ r := ir.NewReturnStmt(base.Pos, nil)
+ r.Results.Append(nh)
+ fn.Body.Append(r)
+
+ if base.Flag.LowerR != 0 {
+ ir.DumpList("genhash body", fn.Body)
+ }
+
+ typecheck.FinishFuncBody()
+
+ fn.SetDupok(true)
+ typecheck.Func(fn)
+
+ ir.CurFunc = fn
+ typecheck.Stmts(fn.Body)
+ ir.CurFunc = nil
+
+ if base.Debug.DclStack != 0 {
+ types.CheckDclstack()
+ }
+
+ fn.SetNilCheckDisabled(true)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+ // Build closure. It doesn't close over any variables, so
+ // it contains just the function pointer.
+ objw.SymPtr(closure, 0, fn.Linksym(), 0)
+ objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
+
+ return closure
+}
+
+func hashfor(t *types.Type) ir.Node {
+ var sym *types.Sym
+
+ switch a, _ := types.AlgType(t); a {
+ case types.AMEM:
+ base.Fatalf("hashfor with AMEM type")
+ case types.AINTER:
+ sym = ir.Pkgs.Runtime.Lookup("interhash")
+ case types.ANILINTER:
+ sym = ir.Pkgs.Runtime.Lookup("nilinterhash")
+ case types.ASTRING:
+ sym = ir.Pkgs.Runtime.Lookup("strhash")
+ case types.AFLOAT32:
+ sym = ir.Pkgs.Runtime.Lookup("f32hash")
+ case types.AFLOAT64:
+ sym = ir.Pkgs.Runtime.Lookup("f64hash")
+ case types.ACPLX64:
+ sym = ir.Pkgs.Runtime.Lookup("c64hash")
+ case types.ACPLX128:
+ sym = ir.Pkgs.Runtime.Lookup("c128hash")
+ default:
+ // Note: the caller of hashfor ensured that this symbol
+ // exists and has a body by calling genhash for t.
+ sym = TypeSymPrefix(".hash", t)
+ }
+
+ n := typecheck.NewName(sym)
+ ir.MarkFunc(n)
+ n.SetType(types.NewSignature(types.NoPkg, nil, []*types.Field{
+ types.NewField(base.Pos, nil, types.NewPtr(t)),
+ types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
+ }, []*types.Field{
+ types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
+ }))
+ return n
+}
+
+// sysClosure returns a closure which will call the
+// given runtime function (with no closed-over variables).
+func sysClosure(name string) *obj.LSym {
+ s := typecheck.LookupRuntimeVar(name + "·f")
+ if len(s.P) == 0 {
+ f := typecheck.LookupRuntimeFunc(name)
+ objw.SymPtr(s, 0, f, 0)
+ objw.Global(s, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
+ }
+ return s
+}
+
+// geneq returns a symbol which is the closure used to compute
+// equality for two objects of type t.
+func geneq(t *types.Type) *obj.LSym {
+ switch AlgType(t) {
+ case types.ANOEQ:
+ // The runtime will panic if it tries to compare
+ // a type with a nil equality function.
+ return nil
+ case types.AMEM0:
+ return sysClosure("memequal0")
+ case types.AMEM8:
+ return sysClosure("memequal8")
+ case types.AMEM16:
+ return sysClosure("memequal16")
+ case types.AMEM32:
+ return sysClosure("memequal32")
+ case types.AMEM64:
+ return sysClosure("memequal64")
+ case types.AMEM128:
+ return sysClosure("memequal128")
+ case types.ASTRING:
+ return sysClosure("strequal")
+ case types.AINTER:
+ return sysClosure("interequal")
+ case types.ANILINTER:
+ return sysClosure("nilinterequal")
+ case types.AFLOAT32:
+ return sysClosure("f32equal")
+ case types.AFLOAT64:
+ return sysClosure("f64equal")
+ case types.ACPLX64:
+ return sysClosure("c64equal")
+ case types.ACPLX128:
+ return sysClosure("c128equal")
+ case types.AMEM:
+ // make equality closure. The size of the type
+ // is encoded in the closure.
+ closure := TypeLinksymLookup(fmt.Sprintf(".eqfunc%d", t.Width))
+ if len(closure.P) != 0 {
+ return closure
+ }
+ if memequalvarlen == nil {
+ memequalvarlen = typecheck.LookupRuntimeVar("memequal_varlen") // asm func
+ }
+ ot := 0
+ ot = objw.SymPtr(closure, ot, memequalvarlen, 0)
+ ot = objw.Uintptr(closure, ot, uint64(t.Width))
+ objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
+ return closure
+ case types.ASPECIAL:
+ break
+ }
+
+ closure := TypeLinksymPrefix(".eqfunc", t)
+ if len(closure.P) > 0 { // already generated
+ return closure
+ }
+ sym := TypeSymPrefix(".eq", t)
+ if base.Flag.LowerR != 0 {
+ fmt.Printf("geneq %v\n", t)
+ }
+
+ // Autogenerate code for equality of structs and arrays.
+
+ base.Pos = base.AutogeneratedPos // less confusing than end of input
+ typecheck.DeclContext = ir.PEXTERN
+
+ // func sym(p, q *T) bool
+ tfn := ir.NewFuncType(base.Pos, nil,
+ []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("p"), nil, types.NewPtr(t)), ir.NewField(base.Pos, typecheck.Lookup("q"), nil, types.NewPtr(t))},
+ []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("r"), nil, types.Types[types.TBOOL])})
+
+ fn := typecheck.DeclFunc(sym, tfn)
+ np := ir.AsNode(tfn.Type().Params().Field(0).Nname)
+ nq := ir.AsNode(tfn.Type().Params().Field(1).Nname)
+ nr := ir.AsNode(tfn.Type().Results().Field(0).Nname)
+
+ // Label to jump to if an equality test fails.
+ neq := typecheck.AutoLabel(".neq")
+
+ // We reach here only for types that have equality but
+ // cannot be handled by the standard algorithms,
+ // so t must be either an array or a struct.
+ switch t.Kind() {
+ default:
+ base.Fatalf("geneq %v", t)
+
+ case types.TARRAY:
+ nelem := t.NumElem()
+
+ // checkAll generates code to check the equality of all array elements.
+ // If unroll is greater than nelem, checkAll generates:
+ //
+ // if eq(p[0], q[0]) && eq(p[1], q[1]) && ... {
+ // } else {
+ // return
+ // }
+ //
+ // And so on.
+ //
+ // Otherwise it generates:
+ //
+ // for i := 0; i < nelem; i++ {
+ // if eq(p[i], q[i]) {
+ // } else {
+ // goto neq
+ // }
+ // }
+ //
+ // TODO(josharian): consider doing some loop unrolling
+ // for larger nelem as well, processing a few elements at a time in a loop.
+ checkAll := func(unroll int64, last bool, eq func(pi, qi ir.Node) ir.Node) {
+ // checkIdx generates a node to check for equality at index i.
+ checkIdx := func(i ir.Node) ir.Node {
+ // pi := p[i]
+ pi := ir.NewIndexExpr(base.Pos, np, i)
+ pi.SetBounded(true)
+ pi.SetType(t.Elem())
+ // qi := q[i]
+ qi := ir.NewIndexExpr(base.Pos, nq, i)
+ qi.SetBounded(true)
+ qi.SetType(t.Elem())
+ return eq(pi, qi)
+ }
+
+ if nelem <= unroll {
+ if last {
+ // Do last comparison in a different manner.
+ nelem--
+ }
+ // Generate a series of checks.
+ for i := int64(0); i < nelem; i++ {
+ // if check {} else { goto neq }
+ nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(i)), nil, nil)
+ nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
+ fn.Body.Append(nif)
+ }
+ if last {
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem))))
+ }
+ } else {
+ // Generate a for loop.
+ // for i := 0; i < nelem; i++
+ i := typecheck.Temp(types.Types[types.TINT])
+ init := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0))
+ cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(nelem))
+ post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
+ loop := ir.NewForStmt(base.Pos, nil, cond, post, nil)
+ loop.PtrInit().Append(init)
+ // if eq(pi, qi) {} else { goto neq }
+ nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil)
+ nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
+ loop.Body.Append(nif)
+ fn.Body.Append(loop)
+ if last {
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
+ }
+ }
+ }
+
+ switch t.Elem().Kind() {
+ case types.TSTRING:
+ // Do two loops. First, check that all the lengths match (cheap).
+ // Second, check that all the contents match (expensive).
+ // TODO: when the array size is small, unroll the length match checks.
+ checkAll(3, false, func(pi, qi ir.Node) ir.Node {
+ // Compare lengths.
+ eqlen, _ := EqString(pi, qi)
+ return eqlen
+ })
+ checkAll(1, true, func(pi, qi ir.Node) ir.Node {
+ // Compare contents.
+ _, eqmem := EqString(pi, qi)
+ return eqmem
+ })
+ case types.TFLOAT32, types.TFLOAT64:
+ checkAll(2, true, func(pi, qi ir.Node) ir.Node {
+ // p[i] == q[i]
+ return ir.NewBinaryExpr(base.Pos, ir.OEQ, pi, qi)
+ })
+ // TODO: pick apart structs, do them piecemeal too
+ default:
+ checkAll(1, true, func(pi, qi ir.Node) ir.Node {
+ // p[i] == q[i]
+ return ir.NewBinaryExpr(base.Pos, ir.OEQ, pi, qi)
+ })
+ }
+
+ case types.TSTRUCT:
+ // Build a list of conditions to satisfy.
+ // The conditions are a list-of-lists. Conditions are reorderable
+ // within each inner list. The outer lists must be evaluated in order.
+ var conds [][]ir.Node
+ conds = append(conds, []ir.Node{})
+ and := func(n ir.Node) {
+ i := len(conds) - 1
+ conds[i] = append(conds[i], n)
+ }
+
+ // Walk the struct using memequal for runs of AMEM
+ // and calling specific equality tests for the others.
+ for i, fields := 0, t.FieldSlice(); i < len(fields); {
+ f := fields[i]
+
+ // Skip blank-named fields.
+ if f.Sym.IsBlank() {
+ i++
+ continue
+ }
+
+ // Compare non-memory fields with field equality.
+ if !isRegularMemory(f.Type) {
+ if eqCanPanic(f.Type) {
+ // Enforce ordering by starting a new set of reorderable conditions.
+ conds = append(conds, []ir.Node{})
+ }
+ p := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym)
+ q := ir.NewSelectorExpr(base.Pos, ir.OXDOT, nq, f.Sym)
+ switch {
+ case f.Type.IsString():
+ eqlen, eqmem := EqString(p, q)
+ and(eqlen)
+ and(eqmem)
+ default:
+ and(ir.NewBinaryExpr(base.Pos, ir.OEQ, p, q))
+ }
+ if eqCanPanic(f.Type) {
+ // Also enforce ordering after something that can panic.
+ conds = append(conds, []ir.Node{})
+ }
+ i++
+ continue
+ }
+
+ // Find maximal length run of memory-only fields.
+ size, next := memrun(t, i)
+
+ // TODO(rsc): All the calls to newname are wrong for
+ // cross-package unexported fields.
+ if s := fields[i:next]; len(s) <= 2 {
+ // Two or fewer fields: use plain field equality.
+ for _, f := range s {
+ and(eqfield(np, nq, f.Sym))
+ }
+ } else {
+ // More than two fields: use memequal.
+ and(eqmem(np, nq, f.Sym, size))
+ }
+ i = next
+ }
+
+ // Sort conditions to put runtime calls last.
+ // Preserve the rest of the ordering.
+ var flatConds []ir.Node
+ for _, c := range conds {
+ isCall := func(n ir.Node) bool {
+ return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC
+ }
+ sort.SliceStable(c, func(i, j int) bool {
+ return !isCall(c[i]) && isCall(c[j])
+ })
+ flatConds = append(flatConds, c...)
+ }
+
+ if len(flatConds) == 0 {
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
+ } else {
+ for _, c := range flatConds[:len(flatConds)-1] {
+ // if cond {} else { goto neq }
+ n := ir.NewIfStmt(base.Pos, c, nil, nil)
+ n.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
+ fn.Body.Append(n)
+ }
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, flatConds[len(flatConds)-1]))
+ }
+ }
+
+ // ret:
+ // return
+ ret := typecheck.AutoLabel(".ret")
+ fn.Body.Append(ir.NewLabelStmt(base.Pos, ret))
+ fn.Body.Append(ir.NewReturnStmt(base.Pos, nil))
+
+ // neq:
+ // r = false
+ // return (or goto ret)
+ fn.Body.Append(ir.NewLabelStmt(base.Pos, neq))
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(false)))
+ if eqCanPanic(t) || anyCall(fn) {
+ // Epilogue is large, so share it with the equal case.
+ fn.Body.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, ret))
+ } else {
+ // Epilogue is small, so don't bother sharing.
+ fn.Body.Append(ir.NewReturnStmt(base.Pos, nil))
+ }
+ // TODO(khr): the epilogue size detection condition above isn't perfect.
+ // We should really do a generic CL that shares epilogues across
+ // the board. See #24936.
+
+ if base.Flag.LowerR != 0 {
+ ir.DumpList("geneq body", fn.Body)
+ }
+
+ typecheck.FinishFuncBody()
+
+ fn.SetDupok(true)
+ typecheck.Func(fn)
+
+ ir.CurFunc = fn
+ typecheck.Stmts(fn.Body)
+ ir.CurFunc = nil
+
+ if base.Debug.DclStack != 0 {
+ types.CheckDclstack()
+ }
+
+ // Disable checknils while compiling this code.
+ // We are comparing a struct or an array,
+ // neither of which can be nil, and our comparisons
+ // are shallow.
+ fn.SetNilCheckDisabled(true)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+ // Generate a closure which points at the function we just generated.
+ objw.SymPtr(closure, 0, fn.Linksym(), 0)
+ objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
+ return closure
+}
+
+func anyCall(fn *ir.Func) bool {
+ return ir.Any(fn, func(n ir.Node) bool {
+ // TODO(rsc): No methods?
+ op := n.Op()
+ return op == ir.OCALL || op == ir.OCALLFUNC
+ })
+}
+
+// eqfield returns the node
+// p.field == q.field
+func eqfield(p ir.Node, q ir.Node, field *types.Sym) ir.Node {
+ nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)
+ ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)
+ ne := ir.NewBinaryExpr(base.Pos, ir.OEQ, nx, ny)
+ return ne
+}
+
+// EqString returns the nodes
+// len(s) == len(t)
+// and
+// memequal(s.ptr, t.ptr, len(s))
+// which can be used to construct string equality comparison.
+// eqlen must be evaluated before eqmem, and shortcircuiting is required.
+func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
+ s = typecheck.Conv(s, types.Types[types.TSTRING])
+ t = typecheck.Conv(t, types.Types[types.TSTRING])
+ sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
+ tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t)
+ slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR])
+ tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR])
+
+ fn := typecheck.LookupRuntime("memequal")
+ fn = typecheck.SubstArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8])
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{sptr, tptr, ir.Copy(slen)})
+ typecheck.Call(call)
+
+ cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen)
+ cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
+ cmp.SetType(types.Types[types.TBOOL])
+ return cmp, call
+}
+
+// EqInterface returns the nodes
+// s.tab == t.tab (or s.typ == t.typ, as appropriate)
+// and
+// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate)
+// which can be used to construct interface equality comparison.
+// eqtab must be evaluated before eqdata, and shortcircuiting is required.
+func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
+ if !types.Identical(s.Type(), t.Type()) {
+ base.Fatalf("EqInterface %v %v", s.Type(), t.Type())
+ }
+ // func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
+ // func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
+ var fn ir.Node
+ if s.Type().IsEmptyInterface() {
+ fn = typecheck.LookupRuntime("efaceeq")
+ } else {
+ fn = typecheck.LookupRuntime("ifaceeq")
+ }
+
+ stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s)
+ ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t)
+ sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s)
+ tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t)
+ sdata.SetType(types.Types[types.TUNSAFEPTR])
+ tdata.SetType(types.Types[types.TUNSAFEPTR])
+ sdata.SetTypecheck(1)
+ tdata.SetTypecheck(1)
+
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{stab, sdata, tdata})
+ typecheck.Call(call)
+
+ cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab)
+ cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
+ cmp.SetType(types.Types[types.TBOOL])
+ return cmp, call
+}
+
+// eqmem returns the node
+// memequal(&p.field, &q.field [, size])
+func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node {
+ nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)))
+ ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)))
+
+ fn, needsize := eqmemfunc(size, nx.Type().Elem())
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
+ call.Args.Append(nx)
+ call.Args.Append(ny)
+ if needsize {
+ call.Args.Append(ir.NewInt(size))
+ }
+
+ return call
+}
+
+func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) {
+ switch size {
+ default:
+ fn = typecheck.LookupRuntime("memequal")
+ needsize = true
+ case 1, 2, 4, 8, 16:
+ buf := fmt.Sprintf("memequal%d", int(size)*8)
+ fn = typecheck.LookupRuntime(buf)
+ }
+
+ fn = typecheck.SubstArgTypes(fn, t, t)
+ return fn, needsize
+}
+
+// memrun finds runs of struct fields for which memory-only algs are appropriate.
+// t is the parent struct type, and start is the field index at which to start the run.
+// size is the length in bytes of the memory included in the run.
+// next is the index just after the end of the memory run.
+func memrun(t *types.Type, start int) (size int64, next int) {
+ next = start
+ for {
+ next++
+ if next == t.NumFields() {
+ break
+ }
+ // Stop run after a padded field.
+ if types.IsPaddedField(t, next-1) {
+ break
+ }
+ // Also, stop before a blank or non-memory field.
+ if f := t.Field(next); f.Sym.IsBlank() || !isRegularMemory(f.Type) {
+ break
+ }
+ }
+ return t.Field(next-1).End() - t.Field(start).Offset, next
+}
+
+func hashmem(t *types.Type) ir.Node {
+ sym := ir.Pkgs.Runtime.Lookup("memhash")
+
+ n := typecheck.NewName(sym)
+ ir.MarkFunc(n)
+ n.SetType(types.NewSignature(types.NoPkg, nil, []*types.Field{
+ types.NewField(base.Pos, nil, types.NewPtr(t)),
+ types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
+ types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
+ }, []*types.Field{
+ types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
+ }))
+ return n
+}
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
similarity index 54%
rename from src/cmd/compile/internal/gc/reflect.go
rename to src/cmd/compile/internal/reflectdata/reflect.go
index 9401eba7a5021..3ff14c87f4fc7 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -2,19 +2,28 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package reflectdata
import (
- "cmd/compile/internal/types"
- "cmd/internal/gcprog"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
"fmt"
"os"
"sort"
"strings"
"sync"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/bitvec"
+ "cmd/compile/internal/escape"
+ "cmd/compile/internal/inline"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/typebits"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/gcprog"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
)
type itabEntry struct {
@@ -23,7 +32,7 @@ type itabEntry struct {
// symbols of each method in
// the itab, sorted by byte offset;
- // filled in by peekitabs
+ // filled in by CompileITabs
entries []*obj.LSym
}
@@ -32,6 +41,10 @@ type ptabEntry struct {
t *types.Type
}
+func CountTabs() (numPTabs, numITabs int) {
+ return len(ptabs), len(itabs)
+}
+
// runtime interface and reflection data structures
var (
signatmu sync.Mutex // protects signatset and signatslice
@@ -39,13 +52,13 @@ var (
signatslice []*types.Type
itabs []itabEntry
- ptabs []ptabEntry
+ ptabs []*ir.Name
)
-type Sig struct {
+type typeSig struct {
name *types.Sym
- isym *types.Sym
- tsym *types.Sym
+ isym *obj.LSym
+ tsym *obj.LSym
type_ *types.Type
mtype *types.Type
}
@@ -61,35 +74,32 @@ const (
MAXELEMSIZE = 128
)
-func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{})
-func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{})
-func commonSize() int { return 4*Widthptr + 8 + 8 } // Sizeof(runtime._type{})
+func structfieldSize() int { return 3 * types.PtrSize } // Sizeof(runtime.structfield{})
+func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{})
+func commonSize() int { return 4*types.PtrSize + 8 + 8 } // Sizeof(runtime._type{})
func uncommonSize(t *types.Type) int { // Sizeof(runtime.uncommontype{})
- if t.Sym == nil && len(methods(t)) == 0 {
+ if t.Sym() == nil && len(methods(t)) == 0 {
return 0
}
return 4 + 2 + 2 + 4 + 4
}
func makefield(name string, t *types.Type) *types.Field {
- f := types.NewField()
- f.Type = t
- f.Sym = (*types.Pkg)(nil).Lookup(name)
- return f
+ sym := (*types.Pkg)(nil).Lookup(name)
+ return types.NewField(src.NoXPos, sym, t)
}
-// bmap makes the map bucket type given the type of the map.
-func bmap(t *types.Type) *types.Type {
+// MapBucketType makes the map bucket type given the type of the map.
+func MapBucketType(t *types.Type) *types.Type {
if t.MapType().Bucket != nil {
return t.MapType().Bucket
}
- bucket := types.New(TSTRUCT)
keytype := t.Key()
elemtype := t.Elem()
- dowidth(keytype)
- dowidth(elemtype)
+ types.CalcSize(keytype)
+ types.CalcSize(elemtype)
if keytype.Width > MAXKEYSIZE {
keytype = types.NewPtr(keytype)
}
@@ -100,7 +110,7 @@ func bmap(t *types.Type) *types.Type {
field := make([]*types.Field, 0, 5)
// The first field is: uint8 topbits[BUCKETSIZE].
- arr := types.NewArray(types.Types[TUINT8], BUCKETSIZE)
+ arr := types.NewArray(types.Types[types.TUINT8], BUCKETSIZE)
field = append(field, makefield("topbits", arr))
arr = types.NewArray(keytype, BUCKETSIZE)
@@ -119,66 +129,66 @@ func bmap(t *types.Type) *types.Type {
// Arrange for the bucket to have no pointers by changing
// the type of the overflow field to uintptr in this case.
// See comment on hmap.overflow in runtime/map.go.
- otyp := types.NewPtr(bucket)
+ otyp := types.Types[types.TUNSAFEPTR]
if !elemtype.HasPointers() && !keytype.HasPointers() {
- otyp = types.Types[TUINTPTR]
+ otyp = types.Types[types.TUINTPTR]
}
overflow := makefield("overflow", otyp)
field = append(field, overflow)
// link up fields
+ bucket := types.NewStruct(types.NoPkg, field[:])
bucket.SetNoalg(true)
- bucket.SetFields(field[:])
- dowidth(bucket)
+ types.CalcSize(bucket)
// Check invariants that map code depends on.
- if !IsComparable(t.Key()) {
- Fatalf("unsupported map key type for %v", t)
+ if !types.IsComparable(t.Key()) {
+ base.Fatalf("unsupported map key type for %v", t)
}
if BUCKETSIZE < 8 {
- Fatalf("bucket size too small for proper alignment")
+ base.Fatalf("bucket size too small for proper alignment")
}
if keytype.Align > BUCKETSIZE {
- Fatalf("key align too big for %v", t)
+ base.Fatalf("key align too big for %v", t)
}
if elemtype.Align > BUCKETSIZE {
- Fatalf("elem align too big for %v", t)
+ base.Fatalf("elem align too big for %v", t)
}
if keytype.Width > MAXKEYSIZE {
- Fatalf("key size to large for %v", t)
+ base.Fatalf("key size to large for %v", t)
}
if elemtype.Width > MAXELEMSIZE {
- Fatalf("elem size to large for %v", t)
+ base.Fatalf("elem size to large for %v", t)
}
if t.Key().Width > MAXKEYSIZE && !keytype.IsPtr() {
- Fatalf("key indirect incorrect for %v", t)
+ base.Fatalf("key indirect incorrect for %v", t)
}
if t.Elem().Width > MAXELEMSIZE && !elemtype.IsPtr() {
- Fatalf("elem indirect incorrect for %v", t)
+ base.Fatalf("elem indirect incorrect for %v", t)
}
if keytype.Width%int64(keytype.Align) != 0 {
- Fatalf("key size not a multiple of key align for %v", t)
+ base.Fatalf("key size not a multiple of key align for %v", t)
}
if elemtype.Width%int64(elemtype.Align) != 0 {
- Fatalf("elem size not a multiple of elem align for %v", t)
+ base.Fatalf("elem size not a multiple of elem align for %v", t)
}
if bucket.Align%keytype.Align != 0 {
- Fatalf("bucket align not multiple of key align %v", t)
+ base.Fatalf("bucket align not multiple of key align %v", t)
}
if bucket.Align%elemtype.Align != 0 {
- Fatalf("bucket align not multiple of elem align %v", t)
+ base.Fatalf("bucket align not multiple of elem align %v", t)
}
if keys.Offset%int64(keytype.Align) != 0 {
- Fatalf("bad alignment of keys in bmap for %v", t)
+ base.Fatalf("bad alignment of keys in bmap for %v", t)
}
if elems.Offset%int64(elemtype.Align) != 0 {
- Fatalf("bad alignment of elems in bmap for %v", t)
+ base.Fatalf("bad alignment of elems in bmap for %v", t)
}
// Double-check that overflow field is final memory in struct,
// with no padding at end.
- if overflow.Offset != bucket.Width-int64(Widthptr) {
- Fatalf("bad offset of overflow in bmap for %v", t)
+ if overflow.Offset != bucket.Width-int64(types.PtrSize) {
+ base.Fatalf("bad offset of overflow in bmap for %v", t)
}
t.MapType().Bucket = bucket
@@ -187,14 +197,14 @@ func bmap(t *types.Type) *types.Type {
return bucket
}
-// hmap builds a type representing a Hmap structure for the given map type.
+// MapType builds a type representing a Hmap structure for the given map type.
// Make sure this stays in sync with runtime/map.go.
-func hmap(t *types.Type) *types.Type {
+func MapType(t *types.Type) *types.Type {
if t.MapType().Hmap != nil {
return t.MapType().Hmap
}
- bmap := bmap(t)
+ bmap := MapBucketType(t)
// build a struct:
// type hmap struct {
@@ -210,26 +220,25 @@ func hmap(t *types.Type) *types.Type {
// }
// must match runtime/map.go:hmap.
fields := []*types.Field{
- makefield("count", types.Types[TINT]),
- makefield("flags", types.Types[TUINT8]),
- makefield("B", types.Types[TUINT8]),
- makefield("noverflow", types.Types[TUINT16]),
- makefield("hash0", types.Types[TUINT32]), // Used in walk.go for OMAKEMAP.
- makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for OMAKEMAP.
+ makefield("count", types.Types[types.TINT]),
+ makefield("flags", types.Types[types.TUINT8]),
+ makefield("B", types.Types[types.TUINT8]),
+ makefield("noverflow", types.Types[types.TUINT16]),
+ makefield("hash0", types.Types[types.TUINT32]), // Used in walk.go for OMAKEMAP.
+ makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for OMAKEMAP.
makefield("oldbuckets", types.NewPtr(bmap)),
- makefield("nevacuate", types.Types[TUINTPTR]),
- makefield("extra", types.Types[TUNSAFEPTR]),
+ makefield("nevacuate", types.Types[types.TUINTPTR]),
+ makefield("extra", types.Types[types.TUNSAFEPTR]),
}
- hmap := types.New(TSTRUCT)
+ hmap := types.NewStruct(types.NoPkg, fields)
hmap.SetNoalg(true)
- hmap.SetFields(fields)
- dowidth(hmap)
+ types.CalcSize(hmap)
// The size of hmap should be 48 bytes on 64 bit
// and 28 bytes on 32 bit platforms.
- if size := int64(8 + 5*Widthptr); hmap.Width != size {
- Fatalf("hmap size not correct: got %d, want %d", hmap.Width, size)
+ if size := int64(8 + 5*types.PtrSize); hmap.Width != size {
+ base.Fatalf("hmap size not correct: got %d, want %d", hmap.Width, size)
}
t.MapType().Hmap = hmap
@@ -237,15 +246,15 @@ func hmap(t *types.Type) *types.Type {
return hmap
}
-// hiter builds a type representing an Hiter structure for the given map type.
+// MapIterType builds a type representing an Hiter structure for the given map type.
// Make sure this stays in sync with runtime/map.go.
-func hiter(t *types.Type) *types.Type {
+func MapIterType(t *types.Type) *types.Type {
if t.MapType().Hiter != nil {
return t.MapType().Hiter
}
- hmap := hmap(t)
- bmap := bmap(t)
+ hmap := MapType(t)
+ bmap := MapBucketType(t)
// build a struct:
// type hiter struct {
@@ -269,210 +278,110 @@ func hiter(t *types.Type) *types.Type {
fields := []*types.Field{
makefield("key", types.NewPtr(t.Key())), // Used in range.go for TMAP.
makefield("elem", types.NewPtr(t.Elem())), // Used in range.go for TMAP.
- makefield("t", types.Types[TUNSAFEPTR]),
+ makefield("t", types.Types[types.TUNSAFEPTR]),
makefield("h", types.NewPtr(hmap)),
makefield("buckets", types.NewPtr(bmap)),
makefield("bptr", types.NewPtr(bmap)),
- makefield("overflow", types.Types[TUNSAFEPTR]),
- makefield("oldoverflow", types.Types[TUNSAFEPTR]),
- makefield("startBucket", types.Types[TUINTPTR]),
- makefield("offset", types.Types[TUINT8]),
- makefield("wrapped", types.Types[TBOOL]),
- makefield("B", types.Types[TUINT8]),
- makefield("i", types.Types[TUINT8]),
- makefield("bucket", types.Types[TUINTPTR]),
- makefield("checkBucket", types.Types[TUINTPTR]),
+ makefield("overflow", types.Types[types.TUNSAFEPTR]),
+ makefield("oldoverflow", types.Types[types.TUNSAFEPTR]),
+ makefield("startBucket", types.Types[types.TUINTPTR]),
+ makefield("offset", types.Types[types.TUINT8]),
+ makefield("wrapped", types.Types[types.TBOOL]),
+ makefield("B", types.Types[types.TUINT8]),
+ makefield("i", types.Types[types.TUINT8]),
+ makefield("bucket", types.Types[types.TUINTPTR]),
+ makefield("checkBucket", types.Types[types.TUINTPTR]),
}
// build iterator struct holding the above fields
- hiter := types.New(TSTRUCT)
+ hiter := types.NewStruct(types.NoPkg, fields)
hiter.SetNoalg(true)
- hiter.SetFields(fields)
- dowidth(hiter)
- if hiter.Width != int64(12*Widthptr) {
- Fatalf("hash_iter size not correct %d %d", hiter.Width, 12*Widthptr)
+ types.CalcSize(hiter)
+ if hiter.Width != int64(12*types.PtrSize) {
+ base.Fatalf("hash_iter size not correct %d %d", hiter.Width, 12*types.PtrSize)
}
t.MapType().Hiter = hiter
hiter.StructType().Map = t
return hiter
}
-// deferstruct makes a runtime._defer structure, with additional space for
-// stksize bytes of args.
-func deferstruct(stksize int64) *types.Type {
- makefield := func(name string, typ *types.Type) *types.Field {
- f := types.NewField()
- f.Type = typ
- // Unlike the global makefield function, this one needs to set Pkg
- // because these types might be compared (in SSA CSE sorting).
- // TODO: unify this makefield and the global one above.
- f.Sym = &types.Sym{Name: name, Pkg: localpkg}
- return f
- }
- argtype := types.NewArray(types.Types[TUINT8], stksize)
- argtype.Width = stksize
- argtype.Align = 1
- // These fields must match the ones in runtime/runtime2.go:_defer and
- // cmd/compile/internal/gc/ssa.go:(*state).call.
- fields := []*types.Field{
- makefield("siz", types.Types[TUINT32]),
- makefield("started", types.Types[TBOOL]),
- makefield("heap", types.Types[TBOOL]),
- makefield("openDefer", types.Types[TBOOL]),
- makefield("sp", types.Types[TUINTPTR]),
- makefield("pc", types.Types[TUINTPTR]),
- // Note: the types here don't really matter. Defer structures
- // are always scanned explicitly during stack copying and GC,
- // so we make them uintptr type even though they are real pointers.
- makefield("fn", types.Types[TUINTPTR]),
- makefield("_panic", types.Types[TUINTPTR]),
- makefield("link", types.Types[TUINTPTR]),
- makefield("framepc", types.Types[TUINTPTR]),
- makefield("varp", types.Types[TUINTPTR]),
- makefield("fd", types.Types[TUINTPTR]),
- makefield("args", argtype),
- }
-
- // build struct holding the above fields
- s := types.New(TSTRUCT)
- s.SetNoalg(true)
- s.SetFields(fields)
- s.Width = widstruct(s, s, 0, 1)
- s.Align = uint8(Widthptr)
- return s
-}
-
-// f is method type, with receiver.
-// return function type, receiver as first argument (or not).
-func methodfunc(f *types.Type, receiver *types.Type) *types.Type {
- inLen := f.Params().Fields().Len()
- if receiver != nil {
- inLen++
- }
- in := make([]*Node, 0, inLen)
-
- if receiver != nil {
- d := anonfield(receiver)
- in = append(in, d)
- }
-
- for _, t := range f.Params().Fields().Slice() {
- d := anonfield(t.Type)
- d.SetIsDDD(t.IsDDD())
- in = append(in, d)
- }
-
- outLen := f.Results().Fields().Len()
- out := make([]*Node, 0, outLen)
- for _, t := range f.Results().Fields().Slice() {
- d := anonfield(t.Type)
- out = append(out, d)
- }
-
- t := functype(nil, in, out)
- if f.Nname() != nil {
- // Link to name of original method function.
- t.SetNname(f.Nname())
- }
-
- return t
-}
-
// methods returns the methods of the non-interface type t, sorted by name.
// Generates stub functions as needed.
-func methods(t *types.Type) []*Sig {
+func methods(t *types.Type) []*typeSig {
// method type
- mt := methtype(t)
+ mt := types.ReceiverBaseType(t)
if mt == nil {
return nil
}
- expandmeth(mt)
+ typecheck.CalcMethods(mt)
// type stored in interface word
it := t
- if !isdirectiface(it) {
+ if !types.IsDirectIface(it) {
it = types.NewPtr(t)
}
// make list of methods for t,
// generating code if necessary.
- var ms []*Sig
+ var ms []*typeSig
for _, f := range mt.AllMethods().Slice() {
+ if f.Sym == nil {
+ base.Fatalf("method with no sym on %v", mt)
+ }
if !f.IsMethod() {
- Fatalf("non-method on %v method %v %v\n", mt, f.Sym, f)
+ base.Fatalf("non-method on %v method %v %v", mt, f.Sym, f)
}
if f.Type.Recv() == nil {
- Fatalf("receiver with no type on %v method %v %v\n", mt, f.Sym, f)
+ base.Fatalf("receiver with no type on %v method %v %v", mt, f.Sym, f)
}
if f.Nointerface() {
continue
}
- method := f.Sym
- if method == nil {
- break
- }
-
// get receiver type for this particular method.
// if pointer receiver but non-pointer t and
// this is not an embedded pointer inside a struct,
// method does not apply.
- if !isMethodApplicable(t, f) {
+ if !types.IsMethodApplicable(t, f) {
continue
}
- sig := &Sig{
- name: method,
- isym: methodSym(it, method),
- tsym: methodSym(t, method),
- type_: methodfunc(f.Type, t),
- mtype: methodfunc(f.Type, nil),
+ sig := &typeSig{
+ name: f.Sym,
+ isym: methodWrapper(it, f),
+ tsym: methodWrapper(t, f),
+ type_: typecheck.NewMethodType(f.Type, t),
+ mtype: typecheck.NewMethodType(f.Type, nil),
}
ms = append(ms, sig)
-
- this := f.Type.Recv().Type
-
- if !sig.isym.Siggen() {
- sig.isym.SetSiggen(true)
- if !types.Identical(this, it) {
- genwrapper(it, f, sig.isym)
- }
- }
-
- if !sig.tsym.Siggen() {
- sig.tsym.SetSiggen(true)
- if !types.Identical(this, t) {
- genwrapper(t, f, sig.tsym)
- }
- }
}
return ms
}
// imethods returns the methods of the interface type t, sorted by name.
-func imethods(t *types.Type) []*Sig {
- var methods []*Sig
+func imethods(t *types.Type) []*typeSig {
+ var methods []*typeSig
for _, f := range t.Fields().Slice() {
- if f.Type.Etype != TFUNC || f.Sym == nil {
+ if f.Type.Kind() != types.TFUNC || f.Sym == nil {
continue
}
if f.Sym.IsBlank() {
- Fatalf("unexpected blank symbol in interface method set")
+ base.Fatalf("unexpected blank symbol in interface method set")
}
if n := len(methods); n > 0 {
last := methods[n-1]
if !last.name.Less(f.Sym) {
- Fatalf("sigcmp vs sortinter %v %v", last.name, f.Sym)
+ base.Fatalf("sigcmp vs sortinter %v %v", last.name, f.Sym)
}
}
- sig := &Sig{
+ sig := &typeSig{
name: f.Sym,
mtype: f.Type,
- type_: methodfunc(f.Type, nil),
+ type_: typecheck.NewMethodType(f.Type, nil),
}
methods = append(methods, sig)
@@ -480,11 +389,7 @@ func imethods(t *types.Type) []*Sig {
// IfaceType.Method is not in the reflect data.
// Generate the method body, so that compiled
// code can refer to it.
- isym := methodSym(t, f.Sym)
- if !isym.Siggen() {
- isym.SetSiggen(true)
- genwrapper(t, f, isym)
- }
+ methodWrapper(t, f)
}
return methods
@@ -496,79 +401,79 @@ func dimportpath(p *types.Pkg) {
}
// If we are compiling the runtime package, there are two runtime packages around
- // -- localpkg and Runtimepkg. We don't want to produce import path symbols for
+ // -- localpkg and Pkgs.Runtime. We don't want to produce import path symbols for
// both of them, so just produce one for localpkg.
- if myimportpath == "runtime" && p == Runtimepkg {
+ if base.Ctxt.Pkgpath == "runtime" && p == ir.Pkgs.Runtime {
return
}
str := p.Path
- if p == localpkg {
+ if p == types.LocalPkg {
// Note: myimportpath != "", or else dgopkgpath won't call dimportpath.
- str = myimportpath
+ str = base.Ctxt.Pkgpath
}
- s := Ctxt.Lookup("type..importpath." + p.Prefix + ".")
+ s := base.Ctxt.Lookup("type..importpath." + p.Prefix + ".")
ot := dnameData(s, 0, str, "", nil, false)
- ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA)
+ objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA)
s.Set(obj.AttrContentAddressable, true)
p.Pathsym = s
}
func dgopkgpath(s *obj.LSym, ot int, pkg *types.Pkg) int {
if pkg == nil {
- return duintptr(s, ot, 0)
+ return objw.Uintptr(s, ot, 0)
}
- if pkg == localpkg && myimportpath == "" {
+ if pkg == types.LocalPkg && base.Ctxt.Pkgpath == "" {
// If we don't know the full import path of the package being compiled
// (i.e. -p was not passed on the compiler command line), emit a reference to
// type..importpath.""., which the linker will rewrite using the correct import path.
// Every package that imports this one directly defines the symbol.
// See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
- ns := Ctxt.Lookup(`type..importpath."".`)
- return dsymptr(s, ot, ns, 0)
+ ns := base.Ctxt.Lookup(`type..importpath."".`)
+ return objw.SymPtr(s, ot, ns, 0)
}
dimportpath(pkg)
- return dsymptr(s, ot, pkg.Pathsym, 0)
+ return objw.SymPtr(s, ot, pkg.Pathsym, 0)
}
// dgopkgpathOff writes an offset relocation in s at offset ot to the pkg path symbol.
func dgopkgpathOff(s *obj.LSym, ot int, pkg *types.Pkg) int {
if pkg == nil {
- return duint32(s, ot, 0)
+ return objw.Uint32(s, ot, 0)
}
- if pkg == localpkg && myimportpath == "" {
+ if pkg == types.LocalPkg && base.Ctxt.Pkgpath == "" {
// If we don't know the full import path of the package being compiled
// (i.e. -p was not passed on the compiler command line), emit a reference to
// type..importpath.""., which the linker will rewrite using the correct import path.
// Every package that imports this one directly defines the symbol.
// See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
- ns := Ctxt.Lookup(`type..importpath."".`)
- return dsymptrOff(s, ot, ns)
+ ns := base.Ctxt.Lookup(`type..importpath."".`)
+ return objw.SymPtrOff(s, ot, ns)
}
dimportpath(pkg)
- return dsymptrOff(s, ot, pkg.Pathsym)
+ return objw.SymPtrOff(s, ot, pkg.Pathsym)
}
// dnameField dumps a reflect.name for a struct field.
func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int {
if !types.IsExported(ft.Sym.Name) && ft.Sym.Pkg != spkg {
- Fatalf("package mismatch for %v", ft.Sym)
+ base.Fatalf("package mismatch for %v", ft.Sym)
}
nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name))
- return dsymptr(lsym, ot, nsym, 0)
+ return objw.SymPtr(lsym, ot, nsym, 0)
}
// dnameData writes the contents of a reflect.name into s at offset ot.
func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int {
if len(name) > 1<<16-1 {
- Fatalf("name too long: %s", name)
+ base.Fatalf("name too long: %s", name)
}
if len(tag) > 1<<16-1 {
- Fatalf("tag too long: %s", tag)
+ base.Fatalf("tag too long: %s", tag)
}
// Encode name and tag. See reflect/type.go for details.
@@ -596,7 +501,7 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b
copy(tb[2:], tag)
}
- ot = int(s.WriteBytes(Ctxt, int64(ot), b))
+ ot = int(s.WriteBytes(base.Ctxt, int64(ot), b))
if pkg != nil {
ot = dgopkgpathOff(s, ot, pkg)
@@ -633,12 +538,12 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym {
sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount)
dnameCount++
}
- s := Ctxt.Lookup(sname)
+ s := base.Ctxt.Lookup(sname)
if len(s.P) > 0 {
return s
}
ot := dnameData(s, 0, name, tag, pkg, exported)
- ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA)
+ objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA)
s.Set(obj.AttrContentAddressable, true)
return s
}
@@ -648,16 +553,16 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym {
// backing array of the []method field is written (by dextratypeData).
func dextratype(lsym *obj.LSym, ot int, t *types.Type, dataAdd int) int {
m := methods(t)
- if t.Sym == nil && len(m) == 0 {
+ if t.Sym() == nil && len(m) == 0 {
return ot
}
- noff := int(Rnd(int64(ot), int64(Widthptr)))
+ noff := int(types.Rnd(int64(ot), int64(types.PtrSize)))
if noff != ot {
- Fatalf("unexpected alignment in dextratype for %v", t)
+ base.Fatalf("unexpected alignment in dextratype for %v", t)
}
for _, a := range m {
- dtypesym(a.type_)
+ writeType(a.type_)
}
ot = dgopkgpathOff(lsym, ot, typePkg(t))
@@ -665,31 +570,31 @@ func dextratype(lsym *obj.LSym, ot int, t *types.Type, dataAdd int) int {
dataAdd += uncommonSize(t)
mcount := len(m)
if mcount != int(uint16(mcount)) {
- Fatalf("too many methods on %v: %d", t, mcount)
+ base.Fatalf("too many methods on %v: %d", t, mcount)
}
xcount := sort.Search(mcount, func(i int) bool { return !types.IsExported(m[i].name.Name) })
if dataAdd != int(uint32(dataAdd)) {
- Fatalf("methods are too far away on %v: %d", t, dataAdd)
+ base.Fatalf("methods are too far away on %v: %d", t, dataAdd)
}
- ot = duint16(lsym, ot, uint16(mcount))
- ot = duint16(lsym, ot, uint16(xcount))
- ot = duint32(lsym, ot, uint32(dataAdd))
- ot = duint32(lsym, ot, 0)
+ ot = objw.Uint16(lsym, ot, uint16(mcount))
+ ot = objw.Uint16(lsym, ot, uint16(xcount))
+ ot = objw.Uint32(lsym, ot, uint32(dataAdd))
+ ot = objw.Uint32(lsym, ot, 0)
return ot
}
func typePkg(t *types.Type) *types.Pkg {
- tsym := t.Sym
+ tsym := t.Sym()
if tsym == nil {
- switch t.Etype {
- case TARRAY, TSLICE, TPTR, TCHAN:
+ switch t.Kind() {
+ case types.TARRAY, types.TSLICE, types.TPTR, types.TCHAN:
if t.Elem() != nil {
- tsym = t.Elem().Sym
+ tsym = t.Elem().Sym()
}
}
}
- if tsym != nil && t != types.Types[t.Etype] && t != types.Errortype {
+ if tsym != nil && t != types.Types[t.Kind()] && t != types.ErrorType {
return tsym.Pkg
}
return nil
@@ -707,16 +612,16 @@ func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int {
}
nsym := dname(a.name.Name, "", pkg, exported)
- ot = dsymptrOff(lsym, ot, nsym)
- ot = dmethodptrOff(lsym, ot, dtypesym(a.mtype))
- ot = dmethodptrOff(lsym, ot, a.isym.Linksym())
- ot = dmethodptrOff(lsym, ot, a.tsym.Linksym())
+ ot = objw.SymPtrOff(lsym, ot, nsym)
+ ot = dmethodptrOff(lsym, ot, writeType(a.mtype))
+ ot = dmethodptrOff(lsym, ot, a.isym)
+ ot = dmethodptrOff(lsym, ot, a.tsym)
}
return ot
}
func dmethodptrOff(s *obj.LSym, ot int, x *obj.LSym) int {
- duint32(s, ot, 0)
+ objw.Uint32(s, ot, 0)
r := obj.Addrel(s)
r.Off = int32(ot)
r.Siz = 4
@@ -726,81 +631,32 @@ func dmethodptrOff(s *obj.LSym, ot int, x *obj.LSym) int {
}
var kinds = []int{
- TINT: objabi.KindInt,
- TUINT: objabi.KindUint,
- TINT8: objabi.KindInt8,
- TUINT8: objabi.KindUint8,
- TINT16: objabi.KindInt16,
- TUINT16: objabi.KindUint16,
- TINT32: objabi.KindInt32,
- TUINT32: objabi.KindUint32,
- TINT64: objabi.KindInt64,
- TUINT64: objabi.KindUint64,
- TUINTPTR: objabi.KindUintptr,
- TFLOAT32: objabi.KindFloat32,
- TFLOAT64: objabi.KindFloat64,
- TBOOL: objabi.KindBool,
- TSTRING: objabi.KindString,
- TPTR: objabi.KindPtr,
- TSTRUCT: objabi.KindStruct,
- TINTER: objabi.KindInterface,
- TCHAN: objabi.KindChan,
- TMAP: objabi.KindMap,
- TARRAY: objabi.KindArray,
- TSLICE: objabi.KindSlice,
- TFUNC: objabi.KindFunc,
- TCOMPLEX64: objabi.KindComplex64,
- TCOMPLEX128: objabi.KindComplex128,
- TUNSAFEPTR: objabi.KindUnsafePointer,
-}
-
-// typeptrdata returns the length in bytes of the prefix of t
-// containing pointer data. Anything after this offset is scalar data.
-func typeptrdata(t *types.Type) int64 {
- if !t.HasPointers() {
- return 0
- }
-
- switch t.Etype {
- case TPTR,
- TUNSAFEPTR,
- TFUNC,
- TCHAN,
- TMAP:
- return int64(Widthptr)
-
- case TSTRING:
- // struct { byte *str; intgo len; }
- return int64(Widthptr)
-
- case TINTER:
- // struct { Itab *tab; void *data; } or
- // struct { Type *type; void *data; }
- // Note: see comment in plive.go:onebitwalktype1.
- return 2 * int64(Widthptr)
-
- case TSLICE:
- // struct { byte *array; uintgo len; uintgo cap; }
- return int64(Widthptr)
-
- case TARRAY:
- // haspointers already eliminated t.NumElem() == 0.
- return (t.NumElem()-1)*t.Elem().Width + typeptrdata(t.Elem())
-
- case TSTRUCT:
- // Find the last field that has pointers.
- var lastPtrField *types.Field
- for _, t1 := range t.Fields().Slice() {
- if t1.Type.HasPointers() {
- lastPtrField = t1
- }
- }
- return lastPtrField.Offset + typeptrdata(lastPtrField.Type)
-
- default:
- Fatalf("typeptrdata: unexpected type, %v", t)
- return 0
- }
+ types.TINT: objabi.KindInt,
+ types.TUINT: objabi.KindUint,
+ types.TINT8: objabi.KindInt8,
+ types.TUINT8: objabi.KindUint8,
+ types.TINT16: objabi.KindInt16,
+ types.TUINT16: objabi.KindUint16,
+ types.TINT32: objabi.KindInt32,
+ types.TUINT32: objabi.KindUint32,
+ types.TINT64: objabi.KindInt64,
+ types.TUINT64: objabi.KindUint64,
+ types.TUINTPTR: objabi.KindUintptr,
+ types.TFLOAT32: objabi.KindFloat32,
+ types.TFLOAT64: objabi.KindFloat64,
+ types.TBOOL: objabi.KindBool,
+ types.TSTRING: objabi.KindString,
+ types.TPTR: objabi.KindPtr,
+ types.TSTRUCT: objabi.KindStruct,
+ types.TINTER: objabi.KindInterface,
+ types.TCHAN: objabi.KindChan,
+ types.TMAP: objabi.KindMap,
+ types.TARRAY: objabi.KindArray,
+ types.TSLICE: objabi.KindSlice,
+ types.TFUNC: objabi.KindFunc,
+ types.TCOMPLEX64: objabi.KindComplex64,
+ types.TCOMPLEX128: objabi.KindComplex128,
+ types.TUNSAFEPTR: objabi.KindUnsafePointer,
}
// tflag is documented in reflect/type.go.
@@ -824,17 +680,17 @@ var (
// dcommontype dumps the contents of a reflect.rtype (runtime._type).
func dcommontype(lsym *obj.LSym, t *types.Type) int {
- dowidth(t)
+ types.CalcSize(t)
eqfunc := geneq(t)
sptrWeak := true
var sptr *obj.LSym
if !t.IsPtr() || t.IsPtrElem() {
tptr := types.NewPtr(t)
- if t.Sym != nil || methods(tptr) != nil {
+ if t.Sym() != nil || methods(tptr) != nil {
sptrWeak = false
}
- sptr = dtypesym(tptr)
+ sptr = writeType(tptr)
}
gcsym, useGCProg, ptrdata := dgcsym(t)
@@ -855,18 +711,18 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
// ptrToThis typeOff
// }
ot := 0
- ot = duintptr(lsym, ot, uint64(t.Width))
- ot = duintptr(lsym, ot, uint64(ptrdata))
- ot = duint32(lsym, ot, typehash(t))
+ ot = objw.Uintptr(lsym, ot, uint64(t.Width))
+ ot = objw.Uintptr(lsym, ot, uint64(ptrdata))
+ ot = objw.Uint32(lsym, ot, types.TypeHash(t))
var tflag uint8
if uncommonSize(t) != 0 {
tflag |= tflagUncommon
}
- if t.Sym != nil && t.Sym.Name != "" {
+ if t.Sym() != nil && t.Sym().Name != "" {
tflag |= tflagNamed
}
- if IsRegularMemory(t) {
+ if isRegularMemory(t) {
tflag |= tflagRegularMemory
}
@@ -880,16 +736,16 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
if !strings.HasPrefix(p, "*") {
p = "*" + p
tflag |= tflagExtraStar
- if t.Sym != nil {
- exported = types.IsExported(t.Sym.Name)
+ if t.Sym() != nil {
+ exported = types.IsExported(t.Sym().Name)
}
} else {
- if t.Elem() != nil && t.Elem().Sym != nil {
- exported = types.IsExported(t.Elem().Sym.Name)
+ if t.Elem() != nil && t.Elem().Sym() != nil {
+ exported = types.IsExported(t.Elem().Sym().Name)
}
}
- ot = duint8(lsym, ot, tflag)
+ ot = objw.Uint8(lsym, ot, tflag)
// runtime (and common sense) expects alignment to be a power of two.
i := int(t.Align)
@@ -898,88 +754,54 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
i = 1
}
if i&(i-1) != 0 {
- Fatalf("invalid alignment %d for %v", t.Align, t)
+ base.Fatalf("invalid alignment %d for %v", t.Align, t)
}
- ot = duint8(lsym, ot, t.Align) // align
- ot = duint8(lsym, ot, t.Align) // fieldAlign
+ ot = objw.Uint8(lsym, ot, t.Align) // align
+ ot = objw.Uint8(lsym, ot, t.Align) // fieldAlign
- i = kinds[t.Etype]
- if isdirectiface(t) {
+ i = kinds[t.Kind()]
+ if types.IsDirectIface(t) {
i |= objabi.KindDirectIface
}
if useGCProg {
i |= objabi.KindGCProg
}
- ot = duint8(lsym, ot, uint8(i)) // kind
+ ot = objw.Uint8(lsym, ot, uint8(i)) // kind
if eqfunc != nil {
- ot = dsymptr(lsym, ot, eqfunc, 0) // equality function
+ ot = objw.SymPtr(lsym, ot, eqfunc, 0) // equality function
} else {
- ot = duintptr(lsym, ot, 0) // type we can't do == with
+ ot = objw.Uintptr(lsym, ot, 0) // type we can't do == with
}
- ot = dsymptr(lsym, ot, gcsym, 0) // gcdata
+ ot = objw.SymPtr(lsym, ot, gcsym, 0) // gcdata
nsym := dname(p, "", nil, exported)
- ot = dsymptrOff(lsym, ot, nsym) // str
+ ot = objw.SymPtrOff(lsym, ot, nsym) // str
// ptrToThis
if sptr == nil {
- ot = duint32(lsym, ot, 0)
+ ot = objw.Uint32(lsym, ot, 0)
} else if sptrWeak {
- ot = dsymptrWeakOff(lsym, ot, sptr)
+ ot = objw.SymPtrWeakOff(lsym, ot, sptr)
} else {
- ot = dsymptrOff(lsym, ot, sptr)
+ ot = objw.SymPtrOff(lsym, ot, sptr)
}
return ot
}
-// typeHasNoAlg reports whether t does not have any associated hash/eq
-// algorithms because t, or some component of t, is marked Noalg.
-func typeHasNoAlg(t *types.Type) bool {
- a, bad := algtype1(t)
- return a == ANOEQ && bad.Noalg()
-}
-
-func typesymname(t *types.Type) string {
- name := t.ShortString()
- // Use a separate symbol name for Noalg types for #17752.
- if typeHasNoAlg(t) {
- name = "noalg." + name
- }
- return name
-}
-
-// Fake package for runtime type info (headers)
-// Don't access directly, use typeLookup below.
-var (
- typepkgmu sync.Mutex // protects typepkg lookups
- typepkg = types.NewPkg("type", "type")
-)
-
-func typeLookup(name string) *types.Sym {
- typepkgmu.Lock()
- s := typepkg.Lookup(name)
- typepkgmu.Unlock()
- return s
-}
-
-func typesym(t *types.Type) *types.Sym {
- return typeLookup(typesymname(t))
-}
-
-// tracksym returns the symbol for tracking use of field/method f, assumed
+// TrackSym returns the symbol for tracking use of field/method f, assumed
// to be a member of struct/interface type t.
-func tracksym(t *types.Type, f *types.Field) *types.Sym {
- return trackpkg.Lookup(t.ShortString() + "." + f.Sym.Name)
+func TrackSym(t *types.Type, f *types.Field) *obj.LSym {
+ return base.PkgLinksym("go.track", t.ShortString()+"."+f.Sym.Name, obj.ABI0)
}
-func typesymprefix(prefix string, t *types.Type) *types.Sym {
+func TypeSymPrefix(prefix string, t *types.Type) *types.Sym {
p := prefix + "." + t.ShortString()
- s := typeLookup(p)
+ s := types.TypeSymLookup(p)
// This function is for looking up type-related generated functions
// (e.g. eq and hash). Make sure they are indeed generated.
signatmu.Lock()
- addsignat(t)
+ NeedRuntimeType(t)
signatmu.Unlock()
//print("algsym: %s -> %+S\n", p, s);
@@ -987,116 +809,68 @@ func typesymprefix(prefix string, t *types.Type) *types.Sym {
return s
}
-func typenamesym(t *types.Type) *types.Sym {
+func TypeSym(t *types.Type) *types.Sym {
if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() {
- Fatalf("typenamesym %v", t)
+ base.Fatalf("TypeSym %v", t)
+ }
+ if t.Kind() == types.TFUNC && t.Recv() != nil {
+ base.Fatalf("misuse of method type: %v", t)
}
- s := typesym(t)
+ s := types.TypeSym(t)
signatmu.Lock()
- addsignat(t)
+ NeedRuntimeType(t)
signatmu.Unlock()
return s
}
-func typename(t *types.Type) *Node {
- s := typenamesym(t)
- if s.Def == nil {
- n := newnamel(src.NoXPos, s)
- n.Type = types.Types[TUINT8]
- n.SetClass(PEXTERN)
- n.SetTypecheck(1)
- s.Def = asTypesNode(n)
- }
-
- n := nod(OADDR, asNode(s.Def), nil)
- n.Type = types.NewPtr(asNode(s.Def).Type)
- n.SetTypecheck(1)
- return n
+func TypeLinksymPrefix(prefix string, t *types.Type) *obj.LSym {
+ return TypeSymPrefix(prefix, t).Linksym()
}
-func itabname(t, itype *types.Type) *Node {
- if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
- Fatalf("itabname(%v, %v)", t, itype)
- }
- s := itabpkg.Lookup(t.ShortString() + "," + itype.ShortString())
- if s.Def == nil {
- n := newname(s)
- n.Type = types.Types[TUINT8]
- n.SetClass(PEXTERN)
- n.SetTypecheck(1)
- s.Def = asTypesNode(n)
- itabs = append(itabs, itabEntry{t: t, itype: itype, lsym: s.Linksym()})
- }
-
- n := nod(OADDR, asNode(s.Def), nil)
- n.Type = types.NewPtr(asNode(s.Def).Type)
- n.SetTypecheck(1)
- return n
+func TypeLinksymLookup(name string) *obj.LSym {
+ return types.TypeSymLookup(name).Linksym()
}
-// isreflexive reports whether t has a reflexive equality operator.
-// That is, if x==x for all x of type t.
-func isreflexive(t *types.Type) bool {
- switch t.Etype {
- case TBOOL,
- TINT,
- TUINT,
- TINT8,
- TUINT8,
- TINT16,
- TUINT16,
- TINT32,
- TUINT32,
- TINT64,
- TUINT64,
- TUINTPTR,
- TPTR,
- TUNSAFEPTR,
- TSTRING,
- TCHAN:
- return true
-
- case TFLOAT32,
- TFLOAT64,
- TCOMPLEX64,
- TCOMPLEX128,
- TINTER:
- return false
-
- case TARRAY:
- return isreflexive(t.Elem())
+func TypeLinksym(t *types.Type) *obj.LSym {
+ return TypeSym(t).Linksym()
+}
- case TSTRUCT:
- for _, t1 := range t.Fields().Slice() {
- if !isreflexive(t1.Type) {
- return false
- }
- }
- return true
+func TypePtr(t *types.Type) *ir.AddrExpr {
+ n := ir.NewLinksymExpr(base.Pos, TypeLinksym(t), types.Types[types.TUINT8])
+ return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
+}
- default:
- Fatalf("bad type for map key: %v", t)
- return false
+func ITabAddr(t, itype *types.Type) *ir.AddrExpr {
+ if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
+ base.Fatalf("ITabAddr(%v, %v)", t, itype)
+ }
+ s, existed := ir.Pkgs.Itab.LookupOK(t.ShortString() + "," + itype.ShortString())
+ if !existed {
+ itabs = append(itabs, itabEntry{t: t, itype: itype, lsym: s.Linksym()})
}
+
+ lsym := s.Linksym()
+ n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8])
+ return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
}
// needkeyupdate reports whether map updates with t as a key
// need the key to be updated.
func needkeyupdate(t *types.Type) bool {
- switch t.Etype {
- case TBOOL, TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32,
- TINT64, TUINT64, TUINTPTR, TPTR, TUNSAFEPTR, TCHAN:
+ switch t.Kind() {
+ case types.TBOOL, types.TINT, types.TUINT, types.TINT8, types.TUINT8, types.TINT16, types.TUINT16, types.TINT32, types.TUINT32,
+ types.TINT64, types.TUINT64, types.TUINTPTR, types.TPTR, types.TUNSAFEPTR, types.TCHAN:
return false
- case TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, // floats and complex can be +0/-0
- TINTER,
- TSTRING: // strings might have smaller backing stores
+ case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, // floats and complex can be +0/-0
+ types.TINTER,
+ types.TSTRING: // strings might have smaller backing stores
return true
- case TARRAY:
+ case types.TARRAY:
return needkeyupdate(t.Elem())
- case TSTRUCT:
+ case types.TSTRUCT:
for _, t1 := range t.Fields().Slice() {
if needkeyupdate(t1.Type) {
return true
@@ -1105,21 +879,21 @@ func needkeyupdate(t *types.Type) bool {
return false
default:
- Fatalf("bad type for map key: %v", t)
+ base.Fatalf("bad type for map key: %v", t)
return true
}
}
// hashMightPanic reports whether the hash of a map key of type t might panic.
func hashMightPanic(t *types.Type) bool {
- switch t.Etype {
- case TINTER:
+ switch t.Kind() {
+ case types.TINTER:
return true
- case TARRAY:
+ case types.TARRAY:
return hashMightPanic(t.Elem())
- case TSTRUCT:
+ case types.TSTRUCT:
for _, t1 := range t.Fields().Slice() {
if hashMightPanic(t1.Type) {
return true
@@ -1136,19 +910,19 @@ func hashMightPanic(t *types.Type) bool {
// They've been separate internally to make error messages
// better, but we have to merge them in the reflect tables.
func formalType(t *types.Type) *types.Type {
- if t == types.Bytetype || t == types.Runetype {
- return types.Types[t.Etype]
+ if t == types.ByteType || t == types.RuneType {
+ return types.Types[t.Kind()]
}
return t
}
-func dtypesym(t *types.Type) *obj.LSym {
+func writeType(t *types.Type) *obj.LSym {
t = formalType(t)
if t.IsUntyped() {
- Fatalf("dtypesym %v", t)
+ base.Fatalf("writeType %v", t)
}
- s := typesym(t)
+ s := types.TypeSym(t)
lsym := s.Linksym()
if s.Siggen() {
return lsym
@@ -1160,77 +934,73 @@ func dtypesym(t *types.Type) *obj.LSym {
// emit the type structures for int, float, etc.
tbase := t
- if t.IsPtr() && t.Sym == nil && t.Elem().Sym != nil {
+ if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
tbase = t.Elem()
}
dupok := 0
- if tbase.Sym == nil {
+ if tbase.Sym() == nil {
dupok = obj.DUPOK
}
- if myimportpath != "runtime" || (tbase != types.Types[tbase.Etype] && tbase != types.Bytetype && tbase != types.Runetype && tbase != types.Errortype) { // int, float, etc
+ if base.Ctxt.Pkgpath != "runtime" || (tbase != types.Types[tbase.Kind()] && tbase != types.ByteType && tbase != types.RuneType && tbase != types.ErrorType) { // int, float, etc
// named types from other files are defined only by those files
- if tbase.Sym != nil && tbase.Sym.Pkg != localpkg {
- if i, ok := typeSymIdx[tbase]; ok {
- lsym.Pkg = tbase.Sym.Pkg.Prefix
- if t != tbase {
- lsym.SymIdx = int32(i[1])
- } else {
- lsym.SymIdx = int32(i[0])
- }
+ if tbase.Sym() != nil && tbase.Sym().Pkg != types.LocalPkg {
+ if i := typecheck.BaseTypeIndex(t); i >= 0 {
+ lsym.Pkg = tbase.Sym().Pkg.Prefix
+ lsym.SymIdx = int32(i)
lsym.Set(obj.AttrIndexed, true)
}
return lsym
}
// TODO(mdempsky): Investigate whether this can happen.
- if tbase.Etype == TFORW {
+ if tbase.Kind() == types.TFORW {
return lsym
}
}
ot := 0
- switch t.Etype {
+ switch t.Kind() {
default:
ot = dcommontype(lsym, t)
ot = dextratype(lsym, ot, t, 0)
- case TARRAY:
+ case types.TARRAY:
// ../../../../runtime/type.go:/arrayType
- s1 := dtypesym(t.Elem())
+ s1 := writeType(t.Elem())
t2 := types.NewSlice(t.Elem())
- s2 := dtypesym(t2)
+ s2 := writeType(t2)
ot = dcommontype(lsym, t)
- ot = dsymptr(lsym, ot, s1, 0)
- ot = dsymptr(lsym, ot, s2, 0)
- ot = duintptr(lsym, ot, uint64(t.NumElem()))
+ ot = objw.SymPtr(lsym, ot, s1, 0)
+ ot = objw.SymPtr(lsym, ot, s2, 0)
+ ot = objw.Uintptr(lsym, ot, uint64(t.NumElem()))
ot = dextratype(lsym, ot, t, 0)
- case TSLICE:
+ case types.TSLICE:
// ../../../../runtime/type.go:/sliceType
- s1 := dtypesym(t.Elem())
+ s1 := writeType(t.Elem())
ot = dcommontype(lsym, t)
- ot = dsymptr(lsym, ot, s1, 0)
+ ot = objw.SymPtr(lsym, ot, s1, 0)
ot = dextratype(lsym, ot, t, 0)
- case TCHAN:
+ case types.TCHAN:
// ../../../../runtime/type.go:/chanType
- s1 := dtypesym(t.Elem())
+ s1 := writeType(t.Elem())
ot = dcommontype(lsym, t)
- ot = dsymptr(lsym, ot, s1, 0)
- ot = duintptr(lsym, ot, uint64(t.ChanDir()))
+ ot = objw.SymPtr(lsym, ot, s1, 0)
+ ot = objw.Uintptr(lsym, ot, uint64(t.ChanDir()))
ot = dextratype(lsym, ot, t, 0)
- case TFUNC:
+ case types.TFUNC:
for _, t1 := range t.Recvs().Fields().Slice() {
- dtypesym(t1.Type)
+ writeType(t1.Type)
}
isddd := false
for _, t1 := range t.Params().Fields().Slice() {
isddd = t1.IsDDD()
- dtypesym(t1.Type)
+ writeType(t1.Type)
}
for _, t1 := range t.Results().Fields().Slice() {
- dtypesym(t1.Type)
+ writeType(t1.Type)
}
ot = dcommontype(lsym, t)
@@ -1239,45 +1009,45 @@ func dtypesym(t *types.Type) *obj.LSym {
if isddd {
outCount |= 1 << 15
}
- ot = duint16(lsym, ot, uint16(inCount))
- ot = duint16(lsym, ot, uint16(outCount))
- if Widthptr == 8 {
+ ot = objw.Uint16(lsym, ot, uint16(inCount))
+ ot = objw.Uint16(lsym, ot, uint16(outCount))
+ if types.PtrSize == 8 {
ot += 4 // align for *rtype
}
- dataAdd := (inCount + t.NumResults()) * Widthptr
+ dataAdd := (inCount + t.NumResults()) * types.PtrSize
ot = dextratype(lsym, ot, t, dataAdd)
// Array of rtype pointers follows funcType.
for _, t1 := range t.Recvs().Fields().Slice() {
- ot = dsymptr(lsym, ot, dtypesym(t1.Type), 0)
+ ot = objw.SymPtr(lsym, ot, writeType(t1.Type), 0)
}
for _, t1 := range t.Params().Fields().Slice() {
- ot = dsymptr(lsym, ot, dtypesym(t1.Type), 0)
+ ot = objw.SymPtr(lsym, ot, writeType(t1.Type), 0)
}
for _, t1 := range t.Results().Fields().Slice() {
- ot = dsymptr(lsym, ot, dtypesym(t1.Type), 0)
+ ot = objw.SymPtr(lsym, ot, writeType(t1.Type), 0)
}
- case TINTER:
+ case types.TINTER:
m := imethods(t)
n := len(m)
for _, a := range m {
- dtypesym(a.type_)
+ writeType(a.type_)
}
// ../../../../runtime/type.go:/interfaceType
ot = dcommontype(lsym, t)
var tpkg *types.Pkg
- if t.Sym != nil && t != types.Types[t.Etype] && t != types.Errortype {
- tpkg = t.Sym.Pkg
+ if t.Sym() != nil && t != types.Types[t.Kind()] && t != types.ErrorType {
+ tpkg = t.Sym().Pkg
}
ot = dgopkgpath(lsym, ot, tpkg)
- ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t))
- ot = duintptr(lsym, ot, uint64(n))
- ot = duintptr(lsym, ot, uint64(n))
+ ot = objw.SymPtr(lsym, ot, lsym, ot+3*types.PtrSize+uncommonSize(t))
+ ot = objw.Uintptr(lsym, ot, uint64(n))
+ ot = objw.Uintptr(lsym, ot, uint64(n))
dataAdd := imethodSize() * n
ot = dextratype(lsym, ot, t, dataAdd)
@@ -1290,40 +1060,40 @@ func dtypesym(t *types.Type) *obj.LSym {
}
nsym := dname(a.name.Name, "", pkg, exported)
- ot = dsymptrOff(lsym, ot, nsym)
- ot = dsymptrOff(lsym, ot, dtypesym(a.type_))
+ ot = objw.SymPtrOff(lsym, ot, nsym)
+ ot = objw.SymPtrOff(lsym, ot, writeType(a.type_))
}
// ../../../../runtime/type.go:/mapType
- case TMAP:
- s1 := dtypesym(t.Key())
- s2 := dtypesym(t.Elem())
- s3 := dtypesym(bmap(t))
+ case types.TMAP:
+ s1 := writeType(t.Key())
+ s2 := writeType(t.Elem())
+ s3 := writeType(MapBucketType(t))
hasher := genhash(t.Key())
ot = dcommontype(lsym, t)
- ot = dsymptr(lsym, ot, s1, 0)
- ot = dsymptr(lsym, ot, s2, 0)
- ot = dsymptr(lsym, ot, s3, 0)
- ot = dsymptr(lsym, ot, hasher, 0)
+ ot = objw.SymPtr(lsym, ot, s1, 0)
+ ot = objw.SymPtr(lsym, ot, s2, 0)
+ ot = objw.SymPtr(lsym, ot, s3, 0)
+ ot = objw.SymPtr(lsym, ot, hasher, 0)
var flags uint32
// Note: flags must match maptype accessors in ../../../../runtime/type.go
// and maptype builder in ../../../../reflect/type.go:MapOf.
if t.Key().Width > MAXKEYSIZE {
- ot = duint8(lsym, ot, uint8(Widthptr))
+ ot = objw.Uint8(lsym, ot, uint8(types.PtrSize))
flags |= 1 // indirect key
} else {
- ot = duint8(lsym, ot, uint8(t.Key().Width))
+ ot = objw.Uint8(lsym, ot, uint8(t.Key().Width))
}
if t.Elem().Width > MAXELEMSIZE {
- ot = duint8(lsym, ot, uint8(Widthptr))
+ ot = objw.Uint8(lsym, ot, uint8(types.PtrSize))
flags |= 2 // indirect value
} else {
- ot = duint8(lsym, ot, uint8(t.Elem().Width))
+ ot = objw.Uint8(lsym, ot, uint8(t.Elem().Width))
}
- ot = duint16(lsym, ot, uint16(bmap(t).Width))
- if isreflexive(t.Key()) {
+ ot = objw.Uint16(lsym, ot, uint16(MapBucketType(t).Width))
+ if types.IsReflexive(t.Key()) {
flags |= 4 // reflexive key
}
if needkeyupdate(t.Key()) {
@@ -1332,11 +1102,11 @@ func dtypesym(t *types.Type) *obj.LSym {
if hashMightPanic(t.Key()) {
flags |= 16 // hash might panic
}
- ot = duint32(lsym, ot, flags)
+ ot = objw.Uint32(lsym, ot, flags)
ot = dextratype(lsym, ot, t, 0)
- case TPTR:
- if t.Elem().Etype == TANY {
+ case types.TPTR:
+ if t.Elem().Kind() == types.TANY {
// ../../../../runtime/type.go:/UnsafePointerType
ot = dcommontype(lsym, t)
ot = dextratype(lsym, ot, t, 0)
@@ -1345,18 +1115,18 @@ func dtypesym(t *types.Type) *obj.LSym {
}
// ../../../../runtime/type.go:/ptrType
- s1 := dtypesym(t.Elem())
+ s1 := writeType(t.Elem())
ot = dcommontype(lsym, t)
- ot = dsymptr(lsym, ot, s1, 0)
+ ot = objw.SymPtr(lsym, ot, s1, 0)
ot = dextratype(lsym, ot, t, 0)
// ../../../../runtime/type.go:/structType
// for security, only the exported fields.
- case TSTRUCT:
+ case types.TSTRUCT:
fields := t.Fields().Slice()
for _, t1 := range fields {
- dtypesym(t1.Type)
+ writeType(t1.Type)
}
// All non-exported struct field names within a struct
@@ -1374,9 +1144,9 @@ func dtypesym(t *types.Type) *obj.LSym {
ot = dcommontype(lsym, t)
ot = dgopkgpath(lsym, ot, spkg)
- ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t))
- ot = duintptr(lsym, ot, uint64(len(fields)))
- ot = duintptr(lsym, ot, uint64(len(fields)))
+ ot = objw.SymPtr(lsym, ot, lsym, ot+3*types.PtrSize+uncommonSize(t))
+ ot = objw.Uintptr(lsym, ot, uint64(len(fields)))
+ ot = objw.Uintptr(lsym, ot, uint64(len(fields)))
dataAdd := len(fields) * structfieldSize()
ot = dextratype(lsym, ot, t, dataAdd)
@@ -1384,40 +1154,40 @@ func dtypesym(t *types.Type) *obj.LSym {
for _, f := range fields {
// ../../../../runtime/type.go:/structField
ot = dnameField(lsym, ot, spkg, f)
- ot = dsymptr(lsym, ot, dtypesym(f.Type), 0)
+ ot = objw.SymPtr(lsym, ot, writeType(f.Type), 0)
offsetAnon := uint64(f.Offset) << 1
if offsetAnon>>1 != uint64(f.Offset) {
- Fatalf("%v: bad field offset for %s", t, f.Sym.Name)
+ base.Fatalf("%v: bad field offset for %s", t, f.Sym.Name)
}
if f.Embedded != 0 {
offsetAnon |= 1
}
- ot = duintptr(lsym, ot, offsetAnon)
+ ot = objw.Uintptr(lsym, ot, offsetAnon)
}
}
ot = dextratypeData(lsym, ot, t)
- ggloblsym(lsym, int32(ot), int16(dupok|obj.RODATA))
+ objw.Global(lsym, int32(ot), int16(dupok|obj.RODATA))
// The linker will leave a table of all the typelinks for
// types in the binary, so the runtime can find them.
//
// When buildmode=shared, all types are in typelinks so the
// runtime can deduplicate type pointers.
- keep := Ctxt.Flag_dynlink
- if !keep && t.Sym == nil {
+ keep := base.Ctxt.Flag_dynlink
+ if !keep && t.Sym() == nil {
// For an unnamed type, we only need the link if the type can
// be created at run time by reflect.PtrTo and similar
// functions. If the type exists in the program, those
// functions must return the existing type structure rather
// than creating a new one.
- switch t.Etype {
- case TPTR, TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRUCT:
+ switch t.Kind() {
+ case types.TPTR, types.TARRAY, types.TCHAN, types.TFUNC, types.TMAP, types.TSLICE, types.TSTRUCT:
keep = true
}
}
// Do not put Noalg types in typelinks. See issue #22605.
- if typeHasNoAlg(t) {
+ if types.TypeHasNoAlg(t) {
keep = false
}
lsym.Set(obj.AttrMakeTypelink, keep)
@@ -1425,9 +1195,9 @@ func dtypesym(t *types.Type) *obj.LSym {
return lsym
}
-// ifaceMethodOffset returns the offset of the i-th method in the interface
+// InterfaceMethodOffset returns the offset of the i-th method in the interface
// type descriptor, ityp.
-func ifaceMethodOffset(ityp *types.Type, i int64) int64 {
+func InterfaceMethodOffset(ityp *types.Type, i int64) int64 {
// interface type descriptor layout is struct {
// _type // commonSize
// pkgpath // 1 word
@@ -1436,12 +1206,12 @@ func ifaceMethodOffset(ityp *types.Type, i int64) int64 {
// [...]imethod
// }
// The size of imethod is 8.
- return int64(commonSize()+4*Widthptr+uncommonSize(ityp)) + i*8
+ return int64(commonSize()+4*types.PtrSize+uncommonSize(ityp)) + i*8
}
// for each itabEntry, gather the methods on
// the concrete type that implement the interface
-func peekitabs() {
+func CompileITabs() {
for i := range itabs {
tab := &itabs[i]
methods := genfun(tab.t, tab.itype)
@@ -1472,7 +1242,7 @@ func genfun(t, it *types.Type) []*obj.LSym {
// so we can find the intersect in a single pass
for _, m := range methods {
if m.name == sigs[0].name {
- out = append(out, m.isym.Linksym())
+ out = append(out, m.isym)
sigs = sigs[1:]
if len(sigs) == 0 {
break
@@ -1481,17 +1251,17 @@ func genfun(t, it *types.Type) []*obj.LSym {
}
if len(sigs) != 0 {
- Fatalf("incomplete itab")
+ base.Fatalf("incomplete itab")
}
return out
}
-// itabsym uses the information gathered in
-// peekitabs to de-virtualize interface methods.
+// ITabSym uses the information gathered in
+// CompileITabs to de-virtualize interface methods.
// Since this is called by the SSA backend, it shouldn't
// generate additional Nodes, Syms, etc.
-func itabsym(it *obj.LSym, offset int64) *obj.LSym {
+func ITabSym(it *obj.LSym, offset int64) *obj.LSym {
var syms []*obj.LSym
if it == nil {
return nil
@@ -1509,54 +1279,45 @@ func itabsym(it *obj.LSym, offset int64) *obj.LSym {
}
// keep this arithmetic in sync with *itab layout
- methodnum := int((offset - 2*int64(Widthptr) - 8) / int64(Widthptr))
+ methodnum := int((offset - 2*int64(types.PtrSize) - 8) / int64(types.PtrSize))
if methodnum >= len(syms) {
return nil
}
return syms[methodnum]
}
-// addsignat ensures that a runtime type descriptor is emitted for t.
-func addsignat(t *types.Type) {
+// NeedRuntimeType ensures that a runtime type descriptor is emitted for t.
+func NeedRuntimeType(t *types.Type) {
if _, ok := signatset[t]; !ok {
signatset[t] = struct{}{}
signatslice = append(signatslice, t)
}
}
-func addsignats(dcls []*Node) {
- // copy types from dcl list to signatset
- for _, n := range dcls {
- if n.Op == OTYPE {
- addsignat(n.Type)
- }
- }
-}
-
-func dumpsignats() {
- // Process signatset. Use a loop, as dtypesym adds
+func WriteRuntimeTypes() {
+ // Process signatset. Use a loop, as writeType adds
// entries to signatset while it is being processed.
signats := make([]typeAndStr, len(signatslice))
for len(signatslice) > 0 {
signats = signats[:0]
// Transfer entries to a slice and sort, for reproducible builds.
for _, t := range signatslice {
- signats = append(signats, typeAndStr{t: t, short: typesymname(t), regular: t.String()})
+ signats = append(signats, typeAndStr{t: t, short: types.TypeSymName(t), regular: t.String()})
delete(signatset, t)
}
signatslice = signatslice[:0]
sort.Sort(typesByString(signats))
for _, ts := range signats {
t := ts.t
- dtypesym(t)
- if t.Sym != nil {
- dtypesym(types.NewPtr(t))
+ writeType(t)
+ if t.Sym() != nil {
+ writeType(types.NewPtr(t))
}
}
}
}
-func dumptabs() {
+func WriteTabs() {
// process itabs
for _, i := range itabs {
// dump empty itab symbol into i.sym
@@ -1567,22 +1328,22 @@ func dumptabs() {
// _ [4]byte
// fun [1]uintptr // variable sized
// }
- o := dsymptr(i.lsym, 0, dtypesym(i.itype), 0)
- o = dsymptr(i.lsym, o, dtypesym(i.t), 0)
- o = duint32(i.lsym, o, typehash(i.t)) // copy of type hash
- o += 4 // skip unused field
+ o := objw.SymPtr(i.lsym, 0, writeType(i.itype), 0)
+ o = objw.SymPtr(i.lsym, o, writeType(i.t), 0)
+ o = objw.Uint32(i.lsym, o, types.TypeHash(i.t)) // copy of type hash
+ o += 4 // skip unused field
for _, fn := range genfun(i.t, i.itype) {
- o = dsymptr(i.lsym, o, fn, 0) // method pointer for each method
+ o = objw.SymPtr(i.lsym, o, fn, 0) // method pointer for each method
}
// Nothing writes static itabs, so they are read only.
- ggloblsym(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
+ objw.Global(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
i.lsym.Set(obj.AttrContentAddressable, true)
}
// process ptabs
- if localpkg.Name == "main" && len(ptabs) > 0 {
+ if types.LocalPkg.Name == "main" && len(ptabs) > 0 {
ot := 0
- s := Ctxt.Lookup("go.plugin.tabs")
+ s := base.Ctxt.Lookup("go.plugin.tabs")
for _, p := range ptabs {
// Dump ptab symbol into go.pluginsym package.
//
@@ -1590,61 +1351,70 @@ func dumptabs() {
// name nameOff
// typ typeOff // pointer to symbol
// }
- nsym := dname(p.s.Name, "", nil, true)
- tsym := dtypesym(p.t)
- ot = dsymptrOff(s, ot, nsym)
- ot = dsymptrOff(s, ot, tsym)
+ nsym := dname(p.Sym().Name, "", nil, true)
+ t := p.Type()
+ if p.Class != ir.PFUNC {
+ t = types.NewPtr(t)
+ }
+ tsym := writeType(t)
+ ot = objw.SymPtrOff(s, ot, nsym)
+ ot = objw.SymPtrOff(s, ot, tsym)
// Plugin exports symbols as interfaces. Mark their types
// as UsedInIface.
tsym.Set(obj.AttrUsedInIface, true)
}
- ggloblsym(s, int32(ot), int16(obj.RODATA))
+ objw.Global(s, int32(ot), int16(obj.RODATA))
ot = 0
- s = Ctxt.Lookup("go.plugin.exports")
+ s = base.Ctxt.Lookup("go.plugin.exports")
for _, p := range ptabs {
- ot = dsymptr(s, ot, p.s.Linksym(), 0)
+ ot = objw.SymPtr(s, ot, p.Linksym(), 0)
}
- ggloblsym(s, int32(ot), int16(obj.RODATA))
+ objw.Global(s, int32(ot), int16(obj.RODATA))
}
}
-func dumpimportstrings() {
+func WriteImportStrings() {
// generate import strings for imported packages
for _, p := range types.ImportedPkgList() {
dimportpath(p)
}
}
-func dumpbasictypes() {
+func WriteBasicTypes() {
// do basic types if compiling package runtime.
// they have to be in at least one package,
// and runtime is always loaded implicitly,
// so this is as good as any.
// another possible choice would be package main,
// but using runtime means fewer copies in object files.
- if myimportpath == "runtime" {
- for i := types.EType(1); i <= TBOOL; i++ {
- dtypesym(types.NewPtr(types.Types[i]))
+ if base.Ctxt.Pkgpath == "runtime" {
+ for i := types.Kind(1); i <= types.TBOOL; i++ {
+ writeType(types.NewPtr(types.Types[i]))
}
- dtypesym(types.NewPtr(types.Types[TSTRING]))
- dtypesym(types.NewPtr(types.Types[TUNSAFEPTR]))
+ writeType(types.NewPtr(types.Types[types.TSTRING]))
+ writeType(types.NewPtr(types.Types[types.TUNSAFEPTR]))
// emit type structs for error and func(error) string.
// The latter is the type of an auto-generated wrapper.
- dtypesym(types.NewPtr(types.Errortype))
+ writeType(types.NewPtr(types.ErrorType))
- dtypesym(functype(nil, []*Node{anonfield(types.Errortype)}, []*Node{anonfield(types.Types[TSTRING])}))
+ writeType(types.NewSignature(types.NoPkg, nil, []*types.Field{
+ types.NewField(base.Pos, nil, types.ErrorType),
+ }, []*types.Field{
+ types.NewField(base.Pos, nil, types.Types[types.TSTRING]),
+ }))
// add paths for runtime and main, which 6l imports implicitly.
- dimportpath(Runtimepkg)
+ dimportpath(ir.Pkgs.Runtime)
- if flag_race {
- dimportpath(racepkg)
+ if base.Flag.Race {
+ dimportpath(types.NewPkg("runtime/race", ""))
}
- if flag_msan {
- dimportpath(msanpkg)
+ if base.Flag.MSan {
+ dimportpath(types.NewPkg("runtime/msan", ""))
}
+
dimportpath(types.NewPkg("main", ""))
}
}
@@ -1673,7 +1443,7 @@ func (a typesByString) Less(i, j int) bool {
// will be equal for the above checks, but different in DWARF output.
// Sort by source position to ensure deterministic order.
// See issues 27013 and 30202.
- if a[i].t.Etype == types.TINTER && a[i].t.Methods().Len() > 0 {
+ if a[i].t.Kind() == types.TINTER && a[i].t.Methods().Len() > 0 {
return a[i].t.Methods().Index(0).Pos.Before(a[j].t.Methods().Index(0).Pos)
}
return false
@@ -1718,8 +1488,8 @@ const maxPtrmaskBytes = 2048
// along with a boolean reporting whether the UseGCProg bit should be set in
// the type kind, and the ptrdata field to record in the reflect type information.
func dgcsym(t *types.Type) (lsym *obj.LSym, useGCProg bool, ptrdata int64) {
- ptrdata = typeptrdata(t)
- if ptrdata/int64(Widthptr) <= maxPtrmaskBytes*8 {
+ ptrdata = types.PtrDataSize(t)
+ if ptrdata/int64(types.PtrSize) <= maxPtrmaskBytes*8 {
lsym = dgcptrmask(t)
return
}
@@ -1731,18 +1501,18 @@ func dgcsym(t *types.Type) (lsym *obj.LSym, useGCProg bool, ptrdata int64) {
// dgcptrmask emits and returns the symbol containing a pointer mask for type t.
func dgcptrmask(t *types.Type) *obj.LSym {
- ptrmask := make([]byte, (typeptrdata(t)/int64(Widthptr)+7)/8)
+ ptrmask := make([]byte, (types.PtrDataSize(t)/int64(types.PtrSize)+7)/8)
fillptrmask(t, ptrmask)
p := fmt.Sprintf("gcbits.%x", ptrmask)
- sym := Runtimepkg.Lookup(p)
+ sym := ir.Pkgs.Runtime.Lookup(p)
lsym := sym.Linksym()
if !sym.Uniq() {
sym.SetUniq(true)
for i, x := range ptrmask {
- duint8(lsym, i, x)
+ objw.Uint8(lsym, i, x)
}
- ggloblsym(lsym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL)
+ objw.Global(lsym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL)
lsym.Set(obj.AttrContentAddressable, true)
}
return lsym
@@ -1759,10 +1529,10 @@ func fillptrmask(t *types.Type, ptrmask []byte) {
return
}
- vec := bvalloc(8 * int32(len(ptrmask)))
- onebitwalktype1(t, 0, vec)
+ vec := bitvec.New(8 * int32(len(ptrmask)))
+ typebits.Set(t, 0, vec)
- nptr := typeptrdata(t) / int64(Widthptr)
+ nptr := types.PtrDataSize(t) / int64(types.PtrSize)
for i := int64(0); i < nptr; i++ {
if vec.Get(int32(i)) {
ptrmask[i/8] |= 1 << (uint(i) % 8)
@@ -1775,80 +1545,78 @@ func fillptrmask(t *types.Type, ptrmask []byte) {
// In practice, the size is typeptrdata(t) except for non-trivial arrays.
// For non-trivial arrays, the program describes the full t.Width size.
func dgcprog(t *types.Type) (*obj.LSym, int64) {
- dowidth(t)
- if t.Width == BADWIDTH {
- Fatalf("dgcprog: %v badwidth", t)
+ types.CalcSize(t)
+ if t.Width == types.BADWIDTH {
+ base.Fatalf("dgcprog: %v badwidth", t)
}
- lsym := typesymprefix(".gcprog", t).Linksym()
- var p GCProg
+ lsym := TypeLinksymPrefix(".gcprog", t)
+ var p gcProg
p.init(lsym)
p.emit(t, 0)
- offset := p.w.BitIndex() * int64(Widthptr)
+ offset := p.w.BitIndex() * int64(types.PtrSize)
p.end()
- if ptrdata := typeptrdata(t); offset < ptrdata || offset > t.Width {
- Fatalf("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Width)
+ if ptrdata := types.PtrDataSize(t); offset < ptrdata || offset > t.Width {
+ base.Fatalf("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Width)
}
return lsym, offset
}
-type GCProg struct {
+type gcProg struct {
lsym *obj.LSym
symoff int
w gcprog.Writer
}
-var Debug_gcprog int // set by -d gcprog
-
-func (p *GCProg) init(lsym *obj.LSym) {
+func (p *gcProg) init(lsym *obj.LSym) {
p.lsym = lsym
p.symoff = 4 // first 4 bytes hold program length
p.w.Init(p.writeByte)
- if Debug_gcprog > 0 {
+ if base.Debug.GCProg > 0 {
fmt.Fprintf(os.Stderr, "compile: start GCProg for %v\n", lsym)
p.w.Debug(os.Stderr)
}
}
-func (p *GCProg) writeByte(x byte) {
- p.symoff = duint8(p.lsym, p.symoff, x)
+func (p *gcProg) writeByte(x byte) {
+ p.symoff = objw.Uint8(p.lsym, p.symoff, x)
}
-func (p *GCProg) end() {
+func (p *gcProg) end() {
p.w.End()
- duint32(p.lsym, 0, uint32(p.symoff-4))
- ggloblsym(p.lsym, int32(p.symoff), obj.DUPOK|obj.RODATA|obj.LOCAL)
- if Debug_gcprog > 0 {
+ objw.Uint32(p.lsym, 0, uint32(p.symoff-4))
+ objw.Global(p.lsym, int32(p.symoff), obj.DUPOK|obj.RODATA|obj.LOCAL)
+ if base.Debug.GCProg > 0 {
fmt.Fprintf(os.Stderr, "compile: end GCProg for %v\n", p.lsym)
}
}
-func (p *GCProg) emit(t *types.Type, offset int64) {
- dowidth(t)
+func (p *gcProg) emit(t *types.Type, offset int64) {
+ types.CalcSize(t)
if !t.HasPointers() {
return
}
- if t.Width == int64(Widthptr) {
- p.w.Ptr(offset / int64(Widthptr))
+ if t.Width == int64(types.PtrSize) {
+ p.w.Ptr(offset / int64(types.PtrSize))
return
}
- switch t.Etype {
+ switch t.Kind() {
default:
- Fatalf("GCProg.emit: unexpected type %v", t)
+ base.Fatalf("gcProg.emit: unexpected type %v", t)
- case TSTRING:
- p.w.Ptr(offset / int64(Widthptr))
+ case types.TSTRING:
+ p.w.Ptr(offset / int64(types.PtrSize))
- case TINTER:
- // Note: the first word isn't a pointer. See comment in plive.go:onebitwalktype1.
- p.w.Ptr(offset/int64(Widthptr) + 1)
+ case types.TINTER:
+ // Note: the first word isn't a pointer. See comment in typebits.Set
+ p.w.Ptr(offset/int64(types.PtrSize) + 1)
- case TSLICE:
- p.w.Ptr(offset / int64(Widthptr))
+ case types.TSLICE:
+ p.w.Ptr(offset / int64(types.PtrSize))
- case TARRAY:
+ case types.TARRAY:
if t.NumElem() == 0 {
// should have been handled by haspointers check above
- Fatalf("GCProg.emit: empty array")
+ base.Fatalf("gcProg.emit: empty array")
}
// Flatten array-of-array-of-array to just a big array by multiplying counts.
@@ -1859,7 +1627,7 @@ func (p *GCProg) emit(t *types.Type, offset int64) {
elem = elem.Elem()
}
- if !p.w.ShouldRepeat(elem.Width/int64(Widthptr), count) {
+ if !p.w.ShouldRepeat(elem.Width/int64(types.PtrSize), count) {
// Cheaper to just emit the bits.
for i := int64(0); i < count; i++ {
p.emit(elem, offset+i*elem.Width)
@@ -1867,35 +1635,202 @@ func (p *GCProg) emit(t *types.Type, offset int64) {
return
}
p.emit(elem, offset)
- p.w.ZeroUntil((offset + elem.Width) / int64(Widthptr))
- p.w.Repeat(elem.Width/int64(Widthptr), count-1)
+ p.w.ZeroUntil((offset + elem.Width) / int64(types.PtrSize))
+ p.w.Repeat(elem.Width/int64(types.PtrSize), count-1)
- case TSTRUCT:
+ case types.TSTRUCT:
for _, t1 := range t.Fields().Slice() {
p.emit(t1.Type, offset+t1.Offset)
}
}
}
-// zeroaddr returns the address of a symbol with at least
+// ZeroAddr returns the address of a symbol with at least
// size bytes of zeros.
-func zeroaddr(size int64) *Node {
+func ZeroAddr(size int64) ir.Node {
if size >= 1<<31 {
- Fatalf("map elem too big %d", size)
- }
- if zerosize < size {
- zerosize = size
- }
- s := mappkg.Lookup("zero")
- if s.Def == nil {
- x := newname(s)
- x.Type = types.Types[TUINT8]
- x.SetClass(PEXTERN)
- x.SetTypecheck(1)
- s.Def = asTypesNode(x)
- }
- z := nod(OADDR, asNode(s.Def), nil)
- z.Type = types.NewPtr(types.Types[TUINT8])
- z.SetTypecheck(1)
- return z
+ base.Fatalf("map elem too big %d", size)
+ }
+ if ZeroSize < size {
+ ZeroSize = size
+ }
+ lsym := base.PkgLinksym("go.map", "zero", obj.ABI0)
+ x := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8])
+ return typecheck.Expr(typecheck.NodAddr(x))
+}
+
+func CollectPTabs() {
+ if !base.Ctxt.Flag_dynlink || types.LocalPkg.Name != "main" {
+ return
+ }
+ for _, exportn := range typecheck.Target.Exports {
+ s := exportn.Sym()
+ nn := ir.AsNode(s.Def)
+ if nn == nil {
+ continue
+ }
+ if nn.Op() != ir.ONAME {
+ continue
+ }
+ n := nn.(*ir.Name)
+ if !types.IsExported(s.Name) {
+ continue
+ }
+ if s.Pkg.Name != "main" {
+ continue
+ }
+ ptabs = append(ptabs, n)
+ }
+}
+
+// Generate a wrapper function to convert from
+// a receiver of type T to a receiver of type U.
+// That is,
+//
+// func (t T) M() {
+// ...
+// }
+//
+// already exists; this function generates
+//
+// func (u U) M() {
+// u.M()
+// }
+//
+// where the types T and U are such that u.M() is valid
+// and calls the T.M method.
+// The resulting function is for use in method tables.
+//
+// rcvr - U
+// method - M func (t T)(), a TFIELD type struct
+func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
+ newnam := ir.MethodSym(rcvr, method.Sym)
+ lsym := newnam.Linksym()
+ if newnam.Siggen() {
+ return lsym
+ }
+ newnam.SetSiggen(true)
+
+ if types.Identical(rcvr, method.Type.Recv().Type) {
+ return lsym
+ }
+
+ // Only generate (*T).M wrappers for T.M in T's own package.
+ if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
+ rcvr.Elem().Sym() != nil && rcvr.Elem().Sym().Pkg != types.LocalPkg {
+ return lsym
+ }
+
+ // Only generate I.M wrappers for I in I's own package
+ // but keep doing it for error.Error (was issue #29304).
+ if rcvr.IsInterface() && rcvr.Sym() != nil && rcvr.Sym().Pkg != types.LocalPkg && rcvr != types.ErrorType {
+ return lsym
+ }
+
+ base.Pos = base.AutogeneratedPos
+ typecheck.DeclContext = ir.PEXTERN
+
+ tfn := ir.NewFuncType(base.Pos,
+ ir.NewField(base.Pos, typecheck.Lookup(".this"), nil, rcvr),
+ typecheck.NewFuncParams(method.Type.Params(), true),
+ typecheck.NewFuncParams(method.Type.Results(), false))
+
+ fn := typecheck.DeclFunc(newnam, tfn)
+ fn.SetDupok(true)
+
+ nthis := ir.AsNode(tfn.Type().Recv().Nname)
+
+ methodrcvr := method.Type.Recv().Type
+
+ // generate nil pointer check for better error
+ if rcvr.IsPtr() && rcvr.Elem() == methodrcvr {
+ // generating wrapper from *T to T.
+ n := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ n.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, nthis, typecheck.NodNil())
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)
+ n.Body = []ir.Node{call}
+ fn.Body.Append(n)
+ }
+
+ dot := typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, nthis, method.Sym))
+
+ // generate call
+ // It's not possible to use a tail call when dynamic linking on ppc64le. The
+ // bad scenario is when a local call is made to the wrapper: the wrapper will
+ // call the implementation, which might be in a different module and so set
+ // the TOC to the appropriate value for that module. But if it returns
+ // directly to the wrapper's caller, nothing will reset it to the correct
+ // value for that function.
+ if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) {
+ // generate tail call: adjust pointer receiver and jump to embedded method.
+ left := dot.X // skip final .M
+ if !left.Type().IsPtr() {
+ left = typecheck.NodAddr(left)
+ }
+ as := ir.NewAssignStmt(base.Pos, nthis, typecheck.ConvNop(left, rcvr))
+ fn.Body.Append(as)
+ fn.Body.Append(ir.NewTailCallStmt(base.Pos, method.Nname.(*ir.Name)))
+ } else {
+ fn.SetWrapper(true) // ignore frame for panic+recover matching
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
+ call.Args = ir.ParamNames(tfn.Type())
+ call.IsDDD = tfn.Type().IsVariadic()
+ if method.Type.NumResults() > 0 {
+ ret := ir.NewReturnStmt(base.Pos, nil)
+ ret.Results = []ir.Node{call}
+ fn.Body.Append(ret)
+ } else {
+ fn.Body.Append(call)
+ }
+ }
+
+ typecheck.FinishFuncBody()
+ if base.Debug.DclStack != 0 {
+ types.CheckDclstack()
+ }
+
+ typecheck.Func(fn)
+ ir.CurFunc = fn
+ typecheck.Stmts(fn.Body)
+
+ // Inline calls within (*T).M wrappers. This is safe because we only
+ // generate those wrappers within the same compilation unit as (T).M.
+ // TODO(mdempsky): Investigate why we can't enable this more generally.
+ if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym() != nil {
+ inline.InlineCalls(fn)
+ }
+ escape.Batch([]*ir.Func{fn}, false)
+
+ ir.CurFunc = nil
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+ return lsym
+}
+
+var ZeroSize int64
+
+// MarkTypeUsedInInterface marks that type t is converted to an interface.
+// This information is used in the linker in dead method elimination.
+func MarkTypeUsedInInterface(t *types.Type, from *obj.LSym) {
+ tsym := TypeLinksym(t)
+ // Emit a marker relocation. The linker will know the type is converted
+ // to an interface if "from" is reachable.
+ r := obj.Addrel(from)
+ r.Sym = tsym
+ r.Type = objabi.R_USEIFACE
+}
+
+// MarkUsedIfaceMethod marks that an interface method is used in the current
+// function. n is OCALLINTER node.
+func MarkUsedIfaceMethod(n *ir.CallExpr) {
+ dot := n.X.(*ir.SelectorExpr)
+ ityp := dot.X.Type()
+ tsym := TypeLinksym(ityp)
+ r := obj.Addrel(ir.CurFunc.LSym)
+ r.Sym = tsym
+ // dot.Xoffset is the method index * Widthptr (the offset of code pointer
+ // in itab).
+ midx := dot.Offset() / int64(types.PtrSize)
+ r.Add = InterfaceMethodOffset(ityp, midx)
+ r.Type = objabi.R_USEIFACEMETHOD
}
diff --git a/src/cmd/compile/internal/riscv64/galign.go b/src/cmd/compile/internal/riscv64/galign.go
index 4db0fac52e42b..338248a7cf27a 100644
--- a/src/cmd/compile/internal/riscv64/galign.go
+++ b/src/cmd/compile/internal/riscv64/galign.go
@@ -5,11 +5,11 @@
package riscv64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/riscv"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &riscv.LinkRISCV64
arch.REGSP = riscv.REG_SP
diff --git a/src/cmd/compile/internal/riscv64/ggen.go b/src/cmd/compile/internal/riscv64/ggen.go
index f7c03fe7c2173..9df739456b9e5 100644
--- a/src/cmd/compile/internal/riscv64/ggen.go
+++ b/src/cmd/compile/internal/riscv64/ggen.go
@@ -5,33 +5,36 @@
package riscv64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/riscv"
)
-func zeroRange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
+func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
if cnt == 0 {
return p
}
// Adjust the frame to account for LR.
- off += gc.Ctxt.FixedFrameSize()
+ off += base.Ctxt.FixedFrameSize()
- if cnt < int64(4*gc.Widthptr) {
- for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
- p = pp.Appendpp(p, riscv.AMOV, obj.TYPE_REG, riscv.REG_ZERO, 0, obj.TYPE_MEM, riscv.REG_SP, off+i)
+ if cnt < int64(4*types.PtrSize) {
+ for i := int64(0); i < cnt; i += int64(types.PtrSize) {
+ p = pp.Append(p, riscv.AMOV, obj.TYPE_REG, riscv.REG_ZERO, 0, obj.TYPE_MEM, riscv.REG_SP, off+i)
}
return p
}
- if cnt <= int64(128*gc.Widthptr) {
- p = pp.Appendpp(p, riscv.AADDI, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_A0, 0)
+ if cnt <= int64(128*types.PtrSize) {
+ p = pp.Append(p, riscv.AADDI, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_A0, 0)
p.Reg = riscv.REG_SP
- p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+ p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
- p.To.Offset = 8 * (128 - cnt/int64(gc.Widthptr))
+ p.To.Sym = ir.Syms.Duffzero
+ p.To.Offset = 8 * (128 - cnt/int64(types.PtrSize))
return p
}
@@ -42,15 +45,15 @@ func zeroRange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
// MOV ZERO, (T0)
// ADD $Widthptr, T0
// BNE T0, T1, loop
- p = pp.Appendpp(p, riscv.AADD, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_T0, 0)
+ p = pp.Append(p, riscv.AADD, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_T0, 0)
p.Reg = riscv.REG_SP
- p = pp.Appendpp(p, riscv.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, riscv.REG_T1, 0)
+ p = pp.Append(p, riscv.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, riscv.REG_T1, 0)
p.Reg = riscv.REG_T0
- p = pp.Appendpp(p, riscv.AMOV, obj.TYPE_REG, riscv.REG_ZERO, 0, obj.TYPE_MEM, riscv.REG_T0, 0)
+ p = pp.Append(p, riscv.AMOV, obj.TYPE_REG, riscv.REG_ZERO, 0, obj.TYPE_MEM, riscv.REG_T0, 0)
loop := p
- p = pp.Appendpp(p, riscv.AADD, obj.TYPE_CONST, 0, int64(gc.Widthptr), obj.TYPE_REG, riscv.REG_T0, 0)
- p = pp.Appendpp(p, riscv.ABNE, obj.TYPE_REG, riscv.REG_T0, 0, obj.TYPE_BRANCH, 0, 0)
+ p = pp.Append(p, riscv.AADD, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, riscv.REG_T0, 0)
+ p = pp.Append(p, riscv.ABNE, obj.TYPE_REG, riscv.REG_T0, 0, obj.TYPE_BRANCH, 0, 0)
p.Reg = riscv.REG_T1
- gc.Patch(p, loop)
+ p.To.SetTarget(loop)
return p
}
diff --git a/src/cmd/compile/internal/riscv64/gsubr.go b/src/cmd/compile/internal/riscv64/gsubr.go
index d40bdf7a1d362..74bccf8d42ab1 100644
--- a/src/cmd/compile/internal/riscv64/gsubr.go
+++ b/src/cmd/compile/internal/riscv64/gsubr.go
@@ -5,12 +5,12 @@
package riscv64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/objw"
"cmd/internal/obj"
"cmd/internal/obj/riscv"
)
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
// Hardware nop is ADD $0, ZERO
p := pp.Prog(riscv.AADD)
p.From.Type = obj.TYPE_CONST
diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go
index 0beb5b4bd180a..70c29a4b7b343 100644
--- a/src/cmd/compile/internal/riscv64/ssa.go
+++ b/src/cmd/compile/internal/riscv64/ssa.go
@@ -5,8 +5,10 @@
package riscv64
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/riscv"
@@ -91,7 +93,7 @@ func loadByType(t *types.Type) obj.As {
case 8:
return riscv.AMOVD
default:
- gc.Fatalf("unknown float width for load %d in type %v", width, t)
+ base.Fatalf("unknown float width for load %d in type %v", width, t)
return 0
}
}
@@ -118,7 +120,7 @@ func loadByType(t *types.Type) obj.As {
case 8:
return riscv.AMOV
default:
- gc.Fatalf("unknown width for load %d in type %v", width, t)
+ base.Fatalf("unknown width for load %d in type %v", width, t)
return 0
}
}
@@ -134,7 +136,7 @@ func storeByType(t *types.Type) obj.As {
case 8:
return riscv.AMOVD
default:
- gc.Fatalf("unknown float width for store %d in type %v", width, t)
+ base.Fatalf("unknown float width for store %d in type %v", width, t)
return 0
}
}
@@ -149,7 +151,7 @@ func storeByType(t *types.Type) obj.As {
case 8:
return riscv.AMOV
default:
- gc.Fatalf("unknown width for store %d in type %v", width, t)
+ base.Fatalf("unknown width for store %d in type %v", width, t)
return 0
}
}
@@ -178,9 +180,9 @@ func largestMove(alignment int64) (obj.As, int64) {
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
// RISC-V has no flags, so this is a no-op.
-func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {}
+func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
s.SetPos(v.Pos)
switch v.Op {
@@ -189,7 +191,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpArg:
// input args need no code
case ssa.OpPhi:
- gc.CheckLoweredPhi(v)
+ ssagen.CheckLoweredPhi(v)
case ssa.OpCopy, ssa.OpRISCV64MOVconvert, ssa.OpRISCV64MOVDreg:
if v.Type.IsMemory() {
return
@@ -219,7 +221,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpStoreReg:
@@ -230,7 +232,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
// nothing to do
case ssa.OpRISCV64MOVBreg, ssa.OpRISCV64MOVHreg, ssa.OpRISCV64MOVWreg,
@@ -321,10 +323,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
- gc.AddAux(&p.From, v)
- case *gc.Node:
+ ssagen.AddAux(&p.From, v)
+ case *ir.Name:
wantreg = "SP"
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVW $off(SP), R
wantreg = "SP"
@@ -340,7 +342,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpRISCV64MOVBstore, ssa.OpRISCV64MOVHstore, ssa.OpRISCV64MOVWstore, ssa.OpRISCV64MOVDstore,
@@ -350,14 +352,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpRISCV64MOVBstorezero, ssa.OpRISCV64MOVHstorezero, ssa.OpRISCV64MOVWstorezero, ssa.OpRISCV64MOVDstorezero:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = riscv.REG_ZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpRISCV64SEQZ, ssa.OpRISCV64SNEZ:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
@@ -375,7 +377,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpRISCV64LoweredAtomicLoad8:
@@ -500,7 +502,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p4.From.Reg = riscv.REG_TMP
p4.Reg = riscv.REG_ZERO
p4.To.Type = obj.TYPE_BRANCH
- gc.Patch(p4, p1)
+ p4.To.SetTarget(p1)
p5 := s.Prog(riscv.AMOV)
p5.From.Type = obj.TYPE_CONST
@@ -509,7 +511,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p5.To.Reg = out
p6 := s.Prog(obj.ANOP)
- gc.Patch(p2, p6)
+ p2.To.SetTarget(p6)
case ssa.OpRISCV64LoweredZero:
mov, sz := largestMove(v.AuxInt)
@@ -535,7 +537,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.Reg = v.Args[0].Reg()
p3.From.Type = obj.TYPE_REG
p3.From.Reg = v.Args[1].Reg()
- gc.Patch(p3, p)
+ p3.To.SetTarget(p)
case ssa.OpRISCV64LoweredMove:
mov, sz := largestMove(v.AuxInt)
@@ -575,7 +577,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p5.Reg = v.Args[1].Reg()
p5.From.Type = obj.TYPE_REG
p5.From.Reg = v.Args[2].Reg()
- gc.Patch(p5, p)
+ p5.To.SetTarget(p)
case ssa.OpRISCV64LoweredNilCheck:
// Issue a load which will fault if arg is nil.
@@ -583,22 +585,22 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(riscv.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = riscv.REG_ZERO
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpRISCV64LoweredGetClosurePtr:
// Closure pointer is S4 (riscv.REG_CTXT).
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpRISCV64LoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(riscv.AMOV)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -612,14 +614,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffzero
+ p.To.Sym = ir.Syms.Duffzero
p.To.Offset = v.AuxInt
case ssa.OpRISCV64DUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.Duffcopy
+ p.To.Sym = ir.Syms.Duffcopy
p.To.Offset = v.AuxInt
default:
@@ -642,7 +644,7 @@ var blockBranch = [...]obj.As{
ssa.BlockRISCV64BNEZ: riscv.ABNEZ,
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.SetPos(b.Pos)
switch b.Kind {
@@ -655,17 +657,17 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.From.Type = obj.TYPE_REG
p.From.Reg = riscv.REG_ZERO
p.Reg = riscv.REG_A0
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
diff --git a/src/cmd/compile/internal/s390x/galign.go b/src/cmd/compile/internal/s390x/galign.go
index cb68fd36c14bf..b004a2db0a39b 100644
--- a/src/cmd/compile/internal/s390x/galign.go
+++ b/src/cmd/compile/internal/s390x/galign.go
@@ -5,11 +5,11 @@
package s390x
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/s390x"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &s390x.Links390x
arch.REGSP = s390x.REGSP
arch.MAXWIDTH = 1 << 50
diff --git a/src/cmd/compile/internal/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go
index 5a837d8574236..488a080c46888 100644
--- a/src/cmd/compile/internal/s390x/ggen.go
+++ b/src/cmd/compile/internal/s390x/ggen.go
@@ -5,7 +5,8 @@
package s390x
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/objw"
"cmd/internal/obj"
"cmd/internal/obj/s390x"
)
@@ -17,20 +18,20 @@ import (
const clearLoopCutoff = 1024
// zerorange clears the stack in the given range.
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
if cnt == 0 {
return p
}
// Adjust the frame to account for LR.
- off += gc.Ctxt.FixedFrameSize()
+ off += base.Ctxt.FixedFrameSize()
reg := int16(s390x.REGSP)
// If the off cannot fit in a 12-bit unsigned displacement then we
// need to create a copy of the stack pointer that we can adjust.
// We also need to do this if we are going to loop.
if off < 0 || off > 4096-clearLoopCutoff || cnt > clearLoopCutoff {
- p = pp.Appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, off, obj.TYPE_REG, s390x.REGRT1, 0)
+ p = pp.Append(p, s390x.AADD, obj.TYPE_CONST, 0, off, obj.TYPE_REG, s390x.REGRT1, 0)
p.Reg = int16(s390x.REGSP)
reg = s390x.REGRT1
off = 0
@@ -39,12 +40,12 @@ func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
// Generate a loop of large clears.
if cnt > clearLoopCutoff {
ireg := int16(s390x.REGRT2) // register holds number of remaining loop iterations
- p = pp.Appendpp(p, s390x.AMOVD, obj.TYPE_CONST, 0, cnt/256, obj.TYPE_REG, ireg, 0)
- p = pp.Appendpp(p, s390x.ACLEAR, obj.TYPE_CONST, 0, 256, obj.TYPE_MEM, reg, off)
+ p = pp.Append(p, s390x.AMOVD, obj.TYPE_CONST, 0, cnt/256, obj.TYPE_REG, ireg, 0)
+ p = pp.Append(p, s390x.ACLEAR, obj.TYPE_CONST, 0, 256, obj.TYPE_MEM, reg, off)
pl := p
- p = pp.Appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, 256, obj.TYPE_REG, reg, 0)
- p = pp.Appendpp(p, s390x.ABRCTG, obj.TYPE_REG, ireg, 0, obj.TYPE_BRANCH, 0, 0)
- gc.Patch(p, pl)
+ p = pp.Append(p, s390x.AADD, obj.TYPE_CONST, 0, 256, obj.TYPE_REG, reg, 0)
+ p = pp.Append(p, s390x.ABRCTG, obj.TYPE_REG, ireg, 0, obj.TYPE_BRANCH, 0, 0)
+ p.To.SetTarget(pl)
cnt = cnt % 256
}
@@ -69,11 +70,11 @@ func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
case 2:
ins = s390x.AMOVH
}
- p = pp.Appendpp(p, ins, obj.TYPE_CONST, 0, 0, obj.TYPE_MEM, reg, off)
+ p = pp.Append(p, ins, obj.TYPE_CONST, 0, 0, obj.TYPE_MEM, reg, off)
// Handle clears that would require multiple move instructions with CLEAR (assembled as XC).
default:
- p = pp.Appendpp(p, s390x.ACLEAR, obj.TYPE_CONST, 0, n, obj.TYPE_MEM, reg, off)
+ p = pp.Append(p, s390x.ACLEAR, obj.TYPE_CONST, 0, n, obj.TYPE_MEM, reg, off)
}
cnt -= n
@@ -83,6 +84,6 @@ func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
return pp.Prog(s390x.ANOPH)
}
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
index 8037357131c79..d4c7a286e2688 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -7,16 +7,17 @@ package s390x
import (
"math"
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/s390x"
)
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
-func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
flive := b.FlagsLiveAtEnd
for _, c := range b.ControlValues() {
flive = c.Type.IsFlags() || flive
@@ -134,7 +135,7 @@ func moveByType(t *types.Type) obj.As {
// dest := dest(To) op src(From)
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
-func opregreg(s *gc.SSAGenState, op obj.As, dest, src int16) *obj.Prog {
+func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
p := s.Prog(op)
p.From.Type = obj.TYPE_REG
p.To.Type = obj.TYPE_REG
@@ -147,7 +148,7 @@ func opregreg(s *gc.SSAGenState, op obj.As, dest, src int16) *obj.Prog {
// dest := src(From) op off
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
-func opregregimm(s *gc.SSAGenState, op obj.As, dest, src int16, off int64) *obj.Prog {
+func opregregimm(s *ssagen.State, op obj.As, dest, src int16, off int64) *obj.Prog {
p := s.Prog(op)
p.From.Type = obj.TYPE_CONST
p.From.Offset = off
@@ -157,7 +158,7 @@ func opregregimm(s *gc.SSAGenState, op obj.As, dest, src int16, off int64) *obj.
return p
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpS390XSLD, ssa.OpS390XSLW,
ssa.OpS390XSRD, ssa.OpS390XSRW,
@@ -394,14 +395,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Type = obj.TYPE_ADDR
p.From.Reg = r
p.From.Index = i
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpS390XMOVDaddr:
p := s.Prog(s390x.AMOVD)
p.From.Type = obj.TYPE_ADDR
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpS390XCMP, ssa.OpS390XCMPW, ssa.OpS390XCMPU, ssa.OpS390XCMPWU:
@@ -447,7 +448,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[1].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpS390XMOVDload,
@@ -458,7 +459,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpS390XMOVBZloadidx, ssa.OpS390XMOVHZloadidx, ssa.OpS390XMOVWZloadidx,
@@ -475,7 +476,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r
p.From.Scale = 1
p.From.Index = i
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpS390XMOVBstore, ssa.OpS390XMOVHstore, ssa.OpS390XMOVWstore, ssa.OpS390XMOVDstore,
@@ -486,7 +487,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpS390XMOVBstoreidx, ssa.OpS390XMOVHstoreidx, ssa.OpS390XMOVWstoreidx, ssa.OpS390XMOVDstoreidx,
ssa.OpS390XMOVHBRstoreidx, ssa.OpS390XMOVWBRstoreidx, ssa.OpS390XMOVDBRstoreidx,
ssa.OpS390XFMOVSstoreidx, ssa.OpS390XFMOVDstoreidx:
@@ -502,7 +503,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = r
p.To.Scale = 1
p.To.Index = i
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpS390XMOVDstoreconst, ssa.OpS390XMOVWstoreconst, ssa.OpS390XMOVHstoreconst, ssa.OpS390XMOVBstoreconst:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
@@ -510,7 +511,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = sc.Val()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, sc.Off())
+ ssagen.AddAux2(&p.To, v, sc.Off())
case ssa.OpS390XMOVBreg, ssa.OpS390XMOVHreg, ssa.OpS390XMOVWreg,
ssa.OpS390XMOVBZreg, ssa.OpS390XMOVHZreg, ssa.OpS390XMOVWZreg,
ssa.OpS390XLDGR, ssa.OpS390XLGDR,
@@ -529,7 +530,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = sc.Val()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, sc.Off())
+ ssagen.AddAux2(&p.To, v, sc.Off())
case ssa.OpCopy:
if v.Type.IsMemory() {
return
@@ -545,7 +546,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpStoreReg:
@@ -556,10 +557,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.OpS390XLoweredGetClosurePtr:
// Closure pointer is R12 (already)
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpS390XLoweredRound32F, ssa.OpS390XLoweredRound64F:
// input is already rounded
case ssa.OpS390XLoweredGetG:
@@ -573,7 +574,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(s390x.AMOVD)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -592,7 +593,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpS390XFLOGR, ssa.OpS390XPOPCNT,
ssa.OpS390XNEG, ssa.OpS390XNEGW,
@@ -636,14 +637,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(s390x.AMOVBZ)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = s390x.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpS390XMVC:
vo := v.AuxValAndOff()
@@ -671,7 +672,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.Reg = v.Args[len(v.Args)-2].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpS390XLoweredMove:
// Inputs must be valid pointers to memory,
// so adjust arg0 and arg1 as part of the expansion.
@@ -708,7 +709,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
bne := s.Prog(s390x.ABLT)
bne.To.Type = obj.TYPE_BRANCH
- gc.Patch(bne, mvc)
+ bne.To.SetTarget(mvc)
if v.AuxInt > 0 {
mvc := s.Prog(s390x.AMVC)
@@ -750,7 +751,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
bne := s.Prog(s390x.ABLT)
bne.To.Type = obj.TYPE_BRANCH
- gc.Patch(bne, clear)
+ bne.To.SetTarget(clear)
if v.AuxInt > 0 {
clear := s.Prog(s390x.ACLEAR)
@@ -763,7 +764,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpS390XMOVBatomicstore, ssa.OpS390XMOVWatomicstore, ssa.OpS390XMOVDatomicstore:
@@ -772,7 +773,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpS390XLAN, ssa.OpS390XLAO:
// LA(N|O) Ry, TMP, 0(Rx)
op := s.Prog(v.Op.Asm())
@@ -807,7 +808,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.OpS390XLoweredAtomicCas32, ssa.OpS390XLoweredAtomicCas64:
// Convert the flags output of CS{,G} into a bool.
// CS{,G} arg1, arg2, arg0
@@ -823,7 +824,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
cs.Reg = v.Args[2].Reg() // new
cs.To.Type = obj.TYPE_MEM
cs.To.Reg = v.Args[0].Reg()
- gc.AddAux(&cs.To, v)
+ ssagen.AddAux(&cs.To, v)
// MOVD $0, ret
movd := s.Prog(s390x.AMOVD)
@@ -845,7 +846,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// NOP (so the BNE has somewhere to land)
nop := s.Prog(obj.ANOP)
- gc.Patch(bne, nop)
+ bne.To.SetTarget(nop)
case ssa.OpS390XLoweredAtomicExchange32, ssa.OpS390XLoweredAtomicExchange64:
// Loop until the CS{,G} succeeds.
// MOV{WZ,D} arg0, ret
@@ -858,7 +859,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
load.From.Reg = v.Args[0].Reg()
load.To.Type = obj.TYPE_REG
load.To.Reg = v.Reg0()
- gc.AddAux(&load.From, v)
+ ssagen.AddAux(&load.From, v)
// CS{,G} ret, arg1, arg0
cs := s.Prog(v.Op.Asm())
@@ -867,12 +868,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
cs.Reg = v.Args[1].Reg() // new
cs.To.Type = obj.TYPE_MEM
cs.To.Reg = v.Args[0].Reg()
- gc.AddAux(&cs.To, v)
+ ssagen.AddAux(&cs.To, v)
// BNE cs
bne := s.Prog(s390x.ABNE)
bne.To.Type = obj.TYPE_BRANCH
- gc.Patch(bne, cs)
+ bne.To.SetTarget(cs)
case ssa.OpS390XSYNC:
s.Prog(s390x.ASYNC)
case ssa.OpClobber:
@@ -907,14 +908,14 @@ func blockAsm(b *ssa.Block) obj.As {
panic("unreachable")
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
// Handle generic blocks first.
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(s390x.ABR)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
return
case ssa.BlockDefer:
diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go
index 519ac214caef2..937c757b2153b 100644
--- a/src/cmd/compile/internal/ssa/block.go
+++ b/src/cmd/compile/internal/ssa/block.go
@@ -52,7 +52,7 @@ type Block struct {
Controls [2]*Value
// Auxiliary info for the block. Its value depends on the Kind.
- Aux interface{}
+ Aux Aux
AuxInt int64
// The unordered set of Values that define the operation of this block.
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index 2dade7a88d947..9e4aa6cd79f27 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -166,7 +166,7 @@ func checkFunc(f *Func) {
f.Fatalf("value %v has an AuxInt that encodes a NaN", v)
}
case auxString:
- if _, ok := v.Aux.(string); !ok {
+ if _, ok := v.Aux.(stringAux); !ok {
f.Fatalf("value %v has Aux type %T, want string", v, v.Aux)
}
canHaveAux = true
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index 63994d1778ed0..c267274366b35 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -431,7 +431,6 @@ var passes = [...]pass{
{name: "early copyelim", fn: copyelim},
{name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt
{name: "short circuit", fn: shortcircuit},
- {name: "decompose args", fn: decomposeArgs, required: !go116lateCallExpansion, disabled: go116lateCallExpansion}, // handled by late call lowering
{name: "decompose user", fn: decomposeUser, required: true},
{name: "pre-opt deadcode", fn: deadcode},
{name: "opt", fn: opt, required: true}, // NB: some generic rules know the name of the opt pass. TODO: split required rules and optimizing rules
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 0fe0337ddfa30..c29bc8fae6f70 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -5,6 +5,8 @@
package ssa
import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
@@ -138,7 +140,7 @@ type Frontend interface {
// Auto returns a Node for an auto variable of the given type.
// The SSA compiler uses this function to allocate space for spills.
- Auto(src.XPos, *types.Type) GCNode
+ Auto(src.XPos, *types.Type) *ir.Name
// Given the name for a compound type, returns the name we should use
// for the parts of that compound type.
@@ -178,32 +180,6 @@ type Frontend interface {
MyImportPath() string
}
-// interface used to hold a *gc.Node (a stack variable).
-// We'd use *gc.Node directly but that would lead to an import cycle.
-type GCNode interface {
- Typ() *types.Type
- String() string
- IsSynthetic() bool
- IsAutoTmp() bool
- StorageClass() StorageClass
-}
-
-type StorageClass uint8
-
-const (
- ClassAuto StorageClass = iota // local stack variable
- ClassParam // argument
- ClassParamOut // return value
-)
-
-const go116lateCallExpansion = true
-
-// LateCallExpansionEnabledWithin returns true if late call expansion should be tested
-// within compilation of a function/method.
-func LateCallExpansionEnabledWithin(f *Func) bool {
- return go116lateCallExpansion
-}
-
// NewConfig returns a new configuration object for the given architecture.
func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config {
c := &Config{arch: arch, Types: types}
@@ -219,9 +195,10 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.registers = registersAMD64[:]
c.gpRegMask = gpRegMaskAMD64
c.fpRegMask = fpRegMaskAMD64
+ c.specialRegMask = specialRegMaskAMD64
c.FPReg = framepointerRegAMD64
c.LinkReg = linkRegAMD64
- c.hasGReg = false
+ c.hasGReg = base.Flag.ABIWrap
case "386":
c.PtrSize = 4
c.RegSize = 4
diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
index 3b4f2be37e7be..f78527410c8dd 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -275,7 +275,7 @@ func lt2Cmp(isLt bool) types.Cmp {
return types.CMPgt
}
-type auxmap map[interface{}]int32
+type auxmap map[Aux]int32
func cmpVal(v, w *Value, auxIDs auxmap) types.Cmp {
// Try to order these comparison by cost (cheaper first)
diff --git a/src/cmd/compile/internal/ssa/cse_test.go b/src/cmd/compile/internal/ssa/cse_test.go
index 9e76645f54fa5..8052016f3af69 100644
--- a/src/cmd/compile/internal/ssa/cse_test.go
+++ b/src/cmd/compile/internal/ssa/cse_test.go
@@ -14,6 +14,8 @@ type tstAux struct {
s string
}
+func (*tstAux) CanBeAnSSAAux() {}
+
// This tests for a bug found when partitioning, but not sorting by the Aux value.
func TestCSEAuxPartitionBug(t *testing.T) {
c := testConfig(t)
diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go
index 0664013b397ed..31d3f62d4e7ce 100644
--- a/src/cmd/compile/internal/ssa/deadstore.go
+++ b/src/cmd/compile/internal/ssa/deadstore.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/src"
)
@@ -136,9 +137,9 @@ func dse(f *Func) {
// reaches stores then we delete all the stores. The other operations will then
// be eliminated by the dead code elimination pass.
func elimDeadAutosGeneric(f *Func) {
- addr := make(map[*Value]GCNode) // values that the address of the auto reaches
- elim := make(map[*Value]GCNode) // values that could be eliminated if the auto is
- used := make(map[GCNode]bool) // used autos that must be kept
+ addr := make(map[*Value]*ir.Name) // values that the address of the auto reaches
+ elim := make(map[*Value]*ir.Name) // values that could be eliminated if the auto is
+ var used ir.NameSet // used autos that must be kept
// visit the value and report whether any of the maps are updated
visit := func(v *Value) (changed bool) {
@@ -146,8 +147,8 @@ func elimDeadAutosGeneric(f *Func) {
switch v.Op {
case OpAddr, OpLocalAddr:
// Propagate the address if it points to an auto.
- n, ok := v.Aux.(GCNode)
- if !ok || n.StorageClass() != ClassAuto {
+ n, ok := v.Aux.(*ir.Name)
+ if !ok || n.Class != ir.PAUTO {
return
}
if addr[v] == nil {
@@ -157,8 +158,8 @@ func elimDeadAutosGeneric(f *Func) {
return
case OpVarDef, OpVarKill:
// v should be eliminated if we eliminate the auto.
- n, ok := v.Aux.(GCNode)
- if !ok || n.StorageClass() != ClassAuto {
+ n, ok := v.Aux.(*ir.Name)
+ if !ok || n.Class != ir.PAUTO {
return
}
if elim[v] == nil {
@@ -173,12 +174,12 @@ func elimDeadAutosGeneric(f *Func) {
// for open-coded defers from being removed (since they
// may not be used by the inline code, but will be used by
// panic processing).
- n, ok := v.Aux.(GCNode)
- if !ok || n.StorageClass() != ClassAuto {
+ n, ok := v.Aux.(*ir.Name)
+ if !ok || n.Class != ir.PAUTO {
return
}
- if !used[n] {
- used[n] = true
+ if !used.Has(n) {
+ used.Add(n)
changed = true
}
return
@@ -211,8 +212,8 @@ func elimDeadAutosGeneric(f *Func) {
if v.Type.IsMemory() || v.Type.IsFlags() || v.Op == OpPhi || v.MemoryArg() != nil {
for _, a := range args {
if n, ok := addr[a]; ok {
- if !used[n] {
- used[n] = true
+ if !used.Has(n) {
+ used.Add(n)
changed = true
}
}
@@ -221,9 +222,9 @@ func elimDeadAutosGeneric(f *Func) {
}
// Propagate any auto addresses through v.
- node := GCNode(nil)
+ var node *ir.Name
for _, a := range args {
- if n, ok := addr[a]; ok && !used[n] {
+ if n, ok := addr[a]; ok && !used.Has(n) {
if node == nil {
node = n
} else if node != n {
@@ -232,7 +233,7 @@ func elimDeadAutosGeneric(f *Func) {
// multiple pointers (e.g. NeqPtr, Phi etc.).
// This is rare, so just propagate the first
// value to keep things simple.
- used[n] = true
+ used.Add(n)
changed = true
}
}
@@ -248,7 +249,7 @@ func elimDeadAutosGeneric(f *Func) {
}
if addr[v] != node {
// This doesn't happen in practice, but catch it just in case.
- used[node] = true
+ used.Add(node)
changed = true
}
return
@@ -268,8 +269,8 @@ func elimDeadAutosGeneric(f *Func) {
}
// keep the auto if its address reaches a control value
for _, c := range b.ControlValues() {
- if n, ok := addr[c]; ok && !used[n] {
- used[n] = true
+ if n, ok := addr[c]; ok && !used.Has(n) {
+ used.Add(n)
changed = true
}
}
@@ -281,7 +282,7 @@ func elimDeadAutosGeneric(f *Func) {
// Eliminate stores to unread autos.
for v, n := range elim {
- if used[n] {
+ if used.Has(n) {
continue
}
// replace with OpCopy
@@ -298,15 +299,15 @@ func elimUnreadAutos(f *Func) {
// Loop over all ops that affect autos taking note of which
// autos we need and also stores that we might be able to
// eliminate.
- seen := make(map[GCNode]bool)
+ var seen ir.NameSet
var stores []*Value
for _, b := range f.Blocks {
for _, v := range b.Values {
- n, ok := v.Aux.(GCNode)
+ n, ok := v.Aux.(*ir.Name)
if !ok {
continue
}
- if n.StorageClass() != ClassAuto {
+ if n.Class != ir.PAUTO {
continue
}
@@ -316,7 +317,7 @@ func elimUnreadAutos(f *Func) {
// If we haven't seen the auto yet
// then this might be a store we can
// eliminate.
- if !seen[n] {
+ if !seen.Has(n) {
stores = append(stores, v)
}
default:
@@ -326,7 +327,7 @@ func elimUnreadAutos(f *Func) {
// because dead loads haven't been
// eliminated yet.
if v.Uses > 0 {
- seen[n] = true
+ seen.Add(n)
}
}
}
@@ -334,8 +335,8 @@ func elimUnreadAutos(f *Func) {
// Eliminate stores to unread autos.
for _, store := range stores {
- n, _ := store.Aux.(GCNode)
- if seen[n] {
+ n, _ := store.Aux.(*ir.Name)
+ if seen.Has(n) {
continue
}
diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go
index 6353f72897858..68b6ab5fe9e76 100644
--- a/src/cmd/compile/internal/ssa/debug.go
+++ b/src/cmd/compile/internal/ssa/debug.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/internal/dwarf"
"cmd/internal/obj"
"encoding/hex"
@@ -24,7 +25,7 @@ type FuncDebug struct {
// Slots is all the slots used in the debug info, indexed by their SlotID.
Slots []LocalSlot
// The user variables, indexed by VarID.
- Vars []GCNode
+ Vars []*ir.Name
// The slots that make up each variable, indexed by VarID.
VarSlots [][]SlotID
// The location list data, indexed by VarID. Must be processed by PutLocationList.
@@ -142,13 +143,13 @@ func (loc VarLoc) absent() bool {
var BlockStart = &Value{
ID: -10000,
Op: OpInvalid,
- Aux: "BlockStart",
+ Aux: StringToAux("BlockStart"),
}
var BlockEnd = &Value{
ID: -20000,
Op: OpInvalid,
- Aux: "BlockEnd",
+ Aux: StringToAux("BlockEnd"),
}
// RegisterSet is a bitmap of registers, indexed by Register.num.
@@ -165,7 +166,7 @@ func (s *debugState) logf(msg string, args ...interface{}) {
type debugState struct {
// See FuncDebug.
slots []LocalSlot
- vars []GCNode
+ vars []*ir.Name
varSlots [][]SlotID
lists [][]byte
@@ -189,7 +190,7 @@ type debugState struct {
// The pending location list entry for each user variable, indexed by VarID.
pendingEntries []pendingEntry
- varParts map[GCNode][]SlotID
+ varParts map[*ir.Name][]SlotID
blockDebug []BlockDebug
pendingSlotLocs []VarLoc
liveSlots []liveSlot
@@ -346,7 +347,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
}
if state.varParts == nil {
- state.varParts = make(map[GCNode][]SlotID)
+ state.varParts = make(map[*ir.Name][]SlotID)
} else {
for n := range state.varParts {
delete(state.varParts, n)
@@ -360,7 +361,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
state.vars = state.vars[:0]
for i, slot := range f.Names {
state.slots = append(state.slots, slot)
- if slot.N.IsSynthetic() {
+ if ir.IsSynthetic(slot.N) {
continue
}
@@ -379,8 +380,8 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
for _, b := range f.Blocks {
for _, v := range b.Values {
if v.Op == OpVarDef || v.Op == OpVarKill {
- n := v.Aux.(GCNode)
- if n.IsSynthetic() {
+ n := v.Aux.(*ir.Name)
+ if ir.IsSynthetic(n) {
continue
}
@@ -425,7 +426,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
state.initializeCache(f, len(state.varParts), len(state.slots))
for i, slot := range f.Names {
- if slot.N.IsSynthetic() {
+ if ir.IsSynthetic(slot.N) {
continue
}
for _, value := range f.NamedValues[slot] {
@@ -717,8 +718,8 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register)
switch {
case v.Op == OpVarDef, v.Op == OpVarKill:
- n := v.Aux.(GCNode)
- if n.IsSynthetic() {
+ n := v.Aux.(*ir.Name)
+ if ir.IsSynthetic(n) {
break
}
diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go
index bf7f1e826b17e..ea988e44f61a6 100644
--- a/src/cmd/compile/internal/ssa/decompose.go
+++ b/src/cmd/compile/internal/ssa/decompose.go
@@ -219,10 +219,6 @@ func decomposeInterfacePhi(v *Value) {
v.AddArg(data)
}
-func decomposeArgs(f *Func) {
- applyRewrite(f, rewriteBlockdecArgs, rewriteValuedecArgs, removeDeadValues)
-}
-
func decomposeUser(f *Func) {
for _, b := range f.Blocks {
for _, v := range b.Values {
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index 679ee8ad1650d..579818e4f3cf7 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -24,653 +24,670 @@ type offsetKey struct {
pt *types.Type
}
-// expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form
-// that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into
-// more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are
-// reached. On the callee side, OpArg nodes are not decomposed until this phase is run.
-// TODO results should not be lowered until this phase.
-func expandCalls(f *Func) {
- // Calls that need lowering have some number of inputs, including a memory input,
- // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
-
- // With the current ABI those inputs need to be converted into stores to memory,
- // rethreading the call's memory input to the first, and the new call now receiving the last.
-
- // With the current ABI, the outputs need to be converted to loads, which will all use the call's
- // memory output as their input.
- if !LateCallExpansionEnabledWithin(f) {
- return
- }
- debug := f.pass.debug > 0
+func isBlockMultiValueExit(b *Block) bool {
+ return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && len(b.Controls) > 0 && b.Controls[0].Op == OpMakeResult
+}
- if debug {
- fmt.Printf("\nexpandsCalls(%s)\n", f.Name)
+// removeTrivialWrapperTypes unwraps layers of
+// struct { singleField SomeType } and [1]SomeType
+// until a non-wrapper type is reached. This is useful
+// for working with assignments to/from interface data
+// fields (either second operand to OpIMake or OpIData)
+// where the wrapping or type conversion can be elided
+// because of type conversions/assertions in source code
+// that do not appear in SSA.
+func removeTrivialWrapperTypes(t *types.Type) *types.Type {
+ for {
+ if t.IsStruct() && t.NumFields() == 1 {
+ t = t.Field(0).Type
+ continue
+ }
+ if t.IsArray() && t.NumElem() == 1 {
+ t = t.Elem()
+ continue
+ }
+ break
}
+ return t
+}
- canSSAType := f.fe.CanSSA
- regSize := f.Config.RegSize
- sp, _ := f.spSb()
- typ := &f.Config.Types
- ptrSize := f.Config.PtrSize
+type expandState struct {
+ f *Func
+ debug bool
+ canSSAType func(*types.Type) bool
+ regSize int64
+ sp *Value
+ typs *Types
+ ptrSize int64
+ hiOffset int64
+ lowOffset int64
+ namedSelects map[*Value][]namedVal
+ sdom SparseTree
+ common map[selKey]*Value
+ offsets map[offsetKey]*Value
+}
- // For 32-bit, need to deal with decomposition of 64-bit integers, which depends on endianness.
- var hiOffset, lowOffset int64
- if f.Config.BigEndian {
- lowOffset = 4
- } else {
- hiOffset = 4
+// intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target
+// that has no 64-bit integer registers.
+func (x *expandState) intPairTypes(et types.Kind) (tHi, tLo *types.Type) {
+ tHi = x.typs.UInt32
+ if et == types.TINT64 {
+ tHi = x.typs.Int32
}
+ tLo = x.typs.UInt32
+ return
+}
- namedSelects := make(map[*Value][]namedVal)
-
- sdom := f.Sdom()
-
- common := make(map[selKey]*Value)
-
- // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target
- // that has no 64-bit integer registers.
- intPairTypes := func(et types.EType) (tHi, tLo *types.Type) {
- tHi = typ.UInt32
- if et == types.TINT64 {
- tHi = typ.Int32
- }
- tLo = typ.UInt32
- return
+// isAlreadyExpandedAggregateType returns whether a type is an SSA-able "aggregate" (multiple register) type
+// that was expanded in an earlier phase (currently, expand_calls is intended to run after decomposeBuiltin,
+// so this is all aggregate types -- small struct and array, complex, interface, string, slice, and 64-bit
+// integer on 32-bit).
+func (x *expandState) isAlreadyExpandedAggregateType(t *types.Type) bool {
+ if !x.canSSAType(t) {
+ return false
}
+ return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice() ||
+ t.Size() > x.regSize && t.IsInteger()
+}
- // isAlreadyExpandedAggregateType returns whether a type is an SSA-able "aggregate" (multiple register) type
- // that was expanded in an earlier phase (currently, expand_calls is intended to run after decomposeBuiltin,
- // so this is all aggregate types -- small struct and array, complex, interface, string, slice, and 64-bit
- // integer on 32-bit).
- isAlreadyExpandedAggregateType := func(t *types.Type) bool {
- if !canSSAType(t) {
- return false
- }
- return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice() ||
- t.Size() > regSize && t.IsInteger()
+// offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP
+// TODO should also optimize offsets from SB?
+func (x *expandState) offsetFrom(from *Value, offset int64, pt *types.Type) *Value {
+ if offset == 0 && from.Type == pt { // this is not actually likely
+ return from
}
-
- offsets := make(map[offsetKey]*Value)
-
- // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP
- // TODO should also optimize offsets from SB?
- offsetFrom := func(from *Value, offset int64, pt *types.Type) *Value {
- if offset == 0 && from.Type == pt { // this is not actually likely
- return from
- }
- // Simplify, canonicalize
- for from.Op == OpOffPtr {
- offset += from.AuxInt
- from = from.Args[0]
- }
- if from == sp {
- return f.ConstOffPtrSP(pt, offset, sp)
- }
- key := offsetKey{from, offset, pt}
- v := offsets[key]
- if v != nil {
- return v
- }
- v = from.Block.NewValue1I(from.Pos.WithNotStmt(), OpOffPtr, pt, offset, from)
- offsets[key] = v
+ // Simplify, canonicalize
+ for from.Op == OpOffPtr {
+ offset += from.AuxInt
+ from = from.Args[0]
+ }
+ if from == x.sp {
+ return x.f.ConstOffPtrSP(pt, offset, x.sp)
+ }
+ key := offsetKey{from, offset, pt}
+ v := x.offsets[key]
+ if v != nil {
return v
}
+ v = from.Block.NewValue1I(from.Pos.WithNotStmt(), OpOffPtr, pt, offset, from)
+ x.offsets[key] = v
+ return v
+}
- // splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates.
- splitSlots := func(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot {
- var locs []LocalSlot
- for i := range ls {
- locs = append(locs, f.fe.SplitSlot(&ls[i], sfx, offset, ty))
- }
- return locs
+// splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates.
+func (x *expandState) splitSlots(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot {
+ var locs []LocalSlot
+ for i := range ls {
+ locs = append(locs, x.f.fe.SplitSlot(&ls[i], sfx, offset, ty))
}
+ return locs
+}
- // removeTrivialWrapperTypes unwraps layers of
- // struct { singleField SomeType } and [1]SomeType
- // until a non-wrapper type is reached. This is useful
- // for working with assignments to/from interface data
- // fields (either second operand to OpIMake or OpIData)
- // where the wrapping or type conversion can be elided
- // because of type conversions/assertions in source code
- // that do not appear in SSA.
- removeTrivialWrapperTypes := func(t *types.Type) *types.Type {
- for {
- if t.IsStruct() && t.NumFields() == 1 {
- t = t.Field(0).Type
- continue
- }
- if t.IsArray() && t.NumElem() == 1 {
- t = t.Elem()
- continue
- }
- break
- }
- return t
+// Calls that need lowering have some number of inputs, including a memory input,
+// and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
+
+// With the current ABI those inputs need to be converted into stores to memory,
+// rethreading the call's memory input to the first, and the new call now receiving the last.
+
+// With the current ABI, the outputs need to be converted to loads, which will all use the call's
+// memory output as their input.
+
+// rewriteSelect recursively walks from leaf selector to a root (OpSelectN, OpLoad, OpArg)
+// through a chain of Struct/Array/builtin Select operations. If the chain of selectors does not
+// end in an expected root, it does nothing (this can happen depending on compiler phase ordering).
+// The "leaf" provides the type, the root supplies the container, and the leaf-to-root path
+// accumulates the offset.
+// It emits the code necessary to implement the leaf select operation that leads to the root.
+//
+// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
+func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64) []LocalSlot {
+ if x.debug {
+ fmt.Printf("rewriteSelect(%s, %s, %d)\n", leaf.LongString(), selector.LongString(), offset)
}
-
- // Calls that need lowering have some number of inputs, including a memory input,
- // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
-
- // With the current ABI those inputs need to be converted into stores to memory,
- // rethreading the call's memory input to the first, and the new call now receiving the last.
-
- // With the current ABI, the outputs need to be converted to loads, which will all use the call's
- // memory output as their input.
-
- // rewriteSelect recursively walks from leaf selector to a root (OpSelectN, OpLoad, OpArg)
- // through a chain of Struct/Array/builtin Select operations. If the chain of selectors does not
- // end in an expected root, it does nothing (this can happen depending on compiler phase ordering).
- // The "leaf" provides the type, the root supplies the container, and the leaf-to-root path
- // accumulates the offset.
- // It emits the code necessary to implement the leaf select operation that leads to the root.
- //
- // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
- var rewriteSelect func(leaf *Value, selector *Value, offset int64) []LocalSlot
- rewriteSelect = func(leaf *Value, selector *Value, offset int64) []LocalSlot {
- if debug {
- fmt.Printf("rewriteSelect(%s, %s, %d)\n", leaf.LongString(), selector.LongString(), offset)
- }
- var locs []LocalSlot
- leafType := leaf.Type
- if len(selector.Args) > 0 {
- w := selector.Args[0]
- if w.Op == OpCopy {
- for w.Op == OpCopy {
- w = w.Args[0]
- }
- selector.SetArg(0, w)
+ var locs []LocalSlot
+ leafType := leaf.Type
+ if len(selector.Args) > 0 {
+ w := selector.Args[0]
+ if w.Op == OpCopy {
+ for w.Op == OpCopy {
+ w = w.Args[0]
}
+ selector.SetArg(0, w)
}
- switch selector.Op {
- case OpArg:
- if !isAlreadyExpandedAggregateType(selector.Type) {
- if leafType == selector.Type { // OpIData leads us here, sometimes.
- leaf.copyOf(selector)
- } else {
- f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
- }
- if debug {
- fmt.Printf("\tOpArg, break\n")
- }
- break
- }
- switch leaf.Op {
- case OpIData, OpStructSelect, OpArraySelect:
- leafType = removeTrivialWrapperTypes(leaf.Type)
- }
- aux := selector.Aux
- auxInt := selector.AuxInt + offset
- if leaf.Block == selector.Block {
- leaf.reset(OpArg)
- leaf.Aux = aux
- leaf.AuxInt = auxInt
- leaf.Type = leafType
+ }
+ switch selector.Op {
+ case OpArg:
+ if !x.isAlreadyExpandedAggregateType(selector.Type) {
+ if leafType == selector.Type { // OpIData leads us here, sometimes.
+ leaf.copyOf(selector)
} else {
- w := selector.Block.NewValue0IA(leaf.Pos, OpArg, leafType, auxInt, aux)
- leaf.copyOf(w)
- if debug {
- fmt.Printf("\tnew %s\n", w.LongString())
- }
- }
- for _, s := range namedSelects[selector] {
- locs = append(locs, f.Names[s.locIndex])
+ x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
}
-
- case OpLoad: // We end up here because of IData of immediate structures.
- // Failure case:
- // (note the failure case is very rare; w/o this case, make.bash and run.bash both pass, as well as
- // the hard cases of building {syscall,math,math/cmplx,math/bits,go/constant} on ppc64le and mips-softfloat).
- //
- // GOSSAFUNC='(*dumper).dump' go build -gcflags=-l -tags=math_big_pure_go cmd/compile/internal/gc
- // cmd/compile/internal/gc/dump.go:136:14: internal compiler error: '(*dumper).dump': not lowered: v827, StructSelect PTR PTR
- // b2: ← b1
- // v20 (+142) = StaticLECall {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v8 v1
- // v21 (142) = SelectN [1] v20
- // v22 (142) = SelectN [0] v20
- // b15: ← b8
- // v71 (+143) = IData v22 (v[Nodes])
- // v73 (+146) = StaticLECall <[]*Node,mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v71 v21
- //
- // translates (w/o the "case OpLoad:" above) to:
- //
- // b2: ← b1
- // v20 (+142) = StaticCall {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v715
- // v23 (142) = Load <*uintptr> v19 v20
- // v823 (142) = IsNonNil v23
- // v67 (+143) = Load <*[]*Node> v880 v20
- // b15: ← b8
- // v827 (146) = StructSelect <*[]*Node> [0] v67
- // v846 (146) = Store {*[]*Node} v769 v827 v20
- // v73 (+146) = StaticCall {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v846
- // i.e., the struct select is generated and remains in because it is not applied to an actual structure.
- // The OpLoad was created to load the single field of the IData
- // This case removes that StructSelect.
- if leafType != selector.Type {
- f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString())
+ if x.debug {
+ fmt.Printf("\tOpArg, break\n")
}
- leaf.copyOf(selector)
- for _, s := range namedSelects[selector] {
- locs = append(locs, f.Names[s.locIndex])
+ break
+ }
+ switch leaf.Op {
+ case OpIData, OpStructSelect, OpArraySelect:
+ leafType = removeTrivialWrapperTypes(leaf.Type)
+ }
+ aux := selector.Aux
+ auxInt := selector.AuxInt + offset
+ if leaf.Block == selector.Block {
+ leaf.reset(OpArg)
+ leaf.Aux = aux
+ leaf.AuxInt = auxInt
+ leaf.Type = leafType
+ } else {
+ w := selector.Block.NewValue0IA(leaf.Pos, OpArg, leafType, auxInt, aux)
+ leaf.copyOf(w)
+ if x.debug {
+ fmt.Printf("\tnew %s\n", w.LongString())
}
+ }
+ for _, s := range x.namedSelects[selector] {
+ locs = append(locs, x.f.Names[s.locIndex])
+ }
- case OpSelectN:
- // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there.
- call := selector.Args[0]
- aux := call.Aux.(*AuxCall)
- which := selector.AuxInt
- if which == aux.NResults() { // mem is after the results.
- // rewrite v as a Copy of call -- the replacement call will produce a mem.
- leaf.copyOf(call)
- } else {
- leafType := removeTrivialWrapperTypes(leaf.Type)
- if canSSAType(leafType) {
- pt := types.NewPtr(leafType)
- off := offsetFrom(sp, offset+aux.OffsetOfResult(which), pt)
- // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
- if leaf.Block == call.Block {
- leaf.reset(OpLoad)
- leaf.SetArgs2(off, call)
- leaf.Type = leafType
- } else {
- w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
- leaf.copyOf(w)
- if debug {
- fmt.Printf("\tnew %s\n", w.LongString())
- }
- }
- for _, s := range namedSelects[selector] {
- locs = append(locs, f.Names[s.locIndex])
- }
+ case OpLoad: // We end up here because of IData of immediate structures.
+ // Failure case:
+ // (note the failure case is very rare; w/o this case, make.bash and run.bash both pass, as well as
+ // the hard cases of building {syscall,math,math/cmplx,math/bits,go/constant} on ppc64le and mips-softfloat).
+ //
+ // GOSSAFUNC='(*dumper).dump' go build -gcflags=-l -tags=math_big_pure_go cmd/compile/internal/gc
+ // cmd/compile/internal/gc/dump.go:136:14: internal compiler error: '(*dumper).dump': not lowered: v827, StructSelect PTR PTR
+ // b2: ← b1
+ // v20 (+142) = StaticLECall {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v8 v1
+ // v21 (142) = SelectN [1] v20
+ // v22 (142) = SelectN [0] v20
+ // b15: ← b8
+ // v71 (+143) = IData v22 (v[Nodes])
+ // v73 (+146) = StaticLECall <[]*Node,mem> {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v71 v21
+ //
+ // translates (w/o the "case OpLoad:" above) to:
+ //
+ // b2: ← b1
+ // v20 (+142) = StaticCall {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v715
+ // v23 (142) = Load <*uintptr> v19 v20
+ // v823 (142) = IsNonNil v23
+ // v67 (+143) = Load <*[]*Node> v880 v20
+ // b15: ← b8
+ // v827 (146) = StructSelect <*[]*Node> [0] v67
+ // v846 (146) = Store {*[]*Node} v769 v827 v20
+ // v73 (+146) = StaticCall {AuxCall{"".Nodes.Slice([Nodes,0])[[]*Node,8]}} [32] v846
+ // i.e., the struct select is generated and remains in because it is not applied to an actual structure.
+ // The OpLoad was created to load the single field of the IData
+ // This case removes that StructSelect.
+ if leafType != selector.Type {
+ x.f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString())
+ }
+ leaf.copyOf(selector)
+ for _, s := range x.namedSelects[selector] {
+ locs = append(locs, x.f.Names[s.locIndex])
+ }
+
+ case OpSelectN:
+ // TODO these may be duplicated. Should memoize. Intermediate selectors will go dead, no worries there.
+ call := selector.Args[0]
+ aux := call.Aux.(*AuxCall)
+ which := selector.AuxInt
+ if which == aux.NResults() { // mem is after the results.
+ // rewrite v as a Copy of call -- the replacement call will produce a mem.
+ leaf.copyOf(call)
+ } else {
+ leafType := removeTrivialWrapperTypes(leaf.Type)
+ if x.canSSAType(leafType) {
+ pt := types.NewPtr(leafType)
+ off := x.offsetFrom(x.sp, offset+aux.OffsetOfResult(which), pt)
+ // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
+ if leaf.Block == call.Block {
+ leaf.reset(OpLoad)
+ leaf.SetArgs2(off, call)
+ leaf.Type = leafType
} else {
- f.Fatalf("Should not have non-SSA-able OpSelectN, selector=%s", selector.LongString())
+ w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
+ leaf.copyOf(w)
+ if x.debug {
+ fmt.Printf("\tnew %s\n", w.LongString())
+ }
+ }
+ for _, s := range x.namedSelects[selector] {
+ locs = append(locs, x.f.Names[s.locIndex])
}
+ } else {
+ x.f.Fatalf("Should not have non-SSA-able OpSelectN, selector=%s", selector.LongString())
}
+ }
- case OpStructSelect:
- w := selector.Args[0]
- var ls []LocalSlot
- if w.Type.Etype != types.TSTRUCT { // IData artifact
- ls = rewriteSelect(leaf, w, offset)
- } else {
- ls = rewriteSelect(leaf, w, offset+w.Type.FieldOff(int(selector.AuxInt)))
- if w.Op != OpIData {
- for _, l := range ls {
- locs = append(locs, f.fe.SplitStruct(l, int(selector.AuxInt)))
- }
+ case OpStructSelect:
+ w := selector.Args[0]
+ var ls []LocalSlot
+ if w.Type.Kind() != types.TSTRUCT { // IData artifact
+ ls = x.rewriteSelect(leaf, w, offset)
+ } else {
+ ls = x.rewriteSelect(leaf, w, offset+w.Type.FieldOff(int(selector.AuxInt)))
+ if w.Op != OpIData {
+ for _, l := range ls {
+ locs = append(locs, x.f.fe.SplitStruct(l, int(selector.AuxInt)))
}
}
+ }
- case OpArraySelect:
- w := selector.Args[0]
- rewriteSelect(leaf, w, offset+selector.Type.Size()*selector.AuxInt)
-
- case OpInt64Hi:
- w := selector.Args[0]
- ls := rewriteSelect(leaf, w, offset+hiOffset)
- locs = splitSlots(ls, ".hi", hiOffset, leafType)
-
- case OpInt64Lo:
- w := selector.Args[0]
- ls := rewriteSelect(leaf, w, offset+lowOffset)
- locs = splitSlots(ls, ".lo", lowOffset, leafType)
-
- case OpStringPtr:
- ls := rewriteSelect(leaf, selector.Args[0], offset)
- locs = splitSlots(ls, ".ptr", 0, typ.BytePtr)
-
- case OpSlicePtr:
- w := selector.Args[0]
- ls := rewriteSelect(leaf, w, offset)
- locs = splitSlots(ls, ".ptr", 0, types.NewPtr(w.Type.Elem()))
-
- case OpITab:
- w := selector.Args[0]
- ls := rewriteSelect(leaf, w, offset)
- sfx := ".itab"
- if w.Type.IsEmptyInterface() {
- sfx = ".type"
- }
- locs = splitSlots(ls, sfx, 0, typ.Uintptr)
+ case OpArraySelect:
+ w := selector.Args[0]
+ x.rewriteSelect(leaf, w, offset+selector.Type.Size()*selector.AuxInt)
+
+ case OpInt64Hi:
+ w := selector.Args[0]
+ ls := x.rewriteSelect(leaf, w, offset+x.hiOffset)
+ locs = x.splitSlots(ls, ".hi", x.hiOffset, leafType)
+
+ case OpInt64Lo:
+ w := selector.Args[0]
+ ls := x.rewriteSelect(leaf, w, offset+x.lowOffset)
+ locs = x.splitSlots(ls, ".lo", x.lowOffset, leafType)
+
+ case OpStringPtr:
+ ls := x.rewriteSelect(leaf, selector.Args[0], offset)
+ locs = x.splitSlots(ls, ".ptr", 0, x.typs.BytePtr)
+
+ case OpSlicePtr:
+ w := selector.Args[0]
+ ls := x.rewriteSelect(leaf, w, offset)
+ locs = x.splitSlots(ls, ".ptr", 0, types.NewPtr(w.Type.Elem()))
+
+ case OpITab:
+ w := selector.Args[0]
+ ls := x.rewriteSelect(leaf, w, offset)
+ sfx := ".itab"
+ if w.Type.IsEmptyInterface() {
+ sfx = ".type"
+ }
+ locs = x.splitSlots(ls, sfx, 0, x.typs.Uintptr)
- case OpComplexReal:
- ls := rewriteSelect(leaf, selector.Args[0], offset)
- locs = splitSlots(ls, ".real", 0, leafType)
+ case OpComplexReal:
+ ls := x.rewriteSelect(leaf, selector.Args[0], offset)
+ locs = x.splitSlots(ls, ".real", 0, leafType)
- case OpComplexImag:
- ls := rewriteSelect(leaf, selector.Args[0], offset+leafType.Width) // result is FloatNN, width of result is offset of imaginary part.
- locs = splitSlots(ls, ".imag", leafType.Width, leafType)
+ case OpComplexImag:
+ ls := x.rewriteSelect(leaf, selector.Args[0], offset+leafType.Width) // result is FloatNN, width of result is offset of imaginary part.
+ locs = x.splitSlots(ls, ".imag", leafType.Width, leafType)
- case OpStringLen, OpSliceLen:
- ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize)
- locs = splitSlots(ls, ".len", ptrSize, leafType)
+ case OpStringLen, OpSliceLen:
+ ls := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize)
+ locs = x.splitSlots(ls, ".len", x.ptrSize, leafType)
- case OpIData:
- ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize)
- locs = splitSlots(ls, ".data", ptrSize, leafType)
+ case OpIData:
+ ls := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize)
+ locs = x.splitSlots(ls, ".data", x.ptrSize, leafType)
- case OpSliceCap:
- ls := rewriteSelect(leaf, selector.Args[0], offset+2*ptrSize)
- locs = splitSlots(ls, ".cap", 2*ptrSize, leafType)
-
- case OpCopy: // If it's an intermediate result, recurse
- locs = rewriteSelect(leaf, selector.Args[0], offset)
- for _, s := range namedSelects[selector] {
- // this copy may have had its own name, preserve that, too.
- locs = append(locs, f.Names[s.locIndex])
- }
+ case OpSliceCap:
+ ls := x.rewriteSelect(leaf, selector.Args[0], offset+2*x.ptrSize)
+ locs = x.splitSlots(ls, ".cap", 2*x.ptrSize, leafType)
- default:
- // Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed).
+ case OpCopy: // If it's an intermediate result, recurse
+ locs = x.rewriteSelect(leaf, selector.Args[0], offset)
+ for _, s := range x.namedSelects[selector] {
+ // this copy may have had its own name, preserve that, too.
+ locs = append(locs, x.f.Names[s.locIndex])
}
- return locs
+ default:
+ // Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed).
}
- // storeArgOrLoad converts stores of SSA-able aggregate arguments (passed to a call) into a series of primitive-typed
- // stores of non-aggregate types. It recursively walks up a chain of selectors until it reaches a Load or an Arg.
- // If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering.
- var storeArgOrLoad func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64) *Value
+ return locs
+}
- // decomposeArgOrLoad is a helper for storeArgOrLoad.
- // It decomposes a Load or an Arg into smaller parts, parameterized by the decomposeOne and decomposeTwo functions
- // passed to it, and returns the new mem. If the type does not match one of the expected aggregate types, it returns nil instead.
- decomposeArgOrLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64,
- decomposeOne func(pos src.XPos, b *Block, base, source, mem *Value, t1 *types.Type, offArg, offStore int64) *Value,
- decomposeTwo func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value) *Value {
- u := source.Type
- switch u.Etype {
- case types.TARRAY:
- elem := u.Elem()
- for i := int64(0); i < u.NumElem(); i++ {
- elemOff := i * elem.Size()
- mem = decomposeOne(pos, b, base, source, mem, elem, source.AuxInt+elemOff, offset+elemOff)
- pos = pos.WithNotStmt()
- }
- return mem
- case types.TSTRUCT:
- for i := 0; i < u.NumFields(); i++ {
- fld := u.Field(i)
- mem = decomposeOne(pos, b, base, source, mem, fld.Type, source.AuxInt+fld.Offset, offset+fld.Offset)
- pos = pos.WithNotStmt()
- }
- return mem
- case types.TINT64, types.TUINT64:
- if t.Width == regSize {
- break
- }
- tHi, tLo := intPairTypes(t.Etype)
- mem = decomposeOne(pos, b, base, source, mem, tHi, source.AuxInt+hiOffset, offset+hiOffset)
+func (x *expandState) rewriteDereference(b *Block, base, a, mem *Value, offset, size int64, typ *types.Type, pos src.XPos) *Value {
+ source := a.Args[0]
+ dst := x.offsetFrom(base, offset, source.Type)
+ if a.Uses == 1 && a.Block == b {
+ a.reset(OpMove)
+ a.Pos = pos
+ a.Type = types.TypeMem
+ a.Aux = typ
+ a.AuxInt = size
+ a.SetArgs3(dst, source, mem)
+ mem = a
+ } else {
+ mem = b.NewValue3A(pos, OpMove, types.TypeMem, typ, dst, source, mem)
+ mem.AuxInt = size
+ }
+ return mem
+}
+
+// decomposeArgOrLoad is a helper for storeArgOrLoad.
+// It decomposes a Load or an Arg into smaller parts, parameterized by the decomposeOne and decomposeTwo functions
+// passed to it, and returns the new mem. If the type does not match one of the expected aggregate types, it returns nil instead.
+func (x *expandState) decomposeArgOrLoad(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64,
+ decomposeOne func(x *expandState, pos src.XPos, b *Block, base, source, mem *Value, t1 *types.Type, offArg, offStore int64) *Value,
+ decomposeTwo func(x *expandState, pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value) *Value {
+ u := source.Type
+ switch u.Kind() {
+ case types.TARRAY:
+ elem := u.Elem()
+ for i := int64(0); i < u.NumElem(); i++ {
+ elemOff := i * elem.Size()
+ mem = decomposeOne(x, pos, b, base, source, mem, elem, source.AuxInt+elemOff, offset+elemOff)
+ pos = pos.WithNotStmt()
+ }
+ return mem
+ case types.TSTRUCT:
+ for i := 0; i < u.NumFields(); i++ {
+ fld := u.Field(i)
+ mem = decomposeOne(x, pos, b, base, source, mem, fld.Type, source.AuxInt+fld.Offset, offset+fld.Offset)
pos = pos.WithNotStmt()
- return decomposeOne(pos, b, base, source, mem, tLo, source.AuxInt+lowOffset, offset+lowOffset)
- case types.TINTER:
- return decomposeTwo(pos, b, base, source, mem, typ.Uintptr, typ.BytePtr, source.AuxInt, offset)
- case types.TSTRING:
- return decomposeTwo(pos, b, base, source, mem, typ.BytePtr, typ.Int, source.AuxInt, offset)
- case types.TCOMPLEX64:
- return decomposeTwo(pos, b, base, source, mem, typ.Float32, typ.Float32, source.AuxInt, offset)
- case types.TCOMPLEX128:
- return decomposeTwo(pos, b, base, source, mem, typ.Float64, typ.Float64, source.AuxInt, offset)
- case types.TSLICE:
- mem = decomposeTwo(pos, b, base, source, mem, typ.BytePtr, typ.Int, source.AuxInt, offset)
- return decomposeOne(pos, b, base, source, mem, typ.Int, source.AuxInt+2*ptrSize, offset+2*ptrSize)
- }
- return nil
- }
-
- // storeOneArg creates a decomposed (one step) arg that is then stored.
- // pos and b locate the store instruction, base is the base of the store target, source is the "base" of the value input,
- // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
- storeOneArg := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value {
- w := common[selKey{source, offArg, t.Width, t}]
- if w == nil {
- w = source.Block.NewValue0IA(source.Pos, OpArg, t, offArg, source.Aux)
- common[selKey{source, offArg, t.Width, t}] = w
- }
- return storeArgOrLoad(pos, b, base, w, mem, t, offStore)
- }
-
- // storeOneLoad creates a decomposed (one step) load that is then stored.
- storeOneLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value {
- from := offsetFrom(source.Args[0], offArg, types.NewPtr(t))
- w := source.Block.NewValue2(source.Pos, OpLoad, t, from, mem)
- return storeArgOrLoad(pos, b, base, w, mem, t, offStore)
- }
-
- storeTwoArg := func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value {
- mem = storeOneArg(pos, b, base, source, mem, t1, offArg, offStore)
+ }
+ return mem
+ case types.TINT64, types.TUINT64:
+ if t.Width == x.regSize {
+ break
+ }
+ tHi, tLo := x.intPairTypes(t.Kind())
+ mem = decomposeOne(x, pos, b, base, source, mem, tHi, source.AuxInt+x.hiOffset, offset+x.hiOffset)
pos = pos.WithNotStmt()
- t1Size := t1.Size()
- return storeOneArg(pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size)
+ return decomposeOne(x, pos, b, base, source, mem, tLo, source.AuxInt+x.lowOffset, offset+x.lowOffset)
+ case types.TINTER:
+ return decomposeTwo(x, pos, b, base, source, mem, x.typs.Uintptr, x.typs.BytePtr, source.AuxInt, offset)
+ case types.TSTRING:
+ return decomposeTwo(x, pos, b, base, source, mem, x.typs.BytePtr, x.typs.Int, source.AuxInt, offset)
+ case types.TCOMPLEX64:
+ return decomposeTwo(x, pos, b, base, source, mem, x.typs.Float32, x.typs.Float32, source.AuxInt, offset)
+ case types.TCOMPLEX128:
+ return decomposeTwo(x, pos, b, base, source, mem, x.typs.Float64, x.typs.Float64, source.AuxInt, offset)
+ case types.TSLICE:
+ mem = decomposeTwo(x, pos, b, base, source, mem, x.typs.BytePtr, x.typs.Int, source.AuxInt, offset)
+ return decomposeOne(x, pos, b, base, source, mem, x.typs.Int, source.AuxInt+2*x.ptrSize, offset+2*x.ptrSize)
}
+ return nil
+}
- storeTwoLoad := func(pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value {
- mem = storeOneLoad(pos, b, base, source, mem, t1, offArg, offStore)
- pos = pos.WithNotStmt()
- t1Size := t1.Size()
- return storeOneLoad(pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size)
+// storeOneArg creates a decomposed (one step) arg that is then stored.
+// pos and b locate the store instruction, base is the base of the store target, source is the "base" of the value input,
+// mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
+func storeOneArg(x *expandState, pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value {
+ w := x.common[selKey{source, offArg, t.Width, t}]
+ if w == nil {
+ w = source.Block.NewValue0IA(source.Pos, OpArg, t, offArg, source.Aux)
+ x.common[selKey{source, offArg, t.Width, t}] = w
}
+ return x.storeArgOrLoad(pos, b, base, w, mem, t, offStore)
+}
- storeArgOrLoad = func(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64) *Value {
- if debug {
- fmt.Printf("\tstoreArgOrLoad(%s; %s; %s; %s; %d)\n", base.LongString(), source.LongString(), mem.String(), t.String(), offset)
- }
+// storeOneLoad creates a decomposed (one step) load that is then stored.
+func storeOneLoad(x *expandState, pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64) *Value {
+ from := x.offsetFrom(source.Args[0], offArg, types.NewPtr(t))
+ w := source.Block.NewValue2(source.Pos, OpLoad, t, from, mem)
+ return x.storeArgOrLoad(pos, b, base, w, mem, t, offStore)
+}
- switch source.Op {
- case OpCopy:
- return storeArgOrLoad(pos, b, base, source.Args[0], mem, t, offset)
+func storeTwoArg(x *expandState, pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value {
+ mem = storeOneArg(x, pos, b, base, source, mem, t1, offArg, offStore)
+ pos = pos.WithNotStmt()
+ t1Size := t1.Size()
+ return storeOneArg(x, pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size)
+}
- case OpLoad:
- ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneLoad, storeTwoLoad)
- if ret != nil {
- return ret
- }
+func storeTwoLoad(x *expandState, pos src.XPos, b *Block, base, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64) *Value {
+ mem = storeOneLoad(x, pos, b, base, source, mem, t1, offArg, offStore)
+ pos = pos.WithNotStmt()
+ t1Size := t1.Size()
+ return storeOneLoad(x, pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size)
+}
- case OpArg:
- ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneArg, storeTwoArg)
- if ret != nil {
- return ret
- }
+// storeArgOrLoad converts stores of SSA-able aggregate arguments (passed to a call) into a series of primitive-typed
+// stores of non-aggregate types. It recursively walks up a chain of selectors until it reaches a Load or an Arg.
+// If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering.
+func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offset int64) *Value {
+ if x.debug {
+ fmt.Printf("\tstoreArgOrLoad(%s; %s; %s; %s; %d)\n", base.LongString(), source.LongString(), mem.String(), t.String(), offset)
+ }
- case OpArrayMake0, OpStructMake0:
- return mem
+ switch source.Op {
+ case OpCopy:
+ return x.storeArgOrLoad(pos, b, base, source.Args[0], mem, t, offset)
- case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4:
- for i := 0; i < t.NumFields(); i++ {
- fld := t.Field(i)
- mem = storeArgOrLoad(pos, b, base, source.Args[i], mem, fld.Type, offset+fld.Offset)
- pos = pos.WithNotStmt()
- }
- return mem
+ case OpLoad:
+ ret := x.decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneLoad, storeTwoLoad)
+ if ret != nil {
+ return ret
+ }
- case OpArrayMake1:
- return storeArgOrLoad(pos, b, base, source.Args[0], mem, t.Elem(), offset)
+ case OpArg:
+ ret := x.decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneArg, storeTwoArg)
+ if ret != nil {
+ return ret
+ }
- case OpInt64Make:
- tHi, tLo := intPairTypes(t.Etype)
- mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, tHi, offset+hiOffset)
- pos = pos.WithNotStmt()
- return storeArgOrLoad(pos, b, base, source.Args[1], mem, tLo, offset+lowOffset)
+ case OpArrayMake0, OpStructMake0:
+ return mem
- case OpComplexMake:
- tPart := typ.Float32
- wPart := t.Width / 2
- if wPart == 8 {
- tPart = typ.Float64
- }
- mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, tPart, offset)
+ case OpStructMake1, OpStructMake2, OpStructMake3, OpStructMake4:
+ for i := 0; i < t.NumFields(); i++ {
+ fld := t.Field(i)
+ mem = x.storeArgOrLoad(pos, b, base, source.Args[i], mem, fld.Type, offset+fld.Offset)
pos = pos.WithNotStmt()
- return storeArgOrLoad(pos, b, base, source.Args[1], mem, tPart, offset+wPart)
+ }
+ return mem
- case OpIMake:
- mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.Uintptr, offset)
- pos = pos.WithNotStmt()
- return storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.BytePtr, offset+ptrSize)
+ case OpArrayMake1:
+ return x.storeArgOrLoad(pos, b, base, source.Args[0], mem, t.Elem(), offset)
- case OpStringMake:
- mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.BytePtr, offset)
- pos = pos.WithNotStmt()
- return storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.Int, offset+ptrSize)
+ case OpInt64Make:
+ tHi, tLo := x.intPairTypes(t.Kind())
+ mem = x.storeArgOrLoad(pos, b, base, source.Args[0], mem, tHi, offset+x.hiOffset)
+ pos = pos.WithNotStmt()
+ return x.storeArgOrLoad(pos, b, base, source.Args[1], mem, tLo, offset+x.lowOffset)
- case OpSliceMake:
- mem = storeArgOrLoad(pos, b, base, source.Args[0], mem, typ.BytePtr, offset)
- pos = pos.WithNotStmt()
- mem = storeArgOrLoad(pos, b, base, source.Args[1], mem, typ.Int, offset+ptrSize)
- return storeArgOrLoad(pos, b, base, source.Args[2], mem, typ.Int, offset+2*ptrSize)
- }
-
- // For nodes that cannot be taken apart -- OpSelectN, other structure selectors.
- switch t.Etype {
- case types.TARRAY:
- elt := t.Elem()
- if source.Type != t && t.NumElem() == 1 && elt.Width == t.Width && t.Width == regSize {
- t = removeTrivialWrapperTypes(t)
- // it could be a leaf type, but the "leaf" could be complex64 (for example)
- return storeArgOrLoad(pos, b, base, source, mem, t, offset)
- }
- for i := int64(0); i < t.NumElem(); i++ {
- sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, elt, offset+i*elt.Width)
- pos = pos.WithNotStmt()
- }
- return mem
-
- case types.TSTRUCT:
- if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == regSize {
- // This peculiar test deals with accesses to immediate interface data.
- // It works okay because everything is the same size.
- // Example code that triggers this can be found in go/constant/value.go, function ToComplex
- // v119 (+881) = IData v6
- // v121 (+882) = StaticLECall {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1
- // This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)"
- // Guard against "struct{struct{*foo}}"
- // Other rewriting phases create minor glitches when they transform IData, for instance the
- // interface-typed Arg "x" of ToFloat in go/constant/value.go
- // v6 (858) = Arg {x} (x[Value], x[Value])
- // is rewritten by decomposeArgs into
- // v141 (858) = Arg {x}
- // v139 (858) = Arg <*uint8> {x} [8]
- // because of a type case clause on line 862 of go/constant/value.go
- // case intVal:
- // return itof(x)
- // v139 is later stored as an intVal == struct{val *big.Int} which naively requires the fields of
- // of a *uint8, which does not succeed.
- t = removeTrivialWrapperTypes(t)
- // it could be a leaf type, but the "leaf" could be complex64 (for example)
- return storeArgOrLoad(pos, b, base, source, mem, t, offset)
- }
+ case OpComplexMake:
+ tPart := x.typs.Float32
+ wPart := t.Width / 2
+ if wPart == 8 {
+ tPart = x.typs.Float64
+ }
+ mem = x.storeArgOrLoad(pos, b, base, source.Args[0], mem, tPart, offset)
+ pos = pos.WithNotStmt()
+ return x.storeArgOrLoad(pos, b, base, source.Args[1], mem, tPart, offset+wPart)
- for i := 0; i < t.NumFields(); i++ {
- fld := t.Field(i)
- sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, fld.Type, offset+fld.Offset)
- pos = pos.WithNotStmt()
- }
- return mem
+ case OpIMake:
+ mem = x.storeArgOrLoad(pos, b, base, source.Args[0], mem, x.typs.Uintptr, offset)
+ pos = pos.WithNotStmt()
+ return x.storeArgOrLoad(pos, b, base, source.Args[1], mem, x.typs.BytePtr, offset+x.ptrSize)
- case types.TINT64, types.TUINT64:
- if t.Width == regSize {
- break
- }
- tHi, tLo := intPairTypes(t.Etype)
- sel := source.Block.NewValue1(pos, OpInt64Hi, tHi, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, tHi, offset+hiOffset)
- pos = pos.WithNotStmt()
- sel = source.Block.NewValue1(pos, OpInt64Lo, tLo, source)
- return storeArgOrLoad(pos, b, base, sel, mem, tLo, offset+lowOffset)
+ case OpStringMake:
+ mem = x.storeArgOrLoad(pos, b, base, source.Args[0], mem, x.typs.BytePtr, offset)
+ pos = pos.WithNotStmt()
+ return x.storeArgOrLoad(pos, b, base, source.Args[1], mem, x.typs.Int, offset+x.ptrSize)
- case types.TINTER:
- sel := source.Block.NewValue1(pos, OpITab, typ.BytePtr, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset)
- pos = pos.WithNotStmt()
- sel = source.Block.NewValue1(pos, OpIData, typ.BytePtr, source)
- return storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset+ptrSize)
+ case OpSliceMake:
+ mem = x.storeArgOrLoad(pos, b, base, source.Args[0], mem, x.typs.BytePtr, offset)
+ pos = pos.WithNotStmt()
+ mem = x.storeArgOrLoad(pos, b, base, source.Args[1], mem, x.typs.Int, offset+x.ptrSize)
+ return x.storeArgOrLoad(pos, b, base, source.Args[2], mem, x.typs.Int, offset+2*x.ptrSize)
+ }
- case types.TSTRING:
- sel := source.Block.NewValue1(pos, OpStringPtr, typ.BytePtr, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset)
+ // For nodes that cannot be taken apart -- OpSelectN, other structure selectors.
+ switch t.Kind() {
+ case types.TARRAY:
+ elt := t.Elem()
+ if source.Type != t && t.NumElem() == 1 && elt.Width == t.Width && t.Width == x.regSize {
+ t = removeTrivialWrapperTypes(t)
+ // it could be a leaf type, but the "leaf" could be complex64 (for example)
+ return x.storeArgOrLoad(pos, b, base, source, mem, t, offset)
+ }
+ for i := int64(0); i < t.NumElem(); i++ {
+ sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, elt, offset+i*elt.Width)
pos = pos.WithNotStmt()
- sel = source.Block.NewValue1(pos, OpStringLen, typ.Int, source)
- return storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+ptrSize)
+ }
+ return mem
- case types.TSLICE:
- et := types.NewPtr(t.Elem())
- sel := source.Block.NewValue1(pos, OpSlicePtr, et, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, et, offset)
- pos = pos.WithNotStmt()
- sel = source.Block.NewValue1(pos, OpSliceLen, typ.Int, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+ptrSize)
- sel = source.Block.NewValue1(pos, OpSliceCap, typ.Int, source)
- return storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+2*ptrSize)
-
- case types.TCOMPLEX64:
- sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float32, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Float32, offset)
- pos = pos.WithNotStmt()
- sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float32, source)
- return storeArgOrLoad(pos, b, base, sel, mem, typ.Float32, offset+4)
+ case types.TSTRUCT:
+ if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == x.regSize {
+ // This peculiar test deals with accesses to immediate interface data.
+ // It works okay because everything is the same size.
+ // Example code that triggers this can be found in go/constant/value.go, function ToComplex
+ // v119 (+881) = IData v6
+ // v121 (+882) = StaticLECall {AuxCall{"".itof([intVal,0])[floatVal,8]}} [16] v119 v1
+ // This corresponds to the generic rewrite rule "(StructSelect [0] (IData x)) => (IData x)"
+ // Guard against "struct{struct{*foo}}"
+ // Other rewriting phases create minor glitches when they transform IData, for instance the
+ // interface-typed Arg "x" of ToFloat in go/constant/value.go
+ // v6 (858) = Arg {x} (x[Value], x[Value])
+ // is rewritten by decomposeArgs into
+ // v141 (858) = Arg {x}
+ // v139 (858) = Arg <*uint8> {x} [8]
+ // because of a type case clause on line 862 of go/constant/value.go
+ // case intVal:
+ // return itof(x)
+ // v139 is later stored as an intVal == struct{val *big.Int} which naively requires the fields of
+ // of a *uint8, which does not succeed.
+ t = removeTrivialWrapperTypes(t)
+ // it could be a leaf type, but the "leaf" could be complex64 (for example)
+ return x.storeArgOrLoad(pos, b, base, source, mem, t, offset)
+ }
- case types.TCOMPLEX128:
- sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float64, source)
- mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset)
+ for i := 0; i < t.NumFields(); i++ {
+ fld := t.Field(i)
+ sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, fld.Type, offset+fld.Offset)
pos = pos.WithNotStmt()
- sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float64, source)
- return storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset+8)
}
+ return mem
- dst := offsetFrom(base, offset, types.NewPtr(t))
- x := b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem)
- if debug {
- fmt.Printf("\t\tstoreArg returns %s\n", x.LongString())
+ case types.TINT64, types.TUINT64:
+ if t.Width == x.regSize {
+ break
}
- return x
+ tHi, tLo := x.intPairTypes(t.Kind())
+ sel := source.Block.NewValue1(pos, OpInt64Hi, tHi, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, tHi, offset+x.hiOffset)
+ pos = pos.WithNotStmt()
+ sel = source.Block.NewValue1(pos, OpInt64Lo, tLo, source)
+ return x.storeArgOrLoad(pos, b, base, sel, mem, tLo, offset+x.lowOffset)
+
+ case types.TINTER:
+ sel := source.Block.NewValue1(pos, OpITab, x.typs.BytePtr, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.BytePtr, offset)
+ pos = pos.WithNotStmt()
+ sel = source.Block.NewValue1(pos, OpIData, x.typs.BytePtr, source)
+ return x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.BytePtr, offset+x.ptrSize)
+
+ case types.TSTRING:
+ sel := source.Block.NewValue1(pos, OpStringPtr, x.typs.BytePtr, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.BytePtr, offset)
+ pos = pos.WithNotStmt()
+ sel = source.Block.NewValue1(pos, OpStringLen, x.typs.Int, source)
+ return x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.Int, offset+x.ptrSize)
+
+ case types.TSLICE:
+ et := types.NewPtr(t.Elem())
+ sel := source.Block.NewValue1(pos, OpSlicePtr, et, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, et, offset)
+ pos = pos.WithNotStmt()
+ sel = source.Block.NewValue1(pos, OpSliceLen, x.typs.Int, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.Int, offset+x.ptrSize)
+ sel = source.Block.NewValue1(pos, OpSliceCap, x.typs.Int, source)
+ return x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.Int, offset+2*x.ptrSize)
+
+ case types.TCOMPLEX64:
+ sel := source.Block.NewValue1(pos, OpComplexReal, x.typs.Float32, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.Float32, offset)
+ pos = pos.WithNotStmt()
+ sel = source.Block.NewValue1(pos, OpComplexImag, x.typs.Float32, source)
+ return x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.Float32, offset+4)
+
+ case types.TCOMPLEX128:
+ sel := source.Block.NewValue1(pos, OpComplexReal, x.typs.Float64, source)
+ mem = x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.Float64, offset)
+ pos = pos.WithNotStmt()
+ sel = source.Block.NewValue1(pos, OpComplexImag, x.typs.Float64, source)
+ return x.storeArgOrLoad(pos, b, base, sel, mem, x.typs.Float64, offset+8)
}
- // rewriteArgs removes all the Args from a call and converts the call args into appropriate
- // stores (or later, register movement). Extra args for interface and closure calls are ignored,
- // but removed.
- rewriteArgs := func(v *Value, firstArg int) *Value {
- // Thread the stores on the memory arg
- aux := v.Aux.(*AuxCall)
- pos := v.Pos.WithNotStmt()
- m0 := v.Args[len(v.Args)-1]
- mem := m0
- for i, a := range v.Args {
- if i < firstArg {
- continue
- }
- if a == m0 { // mem is last.
- break
+ dst := x.offsetFrom(base, offset, types.NewPtr(t))
+ s := b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem)
+ if x.debug {
+ fmt.Printf("\t\tstoreArg returns %s\n", s.LongString())
+ }
+ return s
+}
+
+// rewriteArgs removes all the Args from a call and converts the call args into appropriate
+// stores (or later, register movement). Extra args for interface and closure calls are ignored,
+// but removed.
+func (x *expandState) rewriteArgs(v *Value, firstArg int) *Value {
+ // Thread the stores on the memory arg
+ aux := v.Aux.(*AuxCall)
+ pos := v.Pos.WithNotStmt()
+ m0 := v.MemoryArg()
+ mem := m0
+ for i, a := range v.Args {
+ if i < firstArg {
+ continue
+ }
+ if a == m0 { // mem is last.
+ break
+ }
+ auxI := int64(i - firstArg)
+ if a.Op == OpDereference {
+ if a.MemoryArg() != m0 {
+ x.f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString())
}
- auxI := int64(i - firstArg)
- if a.Op == OpDereference {
- if a.MemoryArg() != m0 {
- f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString())
- }
- // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
- // TODO this will be more complicated with registers in the picture.
- source := a.Args[0]
- dst := f.ConstOffPtrSP(source.Type, aux.OffsetOfArg(auxI), sp)
- if a.Uses == 1 && a.Block == v.Block {
- a.reset(OpMove)
- a.Pos = pos
- a.Type = types.TypeMem
- a.Aux = aux.TypeOfArg(auxI)
- a.AuxInt = aux.SizeOfArg(auxI)
- a.SetArgs3(dst, source, mem)
- mem = a
- } else {
- mem = v.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, source, mem)
- mem.AuxInt = aux.SizeOfArg(auxI)
- }
- } else {
- if debug {
- fmt.Printf("storeArg %s, %v, %d\n", a.LongString(), aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI))
- }
- mem = storeArgOrLoad(pos, v.Block, sp, a, mem, aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI))
+ // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
+ // TODO this will be more complicated with registers in the picture.
+ mem = x.rewriteDereference(v.Block, x.sp, a, mem, aux.OffsetOfArg(auxI), aux.SizeOfArg(auxI), aux.TypeOfArg(auxI), pos)
+ } else {
+ if x.debug {
+ fmt.Printf("storeArg %s, %v, %d\n", a.LongString(), aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI))
}
+ mem = x.storeArgOrLoad(pos, v.Block, x.sp, a, mem, aux.TypeOfArg(auxI), aux.OffsetOfArg(auxI))
}
- v.resetArgs()
- return mem
+ }
+ v.resetArgs()
+ return mem
+}
+
+// expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form
+// that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into
+// more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are
+// reached. On the callee side, OpArg nodes are not decomposed until this phase is run.
+// TODO results should not be lowered until this phase.
+func expandCalls(f *Func) {
+ // Calls that need lowering have some number of inputs, including a memory input,
+ // and produce a tuple of (value1, value2, ..., mem) where valueK may or may not be SSA-able.
+
+ // With the current ABI those inputs need to be converted into stores to memory,
+ // rethreading the call's memory input to the first, and the new call now receiving the last.
+
+ // With the current ABI, the outputs need to be converted to loads, which will all use the call's
+ // memory output as their input.
+ sp, _ := f.spSb()
+ x := &expandState{
+ f: f,
+ debug: f.pass.debug > 0,
+ canSSAType: f.fe.CanSSA,
+ regSize: f.Config.RegSize,
+ sp: sp,
+ typs: &f.Config.Types,
+ ptrSize: f.Config.PtrSize,
+ namedSelects: make(map[*Value][]namedVal),
+ sdom: f.Sdom(),
+ common: make(map[selKey]*Value),
+ offsets: make(map[offsetKey]*Value),
+ }
+
+ // For 32-bit, need to deal with decomposition of 64-bit integers, which depends on endianness.
+ if f.Config.BigEndian {
+ x.lowOffset = 4
+ } else {
+ x.hiOffset = 4
+ }
+
+ if x.debug {
+ fmt.Printf("\nexpandsCalls(%s)\n", f.Name)
}
// TODO if too slow, whole program iteration can be replaced w/ slices of appropriate values, accumulated in first loop here.
@@ -680,28 +697,67 @@ func expandCalls(f *Func) {
for _, v := range b.Values {
switch v.Op {
case OpStaticLECall:
- mem := rewriteArgs(v, 0)
+ mem := x.rewriteArgs(v, 0)
v.SetArgs1(mem)
case OpClosureLECall:
code := v.Args[0]
context := v.Args[1]
- mem := rewriteArgs(v, 2)
+ mem := x.rewriteArgs(v, 2)
v.SetArgs3(code, context, mem)
case OpInterLECall:
code := v.Args[0]
- mem := rewriteArgs(v, 1)
+ mem := x.rewriteArgs(v, 1)
v.SetArgs2(code, mem)
}
}
+ if isBlockMultiValueExit(b) {
+ // Very similar to code in rewriteArgs, but results instead of args.
+ v := b.Controls[0]
+ m0 := v.MemoryArg()
+ mem := m0
+ aux := f.OwnAux
+ pos := v.Pos.WithNotStmt()
+ for j, a := range v.Args {
+ i := int64(j)
+ if a == m0 {
+ break
+ }
+ auxType := aux.TypeOfResult(i)
+ auxBase := b.NewValue2A(v.Pos, OpLocalAddr, types.NewPtr(auxType), aux.results[i].Name, x.sp, mem)
+ auxOffset := int64(0)
+ auxSize := aux.SizeOfResult(i)
+ if a.Op == OpDereference {
+ // Avoid a self-move, and if one is detected try to remove the already-inserted VarDef for the assignment that won't happen.
+ if dAddr, dMem := a.Args[0], a.Args[1]; dAddr.Op == OpLocalAddr && dAddr.Args[0].Op == OpSP &&
+ dAddr.Args[1] == dMem && dAddr.Aux == aux.results[i].Name {
+ if dMem.Op == OpVarDef && dMem.Aux == dAddr.Aux {
+ dMem.copyOf(dMem.MemoryArg()) // elide the VarDef
+ }
+ continue
+ }
+ mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, pos)
+ } else {
+ if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr {
+ addr := a.Args[0]
+ if addr.MemoryArg() == a.MemoryArg() && addr.Aux == aux.results[i].Name {
+ continue
+ }
+ }
+ mem = x.storeArgOrLoad(v.Pos, b, auxBase, a, mem, aux.TypeOfResult(i), auxOffset)
+ }
+ }
+ b.SetControl(mem)
+ v.reset(OpInvalid) // otherwise it can have a mem operand which will fail check(), even though it is dead.
+ }
}
for i, name := range f.Names {
t := name.Type
- if isAlreadyExpandedAggregateType(t) {
+ if x.isAlreadyExpandedAggregateType(t) {
for j, v := range f.NamedValues[name] {
- if v.Op == OpSelectN || v.Op == OpArg && isAlreadyExpandedAggregateType(v.Type) {
- ns := namedSelects[v]
- namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j})
+ if v.Op == OpSelectN || v.Op == OpArg && x.isAlreadyExpandedAggregateType(v.Type) {
+ ns := x.namedSelects[v]
+ x.namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j})
}
}
}
@@ -715,22 +771,22 @@ func expandCalls(f *Func) {
t := v.Aux.(*types.Type)
source := v.Args[1]
tSrc := source.Type
- iAEATt := isAlreadyExpandedAggregateType(t)
+ iAEATt := x.isAlreadyExpandedAggregateType(t)
if !iAEATt {
// guarding against store immediate struct into interface data field -- store type is *uint8
// TODO can this happen recursively?
- iAEATt = isAlreadyExpandedAggregateType(tSrc)
+ iAEATt = x.isAlreadyExpandedAggregateType(tSrc)
if iAEATt {
t = tSrc
}
}
if iAEATt {
- if debug {
+ if x.debug {
fmt.Printf("Splitting store %s\n", v.LongString())
}
dst, mem := v.Args[0], v.Args[2]
- mem = storeArgOrLoad(v.Pos, b, dst, source, mem, t, 0)
+ mem = x.storeArgOrLoad(v.Pos, b, dst, source, mem, t, 0)
v.copyOf(mem)
}
}
@@ -759,7 +815,7 @@ func expandCalls(f *Func) {
switch w.Op {
case OpStructSelect, OpArraySelect, OpSelectN, OpArg:
val2Preds[w] += 1
- if debug {
+ if x.debug {
fmt.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w])
}
}
@@ -768,18 +824,18 @@ func expandCalls(f *Func) {
case OpSelectN:
if _, ok := val2Preds[v]; !ok {
val2Preds[v] = 0
- if debug {
+ if x.debug {
fmt.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
}
}
case OpArg:
- if !isAlreadyExpandedAggregateType(v.Type) {
+ if !x.isAlreadyExpandedAggregateType(v.Type) {
continue
}
if _, ok := val2Preds[v]; !ok {
val2Preds[v] = 0
- if debug {
+ if x.debug {
fmt.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
}
}
@@ -790,7 +846,7 @@ func expandCalls(f *Func) {
which := v.AuxInt
aux := call.Aux.(*AuxCall)
pt := v.Type
- off := offsetFrom(sp, aux.OffsetOfResult(which), pt)
+ off := x.offsetFrom(x.sp, aux.OffsetOfResult(which), pt)
v.copyOf(off)
}
}
@@ -812,7 +868,7 @@ func expandCalls(f *Func) {
if bi == bj {
return vi.ID < vj.ID
}
- return sdom.domorder(bi) > sdom.domorder(bj) // reverse the order to put dominators last.
+ return x.sdom.domorder(bi) > x.sdom.domorder(bj) // reverse the order to put dominators last.
}
// Accumulate order in allOrdered
@@ -846,7 +902,7 @@ func expandCalls(f *Func) {
}
}
- common = make(map[selKey]*Value)
+ x.common = make(map[selKey]*Value)
// Rewrite duplicate selectors as copies where possible.
for i := len(allOrdered) - 1; i >= 0; i-- {
v := allOrdered[i]
@@ -868,7 +924,7 @@ func expandCalls(f *Func) {
offset := int64(0)
switch v.Op {
case OpStructSelect:
- if w.Type.Etype == types.TSTRUCT {
+ if w.Type.Kind() == types.TSTRUCT {
offset = w.Type.FieldOff(int(v.AuxInt))
} else { // Immediate interface data artifact, offset is zero.
f.Fatalf("Expand calls interface data problem, func %s, v=%s, w=%s\n", f.Name, v.LongString(), w.LongString())
@@ -878,26 +934,26 @@ func expandCalls(f *Func) {
case OpSelectN:
offset = w.Aux.(*AuxCall).OffsetOfResult(v.AuxInt)
case OpInt64Hi:
- offset = hiOffset
+ offset = x.hiOffset
case OpInt64Lo:
- offset = lowOffset
+ offset = x.lowOffset
case OpStringLen, OpSliceLen, OpIData:
- offset = ptrSize
+ offset = x.ptrSize
case OpSliceCap:
- offset = 2 * ptrSize
+ offset = 2 * x.ptrSize
case OpComplexImag:
offset = size
}
sk := selKey{from: w, size: size, offset: offset, typ: typ}
- dupe := common[sk]
+ dupe := x.common[sk]
if dupe == nil {
- common[sk] = v
- } else if sdom.IsAncestorEq(dupe.Block, v.Block) {
+ x.common[sk] = v
+ } else if x.sdom.IsAncestorEq(dupe.Block, v.Block) {
v.copyOf(dupe)
} else {
// Because values are processed in dominator order, the old common[s] will never dominate after a miss is seen.
// Installing the new value might match some future values.
- common[sk] = v
+ x.common[sk] = v
}
}
@@ -906,7 +962,7 @@ func expandCalls(f *Func) {
// Rewrite selectors.
for i, v := range allOrdered {
- if debug {
+ if x.debug {
b := v.Block
fmt.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses)
}
@@ -917,13 +973,13 @@ func expandCalls(f *Func) {
if v.Op == OpCopy {
continue
}
- locs := rewriteSelect(v, v, 0)
+ locs := x.rewriteSelect(v, v, 0)
// Install new names.
if v.Type.IsMemory() {
continue
}
// Leaf types may have debug locations
- if !isAlreadyExpandedAggregateType(v.Type) {
+ if !x.isAlreadyExpandedAggregateType(v.Type) {
for _, l := range locs {
f.NamedValues[l] = append(f.NamedValues[l], v)
}
@@ -931,7 +987,7 @@ func expandCalls(f *Func) {
continue
}
// Not-leaf types that had debug locations need to lose them.
- if ns, ok := namedSelects[v]; ok {
+ if ns, ok := x.namedSelects[v]; ok {
toDelete = append(toDelete, ns...)
}
}
diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go
index b4c3e5cfdfb73..32e6d09d1bc32 100644
--- a/src/cmd/compile/internal/ssa/export_test.go
+++ b/src/cmd/compile/internal/ssa/export_test.go
@@ -5,13 +5,13 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
"cmd/internal/obj/s390x"
"cmd/internal/obj/x86"
"cmd/internal/src"
- "fmt"
"testing"
)
@@ -36,10 +36,10 @@ func testConfigArch(tb testing.TB, arch string) *Conf {
tb.Fatalf("unknown arch %s", arch)
}
if ctxt.Arch.PtrSize != 8 {
- tb.Fatal("dummyTypes is 64-bit only")
+ tb.Fatal("testTypes is 64-bit only")
}
c := &Conf{
- config: NewConfig(arch, dummyTypes, ctxt, true),
+ config: NewConfig(arch, testTypes, ctxt, true),
tb: tb,
}
return c
@@ -53,131 +53,94 @@ type Conf struct {
func (c *Conf) Frontend() Frontend {
if c.fe == nil {
- c.fe = DummyFrontend{t: c.tb, ctxt: c.config.ctxt}
+ c.fe = TestFrontend{t: c.tb, ctxt: c.config.ctxt}
}
return c.fe
}
-// DummyFrontend is a test-only frontend.
+// TestFrontend is a test-only frontend.
// It assumes 64 bit integers and pointers.
-type DummyFrontend struct {
+type TestFrontend struct {
t testing.TB
ctxt *obj.Link
}
-type DummyAuto struct {
- t *types.Type
- s string
-}
-
-func (d *DummyAuto) Typ() *types.Type {
- return d.t
-}
-
-func (d *DummyAuto) String() string {
- return d.s
-}
-
-func (d *DummyAuto) StorageClass() StorageClass {
- return ClassAuto
-}
-
-func (d *DummyAuto) IsSynthetic() bool {
- return false
-}
-
-func (d *DummyAuto) IsAutoTmp() bool {
- return true
-}
-
-func (DummyFrontend) StringData(s string) *obj.LSym {
+func (TestFrontend) StringData(s string) *obj.LSym {
return nil
}
-func (DummyFrontend) Auto(pos src.XPos, t *types.Type) GCNode {
- return &DummyAuto{t: t, s: "aDummyAuto"}
+func (TestFrontend) Auto(pos src.XPos, t *types.Type) *ir.Name {
+ n := ir.NewNameAt(pos, &types.Sym{Name: "aFakeAuto"})
+ n.Class = ir.PAUTO
+ return n
}
-func (d DummyFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
- return LocalSlot{N: s.N, Type: dummyTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.Int, Off: s.Off + 8}
+func (d TestFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
+ return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8}
}
-func (d DummyFrontend) SplitInterface(s LocalSlot) (LocalSlot, LocalSlot) {
- return LocalSlot{N: s.N, Type: dummyTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.BytePtr, Off: s.Off + 8}
+func (d TestFrontend) SplitInterface(s LocalSlot) (LocalSlot, LocalSlot) {
+ return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off + 8}
}
-func (d DummyFrontend) SplitSlice(s LocalSlot) (LocalSlot, LocalSlot, LocalSlot) {
+func (d TestFrontend) SplitSlice(s LocalSlot) (LocalSlot, LocalSlot, LocalSlot) {
return LocalSlot{N: s.N, Type: s.Type.Elem().PtrTo(), Off: s.Off},
- LocalSlot{N: s.N, Type: dummyTypes.Int, Off: s.Off + 8},
- LocalSlot{N: s.N, Type: dummyTypes.Int, Off: s.Off + 16}
+ LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8},
+ LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 16}
}
-func (d DummyFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
+func (d TestFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
if s.Type.Size() == 16 {
- return LocalSlot{N: s.N, Type: dummyTypes.Float64, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.Float64, Off: s.Off + 8}
+ return LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off + 8}
}
- return LocalSlot{N: s.N, Type: dummyTypes.Float32, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.Float32, Off: s.Off + 4}
+ return LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off + 4}
}
-func (d DummyFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
+func (d TestFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
if s.Type.IsSigned() {
- return LocalSlot{N: s.N, Type: dummyTypes.Int32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: dummyTypes.UInt32, Off: s.Off}
+ return LocalSlot{N: s.N, Type: testTypes.Int32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
}
- return LocalSlot{N: s.N, Type: dummyTypes.UInt32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: dummyTypes.UInt32, Off: s.Off}
+ return LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
}
-func (d DummyFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
+func (d TestFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
return LocalSlot{N: s.N, Type: s.Type.FieldType(i), Off: s.Off + s.Type.FieldOff(i)}
}
-func (d DummyFrontend) SplitArray(s LocalSlot) LocalSlot {
+func (d TestFrontend) SplitArray(s LocalSlot) LocalSlot {
return LocalSlot{N: s.N, Type: s.Type.Elem(), Off: s.Off}
}
-func (d DummyFrontend) SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot {
+func (d TestFrontend) SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot {
return LocalSlot{N: parent.N, Type: t, Off: offset}
}
-func (DummyFrontend) Line(_ src.XPos) string {
+func (TestFrontend) Line(_ src.XPos) string {
return "unknown.go:0"
}
-func (DummyFrontend) AllocFrame(f *Func) {
+func (TestFrontend) AllocFrame(f *Func) {
}
-func (d DummyFrontend) Syslook(s string) *obj.LSym {
+func (d TestFrontend) Syslook(s string) *obj.LSym {
return d.ctxt.Lookup(s)
}
-func (DummyFrontend) UseWriteBarrier() bool {
+func (TestFrontend) UseWriteBarrier() bool {
return true // only writebarrier_test cares
}
-func (DummyFrontend) SetWBPos(pos src.XPos) {
+func (TestFrontend) SetWBPos(pos src.XPos) {
}
-func (d DummyFrontend) Logf(msg string, args ...interface{}) { d.t.Logf(msg, args...) }
-func (d DummyFrontend) Log() bool { return true }
+func (d TestFrontend) Logf(msg string, args ...interface{}) { d.t.Logf(msg, args...) }
+func (d TestFrontend) Log() bool { return true }
-func (d DummyFrontend) Fatalf(_ src.XPos, msg string, args ...interface{}) { d.t.Fatalf(msg, args...) }
-func (d DummyFrontend) Warnl(_ src.XPos, msg string, args ...interface{}) { d.t.Logf(msg, args...) }
-func (d DummyFrontend) Debug_checknil() bool { return false }
+func (d TestFrontend) Fatalf(_ src.XPos, msg string, args ...interface{}) { d.t.Fatalf(msg, args...) }
+func (d TestFrontend) Warnl(_ src.XPos, msg string, args ...interface{}) { d.t.Logf(msg, args...) }
+func (d TestFrontend) Debug_checknil() bool { return false }
-func (d DummyFrontend) MyImportPath() string {
+func (d TestFrontend) MyImportPath() string {
return "my/import/path"
}
-var dummyTypes Types
+var testTypes Types
func init() {
// Initialize just enough of the universe and the types package to make our tests function.
// TODO(josharian): move universe initialization to the types package,
// so this test setup can share it.
- types.Tconv = func(t *types.Type, flag, mode int) string {
- return t.Etype.String()
- }
- types.Sconv = func(s *types.Sym, flag, mode int) string {
- return "sym"
- }
- types.FormatSym = func(sym *types.Sym, s fmt.State, verb rune, mode int) {
- fmt.Fprintf(s, "sym")
- }
- types.FormatType = func(t *types.Type, s fmt.State, verb rune, mode int) {
- fmt.Fprintf(s, "%v", t.Etype)
- }
- types.Dowidth = func(t *types.Type) {}
-
for _, typ := range [...]struct {
width int64
- et types.EType
+ et types.Kind
}{
{1, types.TINT8},
{1, types.TUINT8},
@@ -198,12 +161,12 @@ func init() {
t.Align = uint8(typ.width)
types.Types[typ.et] = t
}
- dummyTypes.SetTypPtrs()
+ testTypes.SetTypPtrs()
}
-func (d DummyFrontend) DerefItab(sym *obj.LSym, off int64) *obj.LSym { return nil }
+func (d TestFrontend) DerefItab(sym *obj.LSym, off int64) *obj.LSym { return nil }
-func (d DummyFrontend) CanSSA(t *types.Type) bool {
- // There are no un-SSAable types in dummy land.
+func (d TestFrontend) CanSSA(t *types.Type) bool {
+ // There are no un-SSAable types in test land.
return true
}
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index e6f899a2c77b1..de99a8d4af9cb 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -58,6 +58,11 @@ type Func struct {
// of keys to make iteration order deterministic.
Names []LocalSlot
+ // RegArgs is a slice of register-memory pairs that must be spilled and unspilled in the uncommon path of function entry.
+ RegArgs []ArgPair
+ // AuxCall describing parameters and results for this function.
+ OwnAux *AuxCall
+
// WBLoads is a list of Blocks that branch on the write
// barrier flag. Safe-points are disabled from the OpLoad that
// reads the write-barrier flag until the control flow rejoins
@@ -377,13 +382,7 @@ func (b *Block) NewValue0I(pos src.XPos, op Op, t *types.Type, auxint int64) *Va
}
// NewValue returns a new value in the block with no arguments and an aux value.
-func (b *Block) NewValue0A(pos src.XPos, op Op, t *types.Type, aux interface{}) *Value {
- if _, ok := aux.(int64); ok {
- // Disallow int64 aux values. They should be in the auxint field instead.
- // Maybe we want to allow this at some point, but for now we disallow it
- // to prevent errors like using NewValue1A instead of NewValue1I.
- b.Fatalf("aux field has int64 type op=%s type=%s aux=%v", op, t, aux)
- }
+func (b *Block) NewValue0A(pos src.XPos, op Op, t *types.Type, aux Aux) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = 0
v.Aux = aux
@@ -392,7 +391,7 @@ func (b *Block) NewValue0A(pos src.XPos, op Op, t *types.Type, aux interface{})
}
// NewValue returns a new value in the block with no arguments and both an auxint and aux values.
-func (b *Block) NewValue0IA(pos src.XPos, op Op, t *types.Type, auxint int64, aux interface{}) *Value {
+func (b *Block) NewValue0IA(pos src.XPos, op Op, t *types.Type, auxint int64, aux Aux) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = auxint
v.Aux = aux
@@ -421,7 +420,7 @@ func (b *Block) NewValue1I(pos src.XPos, op Op, t *types.Type, auxint int64, arg
}
// NewValue1A returns a new value in the block with one argument and an aux value.
-func (b *Block) NewValue1A(pos src.XPos, op Op, t *types.Type, aux interface{}, arg *Value) *Value {
+func (b *Block) NewValue1A(pos src.XPos, op Op, t *types.Type, aux Aux, arg *Value) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = 0
v.Aux = aux
@@ -432,7 +431,7 @@ func (b *Block) NewValue1A(pos src.XPos, op Op, t *types.Type, aux interface{},
}
// NewValue1IA returns a new value in the block with one argument and both an auxint and aux values.
-func (b *Block) NewValue1IA(pos src.XPos, op Op, t *types.Type, auxint int64, aux interface{}, arg *Value) *Value {
+func (b *Block) NewValue1IA(pos src.XPos, op Op, t *types.Type, auxint int64, aux Aux, arg *Value) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = auxint
v.Aux = aux
@@ -455,7 +454,7 @@ func (b *Block) NewValue2(pos src.XPos, op Op, t *types.Type, arg0, arg1 *Value)
}
// NewValue2A returns a new value in the block with two arguments and one aux values.
-func (b *Block) NewValue2A(pos src.XPos, op Op, t *types.Type, aux interface{}, arg0, arg1 *Value) *Value {
+func (b *Block) NewValue2A(pos src.XPos, op Op, t *types.Type, aux Aux, arg0, arg1 *Value) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = 0
v.Aux = aux
@@ -480,7 +479,7 @@ func (b *Block) NewValue2I(pos src.XPos, op Op, t *types.Type, auxint int64, arg
}
// NewValue2IA returns a new value in the block with two arguments and both an auxint and aux values.
-func (b *Block) NewValue2IA(pos src.XPos, op Op, t *types.Type, auxint int64, aux interface{}, arg0, arg1 *Value) *Value {
+func (b *Block) NewValue2IA(pos src.XPos, op Op, t *types.Type, auxint int64, aux Aux, arg0, arg1 *Value) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = auxint
v.Aux = aux
@@ -521,7 +520,7 @@ func (b *Block) NewValue3I(pos src.XPos, op Op, t *types.Type, auxint int64, arg
}
// NewValue3A returns a new value in the block with three argument and an aux value.
-func (b *Block) NewValue3A(pos src.XPos, op Op, t *types.Type, aux interface{}, arg0, arg1, arg2 *Value) *Value {
+func (b *Block) NewValue3A(pos src.XPos, op Op, t *types.Type, aux Aux, arg0, arg1, arg2 *Value) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = 0
v.Aux = aux
@@ -633,7 +632,7 @@ func (f *Func) ConstNil(t *types.Type) *Value {
}
func (f *Func) ConstEmptyString(t *types.Type) *Value {
v := f.constVal(OpConstString, t, constEmptyStringMagic, false)
- v.Aux = ""
+ v.Aux = StringToAux("")
return v
}
func (f *Func) ConstOffPtrSP(t *types.Type, c int64, sp *Value) *Value {
@@ -777,7 +776,7 @@ func DebugNameMatch(evname, name string) bool {
}
func (f *Func) spSb() (sp, sb *Value) {
- initpos := f.Entry.Pos
+ initpos := src.NoXPos // These are originally created with no position in ssa.go; if they are optimized out then recreated, should be the same.
for _, v := range f.Entry.Values {
if v.Op == OpSB {
sb = v
@@ -786,7 +785,7 @@ func (f *Func) spSb() (sp, sb *Value) {
sp = v
}
if sb != nil && sp != nil {
- break
+ return
}
}
if sb == nil {
diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go
index 568c6436f5bb5..276c444b9a88f 100644
--- a/src/cmd/compile/internal/ssa/func_test.go
+++ b/src/cmd/compile/internal/ssa/func_test.go
@@ -232,7 +232,7 @@ func Bloc(name string, entries ...interface{}) bloc {
}
// Valu defines a value in a block.
-func Valu(name string, op Op, t *types.Type, auxint int64, aux interface{}, args ...string) valu {
+func Valu(name string, op Op, t *types.Type, auxint int64, aux Aux, args ...string) valu {
return valu{name, op, t, auxint, aux, args}
}
@@ -277,7 +277,7 @@ type valu struct {
op Op
t *types.Type
auxint int64
- aux interface{}
+ aux Aux
args []string
}
@@ -402,12 +402,12 @@ func TestEquiv(t *testing.T) {
cfg.Fun("entry",
Bloc("entry",
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
- Valu("a", OpConst64, cfg.config.Types.Int64, 0, 14),
+ Valu("a", OpConstString, cfg.config.Types.String, 0, StringToAux("foo")),
Exit("mem"))),
cfg.Fun("entry",
Bloc("entry",
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
- Valu("a", OpConst64, cfg.config.Types.Int64, 0, 26),
+ Valu("a", OpConstString, cfg.config.Types.String, 0, StringToAux("bar")),
Exit("mem"))),
},
// value args different
diff --git a/src/cmd/compile/internal/ssa/gen/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules
index fbc12fd67219a..df03cb71a6e3c 100644
--- a/src/cmd/compile/internal/ssa/gen/386.rules
+++ b/src/cmd/compile/internal/ssa/gen/386.rules
@@ -475,7 +475,7 @@
(CMPB (MOVLconst [c]) x) => (InvertFlags (CMPBconst x [int8(c)]))
// Canonicalize the order of arguments to comparisons - helps with CSE.
-(CMP(L|W|B) x y) && x.ID > y.ID => (InvertFlags (CMP(L|W|B) y x))
+(CMP(L|W|B) x y) && canonLessThan(x,y) => (InvertFlags (CMP(L|W|B) y x))
// strength reduction
// Assumes that the following costs from https://gmplib.org/~tege/x86-timing.pdf:
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index a866a967b9283..3c75bcfa05f61 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -361,31 +361,31 @@
// Adjust zeros to be a multiple of 16 bytes.
(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE =>
(Zero [s-s%16] (OffPtr destptr [s%16])
- (MOVOstore destptr (MOVOconst [0]) mem))
+ (MOVOstorezero destptr mem))
(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE =>
(Zero [s-s%16] (OffPtr destptr [s%16])
(MOVQstoreconst [makeValAndOff32(0,0)] destptr mem))
(Zero [16] destptr mem) && config.useSSE =>
- (MOVOstore destptr (MOVOconst [0]) mem)
+ (MOVOstorezero destptr mem)
(Zero [32] destptr mem) && config.useSSE =>
- (MOVOstore (OffPtr destptr [16]) (MOVOconst [0])
- (MOVOstore destptr (MOVOconst [0]) mem))
+ (MOVOstorezero (OffPtr destptr [16])
+ (MOVOstorezero destptr mem))
(Zero [48] destptr mem) && config.useSSE =>
- (MOVOstore (OffPtr destptr [32]) (MOVOconst [0])
- (MOVOstore (OffPtr destptr [16]) (MOVOconst [0])
- (MOVOstore destptr (MOVOconst [0]) mem)))
+ (MOVOstorezero (OffPtr destptr [32])
+ (MOVOstorezero (OffPtr destptr [16])
+ (MOVOstorezero destptr mem)))
(Zero [64] destptr mem) && config.useSSE =>
- (MOVOstore (OffPtr destptr [48]) (MOVOconst [0])
- (MOVOstore (OffPtr destptr [32]) (MOVOconst [0])
- (MOVOstore (OffPtr destptr [16]) (MOVOconst [0])
- (MOVOstore destptr (MOVOconst [0]) mem))))
+ (MOVOstorezero (OffPtr destptr [48])
+ (MOVOstorezero (OffPtr destptr [32])
+ (MOVOstorezero (OffPtr destptr [16])
+ (MOVOstorezero destptr mem))))
// Medium zeroing uses a duff device.
(Zero [s] destptr mem)
&& s > 64 && s <= 1024 && s%16 == 0 && !config.noDuffDevice =>
- (DUFFZERO [s] destptr (MOVOconst [0]) mem)
+ (DUFFZERO [s] destptr mem)
// Large zeroing uses REP STOSQ.
(Zero [s] destptr mem)
@@ -459,7 +459,7 @@
(IsInBounds idx len) => (SETB (CMPQ idx len))
(IsSliceInBounds idx len) => (SETBE (CMPQ idx len))
(NilCheck ...) => (LoweredNilCheck ...)
-(GetG ...) => (LoweredGetG ...)
+(GetG mem) && !base.Flag.ABIWrap => (LoweredGetG mem) // only lower in old ABI. in new ABI we have a G register.
(GetClosurePtr ...) => (LoweredGetClosurePtr ...)
(GetCallerPC ...) => (LoweredGetCallerPC ...)
(GetCallerSP ...) => (LoweredGetCallerSP ...)
@@ -916,7 +916,7 @@
(CMPB (MOVLconst [c]) x) => (InvertFlags (CMPBconst x [int8(c)]))
// Canonicalize the order of arguments to comparisons - helps with CSE.
-(CMP(Q|L|W|B) x y) && x.ID > y.ID => (InvertFlags (CMP(Q|L|W|B) y x))
+(CMP(Q|L|W|B) x y) && canonLessThan(x,y) => (InvertFlags (CMP(Q|L|W|B) y x))
// Using MOVZX instead of AND is cheaper.
(AND(Q|L)const [ 0xFF] x) => (MOVBQZX x)
@@ -1900,7 +1900,7 @@
&& c.Val() == 0
&& c2.Val() == 0
&& clobber(x)
- => (MOVOstore [c2.Off32()] {s} p (MOVOconst [0]) mem)
+ => (MOVOstorezero [c2.Off32()] {s} p mem)
// Combine stores into larger (unaligned) stores. Little endian.
(MOVBstore [i] {s} p (SHR(W|L|Q)const [8] w) x:(MOVBstore [i-1] {s} p w mem))
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index de5372670b365..043162e544e5e 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -44,7 +44,7 @@ var regNamesAMD64 = []string{
"R11",
"R12",
"R13",
- "R14",
+ "g", // a.k.a. R14
"R15",
"X0",
"X1",
@@ -61,7 +61,7 @@ var regNamesAMD64 = []string{
"X12",
"X13",
"X14",
- "X15",
+ "X15", // constant 0 in ABIInternal
// If you add registers, update asyncPreempt in runtime
@@ -96,11 +96,14 @@ func init() {
cx = buildReg("CX")
dx = buildReg("DX")
bx = buildReg("BX")
- gp = buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15")
- fp = buildReg("X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15")
+ gp = buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15")
+ g = buildReg("g")
+ fp = buildReg("X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14")
+ x15 = buildReg("X15")
gpsp = gp | buildReg("SP")
gpspsb = gpsp | buildReg("SB")
- callerSave = gp | fp
+ gpspsbg = gpspsb | g
+ callerSave = gp | fp | g // runtime.setg (and anything calling it) may clobber g
)
// Common slices of register masks
var (
@@ -113,10 +116,10 @@ func init() {
gp01 = regInfo{inputs: nil, outputs: gponly}
gp11 = regInfo{inputs: []regMask{gp}, outputs: gponly}
gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
- gp11sb = regInfo{inputs: []regMask{gpspsb}, outputs: gponly}
+ gp11sb = regInfo{inputs: []regMask{gpspsbg}, outputs: gponly}
gp21 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
- gp21sb = regInfo{inputs: []regMask{gpspsb, gpsp}, outputs: gponly}
+ gp21sb = regInfo{inputs: []regMask{gpspsbg, gpsp}, outputs: gponly}
gp21shift = regInfo{inputs: []regMask{gp, cx}, outputs: []regMask{gp}}
gp11div = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{ax, dx}}
gp21hmul = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx}, clobbers: ax}
@@ -125,9 +128,9 @@ func init() {
gp2flags = regInfo{inputs: []regMask{gpsp, gpsp}}
gp1flags = regInfo{inputs: []regMask{gpsp}}
- gp0flagsLoad = regInfo{inputs: []regMask{gpspsb, 0}}
- gp1flagsLoad = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
- gp2flagsLoad = regInfo{inputs: []regMask{gpspsb, gpsp, gpsp, 0}}
+ gp0flagsLoad = regInfo{inputs: []regMask{gpspsbg, 0}}
+ gp1flagsLoad = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}}
+ gp2flagsLoad = regInfo{inputs: []regMask{gpspsbg, gpsp, gpsp, 0}}
flagsgp = regInfo{inputs: nil, outputs: gponly}
gp11flags = regInfo{inputs: []regMask{gp}, outputs: []regMask{gp, 0}}
@@ -136,24 +139,24 @@ func init() {
readflags = regInfo{inputs: nil, outputs: gponly}
flagsgpax = regInfo{inputs: nil, clobbers: ax, outputs: []regMask{gp &^ ax}}
- gpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: gponly}
- gp21load = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: gponly}
- gploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: gponly}
- gp21loadidx = regInfo{inputs: []regMask{gp, gpspsb, gpsp, 0}, outputs: gponly}
+ gpload = regInfo{inputs: []regMask{gpspsbg, 0}, outputs: gponly}
+ gp21load = regInfo{inputs: []regMask{gp, gpspsbg, 0}, outputs: gponly}
+ gploadidx = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}, outputs: gponly}
+ gp21loadidx = regInfo{inputs: []regMask{gp, gpspsbg, gpsp, 0}, outputs: gponly}
gp21pax = regInfo{inputs: []regMask{gp &^ ax, gp}, outputs: []regMask{gp &^ ax}, clobbers: ax}
- gpstore = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
- gpstoreconst = regInfo{inputs: []regMask{gpspsb, 0}}
- gpstoreidx = regInfo{inputs: []regMask{gpspsb, gpsp, gpsp, 0}}
- gpstoreconstidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
- gpstorexchg = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: []regMask{gp}}
+ gpstore = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}}
+ gpstoreconst = regInfo{inputs: []regMask{gpspsbg, 0}}
+ gpstoreidx = regInfo{inputs: []regMask{gpspsbg, gpsp, gpsp, 0}}
+ gpstoreconstidx = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}}
+ gpstorexchg = regInfo{inputs: []regMask{gp, gpspsbg, 0}, outputs: []regMask{gp}}
cmpxchg = regInfo{inputs: []regMask{gp, ax, gp, 0}, outputs: []regMask{gp, 0}, clobbers: ax}
fp01 = regInfo{inputs: nil, outputs: fponly}
fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
- fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly}
- fp21loadidx = regInfo{inputs: []regMask{fp, gpspsb, gpspsb, 0}, outputs: fponly}
+ fp21load = regInfo{inputs: []regMask{fp, gpspsbg, 0}, outputs: fponly}
+ fp21loadidx = regInfo{inputs: []regMask{fp, gpspsbg, gpspsb, 0}, outputs: fponly}
fpgp = regInfo{inputs: fponly, outputs: gponly}
gpfp = regInfo{inputs: gponly, outputs: fponly}
fp11 = regInfo{inputs: fponly, outputs: fponly}
@@ -684,19 +687,20 @@ func init() {
// Note: LEAx{1,2,4,8} must not have OpSB as either argument.
// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
- {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load byte from arg0+auxint+aux. arg1=mem. Zero extend.
- {name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
- {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
- {name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
- {name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
- {name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
- {name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem
- {name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store byte in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128", faultOnNilArg0: true, symEffect: "Read"}, // load 16 bytes from arg0+auxint+aux. arg1=mem
- {name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load byte from arg0+auxint+aux. arg1=mem. Zero extend.
+ {name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
+ {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
+ {name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
+ {name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
+ {name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
+ {name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem
+ {name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store byte in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128", faultOnNilArg0: true, symEffect: "Read"}, // load 16 bytes from arg0+auxint+aux. arg1=mem
+ {name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVOstorezero", argLength: 2, reg: regInfo{inputs: []regMask{gpspsb, 0}}, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of zero to arg0+auxint+aux. arg1=mem
// indexed loads/stores
{name: "MOVBloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBLZX", scale: 1, aux: "SymOff", typ: "UInt8", symEffect: "Read"}, // load a byte from arg0+arg1+auxint+aux. arg2=mem
@@ -735,22 +739,20 @@ func init() {
{name: "MOVQstoreconstidx8", argLength: 3, reg: gpstoreconstidx, asm: "MOVQ", scale: 8, aux: "SymValAndOff", typ: "Mem", symEffect: "Write"}, // store 8 bytes of ... 8*arg1 ...
// arg0 = pointer to start of memory to zero
- // arg1 = value to store (will always be zero)
- // arg2 = mem
+ // arg1 = mem
// auxint = # of bytes to zero
// returns mem
{
name: "DUFFZERO",
aux: "Int64",
- argLength: 3,
+ argLength: 2,
reg: regInfo{
- inputs: []regMask{buildReg("DI"), buildReg("X0")},
+ inputs: []regMask{buildReg("DI")},
clobbers: buildReg("DI"),
},
faultOnNilArg0: true,
unsafePoint: true, // FP maintenance around DUFFCOPY can be clobbered by interrupts
},
- {name: "MOVOconst", reg: regInfo{nil, 0, []regMask{fp}}, typ: "Int128", aux: "Int128", rematerializeable: true},
// arg0 = address of memory to zero
// arg1 = # of 8-byte words to zero
@@ -830,7 +832,7 @@ func init() {
{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
// It saves all GP registers if necessary, but may clobber others.
- {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("DI"), buildReg("AX CX DX BX BP SI R8 R9")}, clobbers: callerSave &^ gp}, clobberFlags: true, aux: "Sym", symEffect: "None"},
+ {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("DI"), buildReg("AX CX DX BX BP SI R8 R9")}, clobbers: callerSave &^ (gp | g)}, clobberFlags: true, aux: "Sym", symEffect: "None"},
{name: "LoweredHasCPUFeature", argLength: 0, reg: gp01, rematerializeable: true, typ: "UInt64", aux: "Sym", symEffect: "None"},
@@ -935,6 +937,7 @@ func init() {
regnames: regNamesAMD64,
gpregmask: gp,
fpregmask: fp,
+ specialregmask: x15,
framepointerreg: int8(num["BP"]),
linkreg: -1, // not used
})
diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules
index 11c36b5da3567..de0df363e4964 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM.rules
@@ -507,7 +507,7 @@
(TEQ x (MOVWconst [c])) => (TEQconst [c] x)
// Canonicalize the order of arguments to comparisons - helps with CSE.
-(CMP x y) && x.ID > y.ID => (InvertFlags (CMP y x))
+(CMP x y) && canonLessThan(x,y) => (InvertFlags (CMP y x))
// don't extend after proper load
// MOVWreg instruction is not emitted if src and dst registers are same, but it ensures the type.
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules
index 3f4d0c1c52769..a0e2a0d5e2722 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules
@@ -1151,7 +1151,7 @@
(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) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW) y x))
// mul-neg => mneg
(NEG (MUL x y)) => (MNEG x y)
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index c06404617297a..a762be65d4259 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -1088,7 +1088,7 @@
(CMPWU (MOVDconst [c]) y) && isU16Bit(c) => (InvertFlags (CMPWUconst y [int32(c)]))
// Canonicalize the order of arguments to comparisons - helps with CSE.
-((CMP|CMPW|CMPU|CMPWU) x y) && x.ID > y.ID => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x))
+((CMP|CMPW|CMPU|CMPWU) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x))
// ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1
// ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0
diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules
index 384f2e807e056..c3421da0a242c 100644
--- a/src/cmd/compile/internal/ssa/gen/S390X.rules
+++ b/src/cmd/compile/internal/ssa/gen/S390X.rules
@@ -785,7 +785,7 @@
=> (RISBGZ x {s390x.NewRotateParams(r.Start, r.Start, -r.Start&63)})
// Canonicalize the order of arguments to comparisons - helps with CSE.
-((CMP|CMPW|CMPU|CMPWU) x y) && x.ID > y.ID => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x))
+((CMP|CMPW|CMPU|CMPWU) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x))
// Use sign/zero extend instead of RISBGZ.
(RISBGZ x {r}) && r == s390x.NewRotateParams(56, 63, 0) => (MOVBZreg x)
diff --git a/src/cmd/compile/internal/ssa/gen/dec64.rules b/src/cmd/compile/internal/ssa/gen/dec64.rules
index 9297ed8d2e0a0..b0f10d0a0f4e9 100644
--- a/src/cmd/compile/internal/ssa/gen/dec64.rules
+++ b/src/cmd/compile/internal/ssa/gen/dec64.rules
@@ -42,20 +42,20 @@
(Store {hi.Type} dst hi mem))
// These are not enabled during decomposeBuiltin if late call expansion, but they are always enabled for softFloat
-(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") =>
+(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin") =>
(Int64Make
(Arg {n} [off+4])
(Arg {n} [off]))
-(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") =>
+(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin") =>
(Int64Make
(Arg {n} [off+4])
(Arg {n} [off]))
-(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") =>
+(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin") =>
(Int64Make
(Arg {n} [off])
(Arg {n} [off+4]))
-(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin") =>
+(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin") =>
(Int64Make
(Arg {n} [off])
(Arg {n} [off+4]))
diff --git a/src/cmd/compile/internal/ssa/gen/decArgs.rules b/src/cmd/compile/internal/ssa/gen/decArgs.rules
deleted file mode 100644
index 1c9a0bb23de24..0000000000000
--- a/src/cmd/compile/internal/ssa/gen/decArgs.rules
+++ /dev/null
@@ -1,58 +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.
-
-// Decompose compound argument values
-// Do this early to simplify tracking names for debugging.
-
-(Arg {n} [off]) && v.Type.IsString() =>
- (StringMake
- (Arg {n} [off])
- (Arg {n} [off+int32(config.PtrSize)]))
-
-(Arg {n} [off]) && v.Type.IsSlice() =>
- (SliceMake
- (Arg {n} [off])
- (Arg {n} [off+int32(config.PtrSize)])
- (Arg {n} [off+2*int32(config.PtrSize)]))
-
-(Arg {n} [off]) && v.Type.IsInterface() =>
- (IMake
- (Arg {n} [off])
- (Arg {n} [off+int32(config.PtrSize)]))
-
-(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 16 =>
- (ComplexMake
- (Arg {n} [off])
- (Arg {n} [off+8]))
-
-(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 8 =>
- (ComplexMake
- (Arg {n} [off])
- (Arg {n} [off+4]))
-
-(Arg ) && t.IsStruct() && t.NumFields() == 0 && fe.CanSSA(t) =>
- (StructMake0)
-(Arg {n} [off]) && t.IsStruct() && t.NumFields() == 1 && fe.CanSSA(t) =>
- (StructMake1
- (Arg {n} [off+int32(t.FieldOff(0))]))
-(Arg {n} [off]) && t.IsStruct() && t.NumFields() == 2 && fe.CanSSA(t) =>
- (StructMake2
- (Arg {n} [off+int32(t.FieldOff(0))])
- (Arg {n} [off+int32(t.FieldOff(1))]))
-(Arg {n} [off]) && t.IsStruct() && t.NumFields() == 3 && fe.CanSSA(t) =>
- (StructMake3
- (Arg {n} [off+int32(t.FieldOff(0))])
- (Arg {n} [off+int32(t.FieldOff(1))])
- (Arg {n} [off+int32(t.FieldOff(2))]))
-(Arg {n} [off]) && t.IsStruct() && t.NumFields() == 4 && fe.CanSSA(t) =>
- (StructMake4
- (Arg {n} [off+int32(t.FieldOff(0))])
- (Arg {n} [off+int32(t.FieldOff(1))])
- (Arg {n} [off+int32(t.FieldOff(2))])
- (Arg {n} [off+int32(t.FieldOff(3))]))
-
-(Arg ) && t.IsArray() && t.NumElem() == 0 =>
- (ArrayMake0)
-(Arg {n} [off]) && t.IsArray() && t.NumElem() == 1 && fe.CanSSA(t) =>
- (ArrayMake1 (Arg {n} [off]))
diff --git a/src/cmd/compile/internal/ssa/gen/decArgsOps.go b/src/cmd/compile/internal/ssa/gen/decArgsOps.go
deleted file mode 100644
index b73d9d3976102..0000000000000
--- a/src/cmd/compile/internal/ssa/gen/decArgsOps.go
+++ /dev/null
@@ -1,20 +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.
-
-// +build ignore
-
-package main
-
-var decArgsOps = []opData{}
-
-var decArgsBlocks = []blockData{}
-
-func init() {
- archs = append(archs, arch{
- name: "decArgs",
- ops: decArgsOps,
- blocks: decArgsBlocks,
- generic: true,
- })
-}
diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go
index aaf9101368fcf..6388aab3621b9 100644
--- a/src/cmd/compile/internal/ssa/gen/rulegen.go
+++ b/src/cmd/compile/internal/ssa/gen/rulegen.go
@@ -582,6 +582,7 @@ func fprint(w io.Writer, n Node) {
"math",
"cmd/internal/obj",
"cmd/internal/objabi",
+ "cmd/compile/internal/base",
"cmd/compile/internal/types",
}, n.Arch.imports...) {
fmt.Fprintf(w, "import %q\n", path)
diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go
index a333982389aa4..4cd0ac8d777b3 100644
--- a/src/cmd/compile/internal/ssa/location.go
+++ b/src/cmd/compile/internal/ssa/location.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/compile/internal/types"
"fmt"
)
@@ -59,7 +60,7 @@ func (r *Register) GCNum() int16 {
// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8}
// parent = &{N: s, Type: string}
type LocalSlot struct {
- N GCNode // an ONAME *gc.Node representing a stack location.
+ N *ir.Name // an ONAME *ir.Name representing a stack location.
Type *types.Type // type of slot
Off int64 // offset of slot in N
@@ -86,3 +87,29 @@ func (t LocPair) String() string {
}
return fmt.Sprintf("<%s,%s>", n0, n1)
}
+
+type ArgPair struct {
+ reg *Register
+ mem LocalSlot
+}
+
+func (ap *ArgPair) Reg() int16 {
+ return ap.reg.objNum
+}
+
+func (ap *ArgPair) Type() *types.Type {
+ return ap.mem.Type
+}
+
+func (ap *ArgPair) Mem() *LocalSlot {
+ return &ap.mem
+}
+
+func (t ArgPair) String() string {
+ n0 := "nil"
+ if t.reg != nil {
+ n0 = t.reg.String()
+ }
+ n1 := t.mem.String()
+ return fmt.Sprintf("<%s,%s>", n0, n1)
+}
diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go
index d1bad529e700c..bae50657c9adc 100644
--- a/src/cmd/compile/internal/ssa/nilcheck.go
+++ b/src/cmd/compile/internal/ssa/nilcheck.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/internal/objabi"
"cmd/internal/src"
)
@@ -235,7 +236,7 @@ func nilcheckelim2(f *Func) {
continue
}
if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
- if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasPointers()) {
+ if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(*ir.Name).Type().HasPointers()) {
// These ops don't really change memory.
continue
// Note: OpVarDef requires that the defined variable not have pointers.
diff --git a/src/cmd/compile/internal/ssa/nilcheck_test.go b/src/cmd/compile/internal/ssa/nilcheck_test.go
index 16d94614d815c..2e32afe2a6ba6 100644
--- a/src/cmd/compile/internal/ssa/nilcheck_test.go
+++ b/src/cmd/compile/internal/ssa/nilcheck_test.go
@@ -212,7 +212,7 @@ func TestNilcheckPhi(t *testing.T) {
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
Valu("sp", OpSP, c.config.Types.Uintptr, 0, nil),
- Valu("baddr", OpLocalAddr, c.config.Types.Bool, 0, "b", "sp", "mem"),
+ Valu("baddr", OpLocalAddr, c.config.Types.Bool, 0, StringToAux("b"), "sp", "mem"),
Valu("bool1", OpLoad, c.config.Types.Bool, 0, nil, "baddr", "mem"),
If("bool1", "b1", "b2")),
Bloc("b1",
diff --git a/src/cmd/compile/internal/ssa/numberlines.go b/src/cmd/compile/internal/ssa/numberlines.go
index f4e62b88c4f6b..2a9c8e4f326fc 100644
--- a/src/cmd/compile/internal/ssa/numberlines.go
+++ b/src/cmd/compile/internal/ssa/numberlines.go
@@ -5,7 +5,6 @@
package ssa
import (
- "cmd/internal/obj"
"cmd/internal/src"
"fmt"
"sort"
@@ -23,15 +22,6 @@ func isPoorStatementOp(op Op) bool {
return false
}
-// LosesStmtMark reports whether a prog with op as loses its statement mark on the way to DWARF.
-// The attributes from some opcodes are lost in translation.
-// TODO: this is an artifact of how funcpctab combines information for instructions at a single PC.
-// Should try to fix it there.
-func LosesStmtMark(as obj.As) bool {
- // is_stmt does not work for these; it DOES for ANOP even though that generates no code.
- return as == obj.APCDATA || as == obj.AFUNCDATA
-}
-
// nextGoodStatementIndex returns an index at i or later that is believed
// to be a good place to start the statement for b. This decision is
// based on v's Op, the possibility of a better later operation, and
diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go
index d1673352bd163..f41d014d4138d 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/ir"
"cmd/compile/internal/types"
"cmd/internal/obj"
"fmt"
@@ -70,7 +71,8 @@ type auxType int8
type Param struct {
Type *types.Type
- Offset int32 // TODO someday this will be a register
+ Offset int32 // Offset of Param if not in a register.
+ Name *ir.Name // For OwnAux, need to prepend stores with Vardefs
}
type AuxCall struct {
@@ -197,6 +199,14 @@ func ClosureAuxCall(args []Param, results []Param) *AuxCall {
return &AuxCall{Fn: nil, args: args, results: results}
}
+func (*AuxCall) CanBeAnSSAAux() {}
+
+// OwnAuxCall returns a function's own AuxCall
+func OwnAuxCall(fn *obj.LSym, args []Param, results []Param) *AuxCall {
+ // TODO if this remains identical to ClosureAuxCall above after new ABI is done, should deduplicate.
+ return &AuxCall{Fn: fn, args: args, results: results}
+}
+
const (
auxNone auxType = iota
auxBool // auxInt is 0/1 for false/true
@@ -247,8 +257,8 @@ const (
// - a *obj.LSym, for an offset from SB (the global pointer)
// - nil, for no offset
type Sym interface {
- String() string
CanBeAnSSASym()
+ CanBeAnSSAAux()
}
// A ValAndOff is used by the several opcodes. It holds
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index e590f6ba5d990..ccfed93475ddb 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -970,6 +970,7 @@ const (
OpAMD64MOVQstore
OpAMD64MOVOload
OpAMD64MOVOstore
+ OpAMD64MOVOstorezero
OpAMD64MOVBloadidx1
OpAMD64MOVWloadidx1
OpAMD64MOVWloadidx2
@@ -998,7 +999,6 @@ const (
OpAMD64MOVQstoreconstidx1
OpAMD64MOVQstoreconstidx8
OpAMD64DUFFZERO
- OpAMD64MOVOconst
OpAMD64REPSTOSQ
OpAMD64CALLstatic
OpAMD64CALLclosure
@@ -6162,11 +6162,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6178,11 +6178,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6193,11 +6193,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6208,11 +6208,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6224,11 +6224,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AMULSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6240,11 +6240,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AMULSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6255,11 +6255,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ADIVSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6270,11 +6270,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ADIVSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6287,10 +6287,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6303,10 +6303,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6318,7 +6318,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVSS,
reg: regInfo{
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6330,7 +6330,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVSD,
reg: regInfo{
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6343,11 +6343,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6360,11 +6360,11 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6377,11 +6377,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6394,11 +6394,11 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6411,8 +6411,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVSS,
reg: regInfo{
inputs: []inputInfo{
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
},
},
@@ -6425,8 +6425,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVSD,
reg: regInfo{
inputs: []inputInfo{
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
},
},
@@ -6439,9 +6439,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
},
},
@@ -6454,9 +6454,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
},
},
@@ -6469,9 +6469,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
},
},
@@ -6484,9 +6484,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
},
},
@@ -6500,11 +6500,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6518,11 +6518,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6536,11 +6536,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6554,11 +6554,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6572,11 +6572,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AMULSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6590,11 +6590,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AMULSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6608,11 +6608,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ADIVSS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6626,11 +6626,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ADIVSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6644,12 +6644,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6663,12 +6663,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6682,12 +6682,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6701,12 +6701,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6720,12 +6720,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6739,12 +6739,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6758,12 +6758,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6777,12 +6777,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6796,12 +6796,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6815,12 +6815,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6834,12 +6834,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6853,12 +6853,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6872,12 +6872,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6891,12 +6891,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6910,12 +6910,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6929,12 +6929,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
- {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -6946,11 +6946,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -6962,11 +6962,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -6978,10 +6978,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -6993,10 +6993,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7010,7 +7010,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7024,7 +7024,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7036,11 +7036,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7052,11 +7052,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7069,10 +7069,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7085,10 +7085,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7101,11 +7101,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMULQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7118,11 +7118,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMULL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7134,10 +7134,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMUL3Q,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7149,10 +7149,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AIMUL3L,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7165,7 +7165,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 4, // DX
outputs: []outputInfo{
@@ -7183,7 +7183,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 4, // DX
outputs: []outputInfo{
@@ -7200,7 +7200,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
@@ -7216,7 +7216,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
@@ -7232,7 +7232,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
@@ -7248,7 +7248,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
@@ -7264,11 +7264,11 @@ var opcodeTable = [...]opInfo{
clobberFlags: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7281,7 +7281,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49147}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 1}, // AX
@@ -7298,7 +7298,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49147}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 1}, // AX
@@ -7315,7 +7315,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49147}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 1}, // AX
@@ -7331,7 +7331,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49147}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 1}, // AX
@@ -7347,7 +7347,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49147}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 1}, // AX
@@ -7363,7 +7363,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49147}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 1}, // AX
@@ -7378,11 +7378,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ANEGL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7394,12 +7394,12 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7411,12 +7411,12 @@ var opcodeTable = [...]opInfo{
asm: x86.AADCQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7428,11 +7428,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7444,11 +7444,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADCQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7459,12 +7459,12 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7475,12 +7475,12 @@ var opcodeTable = [...]opInfo{
asm: x86.ASBBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7492,11 +7492,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7508,11 +7508,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASBBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7525,7 +7525,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 4}, // DX
@@ -7542,7 +7542,7 @@ var opcodeTable = [...]opInfo{
inputs: []inputInfo{
{0, 4}, // DX
{1, 1}, // AX
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{0, 1}, // AX
@@ -7559,11 +7559,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7576,11 +7576,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7593,10 +7593,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7609,10 +7609,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7626,7 +7626,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7640,7 +7640,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7653,11 +7653,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7670,11 +7670,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7687,10 +7687,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7703,10 +7703,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7720,7 +7720,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7734,7 +7734,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7747,11 +7747,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7764,11 +7764,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7781,10 +7781,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7797,10 +7797,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7814,7 +7814,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7828,7 +7828,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7838,8 +7838,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7849,8 +7849,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7860,8 +7860,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7871,8 +7871,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7883,7 +7883,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7894,7 +7894,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7905,7 +7905,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7916,7 +7916,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -7929,8 +7929,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7943,8 +7943,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7957,8 +7957,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPW,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7971,8 +7971,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPB,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7985,7 +7985,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -7998,7 +7998,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8011,7 +8011,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPW,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8024,7 +8024,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMPB,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8037,9 +8037,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8053,9 +8053,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8068,9 +8068,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8084,9 +8084,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8099,9 +8099,9 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8115,9 +8115,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8131,9 +8131,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8146,8 +8146,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8161,8 +8161,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8175,8 +8175,8 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8190,8 +8190,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8204,8 +8204,8 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8219,8 +8219,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8234,8 +8234,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8245,8 +8245,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AUCOMISS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -8256,8 +8256,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AUCOMISD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -8267,8 +8267,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8278,8 +8278,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8291,11 +8291,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8307,11 +8307,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8323,11 +8323,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8339,11 +8339,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8355,11 +8355,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8371,11 +8371,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8386,7 +8386,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8397,7 +8397,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8410,10 +8410,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8426,10 +8426,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8442,10 +8442,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8458,10 +8458,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8474,10 +8474,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8490,10 +8490,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8507,8 +8507,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8522,8 +8522,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8537,8 +8537,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8552,8 +8552,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8567,8 +8567,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8582,8 +8582,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8597,7 +8597,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8611,7 +8611,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTCL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8625,7 +8625,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8639,7 +8639,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTSL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8653,7 +8653,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8667,7 +8667,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ABTRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -8678,8 +8678,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8690,8 +8690,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8702,8 +8702,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8714,8 +8714,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8726,7 +8726,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8737,7 +8737,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8748,7 +8748,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8759,7 +8759,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ATESTB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8772,10 +8772,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8788,10 +8788,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8804,10 +8804,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHLQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8820,10 +8820,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHLL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8836,10 +8836,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8852,10 +8852,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8868,10 +8868,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8884,10 +8884,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8900,10 +8900,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8916,10 +8916,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8932,10 +8932,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8948,10 +8948,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASHRB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8964,10 +8964,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8980,10 +8980,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -8996,10 +8996,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9012,10 +9012,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9028,10 +9028,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9044,10 +9044,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9060,10 +9060,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9076,10 +9076,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASARB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9092,10 +9092,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9108,10 +9108,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9124,10 +9124,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9140,10 +9140,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9156,10 +9156,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9172,10 +9172,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9188,10 +9188,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9204,10 +9204,10 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 2}, // CX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9220,10 +9220,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9236,10 +9236,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9252,10 +9252,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLW,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9268,10 +9268,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AROLB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9286,11 +9286,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9305,11 +9305,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9324,11 +9324,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9343,11 +9343,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9362,11 +9362,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9381,11 +9381,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9400,11 +9400,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9419,11 +9419,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9438,11 +9438,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9457,11 +9457,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9476,12 +9476,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9496,12 +9496,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9516,12 +9516,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9536,12 +9536,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9556,12 +9556,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9576,12 +9576,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9596,12 +9596,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9616,12 +9616,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9636,12 +9636,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9656,12 +9656,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9676,12 +9676,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9696,12 +9696,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9716,12 +9716,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9736,12 +9736,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9756,12 +9756,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9776,12 +9776,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9796,12 +9796,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9816,12 +9816,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9836,12 +9836,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9856,12 +9856,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9876,12 +9876,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9896,12 +9896,12 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9916,12 +9916,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9936,12 +9936,12 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9956,12 +9956,12 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -9975,8 +9975,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -9990,8 +9990,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10005,8 +10005,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10020,8 +10020,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AORQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10035,8 +10035,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10050,8 +10050,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10065,8 +10065,8 @@ var opcodeTable = [...]opInfo{
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10080,8 +10080,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10095,8 +10095,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10110,8 +10110,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10125,9 +10125,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10141,9 +10141,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10157,9 +10157,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10173,9 +10173,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10189,9 +10189,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10205,9 +10205,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10221,9 +10221,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10237,9 +10237,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10253,9 +10253,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10269,9 +10269,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10285,9 +10285,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10301,9 +10301,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10317,9 +10317,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10333,9 +10333,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10349,9 +10349,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10365,9 +10365,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10381,9 +10381,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10397,9 +10397,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10413,9 +10413,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10429,9 +10429,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10445,9 +10445,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10461,9 +10461,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10477,9 +10477,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10493,9 +10493,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10509,9 +10509,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10525,8 +10525,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10540,8 +10540,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10555,8 +10555,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10570,8 +10570,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10585,8 +10585,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10600,8 +10600,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10615,8 +10615,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10630,8 +10630,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10645,8 +10645,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10660,8 +10660,8 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10675,8 +10675,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10690,8 +10690,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10705,8 +10705,8 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10720,8 +10720,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10735,8 +10735,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10750,8 +10750,8 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10765,8 +10765,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10780,8 +10780,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10795,8 +10795,8 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10810,8 +10810,8 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -10823,10 +10823,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ANEGQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10838,10 +10838,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ANEGL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10853,10 +10853,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ANOTQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10868,10 +10868,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ANOTL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10881,11 +10881,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSFQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10896,10 +10896,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSFL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10909,11 +10909,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSRQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10924,10 +10924,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10938,11 +10938,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQEQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10953,11 +10953,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10968,11 +10968,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQLT,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10983,11 +10983,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQGT,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -10998,11 +10998,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQLE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11013,11 +11013,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQGE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11028,11 +11028,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQLS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11043,11 +11043,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQHI,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11058,11 +11058,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQCC,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11073,11 +11073,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQCS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11088,11 +11088,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLEQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11103,11 +11103,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11118,11 +11118,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLLT,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11133,11 +11133,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLGT,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11148,11 +11148,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLLE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11163,11 +11163,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLGE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11178,11 +11178,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLLS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11193,11 +11193,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLHI,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11208,11 +11208,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLCC,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11223,11 +11223,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLCS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11238,11 +11238,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWEQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11253,11 +11253,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11268,11 +11268,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWLT,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11283,11 +11283,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWGT,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11298,11 +11298,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWLE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11313,11 +11313,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWGE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11328,11 +11328,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWLS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11343,11 +11343,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWHI,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11358,11 +11358,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWCC,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11373,11 +11373,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWCS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11388,12 +11388,12 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11404,11 +11404,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11419,11 +11419,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQHI,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11434,11 +11434,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVQCC,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11449,12 +11449,12 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11465,11 +11465,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11480,11 +11480,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLHI,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11495,11 +11495,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVLCC,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11510,12 +11510,12 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11526,11 +11526,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11541,11 +11541,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWHI,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11556,11 +11556,11 @@ var opcodeTable = [...]opInfo{
asm: x86.ACMOVWCC,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11572,10 +11572,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSWAPQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11587,10 +11587,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ABSWAPL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11601,10 +11601,10 @@ var opcodeTable = [...]opInfo{
asm: x86.APOPCNTQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11615,10 +11615,10 @@ var opcodeTable = [...]opInfo{
asm: x86.APOPCNTL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11628,10 +11628,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ASQRTSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -11642,10 +11642,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AROUNDSD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -11656,12 +11656,12 @@ var opcodeTable = [...]opInfo{
asm: x86.AVFMADD231SD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {2, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -11671,7 +11671,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASBBQ,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11681,7 +11681,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASBBL,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11691,7 +11691,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETEQ,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11701,7 +11701,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETNE,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11711,7 +11711,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETLT,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11721,7 +11721,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETLE,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11731,7 +11731,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETGT,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11741,7 +11741,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETGE,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11751,7 +11751,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETCS,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11761,7 +11761,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETLS,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11771,7 +11771,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETHI,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11781,7 +11781,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETCC,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11791,7 +11791,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETOS,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11804,7 +11804,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETEQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11817,7 +11817,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETNE,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11830,7 +11830,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETLT,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11843,7 +11843,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETLE,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11856,7 +11856,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETGT,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11869,7 +11869,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETGE,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11882,7 +11882,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETCS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11895,7 +11895,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETLS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11908,7 +11908,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETHI,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11921,7 +11921,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETCC,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -11933,7 +11933,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
clobbers: 1, // AX
outputs: []outputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11945,7 +11945,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
clobbers: 1, // AX
outputs: []outputInfo{
- {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49134}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11955,7 +11955,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETPC,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11965,7 +11965,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETPS,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11975,7 +11975,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETHI,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11985,7 +11985,7 @@ var opcodeTable = [...]opInfo{
asm: x86.ASETCC,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -11995,10 +11995,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVBQSX,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12008,10 +12008,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVBLZX,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12021,10 +12021,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVWQSX,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12034,10 +12034,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVWLZX,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12047,10 +12047,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVLQSX,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12060,10 +12060,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12075,7 +12075,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVL,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12087,7 +12087,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVQ,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12097,10 +12097,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTTSD2SL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12110,10 +12110,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTTSD2SQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12123,10 +12123,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTTSS2SL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12136,10 +12136,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTTSS2SQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12149,10 +12149,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTSL2SS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12162,10 +12162,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTSL2SD,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12175,10 +12175,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTSQ2SS,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12188,10 +12188,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTSQ2SD,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12201,10 +12201,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTSD2SS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12214,10 +12214,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ACVTSS2SD,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12226,10 +12226,10 @@ var opcodeTable = [...]opInfo{
argLen: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12238,10 +12238,10 @@ var opcodeTable = [...]opInfo{
argLen: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12250,10 +12250,10 @@ var opcodeTable = [...]opInfo{
argLen: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12262,10 +12262,10 @@ var opcodeTable = [...]opInfo{
argLen: 1,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12277,11 +12277,11 @@ var opcodeTable = [...]opInfo{
asm: x86.APXOR,
reg: regInfo{
inputs: []inputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12294,10 +12294,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ALEAQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12310,10 +12310,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ALEAL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12326,10 +12326,10 @@ var opcodeTable = [...]opInfo{
asm: x86.ALEAW,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12343,11 +12343,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12361,11 +12361,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12379,11 +12379,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12396,11 +12396,11 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12413,11 +12413,11 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12430,11 +12430,11 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12447,11 +12447,11 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12464,11 +12464,11 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12481,11 +12481,11 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12498,11 +12498,11 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12515,11 +12515,11 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12532,11 +12532,11 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12549,10 +12549,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVBLZX,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12565,10 +12565,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVBQSX,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12581,10 +12581,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVWLZX,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12597,10 +12597,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVWQSX,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12613,10 +12613,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12629,10 +12629,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVLQSX,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12645,10 +12645,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12661,8 +12661,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVB,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12675,8 +12675,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVW,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12689,8 +12689,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12703,8 +12703,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVQ,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12717,10 +12717,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVUPS,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
},
@@ -12733,8 +12733,21 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVUPS,
reg: regInfo{
inputs: []inputInfo{
- {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVOstorezero",
+ auxType: auxSymOff,
+ argLen: 2,
+ faultOnNilArg0: true,
+ symEffect: SymWrite,
+ asm: x86.AMOVUPS,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
},
},
},
@@ -12748,11 +12761,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12766,11 +12779,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12783,11 +12796,11 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12801,11 +12814,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12818,11 +12831,11 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12835,11 +12848,11 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12853,11 +12866,11 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12870,11 +12883,11 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -12888,9 +12901,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12904,9 +12917,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12919,9 +12932,9 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12935,9 +12948,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12950,9 +12963,9 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12965,9 +12978,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12981,9 +12994,9 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -12996,9 +13009,9 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13011,7 +13024,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVB,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13024,7 +13037,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVW,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13037,7 +13050,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13050,7 +13063,7 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13064,8 +13077,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13079,8 +13092,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13093,8 +13106,8 @@ var opcodeTable = [...]opInfo{
scale: 2,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13108,8 +13121,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13122,8 +13135,8 @@ var opcodeTable = [...]opInfo{
scale: 4,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13137,8 +13150,8 @@ var opcodeTable = [...]opInfo{
scale: 1,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13151,36 +13164,24 @@ var opcodeTable = [...]opInfo{
scale: 8,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
{
name: "DUFFZERO",
auxType: auxInt64,
- argLen: 3,
+ argLen: 2,
faultOnNilArg0: true,
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 128}, // DI
- {1, 65536}, // X0
+ {0, 128}, // DI
},
clobbers: 128, // DI
},
},
- {
- name: "MOVOconst",
- auxType: auxInt128,
- argLen: 0,
- rematerializeable: true,
- reg: regInfo{
- outputs: []outputInfo{
- {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
- },
- },
- },
{
name: "REPSTOSQ",
argLen: 4,
@@ -13201,7 +13202,7 @@ var opcodeTable = [...]opInfo{
clobberFlags: true,
call: true,
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
+ clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
{
@@ -13213,9 +13214,9 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 4}, // DX
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
- 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
+ clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
{
@@ -13226,9 +13227,9 @@ var opcodeTable = [...]opInfo{
call: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
- 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
+ clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
{
@@ -13271,7 +13272,7 @@ var opcodeTable = [...]opInfo{
argLen: 1,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13291,7 +13292,7 @@ var opcodeTable = [...]opInfo{
rematerializeable: true,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13301,7 +13302,7 @@ var opcodeTable = [...]opInfo{
rematerializeable: true,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13313,7 +13314,7 @@ var opcodeTable = [...]opInfo{
faultOnNilArg0: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13328,7 +13329,7 @@ var opcodeTable = [...]opInfo{
{0, 128}, // DI
{1, 879}, // AX CX DX BX BP SI R8 R9
},
- clobbers: 4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ clobbers: 2147418112, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
},
{
@@ -13339,7 +13340,7 @@ var opcodeTable = [...]opInfo{
symEffect: SymNone,
reg: regInfo{
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13413,10 +13414,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVB,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13429,10 +13430,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVL,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13445,10 +13446,10 @@ var opcodeTable = [...]opInfo{
asm: x86.AMOVQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13463,11 +13464,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXCHGB,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13482,11 +13483,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXCHGL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13501,11 +13502,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXCHGQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13521,11 +13522,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXADDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13541,11 +13542,11 @@ var opcodeTable = [...]opInfo{
asm: x86.AXADDQ,
reg: regInfo{
inputs: []inputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
outputs: []outputInfo{
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13571,13 +13572,13 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 1}, // AX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13593,13 +13594,13 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 1}, // AX
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {2, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
clobbers: 1, // AX
outputs: []outputInfo{
{1, 0},
- {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
},
},
},
@@ -13614,8 +13615,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDB,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13630,8 +13631,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13646,8 +13647,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AORB,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -13662,8 +13663,8 @@ var opcodeTable = [...]opInfo{
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
- {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
- {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
},
},
},
@@ -36172,8 +36173,8 @@ var registersAMD64 = [...]Register{
{11, x86.REG_R11, 10, "R11"},
{12, x86.REG_R12, 11, "R12"},
{13, x86.REG_R13, 12, "R13"},
- {14, x86.REG_R14, 13, "R14"},
- {15, x86.REG_R15, 14, "R15"},
+ {14, x86.REGG, -1, "g"},
+ {15, x86.REG_R15, 13, "R15"},
{16, x86.REG_X0, -1, "X0"},
{17, x86.REG_X1, -1, "X1"},
{18, x86.REG_X2, -1, "X2"},
@@ -36192,9 +36193,9 @@ var registersAMD64 = [...]Register{
{31, x86.REG_X15, -1, "X15"},
{32, 0, -1, "SB"},
}
-var gpRegMaskAMD64 = regMask(65519)
-var fpRegMaskAMD64 = regMask(4294901760)
-var specialRegMaskAMD64 = regMask(0)
+var gpRegMaskAMD64 = regMask(49135)
+var fpRegMaskAMD64 = regMask(2147418112)
+var specialRegMaskAMD64 = regMask(2147483648)
var framepointerRegAMD64 = int8(5)
var linkRegAMD64 = int8(-1)
var registersARM = [...]Register{
diff --git a/src/cmd/compile/internal/ssa/poset.go b/src/cmd/compile/internal/ssa/poset.go
index f5a2b3a8c2104..1e04b48ba4612 100644
--- a/src/cmd/compile/internal/ssa/poset.go
+++ b/src/cmd/compile/internal/ssa/poset.go
@@ -136,13 +136,13 @@ type posetNode struct {
// Most internal data structures are pre-allocated and flat, so for instance adding a
// new relation does not cause any allocation. For performance reasons,
// each node has only up to two outgoing edges (like a binary tree), so intermediate
-// "dummy" nodes are required to represent more than two relations. For instance,
+// "extra" nodes are required to represent more than two relations. For instance,
// to record that A r i1
// i2 \ /
// i2
//
- dummy := po.newnode(nil)
- po.changeroot(r, dummy)
- po.upush(undoChangeRoot, dummy, newedge(r, false))
- po.addchild(dummy, r, false)
- po.addchild(dummy, i1, false)
+ extra := po.newnode(nil)
+ po.changeroot(r, extra)
+ po.upush(undoChangeRoot, extra, newedge(r, false))
+ po.addchild(extra, r, false)
+ po.addchild(extra, i1, false)
po.addchild(i1, i2, strict)
case f1 && f2:
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index 0339b073ae15f..8c25b1c81dc0c 100644
--- a/src/cmd/compile/internal/ssa/regalloc.go
+++ b/src/cmd/compile/internal/ssa/regalloc.go
@@ -104,7 +104,7 @@
// If b3 is the primary predecessor of b2, then we use x3 in b2 and
// add a x4:CX->BX copy at the end of b4.
// But the definition of x3 doesn't dominate b2. We should really
-// insert a dummy phi at the start of b2 (x5=phi(x3,x4):BX) to keep
+// insert an extra phi at the start of b2 (x5=phi(x3,x4):BX) to keep
// SSA form. For now, we ignore this problem as remaining in strict
// SSA form isn't needed after regalloc. We'll just leave the use
// of x3 not dominated by the definition of x3, and the CX->BX copy
@@ -114,6 +114,7 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/objabi"
"cmd/internal/src"
@@ -782,9 +783,9 @@ func (s *regAllocState) compatRegs(t *types.Type) regMask {
return 0
}
if t.IsFloat() || t == types.TypeInt128 {
- if t.Etype == types.TFLOAT32 && s.f.Config.fp32RegMask != 0 {
+ if t.Kind() == types.TFLOAT32 && s.f.Config.fp32RegMask != 0 {
m = s.f.Config.fp32RegMask
- } else if t.Etype == types.TFLOAT64 && s.f.Config.fp64RegMask != 0 {
+ } else if t.Kind() == types.TFLOAT64 && s.f.Config.fp64RegMask != 0 {
m = s.f.Config.fp64RegMask
} else {
m = s.f.Config.fpRegMask
@@ -1248,7 +1249,7 @@ func (s *regAllocState) regalloc(f *Func) {
// This forces later liveness analysis to make the
// value live at this point.
v.SetArg(0, s.makeSpill(a, b))
- } else if _, ok := a.Aux.(GCNode); ok && vi.rematerializeable {
+ } else if _, ok := a.Aux.(*ir.Name); ok && vi.rematerializeable {
// Rematerializeable value with a gc.Node. This is the address of
// a stack object (e.g. an LEAQ). Keep the object live.
// Change it to VarLive, which is what plive expects for locals.
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index f5d1a7889fb19..e82aa84cdfa96 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -521,6 +521,18 @@ func shiftIsBounded(v *Value) bool {
return v.AuxInt != 0
}
+// canonLessThan returns whether x is "ordered" less than y, for purposes of normalizing
+// generated code as much as possible.
+func canonLessThan(x, y *Value) bool {
+ if x.Op != y.Op {
+ return x.Op < y.Op
+ }
+ if !x.Pos.SameFileAndLine(y.Pos) {
+ return x.Pos.Before(y.Pos)
+ }
+ return x.ID < y.ID
+}
+
// truncate64Fto32F converts a float64 value to a float32 preserving the bit pattern
// of the mantissa. It will panic if the truncation results in lost information.
func truncate64Fto32F(f float64) float32 {
@@ -678,43 +690,53 @@ func opToAuxInt(o Op) int64 {
return int64(o)
}
-func auxToString(i interface{}) string {
- return i.(string)
+// Aux is an interface to hold miscellaneous data in Blocks and Values.
+type Aux interface {
+ CanBeAnSSAAux()
}
-func auxToSym(i interface{}) Sym {
+
+// stringAux wraps string values for use in Aux.
+type stringAux string
+
+func (stringAux) CanBeAnSSAAux() {}
+
+func auxToString(i Aux) string {
+ return string(i.(stringAux))
+}
+func auxToSym(i Aux) Sym {
// TODO: kind of a hack - allows nil interface through
s, _ := i.(Sym)
return s
}
-func auxToType(i interface{}) *types.Type {
+func auxToType(i Aux) *types.Type {
return i.(*types.Type)
}
-func auxToCall(i interface{}) *AuxCall {
+func auxToCall(i Aux) *AuxCall {
return i.(*AuxCall)
}
-func auxToS390xCCMask(i interface{}) s390x.CCMask {
+func auxToS390xCCMask(i Aux) s390x.CCMask {
return i.(s390x.CCMask)
}
-func auxToS390xRotateParams(i interface{}) s390x.RotateParams {
+func auxToS390xRotateParams(i Aux) s390x.RotateParams {
return i.(s390x.RotateParams)
}
-func stringToAux(s string) interface{} {
- return s
+func StringToAux(s string) Aux {
+ return stringAux(s)
}
-func symToAux(s Sym) interface{} {
+func symToAux(s Sym) Aux {
return s
}
-func callToAux(s *AuxCall) interface{} {
+func callToAux(s *AuxCall) Aux {
return s
}
-func typeToAux(t *types.Type) interface{} {
+func typeToAux(t *types.Type) Aux {
return t
}
-func s390xCCMaskToAux(c s390x.CCMask) interface{} {
+func s390xCCMaskToAux(c s390x.CCMask) Aux {
return c
}
-func s390xRotateParamsToAux(r s390x.RotateParams) interface{} {
+func s390xRotateParamsToAux(r s390x.RotateParams) Aux {
return r
}
@@ -725,7 +747,7 @@ func uaddOvf(a, b int64) bool {
// de-virtualize an InterCall
// 'sym' is the symbol for the itab
-func devirt(v *Value, aux interface{}, sym Sym, offset int64) *AuxCall {
+func devirt(v *Value, aux Aux, sym Sym, offset int64) *AuxCall {
f := v.Block.Func
n, ok := sym.(*obj.LSym)
if !ok {
@@ -748,7 +770,7 @@ func devirt(v *Value, aux interface{}, sym Sym, offset int64) *AuxCall {
// de-virtualize an InterLECall
// 'sym' is the symbol for the itab
-func devirtLESym(v *Value, aux interface{}, sym Sym, offset int64) *obj.LSym {
+func devirtLESym(v *Value, aux Aux, sym Sym, offset int64) *obj.LSym {
n, ok := sym.(*obj.LSym)
if !ok {
return nil
diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go
index 2acdccd5684aa..4e7fdb9e639ce 100644
--- a/src/cmd/compile/internal/ssa/rewrite386.go
+++ b/src/cmd/compile/internal/ssa/rewrite386.go
@@ -1785,12 +1785,12 @@ func rewriteValue386_Op386CMPB(v *Value) bool {
return true
}
// match: (CMPB x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPB y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(Op386InvertFlags)
@@ -2078,12 +2078,12 @@ func rewriteValue386_Op386CMPL(v *Value) bool {
return true
}
// match: (CMPL x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPL y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(Op386InvertFlags)
@@ -2386,12 +2386,12 @@ func rewriteValue386_Op386CMPW(v *Value) bool {
return true
}
// match: (CMPW x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPW y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(Op386InvertFlags)
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index 75d4ff7357a76..03498c719c095 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -4,6 +4,7 @@
package ssa
import "math"
+import "cmd/compile/internal/base"
import "cmd/compile/internal/types"
func rewriteValueAMD64(v *Value) bool {
@@ -767,8 +768,7 @@ func rewriteValueAMD64(v *Value) bool {
v.Op = OpAMD64LoweredGetClosurePtr
return true
case OpGetG:
- v.Op = OpAMD64LoweredGetG
- return true
+ return rewriteValueAMD64_OpGetG(v)
case OpHasCPUFeature:
return rewriteValueAMD64_OpHasCPUFeature(v)
case OpHmul32:
@@ -6749,12 +6749,12 @@ func rewriteValueAMD64_OpAMD64CMPB(v *Value) bool {
return true
}
// match: (CMPB x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPB y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpAMD64InvertFlags)
@@ -7135,12 +7135,12 @@ func rewriteValueAMD64_OpAMD64CMPL(v *Value) bool {
return true
}
// match: (CMPL x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPL y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpAMD64InvertFlags)
@@ -7544,12 +7544,12 @@ func rewriteValueAMD64_OpAMD64CMPQ(v *Value) bool {
return true
}
// match: (CMPQ x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPQ y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpAMD64InvertFlags)
@@ -8106,12 +8106,12 @@ func rewriteValueAMD64_OpAMD64CMPW(v *Value) bool {
return true
}
// match: (CMPW x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPW y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpAMD64InvertFlags)
@@ -14226,7 +14226,7 @@ func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value) bool {
}
// match: (MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem))
// cond: config.useSSE && x.Uses == 1 && c2.Off() + 8 == c.Off() && c.Val() == 0 && c2.Val() == 0 && clobber(x)
- // result: (MOVOstore [c2.Off32()] {s} p (MOVOconst [0]) mem)
+ // result: (MOVOstorezero [c2.Off32()] {s} p mem)
for {
c := auxIntToValAndOff(v.AuxInt)
s := auxToSym(v.Aux)
@@ -14243,12 +14243,10 @@ func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value) bool {
if p != x.Args[0] || !(config.useSSE && x.Uses == 1 && c2.Off()+8 == c.Off() && c.Val() == 0 && c2.Val() == 0 && clobber(x)) {
break
}
- v.reset(OpAMD64MOVOstore)
+ v.reset(OpAMD64MOVOstorezero)
v.AuxInt = int32ToAuxInt(c2.Off32())
v.Aux = symToAux(s)
- v0 := b.NewValue0(x.Pos, OpAMD64MOVOconst, types.TypeInt128)
- v0.AuxInt = int128ToAuxInt(0)
- v.AddArg3(p, v0, mem)
+ v.AddArg2(p, mem)
return true
}
// match: (MOVQstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
@@ -30128,6 +30126,22 @@ func rewriteValueAMD64_OpFloor(v *Value) bool {
return true
}
}
+func rewriteValueAMD64_OpGetG(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (GetG mem)
+ // cond: !base.Flag.ABIWrap
+ // result: (LoweredGetG mem)
+ for {
+ mem := v_0
+ if !(!base.Flag.ABIWrap) {
+ break
+ }
+ v.reset(OpAMD64LoweredGetG)
+ v.AddArg(mem)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpHasCPUFeature(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
@@ -34163,7 +34177,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
}
// match: (Zero [s] destptr mem)
// cond: s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE
- // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstore destptr (MOVOconst [0]) mem))
+ // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstorezero destptr mem))
for {
s := auxIntToInt64(v.AuxInt)
destptr := v_0
@@ -34176,10 +34190,8 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
v0.AuxInt = int64ToAuxInt(s % 16)
v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOstore, types.TypeMem)
- v2 := b.NewValue0(v.Pos, OpAMD64MOVOconst, types.TypeInt128)
- v2.AuxInt = int128ToAuxInt(0)
- v1.AddArg3(destptr, v2, mem)
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v1.AddArg2(destptr, mem)
v.AddArg2(v0, v1)
return true
}
@@ -34206,7 +34218,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
}
// match: (Zero [16] destptr mem)
// cond: config.useSSE
- // result: (MOVOstore destptr (MOVOconst [0]) mem)
+ // result: (MOVOstorezero destptr mem)
for {
if auxIntToInt64(v.AuxInt) != 16 {
break
@@ -34216,15 +34228,13 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstore)
- v0 := b.NewValue0(v.Pos, OpAMD64MOVOconst, types.TypeInt128)
- v0.AuxInt = int128ToAuxInt(0)
- v.AddArg3(destptr, v0, mem)
+ v.reset(OpAMD64MOVOstorezero)
+ v.AddArg2(destptr, mem)
return true
}
// match: (Zero [32] destptr mem)
// cond: config.useSSE
- // result: (MOVOstore (OffPtr destptr [16]) (MOVOconst [0]) (MOVOstore destptr (MOVOconst [0]) mem))
+ // result: (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem))
for {
if auxIntToInt64(v.AuxInt) != 32 {
break
@@ -34234,20 +34244,18 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstore)
+ v.reset(OpAMD64MOVOstorezero)
v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
v0.AuxInt = int64ToAuxInt(16)
v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOconst, types.TypeInt128)
- v1.AuxInt = int128ToAuxInt(0)
- v2 := b.NewValue0(v.Pos, OpAMD64MOVOstore, types.TypeMem)
- v2.AddArg3(destptr, v1, mem)
- v.AddArg3(v0, v1, v2)
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v1.AddArg2(destptr, mem)
+ v.AddArg2(v0, v1)
return true
}
// match: (Zero [48] destptr mem)
// cond: config.useSSE
- // result: (MOVOstore (OffPtr destptr [32]) (MOVOconst [0]) (MOVOstore (OffPtr destptr [16]) (MOVOconst [0]) (MOVOstore destptr (MOVOconst [0]) mem)))
+ // result: (MOVOstorezero (OffPtr destptr [32]) (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem)))
for {
if auxIntToInt64(v.AuxInt) != 48 {
break
@@ -34257,25 +34265,23 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstore)
+ v.reset(OpAMD64MOVOstorezero)
v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
v0.AuxInt = int64ToAuxInt(32)
v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOconst, types.TypeInt128)
- v1.AuxInt = int128ToAuxInt(0)
- v2 := b.NewValue0(v.Pos, OpAMD64MOVOstore, types.TypeMem)
- v3 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v3.AuxInt = int64ToAuxInt(16)
- v3.AddArg(destptr)
- v4 := b.NewValue0(v.Pos, OpAMD64MOVOstore, types.TypeMem)
- v4.AddArg3(destptr, v1, mem)
- v2.AddArg3(v3, v1, v4)
- v.AddArg3(v0, v1, v2)
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v2 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
+ v2.AuxInt = int64ToAuxInt(16)
+ v2.AddArg(destptr)
+ v3 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v3.AddArg2(destptr, mem)
+ v1.AddArg2(v2, v3)
+ v.AddArg2(v0, v1)
return true
}
// match: (Zero [64] destptr mem)
// cond: config.useSSE
- // result: (MOVOstore (OffPtr destptr [48]) (MOVOconst [0]) (MOVOstore (OffPtr destptr [32]) (MOVOconst [0]) (MOVOstore (OffPtr destptr [16]) (MOVOconst [0]) (MOVOstore destptr (MOVOconst [0]) mem))))
+ // result: (MOVOstorezero (OffPtr destptr [48]) (MOVOstorezero (OffPtr destptr [32]) (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem))))
for {
if auxIntToInt64(v.AuxInt) != 64 {
break
@@ -34285,30 +34291,28 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstore)
+ v.reset(OpAMD64MOVOstorezero)
v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
v0.AuxInt = int64ToAuxInt(48)
v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOconst, types.TypeInt128)
- v1.AuxInt = int128ToAuxInt(0)
- v2 := b.NewValue0(v.Pos, OpAMD64MOVOstore, types.TypeMem)
- v3 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v3.AuxInt = int64ToAuxInt(32)
- v3.AddArg(destptr)
- v4 := b.NewValue0(v.Pos, OpAMD64MOVOstore, types.TypeMem)
- v5 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v5.AuxInt = int64ToAuxInt(16)
- v5.AddArg(destptr)
- v6 := b.NewValue0(v.Pos, OpAMD64MOVOstore, types.TypeMem)
- v6.AddArg3(destptr, v1, mem)
- v4.AddArg3(v5, v1, v6)
- v2.AddArg3(v3, v1, v4)
- v.AddArg3(v0, v1, v2)
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v2 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
+ v2.AuxInt = int64ToAuxInt(32)
+ v2.AddArg(destptr)
+ v3 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v4 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
+ v4.AuxInt = int64ToAuxInt(16)
+ v4.AddArg(destptr)
+ v5 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v5.AddArg2(destptr, mem)
+ v3.AddArg2(v4, v5)
+ v1.AddArg2(v2, v3)
+ v.AddArg2(v0, v1)
return true
}
// match: (Zero [s] destptr mem)
// cond: s > 64 && s <= 1024 && s%16 == 0 && !config.noDuffDevice
- // result: (DUFFZERO [s] destptr (MOVOconst [0]) mem)
+ // result: (DUFFZERO [s] destptr mem)
for {
s := auxIntToInt64(v.AuxInt)
destptr := v_0
@@ -34318,9 +34322,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
}
v.reset(OpAMD64DUFFZERO)
v.AuxInt = int64ToAuxInt(s)
- v0 := b.NewValue0(v.Pos, OpAMD64MOVOconst, types.TypeInt128)
- v0.AuxInt = int128ToAuxInt(0)
- v.AddArg3(destptr, v0, mem)
+ v.AddArg2(destptr, mem)
return true
}
// match: (Zero [s] destptr mem)
diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go
index d9d439fa63ee1..c958aae2c4abd 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM.go
@@ -3728,12 +3728,12 @@ func rewriteValueARM_OpARMCMP(v *Value) bool {
return true
}
// match: (CMP x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMP y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpARMInvertFlags)
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go
index 5d5e526add31f..ff1156d9011ea 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM64.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -2772,12 +2772,12 @@ func rewriteValueARM64_OpARM64CMP(v *Value) bool {
return true
}
// match: (CMP x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMP y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpARM64InvertFlags)
@@ -2941,12 +2941,12 @@ func rewriteValueARM64_OpARM64CMPW(v *Value) bool {
return true
}
// match: (CMPW x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPW y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpARM64InvertFlags)
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 455f9b138859b..98f748e5fa41d 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -4777,12 +4777,12 @@ func rewriteValuePPC64_OpPPC64CMP(v *Value) bool {
return true
}
// match: (CMP x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMP y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpPPC64InvertFlags)
@@ -4834,12 +4834,12 @@ func rewriteValuePPC64_OpPPC64CMPU(v *Value) bool {
return true
}
// match: (CMPU x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPU y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpPPC64InvertFlags)
@@ -4964,12 +4964,12 @@ func rewriteValuePPC64_OpPPC64CMPW(v *Value) bool {
return true
}
// match: (CMPW x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPW y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpPPC64InvertFlags)
@@ -5045,12 +5045,12 @@ func rewriteValuePPC64_OpPPC64CMPWU(v *Value) bool {
return true
}
// match: (CMPWU x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPWU y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpPPC64InvertFlags)
diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go
index a9722b820c90d..b52a1b6745e08 100644
--- a/src/cmd/compile/internal/ssa/rewriteS390X.go
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -6332,12 +6332,12 @@ func rewriteValueS390X_OpS390XCMP(v *Value) bool {
return true
}
// match: (CMP x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMP y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpS390XInvertFlags)
@@ -6389,12 +6389,12 @@ func rewriteValueS390X_OpS390XCMPU(v *Value) bool {
return true
}
// match: (CMPU x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPU y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpS390XInvertFlags)
@@ -6624,12 +6624,12 @@ func rewriteValueS390X_OpS390XCMPW(v *Value) bool {
return true
}
// match: (CMPW x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPW y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpS390XInvertFlags)
@@ -6721,12 +6721,12 @@ func rewriteValueS390X_OpS390XCMPWU(v *Value) bool {
return true
}
// match: (CMPWU x y)
- // cond: x.ID > y.ID
+ // cond: canonLessThan(x,y)
// result: (InvertFlags (CMPWU y x))
for {
x := v_0
y := v_1
- if !(x.ID > y.ID) {
+ if !(canonLessThan(x, y)) {
break
}
v.reset(OpS390XInvertFlags)
diff --git a/src/cmd/compile/internal/ssa/rewritedec64.go b/src/cmd/compile/internal/ssa/rewritedec64.go
index c49bc8043e7f2..60b727f45fdcb 100644
--- a/src/cmd/compile/internal/ssa/rewritedec64.go
+++ b/src/cmd/compile/internal/ssa/rewritedec64.go
@@ -184,12 +184,12 @@ func rewriteValuedec64_OpArg(v *Value) bool {
config := b.Func.Config
typ := &b.Func.Config.Types
// match: (Arg {n} [off])
- // cond: is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")
+ // cond: is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")
// result: (Int64Make (Arg {n} [off+4]) (Arg {n} [off]))
for {
off := auxIntToInt32(v.AuxInt)
n := auxToSym(v.Aux)
- if !(is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) {
+ if !(is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")) {
break
}
v.reset(OpInt64Make)
@@ -203,12 +203,12 @@ func rewriteValuedec64_OpArg(v *Value) bool {
return true
}
// match: (Arg {n} [off])
- // cond: is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")
+ // cond: is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")
// result: (Int64Make (Arg {n} [off+4]) (Arg {n} [off]))
for {
off := auxIntToInt32(v.AuxInt)
n := auxToSym(v.Aux)
- if !(is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) {
+ if !(is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")) {
break
}
v.reset(OpInt64Make)
@@ -222,12 +222,12 @@ func rewriteValuedec64_OpArg(v *Value) bool {
return true
}
// match: (Arg {n} [off])
- // cond: is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")
+ // cond: is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")
// result: (Int64Make (Arg {n} [off]) (Arg {n} [off+4]))
for {
off := auxIntToInt32(v.AuxInt)
n := auxToSym(v.Aux)
- if !(is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) {
+ if !(is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")) {
break
}
v.reset(OpInt64Make)
@@ -241,12 +241,12 @@ func rewriteValuedec64_OpArg(v *Value) bool {
return true
}
// match: (Arg {n} [off])
- // cond: is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")
+ // cond: is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")
// result: (Int64Make (Arg {n} [off]) (Arg {n} [off+4]))
for {
off := auxIntToInt32(v.AuxInt)
n := auxToSym(v.Aux)
- if !(is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(go116lateCallExpansion && b.Func.pass.name == "decompose builtin")) {
+ if !(is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() && !(b.Func.pass.name == "decompose builtin")) {
break
}
v.reset(OpInt64Make)
diff --git a/src/cmd/compile/internal/ssa/rewritedecArgs.go b/src/cmd/compile/internal/ssa/rewritedecArgs.go
deleted file mode 100644
index 23ff417eee930..0000000000000
--- a/src/cmd/compile/internal/ssa/rewritedecArgs.go
+++ /dev/null
@@ -1,247 +0,0 @@
-// Code generated from gen/decArgs.rules; DO NOT EDIT.
-// generated with: cd gen; go run *.go
-
-package ssa
-
-func rewriteValuedecArgs(v *Value) bool {
- switch v.Op {
- case OpArg:
- return rewriteValuedecArgs_OpArg(v)
- }
- return false
-}
-func rewriteValuedecArgs_OpArg(v *Value) bool {
- b := v.Block
- config := b.Func.Config
- fe := b.Func.fe
- typ := &b.Func.Config.Types
- // match: (Arg {n} [off])
- // cond: v.Type.IsString()
- // result: (StringMake (Arg {n} [off]) (Arg {n} [off+int32(config.PtrSize)]))
- for {
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(v.Type.IsString()) {
- break
- }
- v.reset(OpStringMake)
- v0 := b.NewValue0(v.Pos, OpArg, typ.BytePtr)
- v0.AuxInt = int32ToAuxInt(off)
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, typ.Int)
- v1.AuxInt = int32ToAuxInt(off + int32(config.PtrSize))
- v1.Aux = symToAux(n)
- v.AddArg2(v0, v1)
- return true
- }
- // match: (Arg {n} [off])
- // cond: v.Type.IsSlice()
- // result: (SliceMake (Arg {n} [off]) (Arg {n} [off+int32(config.PtrSize)]) (Arg {n} [off+2*int32(config.PtrSize)]))
- for {
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(v.Type.IsSlice()) {
- break
- }
- v.reset(OpSliceMake)
- v0 := b.NewValue0(v.Pos, OpArg, v.Type.Elem().PtrTo())
- v0.AuxInt = int32ToAuxInt(off)
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, typ.Int)
- v1.AuxInt = int32ToAuxInt(off + int32(config.PtrSize))
- v1.Aux = symToAux(n)
- v2 := b.NewValue0(v.Pos, OpArg, typ.Int)
- v2.AuxInt = int32ToAuxInt(off + 2*int32(config.PtrSize))
- v2.Aux = symToAux(n)
- v.AddArg3(v0, v1, v2)
- return true
- }
- // match: (Arg {n} [off])
- // cond: v.Type.IsInterface()
- // result: (IMake (Arg {n} [off]) (Arg {n} [off+int32(config.PtrSize)]))
- for {
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(v.Type.IsInterface()) {
- break
- }
- v.reset(OpIMake)
- v0 := b.NewValue0(v.Pos, OpArg, typ.Uintptr)
- v0.AuxInt = int32ToAuxInt(off)
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, typ.BytePtr)
- v1.AuxInt = int32ToAuxInt(off + int32(config.PtrSize))
- v1.Aux = symToAux(n)
- v.AddArg2(v0, v1)
- return true
- }
- // match: (Arg {n} [off])
- // cond: v.Type.IsComplex() && v.Type.Size() == 16
- // result: (ComplexMake (Arg {n} [off]) (Arg {n} [off+8]))
- for {
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(v.Type.IsComplex() && v.Type.Size() == 16) {
- break
- }
- v.reset(OpComplexMake)
- v0 := b.NewValue0(v.Pos, OpArg, typ.Float64)
- v0.AuxInt = int32ToAuxInt(off)
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, typ.Float64)
- v1.AuxInt = int32ToAuxInt(off + 8)
- v1.Aux = symToAux(n)
- v.AddArg2(v0, v1)
- return true
- }
- // match: (Arg {n} [off])
- // cond: v.Type.IsComplex() && v.Type.Size() == 8
- // result: (ComplexMake (Arg {n} [off]) (Arg {n} [off+4]))
- for {
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(v.Type.IsComplex() && v.Type.Size() == 8) {
- break
- }
- v.reset(OpComplexMake)
- v0 := b.NewValue0(v.Pos, OpArg, typ.Float32)
- v0.AuxInt = int32ToAuxInt(off)
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, typ.Float32)
- v1.AuxInt = int32ToAuxInt(off + 4)
- v1.Aux = symToAux(n)
- v.AddArg2(v0, v1)
- return true
- }
- // match: (Arg )
- // cond: t.IsStruct() && t.NumFields() == 0 && fe.CanSSA(t)
- // result: (StructMake0)
- for {
- t := v.Type
- if !(t.IsStruct() && t.NumFields() == 0 && fe.CanSSA(t)) {
- break
- }
- v.reset(OpStructMake0)
- return true
- }
- // match: (Arg {n} [off])
- // cond: t.IsStruct() && t.NumFields() == 1 && fe.CanSSA(t)
- // result: (StructMake1 (Arg {n} [off+int32(t.FieldOff(0))]))
- for {
- t := v.Type
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(t.IsStruct() && t.NumFields() == 1 && fe.CanSSA(t)) {
- break
- }
- v.reset(OpStructMake1)
- v0 := b.NewValue0(v.Pos, OpArg, t.FieldType(0))
- v0.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(0)))
- v0.Aux = symToAux(n)
- v.AddArg(v0)
- return true
- }
- // match: (Arg {n} [off])
- // cond: t.IsStruct() && t.NumFields() == 2 && fe.CanSSA(t)
- // result: (StructMake2 (Arg {n} [off+int32(t.FieldOff(0))]) (Arg {n} [off+int32(t.FieldOff(1))]))
- for {
- t := v.Type
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(t.IsStruct() && t.NumFields() == 2 && fe.CanSSA(t)) {
- break
- }
- v.reset(OpStructMake2)
- v0 := b.NewValue0(v.Pos, OpArg, t.FieldType(0))
- v0.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(0)))
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, t.FieldType(1))
- v1.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(1)))
- v1.Aux = symToAux(n)
- v.AddArg2(v0, v1)
- return true
- }
- // match: (Arg {n} [off])
- // cond: t.IsStruct() && t.NumFields() == 3 && fe.CanSSA(t)
- // result: (StructMake3 (Arg {n} [off+int32(t.FieldOff(0))]) (Arg {n} [off+int32(t.FieldOff(1))]) (Arg {n} [off+int32(t.FieldOff(2))]))
- for {
- t := v.Type
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(t.IsStruct() && t.NumFields() == 3 && fe.CanSSA(t)) {
- break
- }
- v.reset(OpStructMake3)
- v0 := b.NewValue0(v.Pos, OpArg, t.FieldType(0))
- v0.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(0)))
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, t.FieldType(1))
- v1.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(1)))
- v1.Aux = symToAux(n)
- v2 := b.NewValue0(v.Pos, OpArg, t.FieldType(2))
- v2.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(2)))
- v2.Aux = symToAux(n)
- v.AddArg3(v0, v1, v2)
- return true
- }
- // match: (Arg {n} [off])
- // cond: t.IsStruct() && t.NumFields() == 4 && fe.CanSSA(t)
- // result: (StructMake4 (Arg {n} [off+int32(t.FieldOff(0))]) (Arg {n} [off+int32(t.FieldOff(1))]) (Arg {n} [off+int32(t.FieldOff(2))]) (Arg {n} [off+int32(t.FieldOff(3))]))
- for {
- t := v.Type
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(t.IsStruct() && t.NumFields() == 4 && fe.CanSSA(t)) {
- break
- }
- v.reset(OpStructMake4)
- v0 := b.NewValue0(v.Pos, OpArg, t.FieldType(0))
- v0.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(0)))
- v0.Aux = symToAux(n)
- v1 := b.NewValue0(v.Pos, OpArg, t.FieldType(1))
- v1.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(1)))
- v1.Aux = symToAux(n)
- v2 := b.NewValue0(v.Pos, OpArg, t.FieldType(2))
- v2.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(2)))
- v2.Aux = symToAux(n)
- v3 := b.NewValue0(v.Pos, OpArg, t.FieldType(3))
- v3.AuxInt = int32ToAuxInt(off + int32(t.FieldOff(3)))
- v3.Aux = symToAux(n)
- v.AddArg4(v0, v1, v2, v3)
- return true
- }
- // match: (Arg )
- // cond: t.IsArray() && t.NumElem() == 0
- // result: (ArrayMake0)
- for {
- t := v.Type
- if !(t.IsArray() && t.NumElem() == 0) {
- break
- }
- v.reset(OpArrayMake0)
- return true
- }
- // match: (Arg {n} [off])
- // cond: t.IsArray() && t.NumElem() == 1 && fe.CanSSA(t)
- // result: (ArrayMake1 (Arg {n} [off]))
- for {
- t := v.Type
- off := auxIntToInt32(v.AuxInt)
- n := auxToSym(v.Aux)
- if !(t.IsArray() && t.NumElem() == 1 && fe.CanSSA(t)) {
- break
- }
- v.reset(OpArrayMake1)
- v0 := b.NewValue0(v.Pos, OpArg, t.Elem())
- v0.AuxInt = int32ToAuxInt(off)
- v0.Aux = symToAux(n)
- v.AddArg(v0)
- return true
- }
- return false
-}
-func rewriteBlockdecArgs(b *Block) bool {
- switch b.Kind {
- }
- return false
-}
diff --git a/src/cmd/compile/internal/ssa/sizeof_test.go b/src/cmd/compile/internal/ssa/sizeof_test.go
index 60ada011e3e0b..a27002ee3ac3b 100644
--- a/src/cmd/compile/internal/ssa/sizeof_test.go
+++ b/src/cmd/compile/internal/ssa/sizeof_test.go
@@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) {
}{
{Value{}, 72, 112},
{Block{}, 164, 304},
- {LocalSlot{}, 32, 48},
+ {LocalSlot{}, 28, 40},
{valState{}, 28, 40},
}
diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go
index 406a3c3ea53fa..68a6f08a2a9b2 100644
--- a/src/cmd/compile/internal/ssa/stackalloc.go
+++ b/src/cmd/compile/internal/ssa/stackalloc.go
@@ -7,6 +7,7 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/src"
"fmt"
@@ -156,7 +157,7 @@ func (s *stackAllocState) stackalloc() {
if v.Aux == nil {
f.Fatalf("%s has nil Aux\n", v.LongString())
}
- loc := LocalSlot{N: v.Aux.(GCNode), Type: v.Type, Off: v.AuxInt}
+ loc := LocalSlot{N: v.Aux.(*ir.Name), Type: v.Type, Off: v.AuxInt}
if f.pass.debug > stackDebug {
fmt.Printf("stackalloc %s to %s\n", v, loc)
}
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index edc43aaae7218..d000b7cce011a 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/src"
"fmt"
@@ -36,7 +37,7 @@ type Value struct {
// Users of AuxInt which interpret AuxInt as unsigned (e.g. shifts) must be careful.
// Use Value.AuxUnsigned to get the zero-extended value of AuxInt.
AuxInt int64
- Aux interface{}
+ Aux Aux
// Arguments of this value
Args []*Value
@@ -492,3 +493,16 @@ func (v *Value) removeable() bool {
}
return true
}
+
+// TODO(mdempsky): Shouldn't be necessary; see discussion at golang.org/cl/275756
+func (*Value) CanBeAnSSAAux() {}
+
+// AutoVar returns a *Name and int64 representing the auto variable and offset within it
+// where v should be spilled.
+func AutoVar(v *Value) (*ir.Name, int64) {
+ loc := v.Block.Func.RegAlloc[v.ID].(LocalSlot)
+ if v.Type.Size() > loc.Type.Size() {
+ v.Fatalf("spill/restore type %s doesn't fit in slot type %s", v.Type, loc.Type)
+ }
+ return loc.N, loc.Off
+}
diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go
index 849c9e8967c60..4378f2d62763c 100644
--- a/src/cmd/compile/internal/ssa/writebarrier.go
+++ b/src/cmd/compile/internal/ssa/writebarrier.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/compile/internal/reflectdata"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
@@ -270,11 +271,11 @@ func writebarrier(f *Func) {
case OpMoveWB:
fn = typedmemmove
val = w.Args[1]
- typ = w.Aux.(*types.Type).Symbol()
+ typ = reflectdata.TypeLinksym(w.Aux.(*types.Type))
nWBops--
case OpZeroWB:
fn = typedmemclr
- typ = w.Aux.(*types.Type).Symbol()
+ typ = reflectdata.TypeLinksym(w.Aux.(*types.Type))
nWBops--
case OpVarDef, OpVarLive, OpVarKill:
}
diff --git a/src/cmd/compile/internal/ssa/zcse.go b/src/cmd/compile/internal/ssa/zcse.go
index ec38b7d1ba4ae..e08272c345801 100644
--- a/src/cmd/compile/internal/ssa/zcse.go
+++ b/src/cmd/compile/internal/ssa/zcse.go
@@ -57,7 +57,7 @@ func zcse(f *Func) {
type vkey struct {
op Op
ai int64 // aux int
- ax interface{} // aux
+ ax Aux // aux
t *types.Type // type
}
diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go
new file mode 100644
index 0000000000000..7180b3816ceea
--- /dev/null
+++ b/src/cmd/compile/internal/ssagen/abi.go
@@ -0,0 +1,379 @@
+// Copyright 2009 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 ssagen
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+
+ "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"
+ "cmd/internal/objabi"
+)
+
+// useNewABIWrapGen returns TRUE if the compiler should generate an
+// ABI wrapper for the function 'f'.
+func useABIWrapGen(f *ir.Func) bool {
+ if !base.Flag.ABIWrap {
+ return false
+ }
+
+ // Support limit option for bisecting.
+ if base.Flag.ABIWrapLimit == 1 {
+ return false
+ }
+ if base.Flag.ABIWrapLimit < 1 {
+ return true
+ }
+ base.Flag.ABIWrapLimit--
+ if base.Debug.ABIWrap != 0 && base.Flag.ABIWrapLimit == 1 {
+ fmt.Fprintf(os.Stderr, "=-= limit reached after new wrapper for %s\n",
+ f.LSym.Name)
+ }
+
+ return true
+}
+
+// symabiDefs and symabiRefs record the defined and referenced ABIs of
+// symbols required by non-Go code. These are keyed by link symbol
+// name, where the local package prefix is always `"".`
+var symabiDefs, symabiRefs map[string]obj.ABI
+
+func CgoSymABIs() {
+ // The linker expects an ABI0 wrapper for all cgo-exported
+ // functions.
+ for _, prag := range typecheck.Target.CgoPragmas {
+ switch prag[0] {
+ case "cgo_export_static", "cgo_export_dynamic":
+ if symabiRefs == nil {
+ symabiRefs = make(map[string]obj.ABI)
+ }
+ symabiRefs[prag[1]] = obj.ABI0
+ }
+ }
+}
+
+// ReadSymABIs reads a symabis file that specifies definitions and
+// references of text symbols by ABI.
+//
+// The symabis format is a set of lines, where each line is a sequence
+// of whitespace-separated fields. The first field is a verb and is
+// either "def" for defining a symbol ABI or "ref" for referencing a
+// symbol using an ABI. For both "def" and "ref", the second field is
+// the symbol name and the third field is the ABI name, as one of the
+// named cmd/internal/obj.ABI constants.
+func ReadSymABIs(file, myimportpath string) {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-symabis: %v", err)
+ }
+
+ symabiDefs = make(map[string]obj.ABI)
+ symabiRefs = make(map[string]obj.ABI)
+
+ localPrefix := ""
+ if myimportpath != "" {
+ // Symbols in this package may be written either as
+ // "".X or with the package's import path already in
+ // the symbol.
+ localPrefix = objabi.PathToPrefix(myimportpath) + "."
+ }
+
+ for lineNum, line := range strings.Split(string(data), "\n") {
+ lineNum++ // 1-based
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+
+ parts := strings.Fields(line)
+ switch parts[0] {
+ case "def", "ref":
+ // Parse line.
+ if len(parts) != 3 {
+ log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
+ }
+ sym, abistr := parts[1], parts[2]
+ abi, valid := obj.ParseABI(abistr)
+ if !valid {
+ log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
+ }
+
+ // If the symbol is already prefixed with
+ // myimportpath, rewrite it to start with ""
+ // so it matches the compiler's internal
+ // symbol names.
+ if localPrefix != "" && strings.HasPrefix(sym, localPrefix) {
+ sym = `"".` + sym[len(localPrefix):]
+ }
+
+ // Record for later.
+ if parts[0] == "def" {
+ symabiDefs[sym] = abi
+ } else {
+ symabiRefs[sym] = abi
+ }
+ default:
+ log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
+ }
+ }
+}
+
+// InitLSym defines f's obj.LSym and initializes it based on the
+// properties of f. This includes setting the symbol flags and ABI and
+// creating and initializing related DWARF symbols.
+//
+// InitLSym must be called exactly once per function and must be
+// called for both functions with bodies and functions without bodies.
+// 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) {
+ // 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 {
+ return
+ }
+ staticdata.NeedFuncSym(f.Sym())
+ selectLSym(f, hasBody)
+ if hasBody {
+ setupTextLSym(f, 0)
+ }
+}
+
+// selectLSym sets up the LSym for a given function, and
+// makes calls to helpers to create ABI wrappers if needed.
+func selectLSym(f *ir.Func, hasBody bool) {
+ if f.LSym != nil {
+ base.FatalfAt(f.Pos(), "InitLSym called twice on %v", f)
+ }
+
+ if nam := f.Nname; !ir.IsBlank(nam) {
+
+ var wrapperABI obj.ABI
+ needABIWrapper := false
+ defABI, hasDefABI := symabiDefs[nam.Linksym().Name]
+ if hasDefABI && defABI == obj.ABI0 {
+ // Symbol is defined as ABI0. Create an
+ // Internal -> ABI0 wrapper.
+ f.LSym = nam.LinksymABI(obj.ABI0)
+ needABIWrapper, wrapperABI = true, obj.ABIInternal
+ } else {
+ f.LSym = nam.Linksym()
+ // No ABI override. Check that the symbol is
+ // using the expected ABI.
+ want := obj.ABIInternal
+ if f.LSym.ABI() != want {
+ base.Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.LSym.Name, f.LSym.ABI(), want)
+ }
+ }
+ if f.Pragma&ir.Systemstack != 0 {
+ f.LSym.Set(obj.AttrCFunc, true)
+ }
+
+ isLinknameExported := nam.Sym().Linkname != "" && (hasBody || hasDefABI)
+ if abi, ok := symabiRefs[f.LSym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
+ // Either 1) this symbol is definitely
+ // referenced as ABI0 from this package; or 2)
+ // this symbol is defined in this package but
+ // given a linkname, indicating that it may be
+ // referenced from another package. Create an
+ // ABI0 -> Internal wrapper so it can be
+ // called as ABI0. In case 2, it's important
+ // that we know it's defined in this package
+ // since other packages may "pull" symbols
+ // using linkname and we don't want to create
+ // duplicate ABI wrappers.
+ if f.LSym.ABI() != obj.ABI0 {
+ needABIWrapper, wrapperABI = true, obj.ABI0
+ }
+ }
+
+ if needABIWrapper {
+ if !useABIWrapGen(f) {
+ // Fallback: use alias instead. FIXME.
+
+ // These LSyms have the same name as the
+ // native function, so we create them directly
+ // rather than looking them up. The uniqueness
+ // of f.lsym ensures uniqueness of asym.
+ asym := &obj.LSym{
+ Name: f.LSym.Name,
+ Type: objabi.SABIALIAS,
+ R: []obj.Reloc{{Sym: f.LSym}}, // 0 size, so "informational"
+ }
+ asym.SetABI(wrapperABI)
+ asym.Set(obj.AttrDuplicateOK, true)
+ base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym)
+ } else {
+ if base.Debug.ABIWrap != 0 {
+ fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %s.%s\n",
+ wrapperABI, 1-wrapperABI, types.LocalPkg.Path, f.LSym.Name)
+ }
+ makeABIWrapper(f, wrapperABI)
+ }
+ }
+ }
+}
+
+// makeABIWrapper creates a new function that wraps a cross-ABI call
+// to "f". The wrapper is marked as an ABIWRAPPER.
+func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
+
+ // Q: is this needed?
+ savepos := base.Pos
+ savedclcontext := typecheck.DeclContext
+ savedcurfn := ir.CurFunc
+
+ base.Pos = base.AutogeneratedPos
+ typecheck.DeclContext = ir.PEXTERN
+
+ // At the moment we don't support wrapping a method, we'd need machinery
+ // below to handle the receiver. Panic if we see this scenario.
+ ft := f.Nname.Ntype.Type()
+ if ft.NumRecvs() != 0 {
+ panic("makeABIWrapper support for wrapping methods not implemented")
+ }
+
+ // Manufacture a new func type to use for the wrapper.
+ var noReceiver *ir.Field
+ tfn := ir.NewFuncType(base.Pos,
+ noReceiver,
+ typecheck.NewFuncParams(ft.Params(), true),
+ typecheck.NewFuncParams(ft.Results(), false))
+
+ // Reuse f's types.Sym to create a new ODCLFUNC/function.
+ fn := typecheck.DeclFunc(f.Nname.Sym(), tfn)
+ fn.SetDupok(true)
+ fn.SetWrapper(true) // ignore frame for panic+recover matching
+
+ // Select LSYM now.
+ asym := base.Ctxt.LookupABI(f.LSym.Name, wrapperABI)
+ asym.Type = objabi.STEXT
+ if fn.LSym != nil {
+ panic("unexpected")
+ }
+ fn.LSym = asym
+
+ // ABI0-to-ABIInternal wrappers will be mainly loading params from
+ // stack into registers (and/or storing stack locations back to
+ // registers after the wrapped call); in most cases they won't
+ // need to allocate stack space, so it should be OK to mark them
+ // as NOSPLIT in these cases. In addition, my assumption is that
+ // functions written in assembly are NOSPLIT in most (but not all)
+ // cases. In the case of an ABIInternal target that has too many
+ // parameters to fit into registers, the wrapper would need to
+ // allocate stack space, but this seems like an unlikely scenario.
+ // Hence: mark these wrappers NOSPLIT.
+ //
+ // ABIInternal-to-ABI0 wrappers on the other hand will be taking
+ // things in registers and pushing them onto the stack prior to
+ // the ABI0 call, meaning that they will always need to allocate
+ // stack space. If the compiler marks them as NOSPLIT this seems
+ // as though it could lead to situations where the the linker's
+ // nosplit-overflow analysis would trigger a link failure. On the
+ // other hand if they not tagged NOSPLIT then this could cause
+ // problems when building the runtime (since there may be calls to
+ // asm routine in cases where it's not safe to grow the stack). In
+ // most cases the wrapper would be (in effect) inlined, but are
+ // there (perhaps) indirect calls from the runtime that could run
+ // into trouble here.
+ // FIXME: at the moment all.bash does not pass when I leave out
+ // NOSPLIT for these wrappers, so all are currently tagged with NOSPLIT.
+ setupTextLSym(fn, obj.NOSPLIT|obj.ABIWRAPPER)
+
+ // Generate call. Use tail call if no params and no returns,
+ // but a regular call otherwise.
+ //
+ // Note: ideally we would be using a tail call in cases where
+ // there are params but no returns for ABI0->ABIInternal wrappers,
+ // provided that all params fit into registers (e.g. we don't have
+ // to allocate any stack space). Doing this will require some
+ // extra work in typecheck/walk/ssa, might want to add a new node
+ // OTAILCALL or something to this effect.
+ tailcall := tfn.Type().NumResults() == 0 && tfn.Type().NumParams() == 0 && tfn.Type().NumRecvs() == 0
+ if base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink {
+ // cannot tailcall on PPC64 with dynamic linking, as we need
+ // to restore R2 after call.
+ tailcall = false
+ }
+ if base.Ctxt.Arch.Name == "amd64" && wrapperABI == obj.ABIInternal {
+ // cannot tailcall from ABIInternal to ABI0 on AMD64, as we need
+ // to special registers (X15) when returning to ABIInternal.
+ tailcall = false
+ }
+
+ var tail ir.Node
+ if tailcall {
+ tail = ir.NewTailCallStmt(base.Pos, f.Nname)
+ } else {
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
+ call.Args = ir.ParamNames(tfn.Type())
+ call.IsDDD = tfn.Type().IsVariadic()
+ tail = call
+ if tfn.Type().NumResults() > 0 {
+ n := ir.NewReturnStmt(base.Pos, nil)
+ n.Results = []ir.Node{call}
+ tail = n
+ }
+ }
+ fn.Body.Append(tail)
+
+ typecheck.FinishFuncBody()
+ if base.Debug.DclStack != 0 {
+ types.CheckDclstack()
+ }
+
+ typecheck.Func(fn)
+ ir.CurFunc = fn
+ typecheck.Stmts(fn.Body)
+
+ escape.Batch([]*ir.Func{fn}, false)
+
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+ // Restore previous context.
+ base.Pos = savepos
+ typecheck.DeclContext = savedclcontext
+ ir.CurFunc = savedcurfn
+}
+
+// setupTextLsym initializes the LSym for a with-body text symbol.
+func setupTextLSym(f *ir.Func, flag int) {
+ if f.Dupok() {
+ flag |= obj.DUPOK
+ }
+ if f.Wrapper() {
+ flag |= obj.WRAPPER
+ }
+ if f.Needctxt() {
+ flag |= obj.NEEDCTXT
+ }
+ if f.Pragma&ir.Nosplit != 0 {
+ flag |= obj.NOSPLIT
+ }
+ if f.ReflectMethod() {
+ flag |= obj.REFLECTMETHOD
+ }
+
+ // Clumsy but important.
+ // See test/recover.go for test cases and src/reflect/value.go
+ // for the actual functions being considered.
+ if base.Ctxt.Pkgpath == "reflect" {
+ switch f.Sym().Name {
+ case "callReflect", "callMethod":
+ flag |= obj.WRAPPER
+ }
+ }
+
+ base.Ctxt.InitTextSym(f.LSym, flag)
+}
diff --git a/src/cmd/compile/internal/ssagen/arch.go b/src/cmd/compile/internal/ssagen/arch.go
new file mode 100644
index 0000000000000..cc50ab36b5dd6
--- /dev/null
+++ b/src/cmd/compile/internal/ssagen/arch.go
@@ -0,0 +1,42 @@
+// Copyright 2009 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 ssagen
+
+import (
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/ssa"
+ "cmd/internal/obj"
+)
+
+var Arch ArchInfo
+
+// interface to back end
+
+type ArchInfo struct {
+ LinkArch *obj.LinkArch
+
+ REGSP int
+ MAXWIDTH int64
+ SoftFloat bool
+
+ PadFrame func(int64) int64
+
+ // ZeroRange zeroes a range of memory on stack. It is only inserted
+ // at function entry, and it is ok to clobber registers.
+ ZeroRange func(*objw.Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog
+
+ Ginsnop func(*objw.Progs) *obj.Prog
+ Ginsnopdefer func(*objw.Progs) *obj.Prog // special ginsnop for deferreturn
+
+ // SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
+ SSAMarkMoves func(*State, *ssa.Block)
+
+ // SSAGenValue emits Prog(s) for the Value.
+ SSAGenValue func(*State, *ssa.Value)
+
+ // SSAGenBlock emits end-of-block Progs. SSAGenValue should be called
+ // for all values in the block before SSAGenBlock.
+ SSAGenBlock func(s *State, b, next *ssa.Block)
+}
diff --git a/src/cmd/compile/internal/ssagen/nowb.go b/src/cmd/compile/internal/ssagen/nowb.go
new file mode 100644
index 0000000000000..a2434366a022c
--- /dev/null
+++ b/src/cmd/compile/internal/ssagen/nowb.go
@@ -0,0 +1,200 @@
+// Copyright 2009 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 ssagen
+
+import (
+ "bytes"
+ "fmt"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+func EnableNoWriteBarrierRecCheck() {
+ nowritebarrierrecCheck = newNowritebarrierrecChecker()
+}
+
+func NoWriteBarrierRecCheck() {
+ // Write barriers are now known. Check the
+ // call graph.
+ nowritebarrierrecCheck.check()
+ nowritebarrierrecCheck = nil
+}
+
+var nowritebarrierrecCheck *nowritebarrierrecChecker
+
+type nowritebarrierrecChecker struct {
+ // extraCalls contains extra function calls that may not be
+ // visible during later analysis. It maps from the ODCLFUNC of
+ // the caller to a list of callees.
+ extraCalls map[*ir.Func][]nowritebarrierrecCall
+
+ // curfn is the current function during AST walks.
+ curfn *ir.Func
+}
+
+type nowritebarrierrecCall struct {
+ target *ir.Func // caller or callee
+ lineno src.XPos // line of call
+}
+
+// newNowritebarrierrecChecker creates a nowritebarrierrecChecker. It
+// must be called before walk
+func newNowritebarrierrecChecker() *nowritebarrierrecChecker {
+ c := &nowritebarrierrecChecker{
+ extraCalls: make(map[*ir.Func][]nowritebarrierrecCall),
+ }
+
+ // Find all systemstack calls and record their targets. In
+ // general, flow analysis can't see into systemstack, but it's
+ // important to handle it for this check, so we model it
+ // directly. This has to happen before transforming closures in walk since
+ // it's a lot harder to work out the argument after.
+ for _, n := range typecheck.Target.Decls {
+ if n.Op() != ir.ODCLFUNC {
+ continue
+ }
+ c.curfn = n.(*ir.Func)
+ ir.Visit(n, c.findExtraCalls)
+ }
+ c.curfn = nil
+ return c
+}
+
+func (c *nowritebarrierrecChecker) findExtraCalls(nn ir.Node) {
+ if nn.Op() != ir.OCALLFUNC {
+ return
+ }
+ n := nn.(*ir.CallExpr)
+ if n.X == nil || n.X.Op() != ir.ONAME {
+ return
+ }
+ fn := n.X.(*ir.Name)
+ if fn.Class != ir.PFUNC || fn.Defn == nil {
+ return
+ }
+ if !types.IsRuntimePkg(fn.Sym().Pkg) || fn.Sym().Name != "systemstack" {
+ return
+ }
+
+ var callee *ir.Func
+ arg := n.Args[0]
+ switch arg.Op() {
+ case ir.ONAME:
+ arg := arg.(*ir.Name)
+ callee = arg.Defn.(*ir.Func)
+ case ir.OCLOSURE:
+ arg := arg.(*ir.ClosureExpr)
+ callee = arg.Func
+ default:
+ base.Fatalf("expected ONAME or OCLOSURE node, got %+v", arg)
+ }
+ if callee.Op() != ir.ODCLFUNC {
+ base.Fatalf("expected ODCLFUNC node, got %+v", callee)
+ }
+ c.extraCalls[c.curfn] = append(c.extraCalls[c.curfn], nowritebarrierrecCall{callee, n.Pos()})
+}
+
+// recordCall records a call from ODCLFUNC node "from", to function
+// symbol "to" at position pos.
+//
+// This should be done as late as possible during compilation to
+// capture precise call graphs. The target of the call is an LSym
+// because that's all we know after we start SSA.
+//
+// This can be called concurrently for different from Nodes.
+func (c *nowritebarrierrecChecker) recordCall(fn *ir.Func, to *obj.LSym, pos src.XPos) {
+ // We record this information on the *Func so this is concurrent-safe.
+ if fn.NWBRCalls == nil {
+ fn.NWBRCalls = new([]ir.SymAndPos)
+ }
+ *fn.NWBRCalls = append(*fn.NWBRCalls, ir.SymAndPos{Sym: to, Pos: pos})
+}
+
+func (c *nowritebarrierrecChecker) check() {
+ // We walk the call graph as late as possible so we can
+ // capture all calls created by lowering, but this means we
+ // only get to see the obj.LSyms of calls. symToFunc lets us
+ // get back to the ODCLFUNCs.
+ symToFunc := make(map[*obj.LSym]*ir.Func)
+ // funcs records the back-edges of the BFS call graph walk. It
+ // maps from the ODCLFUNC of each function that must not have
+ // write barriers to the call that inhibits them. Functions
+ // that are directly marked go:nowritebarrierrec are in this
+ // map with a zero-valued nowritebarrierrecCall. This also
+ // acts as the set of marks for the BFS of the call graph.
+ funcs := make(map[*ir.Func]nowritebarrierrecCall)
+ // q is the queue of ODCLFUNC Nodes to visit in BFS order.
+ var q ir.NameQueue
+
+ for _, n := range typecheck.Target.Decls {
+ if n.Op() != ir.ODCLFUNC {
+ continue
+ }
+ fn := n.(*ir.Func)
+
+ symToFunc[fn.LSym] = fn
+
+ // Make nowritebarrierrec functions BFS roots.
+ if fn.Pragma&ir.Nowritebarrierrec != 0 {
+ funcs[fn] = nowritebarrierrecCall{}
+ q.PushRight(fn.Nname)
+ }
+ // Check go:nowritebarrier functions.
+ if fn.Pragma&ir.Nowritebarrier != 0 && fn.WBPos.IsKnown() {
+ base.ErrorfAt(fn.WBPos, "write barrier prohibited")
+ }
+ }
+
+ // Perform a BFS of the call graph from all
+ // go:nowritebarrierrec functions.
+ enqueue := func(src, target *ir.Func, pos src.XPos) {
+ if target.Pragma&ir.Yeswritebarrierrec != 0 {
+ // Don't flow into this function.
+ return
+ }
+ if _, ok := funcs[target]; ok {
+ // Already found a path to target.
+ return
+ }
+
+ // Record the path.
+ funcs[target] = nowritebarrierrecCall{target: src, lineno: pos}
+ q.PushRight(target.Nname)
+ }
+ for !q.Empty() {
+ fn := q.PopLeft().Func
+
+ // Check fn.
+ if fn.WBPos.IsKnown() {
+ var err bytes.Buffer
+ call := funcs[fn]
+ for call.target != nil {
+ fmt.Fprintf(&err, "\n\t%v: called by %v", base.FmtPos(call.lineno), call.target.Nname)
+ call = funcs[call.target]
+ }
+ base.ErrorfAt(fn.WBPos, "write barrier prohibited by caller; %v%s", fn.Nname, err.String())
+ continue
+ }
+
+ // Enqueue fn's calls.
+ for _, callee := range c.extraCalls[fn] {
+ enqueue(fn, callee.target, callee.lineno)
+ }
+ if fn.NWBRCalls == nil {
+ continue
+ }
+ for _, callee := range *fn.NWBRCalls {
+ target := symToFunc[callee.Sym]
+ if target != nil {
+ enqueue(fn, target, callee.Pos)
+ }
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/ssagen/pgen.go b/src/cmd/compile/internal/ssagen/pgen.go
new file mode 100644
index 0000000000000..182f8408cfb40
--- /dev/null
+++ b/src/cmd/compile/internal/ssagen/pgen.go
@@ -0,0 +1,273 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssagen
+
+import (
+ "internal/race"
+ "math/rand"
+ "sort"
+ "sync"
+ "time"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/ssa"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+ "cmd/internal/sys"
+)
+
+// cmpstackvarlt reports whether the stack variable a sorts before b.
+//
+// Sort the list of stack variables. Autos after anything else,
+// within autos, unused after used, within used, things with
+// pointers first, zeroed things first, and then decreasing size.
+// Because autos are laid out in decreasing addresses
+// on the stack, pointers first, zeroed things first and decreasing size
+// really means, in memory, things with pointers needing zeroing at
+// the top of the stack and increasing in size.
+// Non-autos sort on offset.
+func cmpstackvarlt(a, b *ir.Name) bool {
+ if (a.Class == ir.PAUTO) != (b.Class == ir.PAUTO) {
+ return b.Class == ir.PAUTO
+ }
+
+ if a.Class != ir.PAUTO {
+ return a.FrameOffset() < b.FrameOffset()
+ }
+
+ if a.Used() != b.Used() {
+ return a.Used()
+ }
+
+ ap := a.Type().HasPointers()
+ bp := b.Type().HasPointers()
+ if ap != bp {
+ return ap
+ }
+
+ ap = a.Needzero()
+ bp = b.Needzero()
+ if ap != bp {
+ return ap
+ }
+
+ if a.Type().Width != b.Type().Width {
+ return a.Type().Width > b.Type().Width
+ }
+
+ return a.Sym().Name < b.Sym().Name
+}
+
+// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
+type byStackVar []*ir.Name
+
+func (s byStackVar) Len() int { return len(s) }
+func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
+func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func (s *ssafn) AllocFrame(f *ssa.Func) {
+ s.stksize = 0
+ s.stkptrsize = 0
+ fn := s.curfn
+
+ // Mark the PAUTO's unused.
+ for _, ln := range fn.Dcl {
+ if ln.Class == ir.PAUTO {
+ ln.SetUsed(false)
+ }
+ }
+
+ for _, l := range f.RegAlloc {
+ if ls, ok := l.(ssa.LocalSlot); ok {
+ ls.N.SetUsed(true)
+ }
+ }
+
+ scratchUsed := false
+ for _, b := range f.Blocks {
+ for _, v := range b.Values {
+ if n, ok := v.Aux.(*ir.Name); ok {
+ switch n.Class {
+ case ir.PPARAM, ir.PPARAMOUT:
+ // Don't modify RegFP; it is a global.
+ if n != ir.RegFP {
+ n.SetUsed(true)
+ }
+ case ir.PAUTO:
+ n.SetUsed(true)
+ }
+ }
+ if !scratchUsed {
+ scratchUsed = v.Op.UsesScratch()
+ }
+
+ }
+ }
+
+ if f.Config.NeedsFpScratch && scratchUsed {
+ s.scratchFpMem = typecheck.TempAt(src.NoXPos, s.curfn, types.Types[types.TUINT64])
+ }
+
+ sort.Sort(byStackVar(fn.Dcl))
+
+ // Reassign stack offsets of the locals that are used.
+ lastHasPtr := false
+ for i, n := range fn.Dcl {
+ if n.Op() != ir.ONAME || n.Class != ir.PAUTO {
+ continue
+ }
+ if !n.Used() {
+ fn.Dcl = fn.Dcl[:i]
+ break
+ }
+
+ types.CalcSize(n.Type())
+ w := n.Type().Width
+ if w >= types.MaxWidth || w < 0 {
+ base.Fatalf("bad width")
+ }
+ if w == 0 && lastHasPtr {
+ // Pad between a pointer-containing object and a zero-sized object.
+ // This prevents a pointer to the zero-sized object from being interpreted
+ // as a pointer to the pointer-containing object (and causing it
+ // to be scanned when it shouldn't be). See issue 24993.
+ w = 1
+ }
+ s.stksize += w
+ s.stksize = types.Rnd(s.stksize, int64(n.Type().Align))
+ if n.Type().HasPointers() {
+ s.stkptrsize = s.stksize
+ lastHasPtr = true
+ } else {
+ lastHasPtr = false
+ }
+ if Arch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
+ s.stksize = types.Rnd(s.stksize, int64(types.PtrSize))
+ }
+ n.SetFrameOffset(-s.stksize)
+ }
+
+ s.stksize = types.Rnd(s.stksize, int64(types.RegSize))
+ s.stkptrsize = types.Rnd(s.stkptrsize, int64(types.RegSize))
+}
+
+const maxStackSize = 1 << 30
+
+// Compile builds an SSA backend function,
+// uses it to generate a plist,
+// and flushes that plist to machine code.
+// worker indicates which of the backend workers is doing the processing.
+func Compile(fn *ir.Func, worker int) {
+ f := buildssa(fn, worker)
+ // Note: check arg size to fix issue 25507.
+ if f.Frontend().(*ssafn).stksize >= maxStackSize || fn.Type().ArgWidth() >= maxStackSize {
+ largeStackFramesMu.Lock()
+ largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: fn.Type().ArgWidth(), pos: fn.Pos()})
+ largeStackFramesMu.Unlock()
+ return
+ }
+ pp := objw.NewProgs(fn, worker)
+ defer pp.Free()
+ genssa(f, pp)
+ // Check frame size again.
+ // The check above included only the space needed for local variables.
+ // After genssa, the space needed includes local variables and the callee arg region.
+ // We must do this check prior to calling pp.Flush.
+ // If there are any oversized stack frames,
+ // the assembler may emit inscrutable complaints about invalid instructions.
+ if pp.Text.To.Offset >= maxStackSize {
+ largeStackFramesMu.Lock()
+ locals := f.Frontend().(*ssafn).stksize
+ largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: fn.Type().ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos()})
+ largeStackFramesMu.Unlock()
+ return
+ }
+
+ pp.Flush() // assemble, fill in boilerplate, etc.
+ // fieldtrack must be called after pp.Flush. See issue 20014.
+ fieldtrack(pp.Text.From.Sym, fn.FieldTrack)
+}
+
+func init() {
+ if race.Enabled {
+ rand.Seed(time.Now().UnixNano())
+ }
+}
+
+// StackOffset returns the stack location of a LocalSlot relative to the
+// stack pointer, suitable for use in a DWARF location entry. This has nothing
+// to do with its offset in the user variable.
+func StackOffset(slot ssa.LocalSlot) int32 {
+ n := slot.N
+ var off int64
+ switch n.Class {
+ case ir.PAUTO:
+ off = n.FrameOffset()
+ if base.Ctxt.FixedFrameSize() == 0 {
+ off -= int64(types.PtrSize)
+ }
+ if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
+ // There is a word space for FP on ARM64 even if the frame pointer is disabled
+ off -= int64(types.PtrSize)
+ }
+ case ir.PPARAM, ir.PPARAMOUT:
+ off = n.FrameOffset() + base.Ctxt.FixedFrameSize()
+ }
+ return int32(off + slot.Off)
+}
+
+// fieldtrack adds R_USEFIELD relocations to fnsym to record any
+// struct fields that it used.
+func fieldtrack(fnsym *obj.LSym, tracked map[*obj.LSym]struct{}) {
+ if fnsym == nil {
+ return
+ }
+ if objabi.Fieldtrack_enabled == 0 || len(tracked) == 0 {
+ return
+ }
+
+ trackSyms := make([]*obj.LSym, 0, len(tracked))
+ for sym := range tracked {
+ trackSyms = append(trackSyms, sym)
+ }
+ sort.Slice(trackSyms, func(i, j int) bool { return trackSyms[i].Name < trackSyms[j].Name })
+ for _, sym := range trackSyms {
+ r := obj.Addrel(fnsym)
+ r.Sym = sym
+ r.Type = objabi.R_USEFIELD
+ }
+}
+
+// largeStack is info about a function whose stack frame is too large (rare).
+type largeStack struct {
+ locals int64
+ args int64
+ callee int64
+ pos src.XPos
+}
+
+var (
+ largeStackFramesMu sync.Mutex // protects largeStackFrames
+ largeStackFrames []largeStack
+)
+
+func CheckLargeStacks() {
+ // Check whether any of the functions we have compiled have gigantic stack frames.
+ sort.Slice(largeStackFrames, func(i, j int) bool {
+ return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
+ })
+ for _, large := range largeStackFrames {
+ if large.callee != 0 {
+ base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
+ } else {
+ base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/ssagen/pgen_test.go b/src/cmd/compile/internal/ssagen/pgen_test.go
new file mode 100644
index 0000000000000..69ed8ad74e974
--- /dev/null
+++ b/src/cmd/compile/internal/ssagen/pgen_test.go
@@ -0,0 +1,209 @@
+// Copyright 2015 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 ssagen
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+func typeWithoutPointers() *types.Type {
+ return types.NewStruct(types.NoPkg, []*types.Field{
+ types.NewField(src.NoXPos, nil, types.New(types.TINT)),
+ })
+}
+
+func typeWithPointers() *types.Type {
+ return types.NewStruct(types.NoPkg, []*types.Field{
+ types.NewField(src.NoXPos, nil, types.NewPtr(types.New(types.TINT))),
+ })
+}
+
+func markUsed(n *ir.Name) *ir.Name {
+ n.SetUsed(true)
+ return n
+}
+
+func markNeedZero(n *ir.Name) *ir.Name {
+ n.SetNeedzero(true)
+ return n
+}
+
+// Test all code paths for cmpstackvarlt.
+func TestCmpstackvar(t *testing.T) {
+ nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name {
+ if s == nil {
+ s = &types.Sym{Name: "."}
+ }
+ n := typecheck.NewName(s)
+ n.SetType(t)
+ n.SetFrameOffset(xoffset)
+ n.Class = cl
+ return n
+ }
+ testdata := []struct {
+ a, b *ir.Name
+ lt bool
+ }{
+ {
+ nod(0, nil, nil, ir.PAUTO),
+ nod(0, nil, nil, ir.PFUNC),
+ false,
+ },
+ {
+ nod(0, nil, nil, ir.PFUNC),
+ nod(0, nil, nil, ir.PAUTO),
+ true,
+ },
+ {
+ nod(0, nil, nil, ir.PFUNC),
+ nod(10, nil, nil, ir.PFUNC),
+ true,
+ },
+ {
+ nod(20, nil, nil, ir.PFUNC),
+ nod(10, nil, nil, ir.PFUNC),
+ false,
+ },
+ {
+ nod(10, nil, nil, ir.PFUNC),
+ nod(10, nil, nil, ir.PFUNC),
+ false,
+ },
+ {
+ nod(10, nil, nil, ir.PPARAM),
+ nod(20, nil, nil, ir.PPARAMOUT),
+ true,
+ },
+ {
+ nod(10, nil, nil, ir.PPARAMOUT),
+ nod(20, nil, nil, ir.PPARAM),
+ true,
+ },
+ {
+ markUsed(nod(0, nil, nil, ir.PAUTO)),
+ nod(0, nil, nil, ir.PAUTO),
+ true,
+ },
+ {
+ nod(0, nil, nil, ir.PAUTO),
+ markUsed(nod(0, nil, nil, ir.PAUTO)),
+ false,
+ },
+ {
+ nod(0, typeWithoutPointers(), nil, ir.PAUTO),
+ nod(0, typeWithPointers(), nil, ir.PAUTO),
+ false,
+ },
+ {
+ nod(0, typeWithPointers(), nil, ir.PAUTO),
+ nod(0, typeWithoutPointers(), nil, ir.PAUTO),
+ true,
+ },
+ {
+ markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)),
+ nod(0, &types.Type{}, nil, ir.PAUTO),
+ true,
+ },
+ {
+ nod(0, &types.Type{}, nil, ir.PAUTO),
+ markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)),
+ false,
+ },
+ {
+ nod(0, &types.Type{Width: 1}, nil, ir.PAUTO),
+ nod(0, &types.Type{Width: 2}, nil, ir.PAUTO),
+ false,
+ },
+ {
+ nod(0, &types.Type{Width: 2}, nil, ir.PAUTO),
+ nod(0, &types.Type{Width: 1}, nil, ir.PAUTO),
+ true,
+ },
+ {
+ nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
+ true,
+ },
+ {
+ nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
+ false,
+ },
+ {
+ nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
+ false,
+ },
+ }
+ for _, d := range testdata {
+ got := cmpstackvarlt(d.a, d.b)
+ if got != d.lt {
+ t.Errorf("want %v < %v", d.a, d.b)
+ }
+ // If we expect a < b to be true, check that b < a is false.
+ if d.lt && cmpstackvarlt(d.b, d.a) {
+ t.Errorf("unexpected %v < %v", d.b, d.a)
+ }
+ }
+}
+
+func TestStackvarSort(t *testing.T) {
+ nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name {
+ n := typecheck.NewName(s)
+ n.SetType(t)
+ n.SetFrameOffset(xoffset)
+ n.Class = cl
+ return n
+ }
+ inp := []*ir.Name{
+ nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
+ nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
+ markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
+ nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
+ }
+ want := []*ir.Name{
+ nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC),
+ markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
+ markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
+ nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
+ nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
+ nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO),
+ }
+ sort.Sort(byStackVar(inp))
+ if !reflect.DeepEqual(want, inp) {
+ t.Error("sort failed")
+ for i := range inp {
+ g := inp[i]
+ w := want[i]
+ eq := reflect.DeepEqual(w, g)
+ if !eq {
+ t.Log(i, w, g)
+ }
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/gc/phi.go b/src/cmd/compile/internal/ssagen/phi.go
similarity index 90%
rename from src/cmd/compile/internal/gc/phi.go
rename to src/cmd/compile/internal/ssagen/phi.go
index 5218cd0ef3d14..01ad211282cf3 100644
--- a/src/cmd/compile/internal/gc/phi.go
+++ b/src/cmd/compile/internal/ssagen/phi.go
@@ -2,14 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package ssagen
import (
+ "container/heap"
+ "fmt"
+
+ "cmd/compile/internal/ir"
"cmd/compile/internal/ssa"
"cmd/compile/internal/types"
"cmd/internal/src"
- "container/heap"
- "fmt"
)
// This file contains the algorithm to place phi nodes in a function.
@@ -22,6 +24,14 @@ const smallBlocks = 500
const debugPhi = false
+// fwdRefAux wraps an arbitrary ir.Node as an ssa.Aux for use with OpFwdref.
+type fwdRefAux struct {
+ _ [0]func() // ensure ir.Node isn't compared for equality
+ N ir.Node
+}
+
+func (fwdRefAux) CanBeAnSSAAux() {}
+
// insertPhis finds all the places in the function where a phi is
// necessary and inserts them.
// Uses FwdRef ops to find all uses of variables, and s.defvars to find
@@ -40,11 +50,11 @@ func (s *state) insertPhis() {
}
type phiState struct {
- s *state // SSA state
- f *ssa.Func // function to work on
- defvars []map[*Node]*ssa.Value // defined variables at end of each block
+ s *state // SSA state
+ f *ssa.Func // function to work on
+ defvars []map[ir.Node]*ssa.Value // defined variables at end of each block
- varnum map[*Node]int32 // variable numbering
+ varnum map[ir.Node]int32 // variable numbering
// properties of the dominator tree
idom []*ssa.Block // dominator parents
@@ -59,7 +69,7 @@ type phiState struct {
hasDef *sparseSet // has a write of the variable we're processing
// miscellaneous
- placeholder *ssa.Value // dummy value to use as a "not set yet" placeholder.
+ placeholder *ssa.Value // value to use as a "not set yet" placeholder.
}
func (s *phiState) insertPhis() {
@@ -70,15 +80,15 @@ func (s *phiState) insertPhis() {
// Find all the variables for which we need to match up reads & writes.
// This step prunes any basic-block-only variables from consideration.
// Generate a numbering for these variables.
- s.varnum = map[*Node]int32{}
- var vars []*Node
+ s.varnum = map[ir.Node]int32{}
+ var vars []ir.Node
var vartypes []*types.Type
for _, b := range s.f.Blocks {
for _, v := range b.Values {
if v.Op != ssa.OpFwdRef {
continue
}
- var_ := v.Aux.(*Node)
+ var_ := v.Aux.(fwdRefAux).N
// Optimization: look back 1 block for the definition.
if len(b.Preds) == 1 {
@@ -179,11 +189,16 @@ levels:
if v.Op == ssa.OpPhi {
v.AuxInt = 0
}
+ // Any remaining FwdRefs are dead code.
+ if v.Op == ssa.OpFwdRef {
+ v.Op = ssa.OpUnknown
+ v.Aux = nil
+ }
}
}
}
-func (s *phiState) insertVarPhis(n int, var_ *Node, defs []*ssa.Block, typ *types.Type) {
+func (s *phiState) insertVarPhis(n int, var_ ir.Node, defs []*ssa.Block, typ *types.Type) {
priq := &s.priq
q := s.q
queued := s.queued
@@ -240,7 +255,9 @@ func (s *phiState) insertVarPhis(n int, var_ *Node, defs []*ssa.Block, typ *type
hasPhi.add(c.ID)
v := c.NewValue0I(currentRoot.Pos, ssa.OpPhi, typ, int64(n)) // TODO: line number right?
// Note: we store the variable number in the phi's AuxInt field. Used temporarily by phi building.
- s.s.addNamedValue(var_, v)
+ if var_.Op() == ir.ONAME {
+ s.s.addNamedValue(var_.(*ir.Name), v)
+ }
for range c.Preds {
v.AddArg(s.placeholder) // Actual args will be filled in by resolveFwdRefs.
}
@@ -318,7 +335,7 @@ func (s *phiState) resolveFwdRefs() {
if v.Op != ssa.OpFwdRef {
continue
}
- n := s.varnum[v.Aux.(*Node)]
+ n := s.varnum[v.Aux.(fwdRefAux).N]
v.Op = ssa.OpCopy
v.Aux = nil
v.AddArg(values[n])
@@ -432,11 +449,11 @@ func (s *sparseSet) clear() {
// Variant to use for small functions.
type simplePhiState struct {
- s *state // SSA state
- f *ssa.Func // function to work on
- fwdrefs []*ssa.Value // list of FwdRefs to be processed
- defvars []map[*Node]*ssa.Value // defined variables at end of each block
- reachable []bool // which blocks are reachable
+ s *state // SSA state
+ f *ssa.Func // function to work on
+ fwdrefs []*ssa.Value // list of FwdRefs to be processed
+ defvars []map[ir.Node]*ssa.Value // defined variables at end of each block
+ reachable []bool // which blocks are reachable
}
func (s *simplePhiState) insertPhis() {
@@ -449,7 +466,7 @@ func (s *simplePhiState) insertPhis() {
continue
}
s.fwdrefs = append(s.fwdrefs, v)
- var_ := v.Aux.(*Node)
+ var_ := v.Aux.(fwdRefAux).N
if _, ok := s.defvars[b.ID][var_]; !ok {
s.defvars[b.ID][var_] = v // treat FwdDefs as definitions.
}
@@ -463,7 +480,7 @@ loop:
v := s.fwdrefs[len(s.fwdrefs)-1]
s.fwdrefs = s.fwdrefs[:len(s.fwdrefs)-1]
b := v.Block
- var_ := v.Aux.(*Node)
+ var_ := v.Aux.(fwdRefAux).N
if b == s.f.Entry {
// No variable should be live at entry.
s.s.Fatalf("Value live at entry. It shouldn't be. func %s, node %v, value %v", s.f.Name, var_, v)
@@ -511,7 +528,7 @@ loop:
}
// lookupVarOutgoing finds the variable's value at the end of block b.
-func (s *simplePhiState) lookupVarOutgoing(b *ssa.Block, t *types.Type, var_ *Node, line src.XPos) *ssa.Value {
+func (s *simplePhiState) lookupVarOutgoing(b *ssa.Block, t *types.Type, var_ ir.Node, line src.XPos) *ssa.Value {
for {
if v := s.defvars[b.ID][var_]; v != nil {
return v
@@ -530,9 +547,11 @@ func (s *simplePhiState) lookupVarOutgoing(b *ssa.Block, t *types.Type, var_ *No
}
}
// Generate a FwdRef for the variable and return that.
- v := b.NewValue0A(line, ssa.OpFwdRef, t, var_)
+ v := b.NewValue0A(line, ssa.OpFwdRef, t, fwdRefAux{N: var_})
s.defvars[b.ID][var_] = v
- s.s.addNamedValue(var_, v)
+ if var_.Op() == ir.ONAME {
+ s.s.addNamedValue(var_.(*ir.Name), v)
+ }
s.fwdrefs = append(s.fwdrefs, v)
return v
}
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
similarity index 56%
rename from src/cmd/compile/internal/gc/ssa.go
rename to src/cmd/compile/internal/ssagen/ssa.go
index 5b74754b53291..6b1ddebd32aae 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -2,19 +2,28 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package ssagen
import (
+ "bufio"
+ "bytes"
"encoding/binary"
"fmt"
+ "go/constant"
"html"
"os"
"path/filepath"
"sort"
+ "strings"
- "bufio"
- "bytes"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/liveness"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/staticdata"
+ "cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
@@ -32,165 +41,181 @@ var ssaDumpStdout bool // whether to dump to stdout
var ssaDumpCFG string // generate CFGs for these phases
const ssaDumpFile = "ssa.html"
-// The max number of defers in a function using open-coded defers. We enforce this
-// limit because the deferBits bitmask is currently a single byte (to minimize code size)
-const maxOpenDefers = 8
-
// ssaDumpInlined holds all inlined functions when ssaDump contains a function name.
-var ssaDumpInlined []*Node
+var ssaDumpInlined []*ir.Func
+
+func DumpInline(fn *ir.Func) {
+ if ssaDump != "" && ssaDump == ir.FuncName(fn) {
+ ssaDumpInlined = append(ssaDumpInlined, fn)
+ }
+}
+
+func InitEnv() {
+ ssaDump = os.Getenv("GOSSAFUNC")
+ ssaDir = os.Getenv("GOSSADIR")
+ if ssaDump != "" {
+ if strings.HasSuffix(ssaDump, "+") {
+ ssaDump = ssaDump[:len(ssaDump)-1]
+ ssaDumpStdout = true
+ }
+ spl := strings.Split(ssaDump, ":")
+ if len(spl) > 1 {
+ ssaDump = spl[0]
+ ssaDumpCFG = spl[1]
+ }
+ }
+}
-func initssaconfig() {
+func InitConfig() {
types_ := ssa.NewTypes()
- if thearch.SoftFloat {
+ if Arch.SoftFloat {
softfloatInit()
}
// Generate a few pointer types that are uncommon in the frontend but common in the backend.
// Caching is disabled in the backend, so generating these here avoids allocations.
- _ = types.NewPtr(types.Types[TINTER]) // *interface{}
- _ = types.NewPtr(types.NewPtr(types.Types[TSTRING])) // **string
- _ = types.NewPtr(types.NewSlice(types.Types[TINTER])) // *[]interface{}
- _ = types.NewPtr(types.NewPtr(types.Bytetype)) // **byte
- _ = types.NewPtr(types.NewSlice(types.Bytetype)) // *[]byte
- _ = types.NewPtr(types.NewSlice(types.Types[TSTRING])) // *[]string
- _ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[TUINT8]))) // ***uint8
- _ = types.NewPtr(types.Types[TINT16]) // *int16
- _ = types.NewPtr(types.Types[TINT64]) // *int64
- _ = types.NewPtr(types.Errortype) // *error
+ _ = types.NewPtr(types.Types[types.TINTER]) // *interface{}
+ _ = types.NewPtr(types.NewPtr(types.Types[types.TSTRING])) // **string
+ _ = types.NewPtr(types.NewSlice(types.Types[types.TINTER])) // *[]interface{}
+ _ = types.NewPtr(types.NewPtr(types.ByteType)) // **byte
+ _ = types.NewPtr(types.NewSlice(types.ByteType)) // *[]byte
+ _ = types.NewPtr(types.NewSlice(types.Types[types.TSTRING])) // *[]string
+ _ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[types.TUINT8]))) // ***uint8
+ _ = types.NewPtr(types.Types[types.TINT16]) // *int16
+ _ = types.NewPtr(types.Types[types.TINT64]) // *int64
+ _ = types.NewPtr(types.ErrorType) // *error
types.NewPtrCacheEnabled = false
- ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug.N == 0)
- ssaConfig.SoftFloat = thearch.SoftFloat
- ssaConfig.Race = flag_race
- ssaCaches = make([]ssa.Cache, nBackendWorkers)
+ ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0)
+ ssaConfig.SoftFloat = Arch.SoftFloat
+ ssaConfig.Race = base.Flag.Race
+ ssaCaches = make([]ssa.Cache, base.Flag.LowerC)
// Set up some runtime functions we'll need to call.
- assertE2I = sysfunc("assertE2I")
- assertE2I2 = sysfunc("assertE2I2")
- assertI2I = sysfunc("assertI2I")
- assertI2I2 = sysfunc("assertI2I2")
- deferproc = sysfunc("deferproc")
- deferprocStack = sysfunc("deferprocStack")
- Deferreturn = sysfunc("deferreturn")
- Duffcopy = sysfunc("duffcopy")
- Duffzero = sysfunc("duffzero")
- gcWriteBarrier = sysfunc("gcWriteBarrier")
- goschedguarded = sysfunc("goschedguarded")
- growslice = sysfunc("growslice")
- msanread = sysfunc("msanread")
- msanwrite = sysfunc("msanwrite")
- msanmove = sysfunc("msanmove")
- newobject = sysfunc("newobject")
- newproc = sysfunc("newproc")
- panicdivide = sysfunc("panicdivide")
- panicdottypeE = sysfunc("panicdottypeE")
- panicdottypeI = sysfunc("panicdottypeI")
- panicnildottype = sysfunc("panicnildottype")
- panicoverflow = sysfunc("panicoverflow")
- panicshift = sysfunc("panicshift")
- raceread = sysfunc("raceread")
- racereadrange = sysfunc("racereadrange")
- racewrite = sysfunc("racewrite")
- racewriterange = sysfunc("racewriterange")
- x86HasPOPCNT = sysvar("x86HasPOPCNT") // bool
- x86HasSSE41 = sysvar("x86HasSSE41") // bool
- x86HasFMA = sysvar("x86HasFMA") // bool
- armHasVFPv4 = sysvar("armHasVFPv4") // bool
- arm64HasATOMICS = sysvar("arm64HasATOMICS") // bool
- typedmemclr = sysfunc("typedmemclr")
- typedmemmove = sysfunc("typedmemmove")
- Udiv = sysvar("udiv") // asm func with special ABI
- writeBarrier = sysvar("writeBarrier") // struct { bool; ... }
- zerobaseSym = sysvar("zerobase")
+ ir.Syms.AssertE2I = typecheck.LookupRuntimeFunc("assertE2I")
+ ir.Syms.AssertE2I2 = typecheck.LookupRuntimeFunc("assertE2I2")
+ ir.Syms.AssertI2I = typecheck.LookupRuntimeFunc("assertI2I")
+ ir.Syms.AssertI2I2 = typecheck.LookupRuntimeFunc("assertI2I2")
+ ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc")
+ ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack")
+ ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn")
+ ir.Syms.Duffcopy = typecheck.LookupRuntimeFunc("duffcopy")
+ ir.Syms.Duffzero = typecheck.LookupRuntimeFunc("duffzero")
+ ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier")
+ ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded")
+ ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice")
+ ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
+ ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite")
+ ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove")
+ ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject")
+ ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc")
+ ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide")
+ ir.Syms.PanicdottypeE = typecheck.LookupRuntimeFunc("panicdottypeE")
+ ir.Syms.PanicdottypeI = typecheck.LookupRuntimeFunc("panicdottypeI")
+ ir.Syms.Panicnildottype = typecheck.LookupRuntimeFunc("panicnildottype")
+ ir.Syms.Panicoverflow = typecheck.LookupRuntimeFunc("panicoverflow")
+ ir.Syms.Panicshift = typecheck.LookupRuntimeFunc("panicshift")
+ ir.Syms.Raceread = typecheck.LookupRuntimeFunc("raceread")
+ ir.Syms.Racereadrange = typecheck.LookupRuntimeFunc("racereadrange")
+ ir.Syms.Racewrite = typecheck.LookupRuntimeFunc("racewrite")
+ ir.Syms.Racewriterange = typecheck.LookupRuntimeFunc("racewriterange")
+ ir.Syms.X86HasPOPCNT = typecheck.LookupRuntimeVar("x86HasPOPCNT") // bool
+ ir.Syms.X86HasSSE41 = typecheck.LookupRuntimeVar("x86HasSSE41") // bool
+ ir.Syms.X86HasFMA = typecheck.LookupRuntimeVar("x86HasFMA") // bool
+ ir.Syms.ARMHasVFPv4 = typecheck.LookupRuntimeVar("armHasVFPv4") // bool
+ ir.Syms.ARM64HasATOMICS = typecheck.LookupRuntimeVar("arm64HasATOMICS") // bool
+ ir.Syms.Staticuint64s = typecheck.LookupRuntimeVar("staticuint64s")
+ ir.Syms.Typedmemclr = typecheck.LookupRuntimeFunc("typedmemclr")
+ ir.Syms.Typedmemmove = typecheck.LookupRuntimeFunc("typedmemmove")
+ ir.Syms.Udiv = typecheck.LookupRuntimeVar("udiv") // asm func with special ABI
+ ir.Syms.WriteBarrier = typecheck.LookupRuntimeVar("writeBarrier") // struct { bool; ... }
+ ir.Syms.Zerobase = typecheck.LookupRuntimeVar("zerobase")
// asm funcs with special ABI
- if thearch.LinkArch.Name == "amd64" {
+ if base.Ctxt.Arch.Name == "amd64" {
GCWriteBarrierReg = map[int16]*obj.LSym{
- x86.REG_AX: sysfunc("gcWriteBarrier"),
- x86.REG_CX: sysfunc("gcWriteBarrierCX"),
- x86.REG_DX: sysfunc("gcWriteBarrierDX"),
- x86.REG_BX: sysfunc("gcWriteBarrierBX"),
- x86.REG_BP: sysfunc("gcWriteBarrierBP"),
- x86.REG_SI: sysfunc("gcWriteBarrierSI"),
- x86.REG_R8: sysfunc("gcWriteBarrierR8"),
- x86.REG_R9: sysfunc("gcWriteBarrierR9"),
- }
- }
-
- if thearch.LinkArch.Family == sys.Wasm {
- BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("goPanicIndex")
- BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("goPanicIndexU")
- BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("goPanicSliceAlen")
- BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("goPanicSliceAlenU")
- BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("goPanicSliceAcap")
- BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("goPanicSliceAcapU")
- BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("goPanicSliceB")
- BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("goPanicSliceBU")
- BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("goPanicSlice3Alen")
- BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("goPanicSlice3AlenU")
- BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("goPanicSlice3Acap")
- BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("goPanicSlice3AcapU")
- BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("goPanicSlice3B")
- BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("goPanicSlice3BU")
- BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("goPanicSlice3C")
- BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("goPanicSlice3CU")
+ x86.REG_AX: typecheck.LookupRuntimeFunc("gcWriteBarrier"),
+ x86.REG_CX: typecheck.LookupRuntimeFunc("gcWriteBarrierCX"),
+ x86.REG_DX: typecheck.LookupRuntimeFunc("gcWriteBarrierDX"),
+ x86.REG_BX: typecheck.LookupRuntimeFunc("gcWriteBarrierBX"),
+ x86.REG_BP: typecheck.LookupRuntimeFunc("gcWriteBarrierBP"),
+ x86.REG_SI: typecheck.LookupRuntimeFunc("gcWriteBarrierSI"),
+ x86.REG_R8: typecheck.LookupRuntimeFunc("gcWriteBarrierR8"),
+ x86.REG_R9: typecheck.LookupRuntimeFunc("gcWriteBarrierR9"),
+ }
+ }
+
+ if Arch.LinkArch.Family == sys.Wasm {
+ BoundsCheckFunc[ssa.BoundsIndex] = typecheck.LookupRuntimeFunc("goPanicIndex")
+ BoundsCheckFunc[ssa.BoundsIndexU] = typecheck.LookupRuntimeFunc("goPanicIndexU")
+ BoundsCheckFunc[ssa.BoundsSliceAlen] = typecheck.LookupRuntimeFunc("goPanicSliceAlen")
+ BoundsCheckFunc[ssa.BoundsSliceAlenU] = typecheck.LookupRuntimeFunc("goPanicSliceAlenU")
+ BoundsCheckFunc[ssa.BoundsSliceAcap] = typecheck.LookupRuntimeFunc("goPanicSliceAcap")
+ BoundsCheckFunc[ssa.BoundsSliceAcapU] = typecheck.LookupRuntimeFunc("goPanicSliceAcapU")
+ BoundsCheckFunc[ssa.BoundsSliceB] = typecheck.LookupRuntimeFunc("goPanicSliceB")
+ BoundsCheckFunc[ssa.BoundsSliceBU] = typecheck.LookupRuntimeFunc("goPanicSliceBU")
+ BoundsCheckFunc[ssa.BoundsSlice3Alen] = typecheck.LookupRuntimeFunc("goPanicSlice3Alen")
+ BoundsCheckFunc[ssa.BoundsSlice3AlenU] = typecheck.LookupRuntimeFunc("goPanicSlice3AlenU")
+ BoundsCheckFunc[ssa.BoundsSlice3Acap] = typecheck.LookupRuntimeFunc("goPanicSlice3Acap")
+ BoundsCheckFunc[ssa.BoundsSlice3AcapU] = typecheck.LookupRuntimeFunc("goPanicSlice3AcapU")
+ BoundsCheckFunc[ssa.BoundsSlice3B] = typecheck.LookupRuntimeFunc("goPanicSlice3B")
+ BoundsCheckFunc[ssa.BoundsSlice3BU] = typecheck.LookupRuntimeFunc("goPanicSlice3BU")
+ BoundsCheckFunc[ssa.BoundsSlice3C] = typecheck.LookupRuntimeFunc("goPanicSlice3C")
+ BoundsCheckFunc[ssa.BoundsSlice3CU] = typecheck.LookupRuntimeFunc("goPanicSlice3CU")
} else {
- BoundsCheckFunc[ssa.BoundsIndex] = sysfunc("panicIndex")
- BoundsCheckFunc[ssa.BoundsIndexU] = sysfunc("panicIndexU")
- BoundsCheckFunc[ssa.BoundsSliceAlen] = sysfunc("panicSliceAlen")
- BoundsCheckFunc[ssa.BoundsSliceAlenU] = sysfunc("panicSliceAlenU")
- BoundsCheckFunc[ssa.BoundsSliceAcap] = sysfunc("panicSliceAcap")
- BoundsCheckFunc[ssa.BoundsSliceAcapU] = sysfunc("panicSliceAcapU")
- BoundsCheckFunc[ssa.BoundsSliceB] = sysfunc("panicSliceB")
- BoundsCheckFunc[ssa.BoundsSliceBU] = sysfunc("panicSliceBU")
- BoundsCheckFunc[ssa.BoundsSlice3Alen] = sysfunc("panicSlice3Alen")
- BoundsCheckFunc[ssa.BoundsSlice3AlenU] = sysfunc("panicSlice3AlenU")
- BoundsCheckFunc[ssa.BoundsSlice3Acap] = sysfunc("panicSlice3Acap")
- BoundsCheckFunc[ssa.BoundsSlice3AcapU] = sysfunc("panicSlice3AcapU")
- BoundsCheckFunc[ssa.BoundsSlice3B] = sysfunc("panicSlice3B")
- BoundsCheckFunc[ssa.BoundsSlice3BU] = sysfunc("panicSlice3BU")
- BoundsCheckFunc[ssa.BoundsSlice3C] = sysfunc("panicSlice3C")
- BoundsCheckFunc[ssa.BoundsSlice3CU] = sysfunc("panicSlice3CU")
- }
- if thearch.LinkArch.PtrSize == 4 {
- ExtendCheckFunc[ssa.BoundsIndex] = sysvar("panicExtendIndex")
- ExtendCheckFunc[ssa.BoundsIndexU] = sysvar("panicExtendIndexU")
- ExtendCheckFunc[ssa.BoundsSliceAlen] = sysvar("panicExtendSliceAlen")
- ExtendCheckFunc[ssa.BoundsSliceAlenU] = sysvar("panicExtendSliceAlenU")
- ExtendCheckFunc[ssa.BoundsSliceAcap] = sysvar("panicExtendSliceAcap")
- ExtendCheckFunc[ssa.BoundsSliceAcapU] = sysvar("panicExtendSliceAcapU")
- ExtendCheckFunc[ssa.BoundsSliceB] = sysvar("panicExtendSliceB")
- ExtendCheckFunc[ssa.BoundsSliceBU] = sysvar("panicExtendSliceBU")
- ExtendCheckFunc[ssa.BoundsSlice3Alen] = sysvar("panicExtendSlice3Alen")
- ExtendCheckFunc[ssa.BoundsSlice3AlenU] = sysvar("panicExtendSlice3AlenU")
- ExtendCheckFunc[ssa.BoundsSlice3Acap] = sysvar("panicExtendSlice3Acap")
- ExtendCheckFunc[ssa.BoundsSlice3AcapU] = sysvar("panicExtendSlice3AcapU")
- ExtendCheckFunc[ssa.BoundsSlice3B] = sysvar("panicExtendSlice3B")
- ExtendCheckFunc[ssa.BoundsSlice3BU] = sysvar("panicExtendSlice3BU")
- ExtendCheckFunc[ssa.BoundsSlice3C] = sysvar("panicExtendSlice3C")
- ExtendCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicExtendSlice3CU")
+ BoundsCheckFunc[ssa.BoundsIndex] = typecheck.LookupRuntimeFunc("panicIndex")
+ BoundsCheckFunc[ssa.BoundsIndexU] = typecheck.LookupRuntimeFunc("panicIndexU")
+ BoundsCheckFunc[ssa.BoundsSliceAlen] = typecheck.LookupRuntimeFunc("panicSliceAlen")
+ BoundsCheckFunc[ssa.BoundsSliceAlenU] = typecheck.LookupRuntimeFunc("panicSliceAlenU")
+ BoundsCheckFunc[ssa.BoundsSliceAcap] = typecheck.LookupRuntimeFunc("panicSliceAcap")
+ BoundsCheckFunc[ssa.BoundsSliceAcapU] = typecheck.LookupRuntimeFunc("panicSliceAcapU")
+ BoundsCheckFunc[ssa.BoundsSliceB] = typecheck.LookupRuntimeFunc("panicSliceB")
+ BoundsCheckFunc[ssa.BoundsSliceBU] = typecheck.LookupRuntimeFunc("panicSliceBU")
+ BoundsCheckFunc[ssa.BoundsSlice3Alen] = typecheck.LookupRuntimeFunc("panicSlice3Alen")
+ BoundsCheckFunc[ssa.BoundsSlice3AlenU] = typecheck.LookupRuntimeFunc("panicSlice3AlenU")
+ BoundsCheckFunc[ssa.BoundsSlice3Acap] = typecheck.LookupRuntimeFunc("panicSlice3Acap")
+ BoundsCheckFunc[ssa.BoundsSlice3AcapU] = typecheck.LookupRuntimeFunc("panicSlice3AcapU")
+ BoundsCheckFunc[ssa.BoundsSlice3B] = typecheck.LookupRuntimeFunc("panicSlice3B")
+ BoundsCheckFunc[ssa.BoundsSlice3BU] = typecheck.LookupRuntimeFunc("panicSlice3BU")
+ BoundsCheckFunc[ssa.BoundsSlice3C] = typecheck.LookupRuntimeFunc("panicSlice3C")
+ BoundsCheckFunc[ssa.BoundsSlice3CU] = typecheck.LookupRuntimeFunc("panicSlice3CU")
+ }
+ if Arch.LinkArch.PtrSize == 4 {
+ ExtendCheckFunc[ssa.BoundsIndex] = typecheck.LookupRuntimeVar("panicExtendIndex")
+ ExtendCheckFunc[ssa.BoundsIndexU] = typecheck.LookupRuntimeVar("panicExtendIndexU")
+ ExtendCheckFunc[ssa.BoundsSliceAlen] = typecheck.LookupRuntimeVar("panicExtendSliceAlen")
+ ExtendCheckFunc[ssa.BoundsSliceAlenU] = typecheck.LookupRuntimeVar("panicExtendSliceAlenU")
+ ExtendCheckFunc[ssa.BoundsSliceAcap] = typecheck.LookupRuntimeVar("panicExtendSliceAcap")
+ ExtendCheckFunc[ssa.BoundsSliceAcapU] = typecheck.LookupRuntimeVar("panicExtendSliceAcapU")
+ ExtendCheckFunc[ssa.BoundsSliceB] = typecheck.LookupRuntimeVar("panicExtendSliceB")
+ ExtendCheckFunc[ssa.BoundsSliceBU] = typecheck.LookupRuntimeVar("panicExtendSliceBU")
+ ExtendCheckFunc[ssa.BoundsSlice3Alen] = typecheck.LookupRuntimeVar("panicExtendSlice3Alen")
+ ExtendCheckFunc[ssa.BoundsSlice3AlenU] = typecheck.LookupRuntimeVar("panicExtendSlice3AlenU")
+ ExtendCheckFunc[ssa.BoundsSlice3Acap] = typecheck.LookupRuntimeVar("panicExtendSlice3Acap")
+ ExtendCheckFunc[ssa.BoundsSlice3AcapU] = typecheck.LookupRuntimeVar("panicExtendSlice3AcapU")
+ ExtendCheckFunc[ssa.BoundsSlice3B] = typecheck.LookupRuntimeVar("panicExtendSlice3B")
+ ExtendCheckFunc[ssa.BoundsSlice3BU] = typecheck.LookupRuntimeVar("panicExtendSlice3BU")
+ ExtendCheckFunc[ssa.BoundsSlice3C] = typecheck.LookupRuntimeVar("panicExtendSlice3C")
+ ExtendCheckFunc[ssa.BoundsSlice3CU] = typecheck.LookupRuntimeVar("panicExtendSlice3CU")
}
// Wasm (all asm funcs with special ABIs)
- WasmMove = sysvar("wasmMove")
- WasmZero = sysvar("wasmZero")
- WasmDiv = sysvar("wasmDiv")
- WasmTruncS = sysvar("wasmTruncS")
- WasmTruncU = sysvar("wasmTruncU")
- SigPanic = sysfunc("sigpanic")
+ ir.Syms.WasmMove = typecheck.LookupRuntimeVar("wasmMove")
+ ir.Syms.WasmZero = typecheck.LookupRuntimeVar("wasmZero")
+ ir.Syms.WasmDiv = typecheck.LookupRuntimeVar("wasmDiv")
+ ir.Syms.WasmTruncS = typecheck.LookupRuntimeVar("wasmTruncS")
+ ir.Syms.WasmTruncU = typecheck.LookupRuntimeVar("wasmTruncU")
+ ir.Syms.SigPanic = typecheck.LookupRuntimeFunc("sigpanic")
}
// getParam returns the Field of ith param of node n (which is a
// function/method/interface call), where the receiver of a method call is
// considered as the 0th parameter. This does not include the receiver of an
// interface call.
-func getParam(n *Node, i int) *types.Field {
- t := n.Left.Type
- if n.Op == OCALLMETH {
- if i == 0 {
- return t.Recv()
- }
- return t.Params().Field(i - 1)
+func getParam(n *ir.CallExpr, i int) *types.Field {
+ t := n.X.Type()
+ if n.Op() == ir.OCALLMETH {
+ base.Fatalf("OCALLMETH missed by walkCall")
}
return t.Params().Field(i)
}
@@ -201,22 +226,22 @@ func dvarint(x *obj.LSym, off int, v int64) int {
panic(fmt.Sprintf("dvarint: bad offset for funcdata - %v", v))
}
if v < 1<<7 {
- return duint8(x, off, uint8(v))
+ return objw.Uint8(x, off, uint8(v))
}
- off = duint8(x, off, uint8((v&127)|128))
+ off = objw.Uint8(x, off, uint8((v&127)|128))
if v < 1<<14 {
- return duint8(x, off, uint8(v>>7))
+ return objw.Uint8(x, off, uint8(v>>7))
}
- off = duint8(x, off, uint8(((v>>7)&127)|128))
+ off = objw.Uint8(x, off, uint8(((v>>7)&127)|128))
if v < 1<<21 {
- return duint8(x, off, uint8(v>>14))
+ return objw.Uint8(x, off, uint8(v>>14))
}
- off = duint8(x, off, uint8(((v>>14)&127)|128))
+ off = objw.Uint8(x, off, uint8(((v>>14)&127)|128))
if v < 1<<28 {
- return duint8(x, off, uint8(v>>21))
+ return objw.Uint8(x, off, uint8(v>>21))
}
- off = duint8(x, off, uint8(((v>>21)&127)|128))
- return duint8(x, off, uint8(v>>28))
+ off = objw.Uint8(x, off, uint8(((v>>21)&127)|128))
+ return objw.Uint8(x, off, uint8(v>>28))
}
// emitOpenDeferInfo emits FUNCDATA information about the defers in a function
@@ -240,8 +265,8 @@ func dvarint(x *obj.LSym, off int, v int64) int {
// - Size of the argument
// - Offset of where argument should be placed in the args frame when making call
func (s *state) emitOpenDeferInfo() {
- x := Ctxt.Lookup(s.curfn.Func.lsym.Name + ".opendefer")
- s.curfn.Func.lsym.Func().OpenCodedDeferInfo = x
+ x := base.Ctxt.Lookup(s.curfn.LSym.Name + ".opendefer")
+ s.curfn.LSym.Func().OpenCodedDeferInfo = x
off := 0
// Compute maxargsize (max size of arguments for all defers)
@@ -249,20 +274,20 @@ func (s *state) emitOpenDeferInfo() {
var maxargsize int64
for i := len(s.openDefers) - 1; i >= 0; i-- {
r := s.openDefers[i]
- argsize := r.n.Left.Type.ArgWidth()
+ argsize := r.n.X.Type().ArgWidth()
if argsize > maxargsize {
maxargsize = argsize
}
}
off = dvarint(x, off, maxargsize)
- off = dvarint(x, off, -s.deferBitsTemp.Xoffset)
+ off = dvarint(x, off, -s.deferBitsTemp.FrameOffset())
off = dvarint(x, off, int64(len(s.openDefers)))
// Write in reverse-order, for ease of running in that order at runtime
for i := len(s.openDefers) - 1; i >= 0; i-- {
r := s.openDefers[i]
- off = dvarint(x, off, r.n.Left.Type.ArgWidth())
- off = dvarint(x, off, -r.closureNode.Xoffset)
+ off = dvarint(x, off, r.n.X.Type().ArgWidth())
+ off = dvarint(x, off, -r.closureNode.FrameOffset())
numArgs := len(r.argNodes)
if r.rcvrNode != nil {
// If there's an interface receiver, treat/place it as the first
@@ -272,13 +297,13 @@ func (s *state) emitOpenDeferInfo() {
}
off = dvarint(x, off, int64(numArgs))
if r.rcvrNode != nil {
- off = dvarint(x, off, -r.rcvrNode.Xoffset)
+ off = dvarint(x, off, -r.rcvrNode.FrameOffset())
off = dvarint(x, off, s.config.PtrSize)
off = dvarint(x, off, 0)
}
for j, arg := range r.argNodes {
f := getParam(r.n, j)
- off = dvarint(x, off, -arg.Xoffset)
+ off = dvarint(x, off, -arg.FrameOffset())
off = dvarint(x, off, f.Type.Size())
off = dvarint(x, off, f.Offset)
}
@@ -287,18 +312,18 @@ func (s *state) emitOpenDeferInfo() {
// buildssa builds an SSA function for fn.
// worker indicates which of the backend workers is doing the processing.
-func buildssa(fn *Node, worker int) *ssa.Func {
- name := fn.funcname()
+func buildssa(fn *ir.Func, worker int) *ssa.Func {
+ name := ir.FuncName(fn)
printssa := false
if ssaDump != "" { // match either a simple name e.g. "(*Reader).Reset", or a package.name e.g. "compress/gzip.(*Reader).Reset"
- printssa = name == ssaDump || myimportpath+"."+name == ssaDump
+ printssa = name == ssaDump || base.Ctxt.Pkgpath+"."+name == ssaDump
}
var astBuf *bytes.Buffer
if printssa {
astBuf = &bytes.Buffer{}
- fdumplist(astBuf, "buildssa-enter", fn.Func.Enter)
- fdumplist(astBuf, "buildssa-body", fn.Nbody)
- fdumplist(astBuf, "buildssa-exit", fn.Func.Exit)
+ ir.FDumpList(astBuf, "buildssa-enter", fn.Enter)
+ ir.FDumpList(astBuf, "buildssa-body", fn.Body)
+ ir.FDumpList(astBuf, "buildssa-exit", fn.Exit)
if ssaDumpStdout {
fmt.Println("generating SSA for", name)
fmt.Print(astBuf.String())
@@ -306,11 +331,11 @@ func buildssa(fn *Node, worker int) *ssa.Func {
}
var s state
- s.pushLine(fn.Pos)
+ s.pushLine(fn.Pos())
defer s.popLine()
- s.hasdefer = fn.Func.HasDefer()
- if fn.Func.Pragma&CgoUnsafeArgs != 0 {
+ s.hasdefer = fn.HasDefer()
+ if fn.Pragma&ir.CgoUnsafeArgs != 0 {
s.cgoUnsafeArgs = true
}
@@ -322,27 +347,34 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.f = ssa.NewFunc(&fe)
s.config = ssaConfig
- s.f.Type = fn.Type
+ s.f.Type = fn.Type()
s.f.Config = ssaConfig
s.f.Cache = &ssaCaches[worker]
s.f.Cache.Reset()
s.f.Name = name
s.f.DebugTest = s.f.DebugHashMatch("GOSSAHASH")
s.f.PrintOrHtmlSSA = printssa
- if fn.Func.Pragma&Nosplit != 0 {
+ if fn.Pragma&ir.Nosplit != 0 {
s.f.NoSplit = true
}
+ if fn.Pragma&ir.RegisterParams != 0 { // TODO remove after register abi is working
+ if strings.Contains(name, ".") {
+ base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name)
+ }
+ s.f.Warnl(fn.Pos(), "declared function %v has register params", fn)
+ }
+
s.panics = map[funcLine]*ssa.Block{}
s.softFloat = s.config.SoftFloat
// Allocate starting block
s.f.Entry = s.f.NewBlock(ssa.BlockPlain)
- s.f.Entry.Pos = fn.Pos
+ s.f.Entry.Pos = fn.Pos()
if printssa {
ssaDF := ssaDumpFile
if ssaDir != "" {
- ssaDF = filepath.Join(ssaDir, myimportpath+"."+name+".html")
+ ssaDF = filepath.Join(ssaDir, base.Ctxt.Pkgpath+"."+name+".html")
ssaD := filepath.Dir(ssaDF)
os.MkdirAll(ssaD, 0755)
}
@@ -354,28 +386,36 @@ func buildssa(fn *Node, worker int) *ssa.Func {
// Allocate starting values
s.labels = map[string]*ssaLabel{}
- s.labeledNodes = map[*Node]*ssaLabel{}
- s.fwdVars = map[*Node]*ssa.Value{}
+ s.fwdVars = map[ir.Node]*ssa.Value{}
s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem)
- s.hasOpenDefers = Debug.N == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed()
+ s.hasOpenDefers = base.Flag.N == 0 && s.hasdefer && !s.curfn.OpenCodedDeferDisallowed()
switch {
- case s.hasOpenDefers && (Ctxt.Flag_shared || Ctxt.Flag_dynlink) && thearch.LinkArch.Name == "386":
+ case s.hasOpenDefers && (base.Ctxt.Flag_shared || base.Ctxt.Flag_dynlink) && base.Ctxt.Arch.Name == "386":
// Don't support open-coded defers for 386 ONLY when using shared
// libraries, because there is extra code (added by rewriteToUseGot())
// preceding the deferreturn/ret code that is generated by gencallret()
// that we don't track correctly.
s.hasOpenDefers = false
}
- if s.hasOpenDefers && s.curfn.Func.Exit.Len() > 0 {
+ if s.hasOpenDefers && len(s.curfn.Exit) > 0 {
// Skip doing open defers if there is any extra exit code (likely
- // copying heap-allocated return values or race detection), since
- // we will not generate that code in the case of the extra
- // deferreturn/ret segment.
+ // race detection), since we will not generate that code in the
+ // case of the extra deferreturn/ret segment.
s.hasOpenDefers = false
}
+ if s.hasOpenDefers {
+ // Similarly, skip if there are any heap-allocated result
+ // parameters that need to be copied back to their stack slots.
+ for _, f := range s.curfn.Type().Results().FieldSlice() {
+ if !f.Nname.(*ir.Name).OnStack() {
+ s.hasOpenDefers = false
+ break
+ }
+ }
+ }
if s.hasOpenDefers &&
- s.curfn.Func.numReturns*s.curfn.Func.numDefers > 15 {
+ s.curfn.NumReturns*s.curfn.NumDefers > 15 {
// Since we are generating defer calls at every exit for
// open-coded defers, skip doing open-coded defers if there are
// too many returns (especially if there are multiple defers).
@@ -384,76 +424,106 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.hasOpenDefers = false
}
- s.sp = s.entryNewValue0(ssa.OpSP, types.Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead
- s.sb = s.entryNewValue0(ssa.OpSB, types.Types[TUINTPTR])
+ s.sp = s.entryNewValue0(ssa.OpSP, types.Types[types.TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead
+ s.sb = s.entryNewValue0(ssa.OpSB, types.Types[types.TUINTPTR])
s.startBlock(s.f.Entry)
- s.vars[&memVar] = s.startmem
+ s.vars[memVar] = s.startmem
if s.hasOpenDefers {
// Create the deferBits variable and stack slot. deferBits is a
// bitmask showing which of the open-coded defers in this function
// have been activated.
- deferBitsTemp := tempAt(src.NoXPos, s.curfn, types.Types[TUINT8])
+ deferBitsTemp := typecheck.TempAt(src.NoXPos, s.curfn, types.Types[types.TUINT8])
+ deferBitsTemp.SetAddrtaken(true)
s.deferBitsTemp = deferBitsTemp
// For this value, AuxInt is initialized to zero by default
- startDeferBits := s.entryNewValue0(ssa.OpConst8, types.Types[TUINT8])
- s.vars[&deferBitsVar] = startDeferBits
+ startDeferBits := s.entryNewValue0(ssa.OpConst8, types.Types[types.TUINT8])
+ s.vars[deferBitsVar] = startDeferBits
s.deferBitsAddr = s.addr(deferBitsTemp)
- s.store(types.Types[TUINT8], s.deferBitsAddr, startDeferBits)
+ s.store(types.Types[types.TUINT8], s.deferBitsAddr, startDeferBits)
// Make sure that the deferBits stack slot is kept alive (for use
// by panics) and stores to deferBits are not eliminated, even if
// all checking code on deferBits in the function exit can be
// eliminated, because the defer statements were all
// unconditional.
- s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, deferBitsTemp, s.mem(), false)
+ s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, deferBitsTemp, s.mem(), false)
}
// Generate addresses of local declarations
- s.decladdrs = map[*Node]*ssa.Value{}
+ s.decladdrs = map[*ir.Name]*ssa.Value{}
var args []ssa.Param
var results []ssa.Param
- for _, n := range fn.Func.Dcl {
- switch n.Class() {
- case PPARAM:
- s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type), n, s.sp, s.startmem)
- args = append(args, ssa.Param{Type: n.Type, Offset: int32(n.Xoffset)})
- case PPARAMOUT:
- s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type), n, s.sp, s.startmem)
- results = append(results, ssa.Param{Type: n.Type, Offset: int32(n.Xoffset)})
- if s.canSSA(n) {
- // Save ssa-able PPARAMOUT variables so we can
- // store them back to the stack at the end of
- // the function.
- s.returns = append(s.returns, n)
- }
- case PAUTO:
+ for _, n := range fn.Dcl {
+ switch n.Class {
+ case ir.PPARAM:
+ s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type()), n, s.sp, s.startmem)
+ args = append(args, ssa.Param{Type: n.Type(), Offset: int32(n.FrameOffset())})
+ case ir.PPARAMOUT:
+ s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type()), n, s.sp, s.startmem)
+ results = append(results, ssa.Param{Type: n.Type(), Offset: int32(n.FrameOffset()), Name: n})
+ case ir.PAUTO:
// processed at each use, to prevent Addr coming
// before the decl.
- case PAUTOHEAP:
- // moved to heap - already handled by frontend
- case PFUNC:
- // local function - already handled by frontend
default:
- s.Fatalf("local variable with class %v unimplemented", n.Class())
+ s.Fatalf("local variable with class %v unimplemented", n.Class)
}
}
+ s.f.OwnAux = ssa.OwnAuxCall(fn.LSym, args, results)
// Populate SSAable arguments.
- for _, n := range fn.Func.Dcl {
- if n.Class() == PPARAM && s.canSSA(n) {
- v := s.newValue0A(ssa.OpArg, n.Type, n)
+ for _, n := range fn.Dcl {
+ if n.Class == ir.PPARAM && s.canSSA(n) {
+ v := s.newValue0A(ssa.OpArg, n.Type(), n)
s.vars[n] = v
s.addNamedValue(n, v) // This helps with debugging information, not needed for compilation itself.
}
}
+ // 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())
+ ptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(typ), offset, clo)
+ offset += typ.Size()
+
+ // If n is a small variable captured by value, promote
+ // it to PAUTO so it can be converted to SSA.
+ //
+ // Note: While we never capture a variable by value if
+ // the user took its address, we may have generated
+ // runtime calls that did (#43701). Since we don't
+ // convert Addrtaken variables to SSA anyway, no point
+ // in promoting them either.
+ if n.Byval() && !n.Addrtaken() && TypeOK(n.Type()) {
+ n.Class = ir.PAUTO
+ fn.Dcl = append(fn.Dcl, n)
+ s.assign(n, s.load(n.Type(), ptr), false, 0)
+ continue
+ }
+
+ if !n.Byval() {
+ ptr = s.load(typ, ptr)
+ }
+ s.setHeapaddr(fn.Pos(), n, ptr)
+ }
+ }
+
// Convert the AST-based IR to the SSA-based IR
- s.stmtList(fn.Func.Enter)
- s.stmtList(fn.Nbody)
+ s.stmtList(fn.Enter)
+ s.zeroResults()
+ s.paramsToHeap()
+ s.stmtList(fn.Body)
// fallthrough to exit
if s.curBlock != nil {
- s.pushLine(fn.Func.Endlineno)
+ s.pushLine(fn.Endlineno)
s.exit()
s.popLine()
}
@@ -464,6 +534,8 @@ func buildssa(fn *Node, worker int) *ssa.Func {
}
}
+ s.f.HTMLWriter.WritePhase("before insert phis", "before insert phis")
+
s.insertPhis()
// Main call to ssa package to compile function
@@ -476,10 +548,104 @@ func buildssa(fn *Node, worker int) *ssa.Func {
return s.f
}
-func dumpSourcesColumn(writer *ssa.HTMLWriter, fn *Node) {
+// zeroResults zeros the return values at the start of the function.
+// We need to do this very early in the function. Defer might stop a
+// panic and show the return values as they exist at the time of
+// panic. For precise stacks, the garbage collector assumes results
+// are always live, so we need to zero them before any allocations,
+// even allocations to move params/results to the heap.
+func (s *state) zeroResults() {
+ for _, f := range s.curfn.Type().Results().FieldSlice() {
+ n := f.Nname.(*ir.Name)
+ if !n.OnStack() {
+ // The local which points to the return value is the
+ // thing that needs zeroing. This is already handled
+ // by a Needzero annotation in plive.go:(*liveness).epilogue.
+ continue
+ }
+ // Zero the stack location containing f.
+ if typ := n.Type(); TypeOK(typ) {
+ s.assign(n, s.zeroVal(typ), false, 0)
+ } else {
+ s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem())
+ s.zero(n.Type(), s.decladdrs[n])
+ }
+ }
+}
+
+// paramsToHeap produces code to allocate memory for heap-escaped parameters
+// and to copy non-result parameters' values from the stack.
+func (s *state) paramsToHeap() {
+ do := func(params *types.Type) {
+ for _, f := range params.FieldSlice() {
+ if f.Nname == nil {
+ continue // anonymous or blank parameter
+ }
+ n := f.Nname.(*ir.Name)
+ if ir.IsBlank(n) || n.OnStack() {
+ continue
+ }
+ s.newHeapaddr(n)
+ if n.Class == ir.PPARAM {
+ s.move(n.Type(), s.expr(n.Heapaddr), s.decladdrs[n])
+ }
+ }
+ }
+
+ typ := s.curfn.Type()
+ do(typ.Recvs())
+ do(typ.Params())
+ do(typ.Results())
+}
+
+// newHeapaddr allocates heap memory for n and sets its heap address.
+func (s *state) newHeapaddr(n *ir.Name) {
+ s.setHeapaddr(n.Pos(), n, s.newObject(n.Type()))
+}
+
+// setHeapaddr allocates a new PAUTO variable to store ptr (which must be non-nil)
+// and then sets it as n's heap address.
+func (s *state) setHeapaddr(pos src.XPos, n *ir.Name, ptr *ssa.Value) {
+ if !ptr.Type.IsPtr() || !types.Identical(n.Type(), ptr.Type.Elem()) {
+ base.FatalfAt(n.Pos(), "setHeapaddr %L with type %v", n, ptr.Type)
+ }
+
+ // Declare variable to hold address.
+ addr := ir.NewNameAt(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 = s.curfn
+ s.curfn.Dcl = append(s.curfn.Dcl, addr)
+ types.CalcSize(addr.Type())
+
+ if n.Class == ir.PPARAMOUT {
+ addr.SetIsOutputParamHeapAddr(true)
+ }
+
+ n.Heapaddr = addr
+ s.assign(addr, ptr, false, 0)
+}
+
+// newObject returns an SSA value denoting new(typ).
+func (s *state) newObject(typ *types.Type) *ssa.Value {
+ if typ.Size() == 0 {
+ return s.newValue1A(ssa.OpAddr, types.NewPtr(typ), ir.Syms.Zerobase, s.sb)
+ }
+ return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, s.reflectType(typ))[0]
+}
+
+// reflectType returns an SSA value representing a pointer to typ's
+// reflection type descriptor.
+func (s *state) reflectType(typ *types.Type) *ssa.Value {
+ lsym := reflectdata.TypeLinksym(typ)
+ return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(types.Types[types.TUINT8]), lsym, s.sb)
+}
+
+func dumpSourcesColumn(writer *ssa.HTMLWriter, fn *ir.Func) {
// Read sources of target function fn.
- fname := Ctxt.PosTable.Pos(fn.Pos).Filename()
- targetFn, err := readFuncLines(fname, fn.Pos.Line(), fn.Func.Endlineno.Line())
+ fname := base.Ctxt.PosTable.Pos(fn.Pos()).Filename()
+ targetFn, err := readFuncLines(fname, fn.Pos().Line(), fn.Endlineno.Line())
if err != nil {
writer.Logf("cannot read sources for function %v: %v", fn, err)
}
@@ -487,15 +653,9 @@ func dumpSourcesColumn(writer *ssa.HTMLWriter, fn *Node) {
// Read sources of inlined functions.
var inlFns []*ssa.FuncLines
for _, fi := range ssaDumpInlined {
- var elno src.XPos
- if fi.Name.Defn == nil {
- // Endlineno is filled from exported data.
- elno = fi.Func.Endlineno
- } else {
- elno = fi.Name.Defn.Func.Endlineno
- }
- fname := Ctxt.PosTable.Pos(fi.Pos).Filename()
- fnLines, err := readFuncLines(fname, fi.Pos.Line(), elno.Line())
+ elno := fi.Endlineno
+ fname := base.Ctxt.PosTable.Pos(fi.Pos()).Filename()
+ fnLines, err := readFuncLines(fname, fi.Pos().Line(), elno.Line())
if err != nil {
writer.Logf("cannot read sources for inlined function %v: %v", fi, err)
continue
@@ -563,25 +723,25 @@ func (s *state) updateUnsetPredPos(b *ssa.Block) {
// Information about each open-coded defer.
type openDeferInfo struct {
- // The ODEFER node representing the function call of the defer
- n *Node
+ // The node representing the call of the defer
+ n *ir.CallExpr
// If defer call is closure call, the address of the argtmp where the
// closure is stored.
closure *ssa.Value
// The node representing the argtmp where the closure is stored - used for
// function, method, or interface call, to store a closure that panic
// processing can use for this defer.
- closureNode *Node
+ closureNode *ir.Name
// If defer call is interface call, the address of the argtmp where the
// receiver is stored
rcvr *ssa.Value
// The node representing the argtmp where the receiver is stored
- rcvrNode *Node
+ rcvrNode *ir.Name
// The addresses of the argtmps where the evaluated arguments of the defer
// function call are stored.
argVals []*ssa.Value
// The nodes representing the argtmps where the args of the defer are stored
- argNodes []*Node
+ argNodes []*ir.Name
}
type state struct {
@@ -592,11 +752,10 @@ type state struct {
f *ssa.Func
// Node for function
- curfn *Node
+ curfn *ir.Func
- // labels and labeled control flow nodes (OFOR, OFORUNTIL, OSWITCH, OSELECT) in f
- labels map[string]*ssaLabel
- labeledNodes map[*Node]*ssaLabel
+ // labels in f
+ labels map[string]*ssaLabel
// unlabeled break and continue statement tracking
breakTo *ssa.Block // current target for plain break statement
@@ -608,18 +767,18 @@ type state struct {
// variable assignments in the current block (map from variable symbol to ssa value)
// *Node is the unique identifier (an ONAME Node) for the variable.
// TODO: keep a single varnum map, then make all of these maps slices instead?
- vars map[*Node]*ssa.Value
+ vars map[ir.Node]*ssa.Value
// fwdVars are variables that are used before they are defined in the current block.
// This map exists just to coalesce multiple references into a single FwdRef op.
// *Node is the unique identifier (an ONAME Node) for the variable.
- fwdVars map[*Node]*ssa.Value
+ fwdVars map[ir.Node]*ssa.Value
// all defined variables at the end of each block. Indexed by block ID.
- defvars []map[*Node]*ssa.Value
+ defvars []map[ir.Node]*ssa.Value
- // addresses of PPARAM and PPARAMOUT variables.
- decladdrs map[*Node]*ssa.Value
+ // addresses of PPARAM and PPARAMOUT variables on the stack.
+ decladdrs map[*ir.Name]*ssa.Value
// starting values. Memory, stack pointer, and globals pointer
startmem *ssa.Value
@@ -627,7 +786,7 @@ type state struct {
sb *ssa.Value
// value representing address of where deferBits autotmp is stored
deferBitsAddr *ssa.Value
- deferBitsTemp *Node
+ deferBitsTemp *ir.Name
// line number stack. The current line number is top of stack
line []src.XPos
@@ -638,9 +797,6 @@ type state struct {
// Used to deduplicate panic calls.
panics map[funcLine]*ssa.Block
- // list of PPARAMOUT (return) variables.
- returns []*Node
-
cgoUnsafeArgs bool
hasdefer bool // whether the function contains a defer statement
softFloat bool
@@ -691,18 +847,22 @@ func (s *state) Fatalf(msg string, args ...interface{}) {
func (s *state) Warnl(pos src.XPos, msg string, args ...interface{}) { s.f.Warnl(pos, msg, args...) }
func (s *state) Debug_checknil() bool { return s.f.Frontend().Debug_checknil() }
+func ssaMarker(name string) *ir.Name {
+ return typecheck.NewName(&types.Sym{Name: name})
+}
+
var (
- // dummy node for the memory variable
- memVar = Node{Op: ONAME, Sym: &types.Sym{Name: "mem"}}
-
- // dummy nodes for temporary variables
- ptrVar = Node{Op: ONAME, Sym: &types.Sym{Name: "ptr"}}
- lenVar = Node{Op: ONAME, Sym: &types.Sym{Name: "len"}}
- newlenVar = Node{Op: ONAME, Sym: &types.Sym{Name: "newlen"}}
- capVar = Node{Op: ONAME, Sym: &types.Sym{Name: "cap"}}
- typVar = Node{Op: ONAME, Sym: &types.Sym{Name: "typ"}}
- okVar = Node{Op: ONAME, Sym: &types.Sym{Name: "ok"}}
- deferBitsVar = Node{Op: ONAME, Sym: &types.Sym{Name: "deferBits"}}
+ // marker node for the memory variable
+ memVar = ssaMarker("mem")
+
+ // marker nodes for temporary variables
+ ptrVar = ssaMarker("ptr")
+ lenVar = ssaMarker("len")
+ newlenVar = ssaMarker("newlen")
+ capVar = ssaMarker("cap")
+ typVar = ssaMarker("typ")
+ okVar = ssaMarker("ok")
+ deferBitsVar = ssaMarker("deferBits")
)
// startBlock sets the current block we're generating code in to b.
@@ -711,7 +871,7 @@ func (s *state) startBlock(b *ssa.Block) {
s.Fatalf("starting block %v when block %v has not ended", b, s.curBlock)
}
s.curBlock = b
- s.vars = map[*Node]*ssa.Value{}
+ s.vars = map[ir.Node]*ssa.Value{}
for n := range s.fwdVars {
delete(s.fwdVars, n)
}
@@ -748,8 +908,8 @@ func (s *state) pushLine(line src.XPos) {
// the frontend may emit node with line number missing,
// use the parent line number in this case.
line = s.peekPos()
- if Debug.K != 0 {
- Warn("buildssa: unknown position (line 0)")
+ if base.Flag.K != 0 {
+ base.Warn("buildssa: unknown position (line 0)")
}
} else {
s.lastPos = line
@@ -774,7 +934,7 @@ func (s *state) newValue0(op ssa.Op, t *types.Type) *ssa.Value {
}
// newValue0A adds a new value with no arguments and an aux value to the current block.
-func (s *state) newValue0A(op ssa.Op, t *types.Type, aux interface{}) *ssa.Value {
+func (s *state) newValue0A(op ssa.Op, t *types.Type, aux ssa.Aux) *ssa.Value {
return s.curBlock.NewValue0A(s.peekPos(), op, t, aux)
}
@@ -789,14 +949,14 @@ func (s *state) newValue1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
}
// newValue1A adds a new value with one argument and an aux value to the current block.
-func (s *state) newValue1A(op ssa.Op, t *types.Type, aux interface{}, arg *ssa.Value) *ssa.Value {
+func (s *state) newValue1A(op ssa.Op, t *types.Type, aux ssa.Aux, arg *ssa.Value) *ssa.Value {
return s.curBlock.NewValue1A(s.peekPos(), op, t, aux, arg)
}
// newValue1Apos adds a new value with one argument 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).
-func (s *state) newValue1Apos(op ssa.Op, t *types.Type, aux interface{}, arg *ssa.Value, isStmt bool) *ssa.Value {
+func (s *state) newValue1Apos(op ssa.Op, t *types.Type, aux ssa.Aux, arg *ssa.Value, isStmt bool) *ssa.Value {
if isStmt {
return s.curBlock.NewValue1A(s.peekPos(), op, t, aux, arg)
}
@@ -814,14 +974,14 @@ func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.
}
// 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 {
+func (s *state) newValue2A(op ssa.Op, t *types.Type, aux ssa.Aux, 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).
-func (s *state) newValue2Apos(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value, isStmt bool) *ssa.Value {
+func (s *state) newValue2Apos(op ssa.Op, t *types.Type, aux ssa.Aux, arg0, arg1 *ssa.Value, isStmt bool) *ssa.Value {
if isStmt {
return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1)
}
@@ -844,14 +1004,14 @@ func (s *state) newValue3I(op ssa.Op, t *types.Type, aux int64, arg0, arg1, arg2
}
// newValue3A adds a new value with three arguments and an aux value to the current block.
-func (s *state) newValue3A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1, arg2 *ssa.Value) *ssa.Value {
+func (s *state) newValue3A(op ssa.Op, t *types.Type, aux ssa.Aux, arg0, arg1, arg2 *ssa.Value) *ssa.Value {
return s.curBlock.NewValue3A(s.peekPos(), op, t, aux, arg0, arg1, arg2)
}
// newValue3Apos adds a new value with three 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).
-func (s *state) newValue3Apos(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1, arg2 *ssa.Value, isStmt bool) *ssa.Value {
+func (s *state) newValue3Apos(op ssa.Op, t *types.Type, aux ssa.Aux, arg0, arg1, arg2 *ssa.Value, isStmt bool) *ssa.Value {
if isStmt {
return s.curBlock.NewValue3A(s.peekPos(), op, t, aux, arg0, arg1, arg2)
}
@@ -874,7 +1034,7 @@ func (s *state) entryNewValue0(op ssa.Op, t *types.Type) *ssa.Value {
}
// entryNewValue0A adds a new value with no arguments and an aux value to the entry block.
-func (s *state) entryNewValue0A(op ssa.Op, t *types.Type, aux interface{}) *ssa.Value {
+func (s *state) entryNewValue0A(op ssa.Op, t *types.Type, aux ssa.Aux) *ssa.Value {
return s.f.Entry.NewValue0A(src.NoXPos, op, t, aux)
}
@@ -889,7 +1049,7 @@ func (s *state) entryNewValue1I(op ssa.Op, t *types.Type, auxint int64, arg *ssa
}
// entryNewValue1A adds a new value with one argument and an aux value to the entry block.
-func (s *state) entryNewValue1A(op ssa.Op, t *types.Type, aux interface{}, arg *ssa.Value) *ssa.Value {
+func (s *state) entryNewValue1A(op ssa.Op, t *types.Type, aux ssa.Aux, arg *ssa.Value) *ssa.Value {
return s.f.Entry.NewValue1A(src.NoXPos, op, t, aux, arg)
}
@@ -899,7 +1059,7 @@ func (s *state) entryNewValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value)
}
// entryNewValue2A adds a new value with two arguments and an aux value to the entry block.
-func (s *state) entryNewValue2A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value {
+func (s *state) entryNewValue2A(op ssa.Op, t *types.Type, aux ssa.Aux, arg0, arg1 *ssa.Value) *ssa.Value {
return s.f.Entry.NewValue2A(src.NoXPos, op, t, aux, arg0, arg1)
}
@@ -915,7 +1075,7 @@ func (s *state) constEmptyString(t *types.Type) *ssa.Value {
return s.f.ConstEmptyString(t)
}
func (s *state) constBool(c bool) *ssa.Value {
- return s.f.ConstBool(types.Types[TBOOL], c)
+ return s.f.ConstBool(types.Types[types.TBOOL], c)
}
func (s *state) constInt8(t *types.Type, c int8) *ssa.Value {
return s.f.ConstInt8(t, c)
@@ -983,7 +1143,7 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind)
// If it is instrumenting for MSAN and t is a struct type, it instruments
// operation for each field, instead of for the whole struct.
func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrumentKind) {
- if !flag_msan || !t.IsStruct() {
+ if !base.Flag.MSan || !t.IsStruct() {
s.instrument(t, addr, kind)
return
}
@@ -997,7 +1157,7 @@ func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrument
}
func (s *state) instrumentMove(t *types.Type, dst, src *ssa.Value) {
- if flag_msan {
+ if base.Flag.MSan {
s.instrument2(t, dst, src, instrumentMove)
} else {
s.instrument(t, src, instrumentRead)
@@ -1006,7 +1166,7 @@ func (s *state) instrumentMove(t *types.Type, dst, src *ssa.Value) {
}
func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrumentKind) {
- if !s.curfn.Func.InstrumentBody() {
+ if !s.curfn.InstrumentBody() {
return
}
@@ -1026,39 +1186,39 @@ func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrume
panic("instrument2: non-nil addr2 for non-move instrumentation")
}
- if flag_msan {
+ if base.Flag.MSan {
switch kind {
case instrumentRead:
- fn = msanread
+ fn = ir.Syms.Msanread
case instrumentWrite:
- fn = msanwrite
+ fn = ir.Syms.Msanwrite
case instrumentMove:
- fn = msanmove
+ fn = ir.Syms.Msanmove
default:
panic("unreachable")
}
needWidth = true
- } else if flag_race && t.NumComponents(types.CountBlankFields) > 1 {
+ } else if base.Flag.Race && t.NumComponents(types.CountBlankFields) > 1 {
// for composite objects we have to write every address
// because a write might happen to any subobject.
// composites with only one element don't have subobjects, though.
switch kind {
case instrumentRead:
- fn = racereadrange
+ fn = ir.Syms.Racereadrange
case instrumentWrite:
- fn = racewriterange
+ fn = ir.Syms.Racewriterange
default:
panic("unreachable")
}
needWidth = true
- } else if flag_race {
+ } else if base.Flag.Race {
// for non-composite objects we can write just the start
// address, as any write must write the first byte.
switch kind {
case instrumentRead:
- fn = raceread
+ fn = ir.Syms.Raceread
case instrumentWrite:
- fn = racewrite
+ fn = ir.Syms.Racewrite
default:
panic("unreachable")
}
@@ -1071,7 +1231,7 @@ func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrume
args = append(args, addr2)
}
if needWidth {
- args = append(args, s.constInt(types.Types[TUINTPTR], w))
+ args = append(args, s.constInt(types.Types[types.TUINTPTR], w))
}
s.rtcall(fn, true, nil, args...)
}
@@ -1086,66 +1246,69 @@ func (s *state) rawLoad(t *types.Type, src *ssa.Value) *ssa.Value {
}
func (s *state) store(t *types.Type, dst, val *ssa.Value) {
- s.vars[&memVar] = s.newValue3A(ssa.OpStore, types.TypeMem, t, dst, val, s.mem())
+ s.vars[memVar] = s.newValue3A(ssa.OpStore, types.TypeMem, t, dst, val, s.mem())
}
func (s *state) zero(t *types.Type, dst *ssa.Value) {
s.instrument(t, dst, instrumentWrite)
store := s.newValue2I(ssa.OpZero, types.TypeMem, t.Size(), dst, s.mem())
store.Aux = t
- s.vars[&memVar] = store
+ s.vars[memVar] = store
}
func (s *state) move(t *types.Type, dst, src *ssa.Value) {
s.instrumentMove(t, dst, src)
store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem())
store.Aux = t
- s.vars[&memVar] = store
+ s.vars[memVar] = store
}
// stmtList converts the statement list n to SSA and adds it to s.
-func (s *state) stmtList(l Nodes) {
- for _, n := range l.Slice() {
+func (s *state) stmtList(l ir.Nodes) {
+ for _, n := range l {
s.stmt(n)
}
}
// stmt converts the statement n to SSA and adds it to s.
-func (s *state) stmt(n *Node) {
- if !(n.Op == OVARKILL || n.Op == OVARLIVE || n.Op == OVARDEF) {
+func (s *state) stmt(n ir.Node) {
+ if !(n.Op() == ir.OVARKILL || n.Op() == ir.OVARLIVE || n.Op() == ir.OVARDEF) {
// OVARKILL, OVARLIVE, and OVARDEF are invisible to the programmer, so we don't use their line numbers to avoid confusion in debugging.
- s.pushLine(n.Pos)
+ s.pushLine(n.Pos())
defer s.popLine()
}
// If s.curBlock is nil, and n isn't a label (which might have an associated goto somewhere),
// then this code is dead. Stop here.
- if s.curBlock == nil && n.Op != OLABEL {
+ if s.curBlock == nil && n.Op() != ir.OLABEL {
return
}
- s.stmtList(n.Ninit)
- switch n.Op {
+ s.stmtList(n.Init())
+ switch n.Op() {
- case OBLOCK:
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
s.stmtList(n.List)
// No-ops
- case OEMPTY, ODCLCONST, ODCLTYPE, OFALL:
+ case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL:
// Expression statements
- case OCALLFUNC:
- if isIntrinsicCall(n) {
+ case ir.OCALLFUNC:
+ n := n.(*ir.CallExpr)
+ if ir.IsIntrinsicCall(n) {
s.intrinsicCall(n)
return
}
fallthrough
- case OCALLMETH, OCALLINTER:
+ case ir.OCALLINTER:
+ n := n.(*ir.CallExpr)
s.callResult(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") {
+ if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class == ir.PFUNC {
+ if fn := n.X.Sym().Name; base.Flag.CompilingRuntime && fn == "throw" ||
+ n.X.Sym().Pkg == ir.Pkgs.Runtime && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") {
m := s.mem()
b := s.endBlock()
b.Kind = ssa.BlockExit
@@ -1155,34 +1318,37 @@ func (s *state) stmt(n *Node) {
// go through SSA.
}
}
- case ODEFER:
- if Debug_defer > 0 {
+ case ir.ODEFER:
+ n := n.(*ir.GoDeferStmt)
+ if base.Debug.Defer > 0 {
var defertype string
if s.hasOpenDefers {
defertype = "open-coded"
- } else if n.Esc == EscNever {
+ } else if n.Esc() == ir.EscNever {
defertype = "stack-allocated"
} else {
defertype = "heap-allocated"
}
- Warnl(n.Pos, "%s defer", defertype)
+ base.WarnfAt(n.Pos(), "%s defer", defertype)
}
if s.hasOpenDefers {
- s.openDeferRecord(n.Left)
+ s.openDeferRecord(n.Call.(*ir.CallExpr))
} else {
d := callDefer
- if n.Esc == EscNever {
+ if n.Esc() == ir.EscNever {
d = callDeferStack
}
- s.callResult(n.Left, d)
+ s.callResult(n.Call.(*ir.CallExpr), d)
}
- case OGO:
- s.callResult(n.Left, callGo)
+ case ir.OGO:
+ n := n.(*ir.GoDeferStmt)
+ s.callResult(n.Call.(*ir.CallExpr), callGo)
- case OAS2DOTTYPE:
- res, resok := s.dottype(n.Right, true)
+ case ir.OAS2DOTTYPE:
+ n := n.(*ir.AssignListStmt)
+ res, resok := s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true)
deref := false
- if !canSSAType(n.Right.Type) {
+ if !TypeOK(n.Rhs[0].Type()) {
if res.Op != ssa.OpLoad {
s.Fatalf("dottype of non-load")
}
@@ -1196,36 +1362,35 @@ func (s *state) stmt(n *Node) {
deref = true
res = res.Args[0]
}
- s.assign(n.List.First(), res, deref, 0)
- s.assign(n.List.Second(), resok, false, 0)
+ s.assign(n.Lhs[0], res, deref, 0)
+ s.assign(n.Lhs[1], resok, false, 0)
return
- case OAS2FUNC:
+ case ir.OAS2FUNC:
// We come here only when it is an intrinsic call returning two values.
- if !isIntrinsicCall(n.Right) {
- s.Fatalf("non-intrinsic AS2FUNC not expanded %v", n.Right)
- }
- v := s.intrinsicCall(n.Right)
- v1 := s.newValue1(ssa.OpSelect0, n.List.First().Type, v)
- v2 := s.newValue1(ssa.OpSelect1, n.List.Second().Type, v)
- s.assign(n.List.First(), v1, false, 0)
- s.assign(n.List.Second(), v2, false, 0)
+ n := n.(*ir.AssignListStmt)
+ call := n.Rhs[0].(*ir.CallExpr)
+ if !ir.IsIntrinsicCall(call) {
+ s.Fatalf("non-intrinsic AS2FUNC not expanded %v", call)
+ }
+ v := s.intrinsicCall(call)
+ v1 := s.newValue1(ssa.OpSelect0, n.Lhs[0].Type(), v)
+ v2 := s.newValue1(ssa.OpSelect1, n.Lhs[1].Type(), v)
+ s.assign(n.Lhs[0], v1, false, 0)
+ s.assign(n.Lhs[1], v2, false, 0)
return
- case ODCL:
- if n.Left.Class() == PAUTOHEAP {
- s.Fatalf("DCL %v", n)
+ case ir.ODCL:
+ n := n.(*ir.Decl)
+ if v := n.X; v.Esc() == ir.EscHeap {
+ s.newHeapaddr(v)
}
- case OLABEL:
- sym := n.Sym
+ case ir.OLABEL:
+ n := n.(*ir.LabelStmt)
+ sym := n.Label
lab := s.label(sym)
- // Associate label with its control flow node, if any
- if ctl := n.labeledControl(); ctl != nil {
- s.labeledNodes[ctl] = lab
- }
-
// The label might already have a target block via a goto.
if lab.target == nil {
lab.target = s.f.NewBlock(ssa.BlockPlain)
@@ -1239,8 +1404,9 @@ func (s *state) stmt(n *Node) {
}
s.startBlock(lab.target)
- case OGOTO:
- sym := n.Sym
+ case ir.OGOTO:
+ n := n.(*ir.BranchStmt)
+ sym := n.Label
lab := s.label(sym)
if lab.target == nil {
@@ -1251,8 +1417,9 @@ func (s *state) stmt(n *Node) {
b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block.
b.AddEdgeTo(lab.target)
- case OAS:
- if n.Left == n.Right && n.Left.Op == ONAME {
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ if n.X == n.Y && n.X.Op() == ir.ONAME {
// An x=x assignment. No point in doing anything
// here. In addition, skipping this assignment
// prevents generating:
@@ -1264,42 +1431,43 @@ func (s *state) stmt(n *Node) {
}
// Evaluate RHS.
- rhs := n.Right
+ rhs := n.Y
if rhs != nil {
- switch rhs.Op {
- case OSTRUCTLIT, OARRAYLIT, OSLICELIT:
+ switch rhs.Op() {
+ case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT:
// All literals with nonzero fields have already been
// rewritten during walk. Any that remain are just T{}
// or equivalents. Use the zero value.
- if !isZero(rhs) {
+ if !ir.IsZero(rhs) {
s.Fatalf("literal with nonzero value in SSA: %v", rhs)
}
rhs = nil
- case OAPPEND:
+ case ir.OAPPEND:
+ rhs := rhs.(*ir.CallExpr)
// Check whether we're writing the result of an append back to the same slice.
// If so, we handle it specially to avoid write barriers on the fast
// (non-growth) path.
- if !samesafeexpr(n.Left, rhs.List.First()) || Debug.N != 0 {
+ if !ir.SameSafeExpr(n.X, rhs.Args[0]) || base.Flag.N != 0 {
break
}
// If the slice can be SSA'd, it'll be on the stack,
// so there will be no write barriers,
// so there's no need to attempt to prevent them.
- if s.canSSA(n.Left) {
- if Debug_append > 0 { // replicating old diagnostic message
- Warnl(n.Pos, "append: len-only update (in local slice)")
+ if s.canSSA(n.X) {
+ if base.Debug.Append > 0 { // replicating old diagnostic message
+ base.WarnfAt(n.Pos(), "append: len-only update (in local slice)")
}
break
}
- if Debug_append > 0 {
- Warnl(n.Pos, "append: len-only update")
+ if base.Debug.Append > 0 {
+ base.WarnfAt(n.Pos(), "append: len-only update")
}
s.append(rhs, true)
return
}
}
- if n.Left.isBlank() {
+ if ir.IsBlank(n.X) {
// _ = rhs
// Just evaluate rhs for side-effects.
if rhs != nil {
@@ -1309,14 +1477,14 @@ func (s *state) stmt(n *Node) {
}
var t *types.Type
- if n.Right != nil {
- t = n.Right.Type
+ if n.Y != nil {
+ t = n.Y.Type()
} else {
- t = n.Left.Type
+ t = n.X.Type()
}
var r *ssa.Value
- deref := !canSSAType(t)
+ deref := !TypeOK(t)
if deref {
if rhs == nil {
r = nil // Signal assign to use OpZero.
@@ -1332,11 +1500,12 @@ func (s *state) stmt(n *Node) {
}
var skip skipMask
- if rhs != nil && (rhs.Op == OSLICE || rhs.Op == OSLICE3 || rhs.Op == OSLICESTR) && samesafeexpr(rhs.Left, n.Left) {
+ if rhs != nil && (rhs.Op() == ir.OSLICE || rhs.Op() == ir.OSLICE3 || rhs.Op() == ir.OSLICESTR) && ir.SameSafeExpr(rhs.(*ir.SliceExpr).X, n.X) {
// We're assigning a slicing operation back to its source.
// Don't write back fields we aren't changing. See issue #14855.
- i, j, k := rhs.SliceBounds()
- if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64Val() == 0) {
+ rhs := rhs.(*ir.SliceExpr)
+ i, j, k := rhs.Low, rhs.High, rhs.Max
+ if i != nil && (i.Op() == ir.OLITERAL && i.Val().Kind() == constant.Int && ir.Int64Val(i) == 0) {
// [0:...] is the same as [:...]
i = nil
}
@@ -1344,10 +1513,10 @@ func (s *state) stmt(n *Node) {
// Currently doesn't really work because (*p)[:len(*p)] appears here as:
// tmp = len(*p)
// (*p)[:tmp]
- //if j != nil && (j.Op == OLEN && samesafeexpr(j.Left, n.Left)) {
+ //if j != nil && (j.Op == OLEN && SameSafeExpr(j.Left, n.Left)) {
// j = nil
//}
- //if k != nil && (k.Op == OCAP && samesafeexpr(k.Left, n.Left)) {
+ //if k != nil && (k.Op == OCAP && SameSafeExpr(k.Left, n.Left)) {
// k = nil
//}
if i == nil {
@@ -1361,83 +1530,86 @@ func (s *state) stmt(n *Node) {
}
}
- s.assign(n.Left, r, deref, skip)
+ s.assign(n.X, r, deref, skip)
- case OIF:
- if Isconst(n.Left, CTBOOL) {
- s.stmtList(n.Left.Ninit)
- if n.Left.BoolVal() {
- s.stmtList(n.Nbody)
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ if ir.IsConst(n.Cond, constant.Bool) {
+ s.stmtList(n.Cond.Init())
+ if ir.BoolVal(n.Cond) {
+ s.stmtList(n.Body)
} else {
- s.stmtList(n.Rlist)
+ s.stmtList(n.Else)
}
break
}
bEnd := s.f.NewBlock(ssa.BlockPlain)
var likely int8
- if n.Likely() {
+ if n.Likely {
likely = 1
}
var bThen *ssa.Block
- if n.Nbody.Len() != 0 {
+ if len(n.Body) != 0 {
bThen = s.f.NewBlock(ssa.BlockPlain)
} else {
bThen = bEnd
}
var bElse *ssa.Block
- if n.Rlist.Len() != 0 {
+ if len(n.Else) != 0 {
bElse = s.f.NewBlock(ssa.BlockPlain)
} else {
bElse = bEnd
}
- s.condBranch(n.Left, bThen, bElse, likely)
+ s.condBranch(n.Cond, bThen, bElse, likely)
- if n.Nbody.Len() != 0 {
+ if len(n.Body) != 0 {
s.startBlock(bThen)
- s.stmtList(n.Nbody)
+ s.stmtList(n.Body)
if b := s.endBlock(); b != nil {
b.AddEdgeTo(bEnd)
}
}
- if n.Rlist.Len() != 0 {
+ if len(n.Else) != 0 {
s.startBlock(bElse)
- s.stmtList(n.Rlist)
+ s.stmtList(n.Else)
if b := s.endBlock(); b != nil {
b.AddEdgeTo(bEnd)
}
}
s.startBlock(bEnd)
- case ORETURN:
- s.stmtList(n.List)
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ s.stmtList(n.Results)
b := s.exit()
b.Pos = s.lastPos.WithIsStmt()
- case ORETJMP:
- s.stmtList(n.List)
+ case ir.OTAILCALL:
+ n := n.(*ir.TailCallStmt)
b := s.exit()
b.Kind = ssa.BlockRetJmp // override BlockRet
- b.Aux = n.Sym.Linksym()
+ b.Aux = callTargetLSym(n.Target, s.curfn.LSym)
- case OCONTINUE, OBREAK:
+ case ir.OCONTINUE, ir.OBREAK:
+ n := n.(*ir.BranchStmt)
var to *ssa.Block
- if n.Sym == nil {
+ if n.Label == nil {
// plain break/continue
- switch n.Op {
- case OCONTINUE:
+ switch n.Op() {
+ case ir.OCONTINUE:
to = s.continueTo
- case OBREAK:
+ case ir.OBREAK:
to = s.breakTo
}
} else {
// labeled break/continue; look up the target
- sym := n.Sym
+ sym := n.Label
lab := s.label(sym)
- switch n.Op {
- case OCONTINUE:
+ switch n.Op() {
+ case ir.OCONTINUE:
to = lab.continueTarget
- case OBREAK:
+ case ir.OBREAK:
to = lab.breakTarget
}
}
@@ -1446,28 +1618,29 @@ func (s *state) stmt(n *Node) {
b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block.
b.AddEdgeTo(to)
- case OFOR, OFORUNTIL:
+ case ir.OFOR, ir.OFORUNTIL:
// OFOR: for Ninit; Left; Right { Nbody }
// cond (Left); body (Nbody); incr (Right)
//
// OFORUNTIL: for Ninit; Left; Right; List { Nbody }
// => body: { Nbody }; incr: Right; if Left { lateincr: List; goto body }; end:
+ n := n.(*ir.ForStmt)
bCond := s.f.NewBlock(ssa.BlockPlain)
bBody := s.f.NewBlock(ssa.BlockPlain)
bIncr := s.f.NewBlock(ssa.BlockPlain)
bEnd := s.f.NewBlock(ssa.BlockPlain)
// ensure empty for loops have correct position; issue #30167
- bBody.Pos = n.Pos
+ bBody.Pos = n.Pos()
// first, jump to condition test (OFOR) or body (OFORUNTIL)
b := s.endBlock()
- if n.Op == OFOR {
+ if n.Op() == ir.OFOR {
b.AddEdgeTo(bCond)
// generate code to test condition
s.startBlock(bCond)
- if n.Left != nil {
- s.condBranch(n.Left, bBody, bEnd, 1)
+ if n.Cond != nil {
+ s.condBranch(n.Cond, bBody, bEnd, 1)
} else {
b := s.endBlock()
b.Kind = ssa.BlockPlain
@@ -1483,16 +1656,17 @@ func (s *state) stmt(n *Node) {
prevBreak := s.breakTo
s.continueTo = bIncr
s.breakTo = bEnd
- lab := s.labeledNodes[n]
- if lab != nil {
+ var lab *ssaLabel
+ if sym := n.Label; sym != nil {
// labeled for loop
+ lab = s.label(sym)
lab.continueTarget = bIncr
lab.breakTarget = bEnd
}
// generate body
s.startBlock(bBody)
- s.stmtList(n.Nbody)
+ s.stmtList(n.Body)
// tear down continue/break
s.continueTo = prevContinue
@@ -1509,15 +1683,15 @@ func (s *state) stmt(n *Node) {
// generate incr (and, for OFORUNTIL, condition)
s.startBlock(bIncr)
- if n.Right != nil {
- s.stmt(n.Right)
+ if n.Post != nil {
+ s.stmt(n.Post)
}
- if n.Op == OFOR {
+ if n.Op() == ir.OFOR {
if b := s.endBlock(); b != nil {
b.AddEdgeTo(bCond)
// It can happen that bIncr ends in a block containing only VARKILL,
// and that muddles the debugging experience.
- if n.Op != OFORUNTIL && b.Pos == src.NoXPos {
+ if n.Op() != ir.OFORUNTIL && b.Pos == src.NoXPos {
b.Pos = bCond.Pos
}
}
@@ -1525,30 +1699,43 @@ func (s *state) stmt(n *Node) {
// bCond is unused in OFORUNTIL, so repurpose it.
bLateIncr := bCond
// test condition
- s.condBranch(n.Left, bLateIncr, bEnd, 1)
+ s.condBranch(n.Cond, bLateIncr, bEnd, 1)
// generate late increment
s.startBlock(bLateIncr)
- s.stmtList(n.List)
+ s.stmtList(n.Late)
s.endBlock().AddEdgeTo(bBody)
}
s.startBlock(bEnd)
- case OSWITCH, OSELECT:
+ case ir.OSWITCH, ir.OSELECT:
// These have been mostly rewritten by the front end into their Nbody fields.
// Our main task is to correctly hook up any break statements.
bEnd := s.f.NewBlock(ssa.BlockPlain)
prevBreak := s.breakTo
s.breakTo = bEnd
- lab := s.labeledNodes[n]
- if lab != nil {
+ var sym *types.Sym
+ var body ir.Nodes
+ if n.Op() == ir.OSWITCH {
+ n := n.(*ir.SwitchStmt)
+ sym = n.Label
+ body = n.Compiled
+ } else {
+ n := n.(*ir.SelectStmt)
+ sym = n.Label
+ body = n.Compiled
+ }
+
+ var lab *ssaLabel
+ if sym != nil {
// labeled
+ lab = s.label(sym)
lab.breakTarget = bEnd
}
// generate body code
- s.stmtList(n.Nbody)
+ s.stmtList(body)
s.breakTo = prevBreak
if lab != nil {
@@ -1565,40 +1752,46 @@ func (s *state) stmt(n *Node) {
}
s.startBlock(bEnd)
- case OVARDEF:
- if !s.canSSA(n.Left) {
- s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, n.Left, s.mem(), false)
+ case ir.OVARDEF:
+ n := n.(*ir.UnaryExpr)
+ if !s.canSSA(n.X) {
+ s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, n.X.(*ir.Name), s.mem(), false)
}
- case OVARKILL:
+ case ir.OVARKILL:
// Insert a varkill op to record that a variable is no longer live.
// We only care about liveness info at call sites, so putting the
// varkill in the store chain is enough to keep it correctly ordered
// with respect to call ops.
- if !s.canSSA(n.Left) {
- s.vars[&memVar] = s.newValue1Apos(ssa.OpVarKill, types.TypeMem, n.Left, s.mem(), false)
+ n := n.(*ir.UnaryExpr)
+ if !s.canSSA(n.X) {
+ s.vars[memVar] = s.newValue1Apos(ssa.OpVarKill, types.TypeMem, n.X.(*ir.Name), s.mem(), false)
}
- case OVARLIVE:
+ case ir.OVARLIVE:
// Insert a varlive op to record that a variable is still live.
- if !n.Left.Name.Addrtaken() {
- s.Fatalf("VARLIVE variable %v must have Addrtaken set", n.Left)
+ n := n.(*ir.UnaryExpr)
+ v := n.X.(*ir.Name)
+ if !v.Addrtaken() {
+ s.Fatalf("VARLIVE variable %v must have Addrtaken set", v)
}
- switch n.Left.Class() {
- case PAUTO, PPARAM, PPARAMOUT:
+ switch v.Class {
+ case ir.PAUTO, ir.PPARAM, ir.PPARAMOUT:
default:
- s.Fatalf("VARLIVE variable %v must be Auto or Arg", n.Left)
+ s.Fatalf("VARLIVE variable %v must be Auto or Arg", v)
}
- s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, n.Left, s.mem())
+ s.vars[memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, v, s.mem())
- case OCHECKNIL:
- p := s.expr(n.Left)
+ case ir.OCHECKNIL:
+ n := n.(*ir.UnaryExpr)
+ p := s.expr(n.X)
s.nilCheck(p)
- case OINLMARK:
- s.newValue1I(ssa.OpInlMark, types.TypeVoid, n.Xoffset, s.mem())
+ case ir.OINLMARK:
+ n := n.(*ir.InlineMarkStmt)
+ s.newValue1I(ssa.OpInlMark, types.TypeVoid, n.Index, s.mem())
default:
- s.Fatalf("unhandled stmt %v", n.Op)
+ s.Fatalf("unhandled stmt %v", n.Op())
}
}
@@ -1610,6 +1803,7 @@ const shareDeferExits = false
// It returns a BlockRet block that ends the control flow. Its control value
// will be set to the final memory state.
func (s *state) exit() *ssa.Block {
+ lateResultLowering := s.f.DebugTest
if s.hasdefer {
if s.hasOpenDefers {
if shareDeferExits && s.lastDeferExit != nil && len(s.openDefers) == s.lastDeferCount {
@@ -1622,28 +1816,65 @@ func (s *state) exit() *ssa.Block {
}
s.openDeferExit()
} else {
- s.rtcall(Deferreturn, true, nil)
+ s.rtcall(ir.Syms.Deferreturn, true, nil)
}
}
- // Run exit code. Typically, this code copies heap-allocated PPARAMOUT
- // variables back to the stack.
- s.stmtList(s.curfn.Func.Exit)
+ var b *ssa.Block
+ var m *ssa.Value
+ // Do actual return.
+ // These currently turn into self-copies (in many cases).
+ if lateResultLowering {
+ resultFields := s.curfn.Type().Results().FieldSlice()
+ results := make([]*ssa.Value, len(resultFields)+1, len(resultFields)+1)
+ m = s.newValue0(ssa.OpMakeResult, s.f.OwnAux.LateExpansionResultType())
+ // Store SSAable and heap-escaped PPARAMOUT variables back to stack locations.
+ for i, f := range resultFields {
+ n := f.Nname.(*ir.Name)
+ s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem())
+ if s.canSSA(n) { // result is in some SSA variable
+ results[i] = s.variable(n, n.Type())
+ } else if !n.OnStack() { // result is actually heap allocated
+ ha := s.expr(n.Heapaddr)
+ s.instrumentFields(n.Type(), ha, instrumentRead)
+ results[i] = s.newValue2(ssa.OpDereference, n.Type(), ha, s.mem())
+ } else { // result is not SSA-able; not escaped, so not on heap, but too large for SSA.
+ // Before register ABI this ought to be a self-move, home=dest,
+ // With register ABI, it's still a self-move if parameter is on stack (i.e., too big or overflowed)
+ results[i] = s.newValue2(ssa.OpDereference, n.Type(), s.addr(n), s.mem())
+ }
+ }
+
+ // Run exit code. Today, this is just racefuncexit, in -race mode.
+ // TODO this seems risky here with a register-ABI, but not clear it is right to do it earlier either.
+ // Spills in register allocation might just fix it.
+ s.stmtList(s.curfn.Exit)
- // Store SSAable PPARAMOUT variables back to stack locations.
- for _, n := range s.returns {
- addr := s.decladdrs[n]
- val := s.variable(n, n.Type)
- s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem())
- s.store(n.Type, addr, val)
- // TODO: if val is ever spilled, we'd like to use the
- // PPARAMOUT slot for spilling it. That won't happen
- // currently.
- }
+ results[len(results)-1] = s.mem()
+ m.AddArgs(results...)
+ } else {
+ // Store SSAable and heap-escaped PPARAMOUT variables back to stack locations.
+ for _, f := range s.curfn.Type().Results().FieldSlice() {
+ n := f.Nname.(*ir.Name)
+ if s.canSSA(n) {
+ val := s.variable(n, n.Type())
+ s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem())
+ s.store(n.Type(), s.decladdrs[n], val)
+ } else if !n.OnStack() {
+ s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem())
+ s.move(n.Type(), s.decladdrs[n], s.expr(n.Heapaddr))
+ } // else, on stack but too large to SSA, the result is already in its destination by construction, so no store needed.
- // Do actual return.
- m := s.mem()
- b := s.endBlock()
+ // TODO: if (SSA) val is ever spilled, we'd like to use the PPARAMOUT slot for spilling it. That won't happen currently.
+ }
+
+ // Run exit code. Today, this is just racefuncexit, in -race mode.
+ s.stmtList(s.curfn.Exit)
+
+ // Do actual return.
+ m = s.mem()
+ }
+ b = s.endBlock()
b.Kind = ssa.BlockRet
b.SetControl(m)
if s.hasdefer && s.hasOpenDefers {
@@ -1653,206 +1884,206 @@ func (s *state) exit() *ssa.Block {
}
type opAndType struct {
- op Op
- etype types.EType
+ op ir.Op
+ etype types.Kind
}
var opToSSA = map[opAndType]ssa.Op{
- opAndType{OADD, TINT8}: ssa.OpAdd8,
- opAndType{OADD, TUINT8}: ssa.OpAdd8,
- opAndType{OADD, TINT16}: ssa.OpAdd16,
- opAndType{OADD, TUINT16}: ssa.OpAdd16,
- opAndType{OADD, TINT32}: ssa.OpAdd32,
- opAndType{OADD, TUINT32}: ssa.OpAdd32,
- opAndType{OADD, TINT64}: ssa.OpAdd64,
- opAndType{OADD, TUINT64}: ssa.OpAdd64,
- opAndType{OADD, TFLOAT32}: ssa.OpAdd32F,
- opAndType{OADD, TFLOAT64}: ssa.OpAdd64F,
-
- opAndType{OSUB, TINT8}: ssa.OpSub8,
- opAndType{OSUB, TUINT8}: ssa.OpSub8,
- opAndType{OSUB, TINT16}: ssa.OpSub16,
- opAndType{OSUB, TUINT16}: ssa.OpSub16,
- opAndType{OSUB, TINT32}: ssa.OpSub32,
- opAndType{OSUB, TUINT32}: ssa.OpSub32,
- opAndType{OSUB, TINT64}: ssa.OpSub64,
- opAndType{OSUB, TUINT64}: ssa.OpSub64,
- opAndType{OSUB, TFLOAT32}: ssa.OpSub32F,
- opAndType{OSUB, TFLOAT64}: ssa.OpSub64F,
-
- opAndType{ONOT, TBOOL}: ssa.OpNot,
-
- opAndType{ONEG, TINT8}: ssa.OpNeg8,
- opAndType{ONEG, TUINT8}: ssa.OpNeg8,
- opAndType{ONEG, TINT16}: ssa.OpNeg16,
- opAndType{ONEG, TUINT16}: ssa.OpNeg16,
- opAndType{ONEG, TINT32}: ssa.OpNeg32,
- opAndType{ONEG, TUINT32}: ssa.OpNeg32,
- opAndType{ONEG, TINT64}: ssa.OpNeg64,
- opAndType{ONEG, TUINT64}: ssa.OpNeg64,
- opAndType{ONEG, TFLOAT32}: ssa.OpNeg32F,
- opAndType{ONEG, TFLOAT64}: ssa.OpNeg64F,
-
- opAndType{OBITNOT, TINT8}: ssa.OpCom8,
- opAndType{OBITNOT, TUINT8}: ssa.OpCom8,
- opAndType{OBITNOT, TINT16}: ssa.OpCom16,
- opAndType{OBITNOT, TUINT16}: ssa.OpCom16,
- opAndType{OBITNOT, TINT32}: ssa.OpCom32,
- opAndType{OBITNOT, TUINT32}: ssa.OpCom32,
- opAndType{OBITNOT, TINT64}: ssa.OpCom64,
- opAndType{OBITNOT, TUINT64}: ssa.OpCom64,
-
- opAndType{OIMAG, TCOMPLEX64}: ssa.OpComplexImag,
- opAndType{OIMAG, TCOMPLEX128}: ssa.OpComplexImag,
- opAndType{OREAL, TCOMPLEX64}: ssa.OpComplexReal,
- opAndType{OREAL, TCOMPLEX128}: ssa.OpComplexReal,
-
- opAndType{OMUL, TINT8}: ssa.OpMul8,
- opAndType{OMUL, TUINT8}: ssa.OpMul8,
- opAndType{OMUL, TINT16}: ssa.OpMul16,
- opAndType{OMUL, TUINT16}: ssa.OpMul16,
- opAndType{OMUL, TINT32}: ssa.OpMul32,
- opAndType{OMUL, TUINT32}: ssa.OpMul32,
- opAndType{OMUL, TINT64}: ssa.OpMul64,
- opAndType{OMUL, TUINT64}: ssa.OpMul64,
- opAndType{OMUL, TFLOAT32}: ssa.OpMul32F,
- opAndType{OMUL, TFLOAT64}: ssa.OpMul64F,
-
- opAndType{ODIV, TFLOAT32}: ssa.OpDiv32F,
- opAndType{ODIV, TFLOAT64}: ssa.OpDiv64F,
-
- opAndType{ODIV, TINT8}: ssa.OpDiv8,
- opAndType{ODIV, TUINT8}: ssa.OpDiv8u,
- opAndType{ODIV, TINT16}: ssa.OpDiv16,
- opAndType{ODIV, TUINT16}: ssa.OpDiv16u,
- opAndType{ODIV, TINT32}: ssa.OpDiv32,
- opAndType{ODIV, TUINT32}: ssa.OpDiv32u,
- opAndType{ODIV, TINT64}: ssa.OpDiv64,
- opAndType{ODIV, TUINT64}: ssa.OpDiv64u,
-
- opAndType{OMOD, TINT8}: ssa.OpMod8,
- opAndType{OMOD, TUINT8}: ssa.OpMod8u,
- opAndType{OMOD, TINT16}: ssa.OpMod16,
- opAndType{OMOD, TUINT16}: ssa.OpMod16u,
- opAndType{OMOD, TINT32}: ssa.OpMod32,
- opAndType{OMOD, TUINT32}: ssa.OpMod32u,
- opAndType{OMOD, TINT64}: ssa.OpMod64,
- opAndType{OMOD, TUINT64}: ssa.OpMod64u,
-
- opAndType{OAND, TINT8}: ssa.OpAnd8,
- opAndType{OAND, TUINT8}: ssa.OpAnd8,
- opAndType{OAND, TINT16}: ssa.OpAnd16,
- opAndType{OAND, TUINT16}: ssa.OpAnd16,
- opAndType{OAND, TINT32}: ssa.OpAnd32,
- opAndType{OAND, TUINT32}: ssa.OpAnd32,
- opAndType{OAND, TINT64}: ssa.OpAnd64,
- opAndType{OAND, TUINT64}: ssa.OpAnd64,
-
- opAndType{OOR, TINT8}: ssa.OpOr8,
- opAndType{OOR, TUINT8}: ssa.OpOr8,
- opAndType{OOR, TINT16}: ssa.OpOr16,
- opAndType{OOR, TUINT16}: ssa.OpOr16,
- opAndType{OOR, TINT32}: ssa.OpOr32,
- opAndType{OOR, TUINT32}: ssa.OpOr32,
- opAndType{OOR, TINT64}: ssa.OpOr64,
- opAndType{OOR, TUINT64}: ssa.OpOr64,
-
- opAndType{OXOR, TINT8}: ssa.OpXor8,
- opAndType{OXOR, TUINT8}: ssa.OpXor8,
- opAndType{OXOR, TINT16}: ssa.OpXor16,
- opAndType{OXOR, TUINT16}: ssa.OpXor16,
- opAndType{OXOR, TINT32}: ssa.OpXor32,
- opAndType{OXOR, TUINT32}: ssa.OpXor32,
- opAndType{OXOR, TINT64}: ssa.OpXor64,
- opAndType{OXOR, TUINT64}: ssa.OpXor64,
-
- opAndType{OEQ, TBOOL}: ssa.OpEqB,
- opAndType{OEQ, TINT8}: ssa.OpEq8,
- opAndType{OEQ, TUINT8}: ssa.OpEq8,
- opAndType{OEQ, TINT16}: ssa.OpEq16,
- opAndType{OEQ, TUINT16}: ssa.OpEq16,
- opAndType{OEQ, TINT32}: ssa.OpEq32,
- opAndType{OEQ, TUINT32}: ssa.OpEq32,
- opAndType{OEQ, TINT64}: ssa.OpEq64,
- opAndType{OEQ, TUINT64}: ssa.OpEq64,
- opAndType{OEQ, TINTER}: ssa.OpEqInter,
- opAndType{OEQ, TSLICE}: ssa.OpEqSlice,
- opAndType{OEQ, TFUNC}: ssa.OpEqPtr,
- opAndType{OEQ, TMAP}: ssa.OpEqPtr,
- opAndType{OEQ, TCHAN}: ssa.OpEqPtr,
- opAndType{OEQ, TPTR}: ssa.OpEqPtr,
- opAndType{OEQ, TUINTPTR}: ssa.OpEqPtr,
- opAndType{OEQ, TUNSAFEPTR}: ssa.OpEqPtr,
- opAndType{OEQ, TFLOAT64}: ssa.OpEq64F,
- opAndType{OEQ, TFLOAT32}: ssa.OpEq32F,
-
- opAndType{ONE, TBOOL}: ssa.OpNeqB,
- opAndType{ONE, TINT8}: ssa.OpNeq8,
- opAndType{ONE, TUINT8}: ssa.OpNeq8,
- opAndType{ONE, TINT16}: ssa.OpNeq16,
- opAndType{ONE, TUINT16}: ssa.OpNeq16,
- opAndType{ONE, TINT32}: ssa.OpNeq32,
- opAndType{ONE, TUINT32}: ssa.OpNeq32,
- opAndType{ONE, TINT64}: ssa.OpNeq64,
- opAndType{ONE, TUINT64}: ssa.OpNeq64,
- opAndType{ONE, TINTER}: ssa.OpNeqInter,
- opAndType{ONE, TSLICE}: ssa.OpNeqSlice,
- opAndType{ONE, TFUNC}: ssa.OpNeqPtr,
- opAndType{ONE, TMAP}: ssa.OpNeqPtr,
- opAndType{ONE, TCHAN}: ssa.OpNeqPtr,
- opAndType{ONE, TPTR}: ssa.OpNeqPtr,
- opAndType{ONE, TUINTPTR}: ssa.OpNeqPtr,
- opAndType{ONE, TUNSAFEPTR}: ssa.OpNeqPtr,
- opAndType{ONE, TFLOAT64}: ssa.OpNeq64F,
- opAndType{ONE, TFLOAT32}: ssa.OpNeq32F,
-
- opAndType{OLT, TINT8}: ssa.OpLess8,
- opAndType{OLT, TUINT8}: ssa.OpLess8U,
- opAndType{OLT, TINT16}: ssa.OpLess16,
- opAndType{OLT, TUINT16}: ssa.OpLess16U,
- opAndType{OLT, TINT32}: ssa.OpLess32,
- opAndType{OLT, TUINT32}: ssa.OpLess32U,
- opAndType{OLT, TINT64}: ssa.OpLess64,
- opAndType{OLT, TUINT64}: ssa.OpLess64U,
- opAndType{OLT, TFLOAT64}: ssa.OpLess64F,
- opAndType{OLT, TFLOAT32}: ssa.OpLess32F,
-
- opAndType{OLE, TINT8}: ssa.OpLeq8,
- opAndType{OLE, TUINT8}: ssa.OpLeq8U,
- opAndType{OLE, TINT16}: ssa.OpLeq16,
- opAndType{OLE, TUINT16}: ssa.OpLeq16U,
- opAndType{OLE, TINT32}: ssa.OpLeq32,
- opAndType{OLE, TUINT32}: ssa.OpLeq32U,
- opAndType{OLE, TINT64}: ssa.OpLeq64,
- opAndType{OLE, TUINT64}: ssa.OpLeq64U,
- opAndType{OLE, TFLOAT64}: ssa.OpLeq64F,
- opAndType{OLE, TFLOAT32}: ssa.OpLeq32F,
-}
-
-func (s *state) concreteEtype(t *types.Type) types.EType {
- e := t.Etype
+ opAndType{ir.OADD, types.TINT8}: ssa.OpAdd8,
+ opAndType{ir.OADD, types.TUINT8}: ssa.OpAdd8,
+ opAndType{ir.OADD, types.TINT16}: ssa.OpAdd16,
+ opAndType{ir.OADD, types.TUINT16}: ssa.OpAdd16,
+ opAndType{ir.OADD, types.TINT32}: ssa.OpAdd32,
+ opAndType{ir.OADD, types.TUINT32}: ssa.OpAdd32,
+ opAndType{ir.OADD, types.TINT64}: ssa.OpAdd64,
+ opAndType{ir.OADD, types.TUINT64}: ssa.OpAdd64,
+ opAndType{ir.OADD, types.TFLOAT32}: ssa.OpAdd32F,
+ opAndType{ir.OADD, types.TFLOAT64}: ssa.OpAdd64F,
+
+ opAndType{ir.OSUB, types.TINT8}: ssa.OpSub8,
+ opAndType{ir.OSUB, types.TUINT8}: ssa.OpSub8,
+ opAndType{ir.OSUB, types.TINT16}: ssa.OpSub16,
+ opAndType{ir.OSUB, types.TUINT16}: ssa.OpSub16,
+ opAndType{ir.OSUB, types.TINT32}: ssa.OpSub32,
+ opAndType{ir.OSUB, types.TUINT32}: ssa.OpSub32,
+ opAndType{ir.OSUB, types.TINT64}: ssa.OpSub64,
+ opAndType{ir.OSUB, types.TUINT64}: ssa.OpSub64,
+ opAndType{ir.OSUB, types.TFLOAT32}: ssa.OpSub32F,
+ opAndType{ir.OSUB, types.TFLOAT64}: ssa.OpSub64F,
+
+ opAndType{ir.ONOT, types.TBOOL}: ssa.OpNot,
+
+ opAndType{ir.ONEG, types.TINT8}: ssa.OpNeg8,
+ opAndType{ir.ONEG, types.TUINT8}: ssa.OpNeg8,
+ opAndType{ir.ONEG, types.TINT16}: ssa.OpNeg16,
+ opAndType{ir.ONEG, types.TUINT16}: ssa.OpNeg16,
+ opAndType{ir.ONEG, types.TINT32}: ssa.OpNeg32,
+ opAndType{ir.ONEG, types.TUINT32}: ssa.OpNeg32,
+ opAndType{ir.ONEG, types.TINT64}: ssa.OpNeg64,
+ opAndType{ir.ONEG, types.TUINT64}: ssa.OpNeg64,
+ opAndType{ir.ONEG, types.TFLOAT32}: ssa.OpNeg32F,
+ opAndType{ir.ONEG, types.TFLOAT64}: ssa.OpNeg64F,
+
+ opAndType{ir.OBITNOT, types.TINT8}: ssa.OpCom8,
+ opAndType{ir.OBITNOT, types.TUINT8}: ssa.OpCom8,
+ opAndType{ir.OBITNOT, types.TINT16}: ssa.OpCom16,
+ opAndType{ir.OBITNOT, types.TUINT16}: ssa.OpCom16,
+ opAndType{ir.OBITNOT, types.TINT32}: ssa.OpCom32,
+ opAndType{ir.OBITNOT, types.TUINT32}: ssa.OpCom32,
+ opAndType{ir.OBITNOT, types.TINT64}: ssa.OpCom64,
+ opAndType{ir.OBITNOT, types.TUINT64}: ssa.OpCom64,
+
+ opAndType{ir.OIMAG, types.TCOMPLEX64}: ssa.OpComplexImag,
+ opAndType{ir.OIMAG, types.TCOMPLEX128}: ssa.OpComplexImag,
+ opAndType{ir.OREAL, types.TCOMPLEX64}: ssa.OpComplexReal,
+ opAndType{ir.OREAL, types.TCOMPLEX128}: ssa.OpComplexReal,
+
+ opAndType{ir.OMUL, types.TINT8}: ssa.OpMul8,
+ opAndType{ir.OMUL, types.TUINT8}: ssa.OpMul8,
+ opAndType{ir.OMUL, types.TINT16}: ssa.OpMul16,
+ opAndType{ir.OMUL, types.TUINT16}: ssa.OpMul16,
+ opAndType{ir.OMUL, types.TINT32}: ssa.OpMul32,
+ opAndType{ir.OMUL, types.TUINT32}: ssa.OpMul32,
+ opAndType{ir.OMUL, types.TINT64}: ssa.OpMul64,
+ opAndType{ir.OMUL, types.TUINT64}: ssa.OpMul64,
+ opAndType{ir.OMUL, types.TFLOAT32}: ssa.OpMul32F,
+ opAndType{ir.OMUL, types.TFLOAT64}: ssa.OpMul64F,
+
+ opAndType{ir.ODIV, types.TFLOAT32}: ssa.OpDiv32F,
+ opAndType{ir.ODIV, types.TFLOAT64}: ssa.OpDiv64F,
+
+ opAndType{ir.ODIV, types.TINT8}: ssa.OpDiv8,
+ opAndType{ir.ODIV, types.TUINT8}: ssa.OpDiv8u,
+ opAndType{ir.ODIV, types.TINT16}: ssa.OpDiv16,
+ opAndType{ir.ODIV, types.TUINT16}: ssa.OpDiv16u,
+ opAndType{ir.ODIV, types.TINT32}: ssa.OpDiv32,
+ opAndType{ir.ODIV, types.TUINT32}: ssa.OpDiv32u,
+ opAndType{ir.ODIV, types.TINT64}: ssa.OpDiv64,
+ opAndType{ir.ODIV, types.TUINT64}: ssa.OpDiv64u,
+
+ opAndType{ir.OMOD, types.TINT8}: ssa.OpMod8,
+ opAndType{ir.OMOD, types.TUINT8}: ssa.OpMod8u,
+ opAndType{ir.OMOD, types.TINT16}: ssa.OpMod16,
+ opAndType{ir.OMOD, types.TUINT16}: ssa.OpMod16u,
+ opAndType{ir.OMOD, types.TINT32}: ssa.OpMod32,
+ opAndType{ir.OMOD, types.TUINT32}: ssa.OpMod32u,
+ opAndType{ir.OMOD, types.TINT64}: ssa.OpMod64,
+ opAndType{ir.OMOD, types.TUINT64}: ssa.OpMod64u,
+
+ opAndType{ir.OAND, types.TINT8}: ssa.OpAnd8,
+ opAndType{ir.OAND, types.TUINT8}: ssa.OpAnd8,
+ opAndType{ir.OAND, types.TINT16}: ssa.OpAnd16,
+ opAndType{ir.OAND, types.TUINT16}: ssa.OpAnd16,
+ opAndType{ir.OAND, types.TINT32}: ssa.OpAnd32,
+ opAndType{ir.OAND, types.TUINT32}: ssa.OpAnd32,
+ opAndType{ir.OAND, types.TINT64}: ssa.OpAnd64,
+ opAndType{ir.OAND, types.TUINT64}: ssa.OpAnd64,
+
+ opAndType{ir.OOR, types.TINT8}: ssa.OpOr8,
+ opAndType{ir.OOR, types.TUINT8}: ssa.OpOr8,
+ opAndType{ir.OOR, types.TINT16}: ssa.OpOr16,
+ opAndType{ir.OOR, types.TUINT16}: ssa.OpOr16,
+ opAndType{ir.OOR, types.TINT32}: ssa.OpOr32,
+ opAndType{ir.OOR, types.TUINT32}: ssa.OpOr32,
+ opAndType{ir.OOR, types.TINT64}: ssa.OpOr64,
+ opAndType{ir.OOR, types.TUINT64}: ssa.OpOr64,
+
+ opAndType{ir.OXOR, types.TINT8}: ssa.OpXor8,
+ opAndType{ir.OXOR, types.TUINT8}: ssa.OpXor8,
+ opAndType{ir.OXOR, types.TINT16}: ssa.OpXor16,
+ opAndType{ir.OXOR, types.TUINT16}: ssa.OpXor16,
+ opAndType{ir.OXOR, types.TINT32}: ssa.OpXor32,
+ opAndType{ir.OXOR, types.TUINT32}: ssa.OpXor32,
+ opAndType{ir.OXOR, types.TINT64}: ssa.OpXor64,
+ opAndType{ir.OXOR, types.TUINT64}: ssa.OpXor64,
+
+ opAndType{ir.OEQ, types.TBOOL}: ssa.OpEqB,
+ opAndType{ir.OEQ, types.TINT8}: ssa.OpEq8,
+ opAndType{ir.OEQ, types.TUINT8}: ssa.OpEq8,
+ opAndType{ir.OEQ, types.TINT16}: ssa.OpEq16,
+ opAndType{ir.OEQ, types.TUINT16}: ssa.OpEq16,
+ opAndType{ir.OEQ, types.TINT32}: ssa.OpEq32,
+ opAndType{ir.OEQ, types.TUINT32}: ssa.OpEq32,
+ opAndType{ir.OEQ, types.TINT64}: ssa.OpEq64,
+ opAndType{ir.OEQ, types.TUINT64}: ssa.OpEq64,
+ opAndType{ir.OEQ, types.TINTER}: ssa.OpEqInter,
+ opAndType{ir.OEQ, types.TSLICE}: ssa.OpEqSlice,
+ opAndType{ir.OEQ, types.TFUNC}: ssa.OpEqPtr,
+ opAndType{ir.OEQ, types.TMAP}: ssa.OpEqPtr,
+ opAndType{ir.OEQ, types.TCHAN}: ssa.OpEqPtr,
+ opAndType{ir.OEQ, types.TPTR}: ssa.OpEqPtr,
+ opAndType{ir.OEQ, types.TUINTPTR}: ssa.OpEqPtr,
+ opAndType{ir.OEQ, types.TUNSAFEPTR}: ssa.OpEqPtr,
+ opAndType{ir.OEQ, types.TFLOAT64}: ssa.OpEq64F,
+ opAndType{ir.OEQ, types.TFLOAT32}: ssa.OpEq32F,
+
+ opAndType{ir.ONE, types.TBOOL}: ssa.OpNeqB,
+ opAndType{ir.ONE, types.TINT8}: ssa.OpNeq8,
+ opAndType{ir.ONE, types.TUINT8}: ssa.OpNeq8,
+ opAndType{ir.ONE, types.TINT16}: ssa.OpNeq16,
+ opAndType{ir.ONE, types.TUINT16}: ssa.OpNeq16,
+ opAndType{ir.ONE, types.TINT32}: ssa.OpNeq32,
+ opAndType{ir.ONE, types.TUINT32}: ssa.OpNeq32,
+ opAndType{ir.ONE, types.TINT64}: ssa.OpNeq64,
+ opAndType{ir.ONE, types.TUINT64}: ssa.OpNeq64,
+ opAndType{ir.ONE, types.TINTER}: ssa.OpNeqInter,
+ opAndType{ir.ONE, types.TSLICE}: ssa.OpNeqSlice,
+ opAndType{ir.ONE, types.TFUNC}: ssa.OpNeqPtr,
+ opAndType{ir.ONE, types.TMAP}: ssa.OpNeqPtr,
+ opAndType{ir.ONE, types.TCHAN}: ssa.OpNeqPtr,
+ opAndType{ir.ONE, types.TPTR}: ssa.OpNeqPtr,
+ opAndType{ir.ONE, types.TUINTPTR}: ssa.OpNeqPtr,
+ opAndType{ir.ONE, types.TUNSAFEPTR}: ssa.OpNeqPtr,
+ opAndType{ir.ONE, types.TFLOAT64}: ssa.OpNeq64F,
+ opAndType{ir.ONE, types.TFLOAT32}: ssa.OpNeq32F,
+
+ opAndType{ir.OLT, types.TINT8}: ssa.OpLess8,
+ opAndType{ir.OLT, types.TUINT8}: ssa.OpLess8U,
+ opAndType{ir.OLT, types.TINT16}: ssa.OpLess16,
+ opAndType{ir.OLT, types.TUINT16}: ssa.OpLess16U,
+ opAndType{ir.OLT, types.TINT32}: ssa.OpLess32,
+ opAndType{ir.OLT, types.TUINT32}: ssa.OpLess32U,
+ opAndType{ir.OLT, types.TINT64}: ssa.OpLess64,
+ opAndType{ir.OLT, types.TUINT64}: ssa.OpLess64U,
+ opAndType{ir.OLT, types.TFLOAT64}: ssa.OpLess64F,
+ opAndType{ir.OLT, types.TFLOAT32}: ssa.OpLess32F,
+
+ opAndType{ir.OLE, types.TINT8}: ssa.OpLeq8,
+ opAndType{ir.OLE, types.TUINT8}: ssa.OpLeq8U,
+ opAndType{ir.OLE, types.TINT16}: ssa.OpLeq16,
+ opAndType{ir.OLE, types.TUINT16}: ssa.OpLeq16U,
+ opAndType{ir.OLE, types.TINT32}: ssa.OpLeq32,
+ opAndType{ir.OLE, types.TUINT32}: ssa.OpLeq32U,
+ opAndType{ir.OLE, types.TINT64}: ssa.OpLeq64,
+ opAndType{ir.OLE, types.TUINT64}: ssa.OpLeq64U,
+ opAndType{ir.OLE, types.TFLOAT64}: ssa.OpLeq64F,
+ opAndType{ir.OLE, types.TFLOAT32}: ssa.OpLeq32F,
+}
+
+func (s *state) concreteEtype(t *types.Type) types.Kind {
+ e := t.Kind()
switch e {
default:
return e
- case TINT:
+ case types.TINT:
if s.config.PtrSize == 8 {
- return TINT64
+ return types.TINT64
}
- return TINT32
- case TUINT:
+ return types.TINT32
+ case types.TUINT:
if s.config.PtrSize == 8 {
- return TUINT64
+ return types.TUINT64
}
- return TUINT32
- case TUINTPTR:
+ return types.TUINT32
+ case types.TUINTPTR:
if s.config.PtrSize == 8 {
- return TUINT64
+ return types.TUINT64
}
- return TUINT32
+ return types.TUINT32
}
}
-func (s *state) ssaOp(op Op, t *types.Type) ssa.Op {
+func (s *state) ssaOp(op ir.Op, t *types.Type) ssa.Op {
etype := s.concreteEtype(t)
x, ok := opToSSA[opAndType{op, etype}]
if !ok {
@@ -1861,186 +2092,164 @@ func (s *state) ssaOp(op Op, t *types.Type) ssa.Op {
return x
}
-func floatForComplex(t *types.Type) *types.Type {
- switch t.Etype {
- case TCOMPLEX64:
- return types.Types[TFLOAT32]
- case TCOMPLEX128:
- return types.Types[TFLOAT64]
- }
- Fatalf("unexpected type: %v", t)
- return nil
-}
-
-func complexForFloat(t *types.Type) *types.Type {
- switch t.Etype {
- case TFLOAT32:
- return types.Types[TCOMPLEX64]
- case TFLOAT64:
- return types.Types[TCOMPLEX128]
- }
- Fatalf("unexpected type: %v", t)
- return nil
-}
-
type opAndTwoTypes struct {
- op Op
- etype1 types.EType
- etype2 types.EType
+ op ir.Op
+ etype1 types.Kind
+ etype2 types.Kind
}
type twoTypes struct {
- etype1 types.EType
- etype2 types.EType
+ etype1 types.Kind
+ etype2 types.Kind
}
type twoOpsAndType struct {
op1 ssa.Op
op2 ssa.Op
- intermediateType types.EType
+ intermediateType types.Kind
}
var fpConvOpToSSA = map[twoTypes]twoOpsAndType{
- twoTypes{TINT8, TFLOAT32}: twoOpsAndType{ssa.OpSignExt8to32, ssa.OpCvt32to32F, TINT32},
- twoTypes{TINT16, TFLOAT32}: twoOpsAndType{ssa.OpSignExt16to32, ssa.OpCvt32to32F, TINT32},
- twoTypes{TINT32, TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32to32F, TINT32},
- twoTypes{TINT64, TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64to32F, TINT64},
-
- twoTypes{TINT8, TFLOAT64}: twoOpsAndType{ssa.OpSignExt8to32, ssa.OpCvt32to64F, TINT32},
- twoTypes{TINT16, TFLOAT64}: twoOpsAndType{ssa.OpSignExt16to32, ssa.OpCvt32to64F, TINT32},
- twoTypes{TINT32, TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32to64F, TINT32},
- twoTypes{TINT64, TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64to64F, TINT64},
-
- twoTypes{TFLOAT32, TINT8}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to8, TINT32},
- twoTypes{TFLOAT32, TINT16}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to16, TINT32},
- twoTypes{TFLOAT32, TINT32}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpCopy, TINT32},
- twoTypes{TFLOAT32, TINT64}: twoOpsAndType{ssa.OpCvt32Fto64, ssa.OpCopy, TINT64},
-
- twoTypes{TFLOAT64, TINT8}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to8, TINT32},
- twoTypes{TFLOAT64, TINT16}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to16, TINT32},
- twoTypes{TFLOAT64, TINT32}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpCopy, TINT32},
- twoTypes{TFLOAT64, TINT64}: twoOpsAndType{ssa.OpCvt64Fto64, ssa.OpCopy, TINT64},
+ twoTypes{types.TINT8, types.TFLOAT32}: twoOpsAndType{ssa.OpSignExt8to32, ssa.OpCvt32to32F, types.TINT32},
+ twoTypes{types.TINT16, types.TFLOAT32}: twoOpsAndType{ssa.OpSignExt16to32, ssa.OpCvt32to32F, types.TINT32},
+ twoTypes{types.TINT32, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32to32F, types.TINT32},
+ twoTypes{types.TINT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64to32F, types.TINT64},
+
+ twoTypes{types.TINT8, types.TFLOAT64}: twoOpsAndType{ssa.OpSignExt8to32, ssa.OpCvt32to64F, types.TINT32},
+ twoTypes{types.TINT16, types.TFLOAT64}: twoOpsAndType{ssa.OpSignExt16to32, ssa.OpCvt32to64F, types.TINT32},
+ twoTypes{types.TINT32, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32to64F, types.TINT32},
+ twoTypes{types.TINT64, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64to64F, types.TINT64},
+
+ twoTypes{types.TFLOAT32, types.TINT8}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to8, types.TINT32},
+ twoTypes{types.TFLOAT32, types.TINT16}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to16, types.TINT32},
+ twoTypes{types.TFLOAT32, types.TINT32}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpCopy, types.TINT32},
+ twoTypes{types.TFLOAT32, types.TINT64}: twoOpsAndType{ssa.OpCvt32Fto64, ssa.OpCopy, types.TINT64},
+
+ twoTypes{types.TFLOAT64, types.TINT8}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to8, types.TINT32},
+ twoTypes{types.TFLOAT64, types.TINT16}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to16, types.TINT32},
+ twoTypes{types.TFLOAT64, types.TINT32}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpCopy, types.TINT32},
+ twoTypes{types.TFLOAT64, types.TINT64}: twoOpsAndType{ssa.OpCvt64Fto64, ssa.OpCopy, types.TINT64},
// unsigned
- twoTypes{TUINT8, TFLOAT32}: twoOpsAndType{ssa.OpZeroExt8to32, ssa.OpCvt32to32F, TINT32},
- twoTypes{TUINT16, TFLOAT32}: twoOpsAndType{ssa.OpZeroExt16to32, ssa.OpCvt32to32F, TINT32},
- twoTypes{TUINT32, TFLOAT32}: twoOpsAndType{ssa.OpZeroExt32to64, ssa.OpCvt64to32F, TINT64}, // go wide to dodge unsigned
- twoTypes{TUINT64, TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpInvalid, TUINT64}, // Cvt64Uto32F, branchy code expansion instead
-
- twoTypes{TUINT8, TFLOAT64}: twoOpsAndType{ssa.OpZeroExt8to32, ssa.OpCvt32to64F, TINT32},
- twoTypes{TUINT16, TFLOAT64}: twoOpsAndType{ssa.OpZeroExt16to32, ssa.OpCvt32to64F, TINT32},
- twoTypes{TUINT32, TFLOAT64}: twoOpsAndType{ssa.OpZeroExt32to64, ssa.OpCvt64to64F, TINT64}, // go wide to dodge unsigned
- twoTypes{TUINT64, TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpInvalid, TUINT64}, // Cvt64Uto64F, branchy code expansion instead
-
- twoTypes{TFLOAT32, TUINT8}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to8, TINT32},
- twoTypes{TFLOAT32, TUINT16}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to16, TINT32},
- twoTypes{TFLOAT32, TUINT32}: twoOpsAndType{ssa.OpCvt32Fto64, ssa.OpTrunc64to32, TINT64}, // go wide to dodge unsigned
- twoTypes{TFLOAT32, TUINT64}: twoOpsAndType{ssa.OpInvalid, ssa.OpCopy, TUINT64}, // Cvt32Fto64U, branchy code expansion instead
-
- twoTypes{TFLOAT64, TUINT8}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to8, TINT32},
- twoTypes{TFLOAT64, TUINT16}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to16, TINT32},
- twoTypes{TFLOAT64, TUINT32}: twoOpsAndType{ssa.OpCvt64Fto64, ssa.OpTrunc64to32, TINT64}, // go wide to dodge unsigned
- twoTypes{TFLOAT64, TUINT64}: twoOpsAndType{ssa.OpInvalid, ssa.OpCopy, TUINT64}, // Cvt64Fto64U, branchy code expansion instead
+ twoTypes{types.TUINT8, types.TFLOAT32}: twoOpsAndType{ssa.OpZeroExt8to32, ssa.OpCvt32to32F, types.TINT32},
+ twoTypes{types.TUINT16, types.TFLOAT32}: twoOpsAndType{ssa.OpZeroExt16to32, ssa.OpCvt32to32F, types.TINT32},
+ twoTypes{types.TUINT32, types.TFLOAT32}: twoOpsAndType{ssa.OpZeroExt32to64, ssa.OpCvt64to32F, types.TINT64}, // go wide to dodge unsigned
+ twoTypes{types.TUINT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpInvalid, types.TUINT64}, // Cvt64Uto32F, branchy code expansion instead
+
+ twoTypes{types.TUINT8, types.TFLOAT64}: twoOpsAndType{ssa.OpZeroExt8to32, ssa.OpCvt32to64F, types.TINT32},
+ twoTypes{types.TUINT16, types.TFLOAT64}: twoOpsAndType{ssa.OpZeroExt16to32, ssa.OpCvt32to64F, types.TINT32},
+ twoTypes{types.TUINT32, types.TFLOAT64}: twoOpsAndType{ssa.OpZeroExt32to64, ssa.OpCvt64to64F, types.TINT64}, // go wide to dodge unsigned
+ twoTypes{types.TUINT64, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpInvalid, types.TUINT64}, // Cvt64Uto64F, branchy code expansion instead
+
+ twoTypes{types.TFLOAT32, types.TUINT8}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to8, types.TINT32},
+ twoTypes{types.TFLOAT32, types.TUINT16}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to16, types.TINT32},
+ twoTypes{types.TFLOAT32, types.TUINT32}: twoOpsAndType{ssa.OpCvt32Fto64, ssa.OpTrunc64to32, types.TINT64}, // go wide to dodge unsigned
+ twoTypes{types.TFLOAT32, types.TUINT64}: twoOpsAndType{ssa.OpInvalid, ssa.OpCopy, types.TUINT64}, // Cvt32Fto64U, branchy code expansion instead
+
+ twoTypes{types.TFLOAT64, types.TUINT8}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to8, types.TINT32},
+ twoTypes{types.TFLOAT64, types.TUINT16}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to16, types.TINT32},
+ twoTypes{types.TFLOAT64, types.TUINT32}: twoOpsAndType{ssa.OpCvt64Fto64, ssa.OpTrunc64to32, types.TINT64}, // go wide to dodge unsigned
+ twoTypes{types.TFLOAT64, types.TUINT64}: twoOpsAndType{ssa.OpInvalid, ssa.OpCopy, types.TUINT64}, // Cvt64Fto64U, branchy code expansion instead
// float
- twoTypes{TFLOAT64, TFLOAT32}: twoOpsAndType{ssa.OpCvt64Fto32F, ssa.OpCopy, TFLOAT32},
- twoTypes{TFLOAT64, TFLOAT64}: twoOpsAndType{ssa.OpRound64F, ssa.OpCopy, TFLOAT64},
- twoTypes{TFLOAT32, TFLOAT32}: twoOpsAndType{ssa.OpRound32F, ssa.OpCopy, TFLOAT32},
- twoTypes{TFLOAT32, TFLOAT64}: twoOpsAndType{ssa.OpCvt32Fto64F, ssa.OpCopy, TFLOAT64},
+ twoTypes{types.TFLOAT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCvt64Fto32F, ssa.OpCopy, types.TFLOAT32},
+ twoTypes{types.TFLOAT64, types.TFLOAT64}: twoOpsAndType{ssa.OpRound64F, ssa.OpCopy, types.TFLOAT64},
+ twoTypes{types.TFLOAT32, types.TFLOAT32}: twoOpsAndType{ssa.OpRound32F, ssa.OpCopy, types.TFLOAT32},
+ twoTypes{types.TFLOAT32, types.TFLOAT64}: twoOpsAndType{ssa.OpCvt32Fto64F, ssa.OpCopy, types.TFLOAT64},
}
// this map is used only for 32-bit arch, and only includes the difference
// on 32-bit arch, don't use int64<->float conversion for uint32
var fpConvOpToSSA32 = map[twoTypes]twoOpsAndType{
- twoTypes{TUINT32, TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto32F, TUINT32},
- twoTypes{TUINT32, TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto64F, TUINT32},
- twoTypes{TFLOAT32, TUINT32}: twoOpsAndType{ssa.OpCvt32Fto32U, ssa.OpCopy, TUINT32},
- twoTypes{TFLOAT64, TUINT32}: twoOpsAndType{ssa.OpCvt64Fto32U, ssa.OpCopy, TUINT32},
+ twoTypes{types.TUINT32, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto32F, types.TUINT32},
+ twoTypes{types.TUINT32, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto64F, types.TUINT32},
+ twoTypes{types.TFLOAT32, types.TUINT32}: twoOpsAndType{ssa.OpCvt32Fto32U, ssa.OpCopy, types.TUINT32},
+ twoTypes{types.TFLOAT64, types.TUINT32}: twoOpsAndType{ssa.OpCvt64Fto32U, ssa.OpCopy, types.TUINT32},
}
// uint64<->float conversions, only on machines that have instructions for that
var uint64fpConvOpToSSA = map[twoTypes]twoOpsAndType{
- twoTypes{TUINT64, TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto32F, TUINT64},
- twoTypes{TUINT64, TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto64F, TUINT64},
- twoTypes{TFLOAT32, TUINT64}: twoOpsAndType{ssa.OpCvt32Fto64U, ssa.OpCopy, TUINT64},
- twoTypes{TFLOAT64, TUINT64}: twoOpsAndType{ssa.OpCvt64Fto64U, ssa.OpCopy, TUINT64},
+ twoTypes{types.TUINT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto32F, types.TUINT64},
+ twoTypes{types.TUINT64, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto64F, types.TUINT64},
+ twoTypes{types.TFLOAT32, types.TUINT64}: twoOpsAndType{ssa.OpCvt32Fto64U, ssa.OpCopy, types.TUINT64},
+ twoTypes{types.TFLOAT64, types.TUINT64}: twoOpsAndType{ssa.OpCvt64Fto64U, ssa.OpCopy, types.TUINT64},
}
var shiftOpToSSA = map[opAndTwoTypes]ssa.Op{
- opAndTwoTypes{OLSH, TINT8, TUINT8}: ssa.OpLsh8x8,
- opAndTwoTypes{OLSH, TUINT8, TUINT8}: ssa.OpLsh8x8,
- opAndTwoTypes{OLSH, TINT8, TUINT16}: ssa.OpLsh8x16,
- opAndTwoTypes{OLSH, TUINT8, TUINT16}: ssa.OpLsh8x16,
- opAndTwoTypes{OLSH, TINT8, TUINT32}: ssa.OpLsh8x32,
- opAndTwoTypes{OLSH, TUINT8, TUINT32}: ssa.OpLsh8x32,
- opAndTwoTypes{OLSH, TINT8, TUINT64}: ssa.OpLsh8x64,
- opAndTwoTypes{OLSH, TUINT8, TUINT64}: ssa.OpLsh8x64,
-
- opAndTwoTypes{OLSH, TINT16, TUINT8}: ssa.OpLsh16x8,
- opAndTwoTypes{OLSH, TUINT16, TUINT8}: ssa.OpLsh16x8,
- opAndTwoTypes{OLSH, TINT16, TUINT16}: ssa.OpLsh16x16,
- opAndTwoTypes{OLSH, TUINT16, TUINT16}: ssa.OpLsh16x16,
- opAndTwoTypes{OLSH, TINT16, TUINT32}: ssa.OpLsh16x32,
- opAndTwoTypes{OLSH, TUINT16, TUINT32}: ssa.OpLsh16x32,
- opAndTwoTypes{OLSH, TINT16, TUINT64}: ssa.OpLsh16x64,
- opAndTwoTypes{OLSH, TUINT16, TUINT64}: ssa.OpLsh16x64,
-
- opAndTwoTypes{OLSH, TINT32, TUINT8}: ssa.OpLsh32x8,
- opAndTwoTypes{OLSH, TUINT32, TUINT8}: ssa.OpLsh32x8,
- opAndTwoTypes{OLSH, TINT32, TUINT16}: ssa.OpLsh32x16,
- opAndTwoTypes{OLSH, TUINT32, TUINT16}: ssa.OpLsh32x16,
- opAndTwoTypes{OLSH, TINT32, TUINT32}: ssa.OpLsh32x32,
- opAndTwoTypes{OLSH, TUINT32, TUINT32}: ssa.OpLsh32x32,
- opAndTwoTypes{OLSH, TINT32, TUINT64}: ssa.OpLsh32x64,
- opAndTwoTypes{OLSH, TUINT32, TUINT64}: ssa.OpLsh32x64,
-
- opAndTwoTypes{OLSH, TINT64, TUINT8}: ssa.OpLsh64x8,
- opAndTwoTypes{OLSH, TUINT64, TUINT8}: ssa.OpLsh64x8,
- opAndTwoTypes{OLSH, TINT64, TUINT16}: ssa.OpLsh64x16,
- opAndTwoTypes{OLSH, TUINT64, TUINT16}: ssa.OpLsh64x16,
- opAndTwoTypes{OLSH, TINT64, TUINT32}: ssa.OpLsh64x32,
- opAndTwoTypes{OLSH, TUINT64, TUINT32}: ssa.OpLsh64x32,
- opAndTwoTypes{OLSH, TINT64, TUINT64}: ssa.OpLsh64x64,
- opAndTwoTypes{OLSH, TUINT64, TUINT64}: ssa.OpLsh64x64,
-
- opAndTwoTypes{ORSH, TINT8, TUINT8}: ssa.OpRsh8x8,
- opAndTwoTypes{ORSH, TUINT8, TUINT8}: ssa.OpRsh8Ux8,
- opAndTwoTypes{ORSH, TINT8, TUINT16}: ssa.OpRsh8x16,
- opAndTwoTypes{ORSH, TUINT8, TUINT16}: ssa.OpRsh8Ux16,
- opAndTwoTypes{ORSH, TINT8, TUINT32}: ssa.OpRsh8x32,
- opAndTwoTypes{ORSH, TUINT8, TUINT32}: ssa.OpRsh8Ux32,
- opAndTwoTypes{ORSH, TINT8, TUINT64}: ssa.OpRsh8x64,
- opAndTwoTypes{ORSH, TUINT8, TUINT64}: ssa.OpRsh8Ux64,
-
- opAndTwoTypes{ORSH, TINT16, TUINT8}: ssa.OpRsh16x8,
- opAndTwoTypes{ORSH, TUINT16, TUINT8}: ssa.OpRsh16Ux8,
- opAndTwoTypes{ORSH, TINT16, TUINT16}: ssa.OpRsh16x16,
- opAndTwoTypes{ORSH, TUINT16, TUINT16}: ssa.OpRsh16Ux16,
- opAndTwoTypes{ORSH, TINT16, TUINT32}: ssa.OpRsh16x32,
- opAndTwoTypes{ORSH, TUINT16, TUINT32}: ssa.OpRsh16Ux32,
- opAndTwoTypes{ORSH, TINT16, TUINT64}: ssa.OpRsh16x64,
- opAndTwoTypes{ORSH, TUINT16, TUINT64}: ssa.OpRsh16Ux64,
-
- opAndTwoTypes{ORSH, TINT32, TUINT8}: ssa.OpRsh32x8,
- opAndTwoTypes{ORSH, TUINT32, TUINT8}: ssa.OpRsh32Ux8,
- opAndTwoTypes{ORSH, TINT32, TUINT16}: ssa.OpRsh32x16,
- opAndTwoTypes{ORSH, TUINT32, TUINT16}: ssa.OpRsh32Ux16,
- opAndTwoTypes{ORSH, TINT32, TUINT32}: ssa.OpRsh32x32,
- opAndTwoTypes{ORSH, TUINT32, TUINT32}: ssa.OpRsh32Ux32,
- opAndTwoTypes{ORSH, TINT32, TUINT64}: ssa.OpRsh32x64,
- opAndTwoTypes{ORSH, TUINT32, TUINT64}: ssa.OpRsh32Ux64,
-
- opAndTwoTypes{ORSH, TINT64, TUINT8}: ssa.OpRsh64x8,
- opAndTwoTypes{ORSH, TUINT64, TUINT8}: ssa.OpRsh64Ux8,
- opAndTwoTypes{ORSH, TINT64, TUINT16}: ssa.OpRsh64x16,
- opAndTwoTypes{ORSH, TUINT64, TUINT16}: ssa.OpRsh64Ux16,
- opAndTwoTypes{ORSH, TINT64, TUINT32}: ssa.OpRsh64x32,
- opAndTwoTypes{ORSH, TUINT64, TUINT32}: ssa.OpRsh64Ux32,
- opAndTwoTypes{ORSH, TINT64, TUINT64}: ssa.OpRsh64x64,
- opAndTwoTypes{ORSH, TUINT64, TUINT64}: ssa.OpRsh64Ux64,
-}
-
-func (s *state) ssaShiftOp(op Op, t *types.Type, u *types.Type) ssa.Op {
+ opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT8}: ssa.OpLsh8x8,
+ opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT8}: ssa.OpLsh8x8,
+ opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT16}: ssa.OpLsh8x16,
+ opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT16}: ssa.OpLsh8x16,
+ opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT32}: ssa.OpLsh8x32,
+ opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT32}: ssa.OpLsh8x32,
+ opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT64}: ssa.OpLsh8x64,
+ opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT64}: ssa.OpLsh8x64,
+
+ opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT8}: ssa.OpLsh16x8,
+ opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT8}: ssa.OpLsh16x8,
+ opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT16}: ssa.OpLsh16x16,
+ opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT16}: ssa.OpLsh16x16,
+ opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT32}: ssa.OpLsh16x32,
+ opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT32}: ssa.OpLsh16x32,
+ opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT64}: ssa.OpLsh16x64,
+ opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT64}: ssa.OpLsh16x64,
+
+ opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT8}: ssa.OpLsh32x8,
+ opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT8}: ssa.OpLsh32x8,
+ opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT16}: ssa.OpLsh32x16,
+ opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT16}: ssa.OpLsh32x16,
+ opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT32}: ssa.OpLsh32x32,
+ opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT32}: ssa.OpLsh32x32,
+ opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT64}: ssa.OpLsh32x64,
+ opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT64}: ssa.OpLsh32x64,
+
+ opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT8}: ssa.OpLsh64x8,
+ opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT8}: ssa.OpLsh64x8,
+ opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT16}: ssa.OpLsh64x16,
+ opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT16}: ssa.OpLsh64x16,
+ opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT32}: ssa.OpLsh64x32,
+ opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT32}: ssa.OpLsh64x32,
+ opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT64}: ssa.OpLsh64x64,
+ opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT64}: ssa.OpLsh64x64,
+
+ opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT8}: ssa.OpRsh8x8,
+ opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT8}: ssa.OpRsh8Ux8,
+ opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT16}: ssa.OpRsh8x16,
+ opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT16}: ssa.OpRsh8Ux16,
+ opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT32}: ssa.OpRsh8x32,
+ opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT32}: ssa.OpRsh8Ux32,
+ opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT64}: ssa.OpRsh8x64,
+ opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT64}: ssa.OpRsh8Ux64,
+
+ opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT8}: ssa.OpRsh16x8,
+ opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT8}: ssa.OpRsh16Ux8,
+ opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT16}: ssa.OpRsh16x16,
+ opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT16}: ssa.OpRsh16Ux16,
+ opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT32}: ssa.OpRsh16x32,
+ opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT32}: ssa.OpRsh16Ux32,
+ opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT64}: ssa.OpRsh16x64,
+ opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT64}: ssa.OpRsh16Ux64,
+
+ opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT8}: ssa.OpRsh32x8,
+ opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT8}: ssa.OpRsh32Ux8,
+ opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT16}: ssa.OpRsh32x16,
+ opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT16}: ssa.OpRsh32Ux16,
+ opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT32}: ssa.OpRsh32x32,
+ opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT32}: ssa.OpRsh32Ux32,
+ opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT64}: ssa.OpRsh32x64,
+ opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT64}: ssa.OpRsh32Ux64,
+
+ opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT8}: ssa.OpRsh64x8,
+ opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT8}: ssa.OpRsh64Ux8,
+ opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT16}: ssa.OpRsh64x16,
+ opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT16}: ssa.OpRsh64Ux16,
+ opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT32}: ssa.OpRsh64x32,
+ opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT32}: ssa.OpRsh64Ux32,
+ opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT64}: ssa.OpRsh64x64,
+ opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT64}: ssa.OpRsh64Ux64,
+}
+
+func (s *state) ssaShiftOp(op ir.Op, t *types.Type, u *types.Type) ssa.Op {
etype1 := s.concreteEtype(t)
etype2 := s.concreteEtype(u)
x, ok := shiftOpToSSA[opAndTwoTypes{op, etype1, etype2}]
@@ -2051,117 +2260,126 @@ func (s *state) ssaShiftOp(op Op, t *types.Type, u *types.Type) ssa.Op {
}
// expr converts the expression n to ssa, adds it to s and returns the ssa result.
-func (s *state) expr(n *Node) *ssa.Value {
- if !(n.Op == ONAME || n.Op == OLITERAL && n.Sym != nil) {
+func (s *state) expr(n ir.Node) *ssa.Value {
+ if ir.HasUniquePos(n) {
// ONAMEs and named OLITERALs have the line number
// of the decl, not the use. See issue 14742.
- s.pushLine(n.Pos)
+ s.pushLine(n.Pos())
defer s.popLine()
}
- s.stmtList(n.Ninit)
- switch n.Op {
- case OBYTES2STRTMP:
- slice := s.expr(n.Left)
+ s.stmtList(n.Init())
+ switch n.Op() {
+ case ir.OBYTES2STRTMP:
+ n := n.(*ir.ConvExpr)
+ slice := s.expr(n.X)
ptr := s.newValue1(ssa.OpSlicePtr, s.f.Config.Types.BytePtr, slice)
- len := s.newValue1(ssa.OpSliceLen, types.Types[TINT], slice)
- return s.newValue2(ssa.OpStringMake, n.Type, ptr, len)
- case OSTR2BYTESTMP:
- str := s.expr(n.Left)
+ len := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], slice)
+ return s.newValue2(ssa.OpStringMake, n.Type(), ptr, len)
+ case ir.OSTR2BYTESTMP:
+ n := n.(*ir.ConvExpr)
+ str := s.expr(n.X)
ptr := s.newValue1(ssa.OpStringPtr, s.f.Config.Types.BytePtr, str)
- len := s.newValue1(ssa.OpStringLen, types.Types[TINT], str)
- return s.newValue3(ssa.OpSliceMake, n.Type, ptr, len, len)
- case OCFUNC:
- aux := n.Left.Sym.Linksym()
- return s.entryNewValue1A(ssa.OpAddr, n.Type, aux, s.sb)
- case ONAME:
- if n.Class() == PFUNC {
+ len := s.newValue1(ssa.OpStringLen, types.Types[types.TINT], str)
+ return s.newValue3(ssa.OpSliceMake, n.Type(), ptr, len, len)
+ case ir.OCFUNC:
+ n := n.(*ir.UnaryExpr)
+ aux := n.X.(*ir.Name).Linksym()
+ return s.entryNewValue1A(ssa.OpAddr, n.Type(), aux, s.sb)
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ if n.Class == ir.PFUNC {
// "value" of a function is the address of the function's closure
- sym := funcsym(n.Sym).Linksym()
- return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(n.Type), sym, s.sb)
+ sym := staticdata.FuncLinksym(n)
+ return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(n.Type()), sym, s.sb)
}
if s.canSSA(n) {
- return s.variable(n, n.Type)
- }
- addr := s.addr(n)
- return s.load(n.Type, addr)
- case OCLOSUREVAR:
- addr := s.addr(n)
- return s.load(n.Type, addr)
- case OLITERAL:
- switch u := n.Val().U.(type) {
- case *Mpint:
- i := u.Int64()
- switch n.Type.Size() {
+ return s.variable(n, n.Type())
+ }
+ return s.load(n.Type(), s.addr(n))
+ case ir.OLINKSYMOFFSET:
+ n := n.(*ir.LinksymOffsetExpr)
+ return s.load(n.Type(), s.addr(n))
+ case ir.ONIL:
+ n := n.(*ir.NilExpr)
+ t := n.Type()
+ switch {
+ case t.IsSlice():
+ return s.constSlice(t)
+ case t.IsInterface():
+ return s.constInterface(t)
+ default:
+ return s.constNil(t)
+ }
+ case ir.OLITERAL:
+ switch u := n.Val(); u.Kind() {
+ case constant.Int:
+ i := ir.IntVal(n.Type(), u)
+ switch n.Type().Size() {
case 1:
- return s.constInt8(n.Type, int8(i))
+ return s.constInt8(n.Type(), int8(i))
case 2:
- return s.constInt16(n.Type, int16(i))
+ return s.constInt16(n.Type(), int16(i))
case 4:
- return s.constInt32(n.Type, int32(i))
+ return s.constInt32(n.Type(), int32(i))
case 8:
- return s.constInt64(n.Type, i)
+ return s.constInt64(n.Type(), i)
default:
- s.Fatalf("bad integer size %d", n.Type.Size())
+ s.Fatalf("bad integer size %d", n.Type().Size())
return nil
}
- case string:
- if u == "" {
- return s.constEmptyString(n.Type)
- }
- return s.entryNewValue0A(ssa.OpConstString, n.Type, u)
- case bool:
- return s.constBool(u)
- case *NilVal:
- t := n.Type
- switch {
- case t.IsSlice():
- return s.constSlice(t)
- case t.IsInterface():
- return s.constInterface(t)
- default:
- return s.constNil(t)
+ case constant.String:
+ i := constant.StringVal(u)
+ if i == "" {
+ return s.constEmptyString(n.Type())
}
- case *Mpflt:
- switch n.Type.Size() {
+ return s.entryNewValue0A(ssa.OpConstString, n.Type(), ssa.StringToAux(i))
+ case constant.Bool:
+ return s.constBool(constant.BoolVal(u))
+ case constant.Float:
+ f, _ := constant.Float64Val(u)
+ switch n.Type().Size() {
case 4:
- return s.constFloat32(n.Type, u.Float32())
+ return s.constFloat32(n.Type(), f)
case 8:
- return s.constFloat64(n.Type, u.Float64())
+ return s.constFloat64(n.Type(), f)
default:
- s.Fatalf("bad float size %d", n.Type.Size())
+ s.Fatalf("bad float size %d", n.Type().Size())
return nil
}
- case *Mpcplx:
- r := &u.Real
- i := &u.Imag
- switch n.Type.Size() {
+ case constant.Complex:
+ re, _ := constant.Float64Val(constant.Real(u))
+ im, _ := constant.Float64Val(constant.Imag(u))
+ switch n.Type().Size() {
case 8:
- pt := types.Types[TFLOAT32]
- return s.newValue2(ssa.OpComplexMake, n.Type,
- s.constFloat32(pt, r.Float32()),
- s.constFloat32(pt, i.Float32()))
+ pt := types.Types[types.TFLOAT32]
+ return s.newValue2(ssa.OpComplexMake, n.Type(),
+ s.constFloat32(pt, re),
+ s.constFloat32(pt, im))
case 16:
- pt := types.Types[TFLOAT64]
- return s.newValue2(ssa.OpComplexMake, n.Type,
- s.constFloat64(pt, r.Float64()),
- s.constFloat64(pt, i.Float64()))
+ pt := types.Types[types.TFLOAT64]
+ return s.newValue2(ssa.OpComplexMake, n.Type(),
+ s.constFloat64(pt, re),
+ s.constFloat64(pt, im))
default:
- s.Fatalf("bad float size %d", n.Type.Size())
+ s.Fatalf("bad complex size %d", n.Type().Size())
return nil
}
-
default:
- s.Fatalf("unhandled OLITERAL %v", n.Val().Ctype())
+ s.Fatalf("unhandled OLITERAL %v", u.Kind())
return nil
}
- case OCONVNOP:
- to := n.Type
- from := n.Left.Type
+ case ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ to := n.Type()
+ from := n.X.Type()
// Assume everything will work out, so set up our return value.
// Anything interesting that happens from here is a fatal.
- x := s.expr(n.Left)
+ x := s.expr(n.X)
+ if to == from {
+ return x
+ }
// Special case for not confusing GC and liveness.
// We don't want pointers accidentally classified
@@ -2174,12 +2392,12 @@ func (s *state) expr(n *Node) *ssa.Value {
v := s.newValue1(ssa.OpCopy, to, x) // ensure that v has the right type
// CONVNOP closure
- if to.Etype == TFUNC && from.IsPtrShaped() {
+ if to.Kind() == types.TFUNC && from.IsPtrShaped() {
return v
}
// named <--> unnamed type or typed <--> untyped const
- if from.Etype == to.Etype {
+ if from.Kind() == to.Kind() {
return v
}
@@ -2189,30 +2407,30 @@ func (s *state) expr(n *Node) *ssa.Value {
}
// map <--> *hmap
- if to.Etype == TMAP && from.IsPtr() &&
+ if to.Kind() == types.TMAP && from.IsPtr() &&
to.MapType().Hmap == from.Elem() {
return v
}
- dowidth(from)
- dowidth(to)
+ types.CalcSize(from)
+ types.CalcSize(to)
if from.Width != to.Width {
s.Fatalf("CONVNOP width mismatch %v (%d) -> %v (%d)\n", from, from.Width, to, to.Width)
return nil
}
- if etypesign(from.Etype) != etypesign(to.Etype) {
- s.Fatalf("CONVNOP sign mismatch %v (%s) -> %v (%s)\n", from, from.Etype, to, to.Etype)
+ if etypesign(from.Kind()) != etypesign(to.Kind()) {
+ s.Fatalf("CONVNOP sign mismatch %v (%s) -> %v (%s)\n", from, from.Kind(), to, to.Kind())
return nil
}
- if instrumenting {
+ if base.Flag.Cfg.Instrumenting {
// These appear to be fine, but they fail the
// integer constraint below, so okay them here.
// Sample non-integer conversion: map[string]string -> *uint8
return v
}
- if etypesign(from.Etype) == 0 {
+ if etypesign(from.Kind()) == 0 {
s.Fatalf("CONVNOP unrecognized non-integer %v -> %v\n", from, to)
return nil
}
@@ -2220,13 +2438,14 @@ func (s *state) expr(n *Node) *ssa.Value {
// integer, same width, same sign
return v
- case OCONV:
- x := s.expr(n.Left)
- ft := n.Left.Type // from type
- tt := n.Type // to type
- if ft.IsBoolean() && tt.IsKind(TUINT8) {
+ case ir.OCONV:
+ n := n.(*ir.ConvExpr)
+ x := s.expr(n.X)
+ ft := n.X.Type() // from type
+ tt := n.Type() // to type
+ if ft.IsBoolean() && tt.IsKind(types.TUINT8) {
// Bool -> uint8 is generated internally when indexing into runtime.staticbyte.
- return s.newValue1(ssa.OpCopy, n.Type, x)
+ return s.newValue1(ssa.OpCopy, n.Type(), x)
}
if ft.IsInteger() && tt.IsInteger() {
var op ssa.Op
@@ -2287,23 +2506,23 @@ func (s *state) expr(n *Node) *ssa.Value {
s.Fatalf("weird integer sign extension %v -> %v", ft, tt)
}
}
- return s.newValue1(op, n.Type, x)
+ return s.newValue1(op, n.Type(), x)
}
if ft.IsFloat() || tt.IsFloat() {
conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
- if s.config.RegSize == 4 && thearch.LinkArch.Family != sys.MIPS && !s.softFloat {
+ if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat {
if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
conv = conv1
}
}
- if thearch.LinkArch.Family == sys.ARM64 || thearch.LinkArch.Family == sys.Wasm || thearch.LinkArch.Family == sys.S390X || s.softFloat {
+ if Arch.LinkArch.Family == sys.ARM64 || Arch.LinkArch.Family == sys.Wasm || Arch.LinkArch.Family == sys.S390X || s.softFloat {
if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
conv = conv1
}
}
- if thearch.LinkArch.Family == sys.MIPS && !s.softFloat {
+ if Arch.LinkArch.Family == sys.MIPS && !s.softFloat {
if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() {
// tt is float32 or float64, and ft is also unsigned
if tt.Size() == 4 {
@@ -2334,12 +2553,12 @@ func (s *state) expr(n *Node) *ssa.Value {
if op2 == ssa.OpCopy {
return x
}
- return s.newValueOrSfCall1(op2, n.Type, x)
+ return s.newValueOrSfCall1(op2, n.Type(), x)
}
if op2 == ssa.OpCopy {
- return s.newValueOrSfCall1(op1, n.Type, x)
+ return s.newValueOrSfCall1(op1, n.Type(), x)
}
- return s.newValueOrSfCall1(op2, n.Type, s.newValueOrSfCall1(op1, types.Types[it], x))
+ return s.newValueOrSfCall1(op2, n.Type(), s.newValueOrSfCall1(op1, types.Types[it], x))
}
// Tricky 64-bit unsigned cases.
if ft.IsInteger() {
@@ -2381,63 +2600,66 @@ func (s *state) expr(n *Node) *ssa.Value {
} else {
s.Fatalf("weird complex conversion %v -> %v", ft, tt)
}
- ftp := floatForComplex(ft)
- ttp := floatForComplex(tt)
+ ftp := types.FloatForComplex(ft)
+ ttp := types.FloatForComplex(tt)
return s.newValue2(ssa.OpComplexMake, tt,
s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, x)),
s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x)))
}
- s.Fatalf("unhandled OCONV %s -> %s", n.Left.Type.Etype, n.Type.Etype)
+ s.Fatalf("unhandled OCONV %s -> %s", n.X.Type().Kind(), n.Type().Kind())
return nil
- case ODOTTYPE:
+ case ir.ODOTTYPE:
+ n := n.(*ir.TypeAssertExpr)
res, _ := s.dottype(n, false)
return res
// binary ops
- case OLT, OEQ, ONE, OLE, OGE, OGT:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
- if n.Left.Type.IsComplex() {
- pt := floatForComplex(n.Left.Type)
- op := s.ssaOp(OEQ, pt)
- r := s.newValueOrSfCall2(op, types.Types[TBOOL], s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b))
- i := s.newValueOrSfCall2(op, types.Types[TBOOL], s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b))
- c := s.newValue2(ssa.OpAndB, types.Types[TBOOL], r, i)
- switch n.Op {
- case OEQ:
+ case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
+ if n.X.Type().IsComplex() {
+ pt := types.FloatForComplex(n.X.Type())
+ op := s.ssaOp(ir.OEQ, pt)
+ r := s.newValueOrSfCall2(op, types.Types[types.TBOOL], s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b))
+ i := s.newValueOrSfCall2(op, types.Types[types.TBOOL], s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b))
+ c := s.newValue2(ssa.OpAndB, types.Types[types.TBOOL], r, i)
+ switch n.Op() {
+ case ir.OEQ:
return c
- case ONE:
- return s.newValue1(ssa.OpNot, types.Types[TBOOL], c)
+ case ir.ONE:
+ return s.newValue1(ssa.OpNot, types.Types[types.TBOOL], c)
default:
- s.Fatalf("ordered complex compare %v", n.Op)
+ s.Fatalf("ordered complex compare %v", n.Op())
}
}
// Convert OGE and OGT into OLE and OLT.
- op := n.Op
+ op := n.Op()
switch op {
- case OGE:
- op, a, b = OLE, b, a
- case OGT:
- op, a, b = OLT, b, a
+ case ir.OGE:
+ op, a, b = ir.OLE, b, a
+ case ir.OGT:
+ op, a, b = ir.OLT, b, a
}
- if n.Left.Type.IsFloat() {
+ if n.X.Type().IsFloat() {
// float comparison
- return s.newValueOrSfCall2(s.ssaOp(op, n.Left.Type), types.Types[TBOOL], a, b)
+ return s.newValueOrSfCall2(s.ssaOp(op, n.X.Type()), types.Types[types.TBOOL], a, b)
}
// integer comparison
- return s.newValue2(s.ssaOp(op, n.Left.Type), types.Types[TBOOL], a, b)
- case OMUL:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
- if n.Type.IsComplex() {
+ return s.newValue2(s.ssaOp(op, n.X.Type()), types.Types[types.TBOOL], a, b)
+ case ir.OMUL:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
+ if n.Type().IsComplex() {
mulop := ssa.OpMul64F
addop := ssa.OpAdd64F
subop := ssa.OpSub64F
- pt := floatForComplex(n.Type) // Could be Float32 or Float64
- wt := types.Types[TFLOAT64] // Compute in Float64 to minimize cancellation error
+ pt := types.FloatForComplex(n.Type()) // Could be Float32 or Float64
+ wt := types.Types[types.TFLOAT64] // Compute in Float64 to minimize cancellation error
areal := s.newValue1(ssa.OpComplexReal, pt, a)
breal := s.newValue1(ssa.OpComplexReal, pt, b)
@@ -2459,19 +2681,20 @@ func (s *state) expr(n *Node) *ssa.Value {
ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, ximag)
}
- return s.newValue2(ssa.OpComplexMake, n.Type, xreal, ximag)
+ return s.newValue2(ssa.OpComplexMake, n.Type(), xreal, ximag)
}
- if n.Type.IsFloat() {
- return s.newValueOrSfCall2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+ if n.Type().IsFloat() {
+ return s.newValueOrSfCall2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
}
- return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+ return s.newValue2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
- case ODIV:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
- if n.Type.IsComplex() {
+ case ir.ODIV:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
+ if n.Type().IsComplex() {
// TODO this is not executed because the front-end substitutes a runtime call.
// That probably ought to change; with modest optimization the widen/narrow
// conversions could all be elided in larger expression trees.
@@ -2479,8 +2702,8 @@ func (s *state) expr(n *Node) *ssa.Value {
addop := ssa.OpAdd64F
subop := ssa.OpSub64F
divop := ssa.OpDiv64F
- pt := floatForComplex(n.Type) // Could be Float32 or Float64
- wt := types.Types[TFLOAT64] // Compute in Float64 to minimize cancellation error
+ pt := types.FloatForComplex(n.Type()) // Could be Float32 or Float64
+ wt := types.Types[types.TFLOAT64] // Compute in Float64 to minimize cancellation error
areal := s.newValue1(ssa.OpComplexReal, pt, a)
breal := s.newValue1(ssa.OpComplexReal, pt, b)
@@ -2509,50 +2732,55 @@ func (s *state) expr(n *Node) *ssa.Value {
xreal = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, xreal)
ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, ximag)
}
- return s.newValue2(ssa.OpComplexMake, n.Type, xreal, ximag)
+ return s.newValue2(ssa.OpComplexMake, n.Type(), xreal, ximag)
}
- if n.Type.IsFloat() {
- return s.newValueOrSfCall2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+ if n.Type().IsFloat() {
+ return s.newValueOrSfCall2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
}
return s.intDivide(n, a, b)
- case OMOD:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
+ case ir.OMOD:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
return s.intDivide(n, a, b)
- case OADD, OSUB:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
- if n.Type.IsComplex() {
- pt := floatForComplex(n.Type)
- op := s.ssaOp(n.Op, pt)
- return s.newValue2(ssa.OpComplexMake, n.Type,
+ case ir.OADD, ir.OSUB:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
+ if n.Type().IsComplex() {
+ pt := types.FloatForComplex(n.Type())
+ op := s.ssaOp(n.Op(), pt)
+ return s.newValue2(ssa.OpComplexMake, n.Type(),
s.newValueOrSfCall2(op, pt, s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b)),
s.newValueOrSfCall2(op, pt, s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b)))
}
- if n.Type.IsFloat() {
- return s.newValueOrSfCall2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
- }
- return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
- case OAND, OOR, OXOR:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
- return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
- case OANDNOT:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
- b = s.newValue1(s.ssaOp(OBITNOT, b.Type), b.Type, b)
- return s.newValue2(s.ssaOp(OAND, n.Type), a.Type, a, b)
- case OLSH, ORSH:
- a := s.expr(n.Left)
- b := s.expr(n.Right)
+ if n.Type().IsFloat() {
+ return s.newValueOrSfCall2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
+ }
+ return s.newValue2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
+ case ir.OAND, ir.OOR, ir.OXOR:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
+ return s.newValue2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
+ case ir.OANDNOT:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
+ b = s.newValue1(s.ssaOp(ir.OBITNOT, b.Type), b.Type, b)
+ return s.newValue2(s.ssaOp(ir.OAND, n.Type()), a.Type, a, b)
+ case ir.OLSH, ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ a := s.expr(n.X)
+ b := s.expr(n.Y)
bt := b.Type
if bt.IsSigned() {
- cmp := s.newValue2(s.ssaOp(OLE, bt), types.Types[TBOOL], s.zeroVal(bt), b)
- s.check(cmp, panicshift)
+ cmp := s.newValue2(s.ssaOp(ir.OLE, bt), types.Types[types.TBOOL], s.zeroVal(bt), b)
+ s.check(cmp, ir.Syms.Panicshift)
bt = bt.ToUnsigned()
}
- return s.newValue2(s.ssaShiftOp(n.Op, n.Type, bt), a.Type, a, b)
- case OANDAND, OOROR:
+ return s.newValue2(s.ssaShiftOp(n.Op(), n.Type(), bt), a.Type, a, b)
+ case ir.OANDAND, ir.OOROR:
// To implement OANDAND (and OOROR), we introduce a
// new temporary variable to hold the result. The
// variable is associated with the OANDAND node in the
@@ -2566,7 +2794,8 @@ func (s *state) expr(n *Node) *ssa.Value {
// }
// Using var in the subsequent block introduces the
// necessary phi variable.
- el := s.expr(n.Left)
+ n := n.(*ir.LogicalExpr)
+ el := s.expr(n.X)
s.vars[n] = el
b := s.endBlock()
@@ -2579,255 +2808,272 @@ func (s *state) expr(n *Node) *ssa.Value {
bRight := s.f.NewBlock(ssa.BlockPlain)
bResult := s.f.NewBlock(ssa.BlockPlain)
- if n.Op == OANDAND {
+ if n.Op() == ir.OANDAND {
b.AddEdgeTo(bRight)
b.AddEdgeTo(bResult)
- } else if n.Op == OOROR {
+ } else if n.Op() == ir.OOROR {
b.AddEdgeTo(bResult)
b.AddEdgeTo(bRight)
}
s.startBlock(bRight)
- er := s.expr(n.Right)
+ er := s.expr(n.Y)
s.vars[n] = er
b = s.endBlock()
b.AddEdgeTo(bResult)
s.startBlock(bResult)
- return s.variable(n, types.Types[TBOOL])
- case OCOMPLEX:
- r := s.expr(n.Left)
- i := s.expr(n.Right)
- return s.newValue2(ssa.OpComplexMake, n.Type, r, i)
+ return s.variable(n, types.Types[types.TBOOL])
+ case ir.OCOMPLEX:
+ n := n.(*ir.BinaryExpr)
+ r := s.expr(n.X)
+ i := s.expr(n.Y)
+ return s.newValue2(ssa.OpComplexMake, n.Type(), r, i)
// unary ops
- case ONEG:
- a := s.expr(n.Left)
- if n.Type.IsComplex() {
- tp := floatForComplex(n.Type)
- negop := s.ssaOp(n.Op, tp)
- return s.newValue2(ssa.OpComplexMake, n.Type,
+ case ir.ONEG:
+ n := n.(*ir.UnaryExpr)
+ a := s.expr(n.X)
+ if n.Type().IsComplex() {
+ tp := types.FloatForComplex(n.Type())
+ negop := s.ssaOp(n.Op(), tp)
+ return s.newValue2(ssa.OpComplexMake, n.Type(),
s.newValue1(negop, tp, s.newValue1(ssa.OpComplexReal, tp, a)),
s.newValue1(negop, tp, s.newValue1(ssa.OpComplexImag, tp, a)))
}
- return s.newValue1(s.ssaOp(n.Op, n.Type), a.Type, a)
- case ONOT, OBITNOT:
- a := s.expr(n.Left)
- return s.newValue1(s.ssaOp(n.Op, n.Type), a.Type, a)
- case OIMAG, OREAL:
- a := s.expr(n.Left)
- return s.newValue1(s.ssaOp(n.Op, n.Left.Type), n.Type, a)
- case OPLUS:
- return s.expr(n.Left)
-
- case OADDR:
- return s.addr(n.Left)
-
- case ORESULT:
+ return s.newValue1(s.ssaOp(n.Op(), n.Type()), a.Type, a)
+ case ir.ONOT, ir.OBITNOT:
+ n := n.(*ir.UnaryExpr)
+ a := s.expr(n.X)
+ return s.newValue1(s.ssaOp(n.Op(), n.Type()), a.Type, a)
+ case ir.OIMAG, ir.OREAL:
+ n := n.(*ir.UnaryExpr)
+ a := s.expr(n.X)
+ return s.newValue1(s.ssaOp(n.Op(), n.X.Type()), n.Type(), a)
+ case ir.OPLUS:
+ n := n.(*ir.UnaryExpr)
+ return s.expr(n.X)
+
+ case ir.OADDR:
+ n := n.(*ir.AddrExpr)
+ return s.addr(n.X)
+
+ case ir.ORESULT:
+ n := n.(*ir.ResultExpr)
if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall {
// Do the old thing
- addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
- return s.rawLoad(n.Type, addr)
+ addr := s.constOffPtrSP(types.NewPtr(n.Type()), n.Offset)
+ return s.rawLoad(n.Type(), addr)
}
- which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset)
+ which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Offset)
if which == -1 {
// Do the old thing // TODO: Panic instead.
- addr := s.constOffPtrSP(types.NewPtr(n.Type), n.Xoffset)
- return s.rawLoad(n.Type, addr)
+ addr := s.constOffPtrSP(types.NewPtr(n.Type()), n.Offset)
+ return s.rawLoad(n.Type(), addr)
}
- if canSSAType(n.Type) {
- return s.newValue1I(ssa.OpSelectN, n.Type, which, s.prevCall)
+ if TypeOK(n.Type()) {
+ return s.newValue1I(ssa.OpSelectN, n.Type(), which, s.prevCall)
} else {
- addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(n.Type), which, s.prevCall)
- return s.rawLoad(n.Type, addr)
+ addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(n.Type()), which, s.prevCall)
+ return s.rawLoad(n.Type(), addr)
}
- case ODEREF:
- p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
- return s.load(n.Type, p)
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ p := s.exprPtr(n.X, n.Bounded(), n.Pos())
+ return s.load(n.Type(), p)
- case ODOT:
- if n.Left.Op == OSTRUCTLIT {
+ case ir.ODOT:
+ n := n.(*ir.SelectorExpr)
+ if n.X.Op() == ir.OSTRUCTLIT {
// All literals with nonzero fields have already been
// rewritten during walk. Any that remain are just T{}
// or equivalents. Use the zero value.
- if !isZero(n.Left) {
- s.Fatalf("literal with nonzero value in SSA: %v", n.Left)
+ if !ir.IsZero(n.X) {
+ s.Fatalf("literal with nonzero value in SSA: %v", n.X)
}
- return s.zeroVal(n.Type)
+ return s.zeroVal(n.Type())
}
// If n is addressable and can't be represented in
// SSA, then load just the selected field. This
// prevents false memory dependencies in race/msan
// instrumentation.
- if islvalue(n) && !s.canSSA(n) {
+ if ir.IsAddressable(n) && !s.canSSA(n) {
p := s.addr(n)
- return s.load(n.Type, p)
+ return s.load(n.Type(), p)
}
- v := s.expr(n.Left)
- return s.newValue1I(ssa.OpStructSelect, n.Type, int64(fieldIdx(n)), v)
+ v := s.expr(n.X)
+ return s.newValue1I(ssa.OpStructSelect, n.Type(), int64(fieldIdx(n)), v)
- case ODOTPTR:
- p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
- p = s.newValue1I(ssa.OpOffPtr, types.NewPtr(n.Type), n.Xoffset, p)
- return s.load(n.Type, p)
+ case ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ p := s.exprPtr(n.X, n.Bounded(), n.Pos())
+ p = s.newValue1I(ssa.OpOffPtr, types.NewPtr(n.Type()), n.Offset(), p)
+ return s.load(n.Type(), p)
- case OINDEX:
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
switch {
- case n.Left.Type.IsString():
- if n.Bounded() && Isconst(n.Left, CTSTR) && Isconst(n.Right, CTINT) {
+ case n.X.Type().IsString():
+ if n.Bounded() && ir.IsConst(n.X, constant.String) && ir.IsConst(n.Index, constant.Int) {
// Replace "abc"[1] with 'b'.
// Delayed until now because "abc"[1] is not an ideal constant.
// See test/fixedbugs/issue11370.go.
- return s.newValue0I(ssa.OpConst8, types.Types[TUINT8], int64(int8(n.Left.StringVal()[n.Right.Int64Val()])))
+ return s.newValue0I(ssa.OpConst8, types.Types[types.TUINT8], int64(int8(ir.StringVal(n.X)[ir.Int64Val(n.Index)])))
}
- a := s.expr(n.Left)
- i := s.expr(n.Right)
- len := s.newValue1(ssa.OpStringLen, types.Types[TINT], a)
+ a := s.expr(n.X)
+ i := s.expr(n.Index)
+ len := s.newValue1(ssa.OpStringLen, types.Types[types.TINT], a)
i = s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded())
ptrtyp := s.f.Config.Types.BytePtr
ptr := s.newValue1(ssa.OpStringPtr, ptrtyp, a)
- if Isconst(n.Right, CTINT) {
- ptr = s.newValue1I(ssa.OpOffPtr, ptrtyp, n.Right.Int64Val(), ptr)
+ if ir.IsConst(n.Index, constant.Int) {
+ ptr = s.newValue1I(ssa.OpOffPtr, ptrtyp, ir.Int64Val(n.Index), ptr)
} else {
ptr = s.newValue2(ssa.OpAddPtr, ptrtyp, ptr, i)
}
- return s.load(types.Types[TUINT8], ptr)
- case n.Left.Type.IsSlice():
+ return s.load(types.Types[types.TUINT8], ptr)
+ case n.X.Type().IsSlice():
p := s.addr(n)
- return s.load(n.Left.Type.Elem(), p)
- case n.Left.Type.IsArray():
- if canSSAType(n.Left.Type) {
+ return s.load(n.X.Type().Elem(), p)
+ case n.X.Type().IsArray():
+ if TypeOK(n.X.Type()) {
// SSA can handle arrays of length at most 1.
- bound := n.Left.Type.NumElem()
- a := s.expr(n.Left)
- i := s.expr(n.Right)
+ bound := n.X.Type().NumElem()
+ a := s.expr(n.X)
+ i := s.expr(n.Index)
if bound == 0 {
// Bounds check will never succeed. Might as well
// use constants for the bounds check.
- z := s.constInt(types.Types[TINT], 0)
+ z := s.constInt(types.Types[types.TINT], 0)
s.boundsCheck(z, z, ssa.BoundsIndex, false)
// The return value won't be live, return junk.
- return s.newValue0(ssa.OpUnknown, n.Type)
+ return s.newValue0(ssa.OpUnknown, n.Type())
}
- len := s.constInt(types.Types[TINT], bound)
+ len := s.constInt(types.Types[types.TINT], bound)
s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded()) // checks i == 0
- return s.newValue1I(ssa.OpArraySelect, n.Type, 0, a)
+ return s.newValue1I(ssa.OpArraySelect, n.Type(), 0, a)
}
p := s.addr(n)
- return s.load(n.Left.Type.Elem(), p)
+ return s.load(n.X.Type().Elem(), p)
default:
- s.Fatalf("bad type for index %v", n.Left.Type)
+ s.Fatalf("bad type for index %v", n.X.Type())
return nil
}
- case OLEN, OCAP:
+ case ir.OLEN, ir.OCAP:
+ n := n.(*ir.UnaryExpr)
switch {
- case n.Left.Type.IsSlice():
+ case n.X.Type().IsSlice():
op := ssa.OpSliceLen
- if n.Op == OCAP {
+ if n.Op() == ir.OCAP {
op = ssa.OpSliceCap
}
- return s.newValue1(op, types.Types[TINT], s.expr(n.Left))
- case n.Left.Type.IsString(): // string; not reachable for OCAP
- return s.newValue1(ssa.OpStringLen, types.Types[TINT], s.expr(n.Left))
- case n.Left.Type.IsMap(), n.Left.Type.IsChan():
- return s.referenceTypeBuiltin(n, s.expr(n.Left))
+ return s.newValue1(op, types.Types[types.TINT], s.expr(n.X))
+ case n.X.Type().IsString(): // string; not reachable for OCAP
+ return s.newValue1(ssa.OpStringLen, types.Types[types.TINT], s.expr(n.X))
+ case n.X.Type().IsMap(), n.X.Type().IsChan():
+ return s.referenceTypeBuiltin(n, s.expr(n.X))
default: // array
- return s.constInt(types.Types[TINT], n.Left.Type.NumElem())
+ return s.constInt(types.Types[types.TINT], n.X.Type().NumElem())
}
- case OSPTR:
- a := s.expr(n.Left)
- if n.Left.Type.IsSlice() {
- return s.newValue1(ssa.OpSlicePtr, n.Type, a)
+ case ir.OSPTR:
+ n := n.(*ir.UnaryExpr)
+ a := s.expr(n.X)
+ if n.X.Type().IsSlice() {
+ return s.newValue1(ssa.OpSlicePtr, n.Type(), a)
} else {
- return s.newValue1(ssa.OpStringPtr, n.Type, a)
- }
-
- case OITAB:
- a := s.expr(n.Left)
- return s.newValue1(ssa.OpITab, n.Type, a)
-
- case OIDATA:
- a := s.expr(n.Left)
- return s.newValue1(ssa.OpIData, n.Type, a)
-
- case OEFACE:
- tab := s.expr(n.Left)
- data := s.expr(n.Right)
- return s.newValue2(ssa.OpIMake, n.Type, tab, data)
-
- case OSLICEHEADER:
- p := s.expr(n.Left)
- l := s.expr(n.List.First())
- c := s.expr(n.List.Second())
- return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
-
- case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
- v := s.expr(n.Left)
+ return s.newValue1(ssa.OpStringPtr, n.Type(), a)
+ }
+
+ case ir.OITAB:
+ n := n.(*ir.UnaryExpr)
+ a := s.expr(n.X)
+ return s.newValue1(ssa.OpITab, n.Type(), a)
+
+ case ir.OIDATA:
+ n := n.(*ir.UnaryExpr)
+ a := s.expr(n.X)
+ return s.newValue1(ssa.OpIData, n.Type(), a)
+
+ case ir.OEFACE:
+ n := n.(*ir.BinaryExpr)
+ tab := s.expr(n.X)
+ data := s.expr(n.Y)
+ return s.newValue2(ssa.OpIMake, n.Type(), tab, data)
+
+ case ir.OSLICEHEADER:
+ n := n.(*ir.SliceHeaderExpr)
+ p := s.expr(n.Ptr)
+ l := s.expr(n.Len)
+ c := s.expr(n.Cap)
+ return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c)
+
+ case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR:
+ n := n.(*ir.SliceExpr)
+ v := s.expr(n.X)
var i, j, k *ssa.Value
- low, high, max := n.SliceBounds()
- if low != nil {
- i = s.expr(low)
+ if n.Low != nil {
+ i = s.expr(n.Low)
}
- if high != nil {
- j = s.expr(high)
+ if n.High != nil {
+ j = s.expr(n.High)
}
- if max != nil {
- k = s.expr(max)
+ if n.Max != nil {
+ k = s.expr(n.Max)
}
p, l, c := s.slice(v, i, j, k, n.Bounded())
- return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
+ return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c)
- case OSLICESTR:
- v := s.expr(n.Left)
+ case ir.OSLICESTR:
+ n := n.(*ir.SliceExpr)
+ v := s.expr(n.X)
var i, j *ssa.Value
- low, high, _ := n.SliceBounds()
- if low != nil {
- i = s.expr(low)
+ if n.Low != nil {
+ i = s.expr(n.Low)
}
- if high != nil {
- j = s.expr(high)
+ if n.High != nil {
+ j = s.expr(n.High)
}
p, l, _ := s.slice(v, i, j, nil, n.Bounded())
- return s.newValue2(ssa.OpStringMake, n.Type, p, l)
+ return s.newValue2(ssa.OpStringMake, n.Type(), p, l)
- case OCALLFUNC:
- if isIntrinsicCall(n) {
+ case ir.OCALLFUNC:
+ n := n.(*ir.CallExpr)
+ if ir.IsIntrinsicCall(n) {
return s.intrinsicCall(n)
}
fallthrough
- case OCALLINTER, OCALLMETH:
+ case ir.OCALLINTER, ir.OCALLMETH:
+ n := n.(*ir.CallExpr)
return s.callResult(n, callNormal)
- case OGETG:
- return s.newValue1(ssa.OpGetG, n.Type, s.mem())
+ case ir.OGETG:
+ n := n.(*ir.CallExpr)
+ return s.newValue1(ssa.OpGetG, n.Type(), s.mem())
- case OAPPEND:
- return s.append(n, false)
+ case ir.OAPPEND:
+ return s.append(n.(*ir.CallExpr), false)
- case OSTRUCTLIT, OARRAYLIT:
+ case ir.OSTRUCTLIT, ir.OARRAYLIT:
// All literals with nonzero fields have already been
// rewritten during walk. Any that remain are just T{}
// or equivalents. Use the zero value.
- if !isZero(n) {
+ n := n.(*ir.CompLitExpr)
+ if !ir.IsZero(n) {
s.Fatalf("literal with nonzero value in SSA: %v", n)
}
- return s.zeroVal(n.Type)
+ return s.zeroVal(n.Type())
- case ONEWOBJ:
- if n.Type.Elem().Size() == 0 {
- return s.newValue1A(ssa.OpAddr, n.Type, zerobaseSym, s.sb)
- }
- typ := s.expr(n.Left)
- vv := s.rtcall(newobject, true, []*types.Type{n.Type}, typ)
- return vv[0]
+ case ir.ONEW:
+ n := n.(*ir.UnaryExpr)
+ return s.newObject(n.Type().Elem())
default:
- s.Fatalf("unhandled expr %v", n.Op)
+ s.Fatalf("unhandled expr %v", n.Op())
return nil
}
}
@@ -2838,7 +3084,7 @@ func (s *state) expr(n *Node) *ssa.Value {
// If inplace is true, it writes the result of the OAPPEND expression n
// back to the slice being appended to, and returns nil.
// inplace MUST be set to false if the slice can be SSA'd.
-func (s *state) append(n *Node, inplace bool) *ssa.Value {
+func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value {
// If inplace is false, process as expression "append(s, e1, e2, e3)":
//
// ptr, len, cap := s
@@ -2872,16 +3118,16 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
// *(ptr+len+1) = e2
// *(ptr+len+2) = e3
- et := n.Type.Elem()
+ et := n.Type().Elem()
pt := types.NewPtr(et)
// Evaluate slice
- sn := n.List.First() // the slice node is the first in the list
+ sn := n.Args[0] // the slice node is the first in the list
var slice, addr *ssa.Value
if inplace {
addr = s.addr(sn)
- slice = s.load(n.Type, addr)
+ slice = s.load(n.Type(), addr)
} else {
slice = s.expr(sn)
}
@@ -2891,20 +3137,20 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
assign := s.f.NewBlock(ssa.BlockPlain)
// Decide if we need to grow
- nargs := int64(n.List.Len() - 1)
+ nargs := int64(len(n.Args) - 1)
p := s.newValue1(ssa.OpSlicePtr, pt, slice)
- l := s.newValue1(ssa.OpSliceLen, types.Types[TINT], slice)
- c := s.newValue1(ssa.OpSliceCap, types.Types[TINT], slice)
- nl := s.newValue2(s.ssaOp(OADD, types.Types[TINT]), types.Types[TINT], l, s.constInt(types.Types[TINT], nargs))
+ l := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], slice)
+ c := s.newValue1(ssa.OpSliceCap, types.Types[types.TINT], slice)
+ nl := s.newValue2(s.ssaOp(ir.OADD, types.Types[types.TINT]), types.Types[types.TINT], l, s.constInt(types.Types[types.TINT], nargs))
- cmp := s.newValue2(s.ssaOp(OLT, types.Types[TUINT]), types.Types[TBOOL], c, nl)
- s.vars[&ptrVar] = p
+ cmp := s.newValue2(s.ssaOp(ir.OLT, types.Types[types.TUINT]), types.Types[types.TBOOL], c, nl)
+ s.vars[ptrVar] = p
if !inplace {
- s.vars[&newlenVar] = nl
- s.vars[&capVar] = c
+ s.vars[newlenVar] = nl
+ s.vars[capVar] = c
} else {
- s.vars[&lenVar] = l
+ s.vars[lenVar] = l
}
b := s.endBlock()
@@ -2916,24 +3162,27 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
// Call growslice
s.startBlock(grow)
- taddr := s.expr(n.Left)
- r := s.rtcall(growslice, true, []*types.Type{pt, types.Types[TINT], types.Types[TINT]}, taddr, p, l, c, nl)
+ taddr := s.expr(n.X)
+ r := s.rtcall(ir.Syms.Growslice, true, []*types.Type{pt, types.Types[types.TINT], types.Types[types.TINT]}, taddr, p, l, c, nl)
if inplace {
- if sn.Op == ONAME && sn.Class() != PEXTERN {
- // Tell liveness we're about to build a new slice
- s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, sn, s.mem())
+ if sn.Op() == ir.ONAME {
+ sn := sn.(*ir.Name)
+ if sn.Class != ir.PEXTERN {
+ // Tell liveness we're about to build a new slice
+ s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, sn, s.mem())
+ }
}
- capaddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, sliceCapOffset, addr)
- s.store(types.Types[TINT], capaddr, r[2])
+ capaddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, types.SliceCapOffset, addr)
+ s.store(types.Types[types.TINT], capaddr, r[2])
s.store(pt, addr, r[0])
// load the value we just stored to avoid having to spill it
- s.vars[&ptrVar] = s.load(pt, addr)
- s.vars[&lenVar] = r[1] // avoid a spill in the fast path
+ s.vars[ptrVar] = s.load(pt, addr)
+ s.vars[lenVar] = r[1] // avoid a spill in the fast path
} else {
- s.vars[&ptrVar] = r[0]
- s.vars[&newlenVar] = s.newValue2(s.ssaOp(OADD, types.Types[TINT]), types.Types[TINT], r[1], s.constInt(types.Types[TINT], nargs))
- s.vars[&capVar] = r[2]
+ s.vars[ptrVar] = r[0]
+ s.vars[newlenVar] = s.newValue2(s.ssaOp(ir.OADD, types.Types[types.TINT]), types.Types[types.TINT], r[1], s.constInt(types.Types[types.TINT], nargs))
+ s.vars[capVar] = r[2]
}
b = s.endBlock()
@@ -2943,10 +3192,10 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
s.startBlock(assign)
if inplace {
- l = s.variable(&lenVar, types.Types[TINT]) // generates phi for len
- nl = s.newValue2(s.ssaOp(OADD, types.Types[TINT]), types.Types[TINT], l, s.constInt(types.Types[TINT], nargs))
- lenaddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, sliceLenOffset, addr)
- s.store(types.Types[TINT], lenaddr, nl)
+ l = s.variable(lenVar, types.Types[types.TINT]) // generates phi for len
+ nl = s.newValue2(s.ssaOp(ir.OADD, types.Types[types.TINT]), types.Types[types.TINT], l, s.constInt(types.Types[types.TINT], nargs))
+ lenaddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, types.SliceLenOffset, addr)
+ s.store(types.Types[types.TINT], lenaddr, nl)
}
// Evaluate args
@@ -2957,8 +3206,8 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
store bool
}
args := make([]argRec, 0, nargs)
- for _, n := range n.List.Slice()[1:] {
- if canSSAType(n.Type) {
+ for _, n := range n.Args[1:] {
+ if TypeOK(n.Type()) {
args = append(args, argRec{v: s.expr(n), store: true})
} else {
v := s.addr(n)
@@ -2966,14 +3215,14 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
}
}
- p = s.variable(&ptrVar, pt) // generates phi for ptr
+ p = s.variable(ptrVar, pt) // generates phi for ptr
if !inplace {
- nl = s.variable(&newlenVar, types.Types[TINT]) // generates phi for nl
- c = s.variable(&capVar, types.Types[TINT]) // generates phi for cap
+ nl = s.variable(newlenVar, types.Types[types.TINT]) // generates phi for nl
+ c = s.variable(capVar, types.Types[types.TINT]) // generates phi for cap
}
p2 := s.newValue2(ssa.OpPtrIndex, pt, p, l)
for i, arg := range args {
- addr := s.newValue2(ssa.OpPtrIndex, pt, p2, s.constInt(types.Types[TINT], int64(i)))
+ addr := s.newValue2(ssa.OpPtrIndex, pt, p2, s.constInt(types.Types[types.TINT], int64(i)))
if arg.store {
s.storeType(et, addr, arg.v, 0, true)
} else {
@@ -2981,29 +3230,30 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
}
}
- delete(s.vars, &ptrVar)
+ delete(s.vars, ptrVar)
if inplace {
- delete(s.vars, &lenVar)
+ delete(s.vars, lenVar)
return nil
}
- delete(s.vars, &newlenVar)
- delete(s.vars, &capVar)
+ delete(s.vars, newlenVar)
+ delete(s.vars, capVar)
// make result
- return s.newValue3(ssa.OpSliceMake, n.Type, p, nl, c)
+ return s.newValue3(ssa.OpSliceMake, n.Type(), p, nl, c)
}
// condBranch evaluates the boolean expression cond and branches to yes
// if cond is true and no if cond is false.
// This function is intended to handle && and || better than just calling
// s.expr(cond) and branching on the result.
-func (s *state) condBranch(cond *Node, yes, no *ssa.Block, likely int8) {
- switch cond.Op {
- case OANDAND:
+func (s *state) condBranch(cond ir.Node, yes, no *ssa.Block, likely int8) {
+ switch cond.Op() {
+ case ir.OANDAND:
+ cond := cond.(*ir.LogicalExpr)
mid := s.f.NewBlock(ssa.BlockPlain)
- s.stmtList(cond.Ninit)
- s.condBranch(cond.Left, mid, no, max8(likely, 0))
+ s.stmtList(cond.Init())
+ s.condBranch(cond.X, mid, no, max8(likely, 0))
s.startBlock(mid)
- s.condBranch(cond.Right, yes, no, likely)
+ s.condBranch(cond.Y, yes, no, likely)
return
// Note: if likely==1, then both recursive calls pass 1.
// If likely==-1, then we don't have enough information to decide
@@ -3011,19 +3261,26 @@ func (s *state) condBranch(cond *Node, yes, no *ssa.Block, likely int8) {
// the likeliness of the first branch.
// TODO: have the frontend give us branch prediction hints for
// OANDAND and OOROR nodes (if it ever has such info).
- case OOROR:
+ case ir.OOROR:
+ cond := cond.(*ir.LogicalExpr)
mid := s.f.NewBlock(ssa.BlockPlain)
- s.stmtList(cond.Ninit)
- s.condBranch(cond.Left, yes, mid, min8(likely, 0))
+ s.stmtList(cond.Init())
+ s.condBranch(cond.X, yes, mid, min8(likely, 0))
s.startBlock(mid)
- s.condBranch(cond.Right, yes, no, likely)
+ s.condBranch(cond.Y, yes, no, likely)
return
// Note: if likely==-1, then both recursive calls pass -1.
// If likely==1, then we don't have enough info to decide
// the likelihood of the first branch.
- case ONOT:
- s.stmtList(cond.Ninit)
- s.condBranch(cond.Left, no, yes, -likely)
+ case ir.ONOT:
+ cond := cond.(*ir.UnaryExpr)
+ s.stmtList(cond.Init())
+ s.condBranch(cond.X, no, yes, -likely)
+ return
+ case ir.OCONVNOP:
+ cond := cond.(*ir.ConvExpr)
+ s.stmtList(cond.Init())
+ s.condBranch(cond.X, yes, no, likely)
return
}
c := s.expr(cond)
@@ -3048,17 +3305,17 @@ const (
// If deref is true, then we do left = *right instead (and right has already been nil-checked).
// If deref is true and right == nil, just do left = 0.
// skip indicates assignments (at the top level) that can be avoided.
-func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask) {
- if left.Op == ONAME && left.isBlank() {
+func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask) {
+ if left.Op() == ir.ONAME && ir.IsBlank(left) {
return
}
- t := left.Type
- dowidth(t)
+ t := left.Type()
+ types.CalcSize(t)
if s.canSSA(left) {
if deref {
s.Fatalf("can SSA LHS %v but not RHS %s", left, right)
}
- if left.Op == ODOT {
+ if left.Op() == ir.ODOT {
// We're assigning to a field of an ssa-able value.
// We need to build a new structure with the new value for the
// field we're assigning and the old values for the other fields.
@@ -3069,12 +3326,13 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask)
// For the x.b = 5 assignment we want to generate x = T{x.a, 5, x.c}
// Grab information about the structure type.
- t := left.Left.Type
+ left := left.(*ir.SelectorExpr)
+ t := left.X.Type()
nf := t.NumFields()
idx := fieldIdx(left)
// Grab old value of structure.
- old := s.expr(left.Left)
+ old := s.expr(left.X)
// Make new structure.
new := s.newValue0(ssa.StructMakeOp(t.NumFields()), t)
@@ -3089,23 +3347,24 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask)
}
// Recursively assign the new value we've made to the base of the dot op.
- s.assign(left.Left, new, false, 0)
+ s.assign(left.X, new, false, 0)
// TODO: do we need to update named values here?
return
}
- if left.Op == OINDEX && left.Left.Type.IsArray() {
- s.pushLine(left.Pos)
+ if left.Op() == ir.OINDEX && left.(*ir.IndexExpr).X.Type().IsArray() {
+ left := left.(*ir.IndexExpr)
+ s.pushLine(left.Pos())
defer s.popLine()
// We're assigning to an element of an ssa-able array.
// a[i] = v
- t := left.Left.Type
+ t := left.X.Type()
n := t.NumElem()
- i := s.expr(left.Right) // index
+ i := s.expr(left.Index) // index
if n == 0 {
// The bounds check must fail. Might as well
// ignore the actual index and just use zeros.
- z := s.constInt(types.Types[TINT], 0)
+ z := s.constInt(types.Types[types.TINT], 0)
s.boundsCheck(z, z, ssa.BoundsIndex, false)
return
}
@@ -3113,12 +3372,13 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask)
s.Fatalf("assigning to non-1-length array")
}
// Rewrite to a = [1]{v}
- len := s.constInt(types.Types[TINT], 1)
+ len := s.constInt(types.Types[types.TINT], 1)
s.boundsCheck(i, len, ssa.BoundsIndex, false) // checks i == 0
v := s.newValue1(ssa.OpArrayMake1, t, right)
- s.assign(left.Left, v, false, 0)
+ s.assign(left.X, v, false, 0)
return
}
+ left := left.(*ir.Name)
// Update variable assignment.
s.vars[left] = right
s.addNamedValue(left, right)
@@ -3127,19 +3387,19 @@ func (s *state) assign(left *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 == ONAME && base.Class() != PEXTERN && skip == 0 {
- s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, base, s.mem(), !base.IsAutoTmp())
+ if base, ok := clobberBase(left).(*ir.Name); ok && base.OnStack() && 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.
addr := s.addr(left)
- if isReflectHeaderDataField(left) {
+ if ir.IsReflectHeaderDataField(left) {
// Package unsafe's documentation says storing pointers into
// reflect.SliceHeader and reflect.StringHeader's Data fields
// is valid, even though they have type uintptr (#19168).
// Mark it pointer type to signal the writebarrier pass to
// insert a write barrier.
- t = types.Types[TUNSAFEPTR]
+ t = types.Types[types.TUNSAFEPTR]
}
if deref {
// Treat as a mem->mem move.
@@ -3151,7 +3411,7 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask)
return
}
// Treat as a store.
- s.storeType(t, addr, right, skip, !left.IsAutoTmp())
+ s.storeType(t, addr, right, skip, !ir.IsAutoTmp(left))
}
// zeroVal returns the zero value for type t.
@@ -3182,10 +3442,10 @@ func (s *state) zeroVal(t *types.Type) *ssa.Value {
case t.IsComplex():
switch t.Size() {
case 8:
- z := s.constFloat32(types.Types[TFLOAT32], 0)
+ z := s.constFloat32(types.Types[types.TFLOAT32], 0)
return s.entryNewValue2(ssa.OpComplexMake, t, z, z)
case 16:
- z := s.constFloat64(types.Types[TFLOAT64], 0)
+ z := s.constFloat64(types.Types[types.TFLOAT64], 0)
return s.entryNewValue2(ssa.OpComplexMake, t, z, z)
default:
s.Fatalf("bad sized complex type %v", t)
@@ -3231,7 +3491,7 @@ const (
type sfRtCallDef struct {
rtfn *obj.LSym
- rtype types.EType
+ rtype types.Kind
}
var softFloatOps map[ssa.Op]sfRtCallDef
@@ -3239,38 +3499,38 @@ var softFloatOps map[ssa.Op]sfRtCallDef
func softfloatInit() {
// Some of these operations get transformed by sfcall.
softFloatOps = map[ssa.Op]sfRtCallDef{
- ssa.OpAdd32F: sfRtCallDef{sysfunc("fadd32"), TFLOAT32},
- ssa.OpAdd64F: sfRtCallDef{sysfunc("fadd64"), TFLOAT64},
- ssa.OpSub32F: sfRtCallDef{sysfunc("fadd32"), TFLOAT32},
- ssa.OpSub64F: sfRtCallDef{sysfunc("fadd64"), TFLOAT64},
- ssa.OpMul32F: sfRtCallDef{sysfunc("fmul32"), TFLOAT32},
- ssa.OpMul64F: sfRtCallDef{sysfunc("fmul64"), TFLOAT64},
- ssa.OpDiv32F: sfRtCallDef{sysfunc("fdiv32"), TFLOAT32},
- ssa.OpDiv64F: sfRtCallDef{sysfunc("fdiv64"), TFLOAT64},
-
- ssa.OpEq64F: sfRtCallDef{sysfunc("feq64"), TBOOL},
- ssa.OpEq32F: sfRtCallDef{sysfunc("feq32"), TBOOL},
- ssa.OpNeq64F: sfRtCallDef{sysfunc("feq64"), TBOOL},
- ssa.OpNeq32F: sfRtCallDef{sysfunc("feq32"), TBOOL},
- ssa.OpLess64F: sfRtCallDef{sysfunc("fgt64"), TBOOL},
- ssa.OpLess32F: sfRtCallDef{sysfunc("fgt32"), TBOOL},
- ssa.OpLeq64F: sfRtCallDef{sysfunc("fge64"), TBOOL},
- ssa.OpLeq32F: sfRtCallDef{sysfunc("fge32"), TBOOL},
-
- ssa.OpCvt32to32F: sfRtCallDef{sysfunc("fint32to32"), TFLOAT32},
- ssa.OpCvt32Fto32: sfRtCallDef{sysfunc("f32toint32"), TINT32},
- ssa.OpCvt64to32F: sfRtCallDef{sysfunc("fint64to32"), TFLOAT32},
- ssa.OpCvt32Fto64: sfRtCallDef{sysfunc("f32toint64"), TINT64},
- ssa.OpCvt64Uto32F: sfRtCallDef{sysfunc("fuint64to32"), TFLOAT32},
- ssa.OpCvt32Fto64U: sfRtCallDef{sysfunc("f32touint64"), TUINT64},
- ssa.OpCvt32to64F: sfRtCallDef{sysfunc("fint32to64"), TFLOAT64},
- ssa.OpCvt64Fto32: sfRtCallDef{sysfunc("f64toint32"), TINT32},
- ssa.OpCvt64to64F: sfRtCallDef{sysfunc("fint64to64"), TFLOAT64},
- ssa.OpCvt64Fto64: sfRtCallDef{sysfunc("f64toint64"), TINT64},
- ssa.OpCvt64Uto64F: sfRtCallDef{sysfunc("fuint64to64"), TFLOAT64},
- ssa.OpCvt64Fto64U: sfRtCallDef{sysfunc("f64touint64"), TUINT64},
- ssa.OpCvt32Fto64F: sfRtCallDef{sysfunc("f32to64"), TFLOAT64},
- ssa.OpCvt64Fto32F: sfRtCallDef{sysfunc("f64to32"), TFLOAT32},
+ ssa.OpAdd32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd32"), types.TFLOAT32},
+ ssa.OpAdd64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd64"), types.TFLOAT64},
+ ssa.OpSub32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd32"), types.TFLOAT32},
+ ssa.OpSub64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd64"), types.TFLOAT64},
+ ssa.OpMul32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fmul32"), types.TFLOAT32},
+ ssa.OpMul64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fmul64"), types.TFLOAT64},
+ ssa.OpDiv32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fdiv32"), types.TFLOAT32},
+ ssa.OpDiv64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fdiv64"), types.TFLOAT64},
+
+ ssa.OpEq64F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq64"), types.TBOOL},
+ ssa.OpEq32F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq32"), types.TBOOL},
+ ssa.OpNeq64F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq64"), types.TBOOL},
+ ssa.OpNeq32F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq32"), types.TBOOL},
+ ssa.OpLess64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fgt64"), types.TBOOL},
+ ssa.OpLess32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fgt32"), types.TBOOL},
+ ssa.OpLeq64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fge64"), types.TBOOL},
+ ssa.OpLeq32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fge32"), types.TBOOL},
+
+ ssa.OpCvt32to32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint32to32"), types.TFLOAT32},
+ ssa.OpCvt32Fto32: sfRtCallDef{typecheck.LookupRuntimeFunc("f32toint32"), types.TINT32},
+ ssa.OpCvt64to32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint64to32"), types.TFLOAT32},
+ ssa.OpCvt32Fto64: sfRtCallDef{typecheck.LookupRuntimeFunc("f32toint64"), types.TINT64},
+ ssa.OpCvt64Uto32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fuint64to32"), types.TFLOAT32},
+ ssa.OpCvt32Fto64U: sfRtCallDef{typecheck.LookupRuntimeFunc("f32touint64"), types.TUINT64},
+ ssa.OpCvt32to64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint32to64"), types.TFLOAT64},
+ ssa.OpCvt64Fto32: sfRtCallDef{typecheck.LookupRuntimeFunc("f64toint32"), types.TINT32},
+ ssa.OpCvt64to64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint64to64"), types.TFLOAT64},
+ ssa.OpCvt64Fto64: sfRtCallDef{typecheck.LookupRuntimeFunc("f64toint64"), types.TINT64},
+ ssa.OpCvt64Uto64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fuint64to64"), types.TFLOAT64},
+ ssa.OpCvt64Fto64U: sfRtCallDef{typecheck.LookupRuntimeFunc("f64touint64"), types.TUINT64},
+ ssa.OpCvt32Fto64F: sfRtCallDef{typecheck.LookupRuntimeFunc("f32to64"), types.TFLOAT64},
+ ssa.OpCvt64Fto32F: sfRtCallDef{typecheck.LookupRuntimeFunc("f64to32"), types.TFLOAT32},
}
}
@@ -3286,7 +3546,7 @@ func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) {
args[0], args[1] = args[1], args[0]
case ssa.OpSub32F,
ssa.OpSub64F:
- args[1] = s.newValue1(s.ssaOp(ONEG, types.Types[callDef.rtype]), args[1].Type, args[1])
+ args[1] = s.newValue1(s.ssaOp(ir.ONEG, types.Types[callDef.rtype]), args[1].Type, args[1])
}
result := s.rtcall(callDef.rtfn, true, []*types.Type{types.Types[callDef.rtype]}, args...)[0]
@@ -3302,7 +3562,7 @@ var intrinsics map[intrinsicKey]intrinsicBuilder
// An intrinsicBuilder converts a call node n into an ssa value that
// implements that call as an intrinsic. args is a list of arguments to the func.
-type intrinsicBuilder func(s *state, n *Node, args []*ssa.Value) *ssa.Value
+type intrinsicBuilder func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value
type intrinsicKey struct {
arch *sys.Arch
@@ -3310,7 +3570,7 @@ type intrinsicKey struct {
fn string
}
-func init() {
+func InitTables() {
intrinsics = map[intrinsicKey]intrinsicBuilder{}
var all []*sys.Arch
@@ -3365,175 +3625,175 @@ func init() {
}
/******** runtime ********/
- if !instrumenting {
+ if !base.Flag.Cfg.Instrumenting {
add("runtime", "slicebytetostringtmp",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
// Compiler frontend optimizations emit OBYTES2STRTMP nodes
// for the backend instead of slicebytetostringtmp calls
// when not instrumenting.
- return s.newValue2(ssa.OpStringMake, n.Type, args[0], args[1])
+ return s.newValue2(ssa.OpStringMake, n.Type(), args[0], args[1])
},
all...)
}
addF("runtime/internal/math", "MulUintptr",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
- return s.newValue2(ssa.OpMul32uover, types.NewTuple(types.Types[TUINT], types.Types[TUINT]), args[0], args[1])
+ return s.newValue2(ssa.OpMul32uover, types.NewTuple(types.Types[types.TUINT], types.Types[types.TUINT]), args[0], args[1])
}
- return s.newValue2(ssa.OpMul64uover, types.NewTuple(types.Types[TUINT], types.Types[TUINT]), args[0], args[1])
+ return s.newValue2(ssa.OpMul64uover, types.NewTuple(types.Types[types.TUINT], types.Types[types.TUINT]), args[0], args[1])
},
sys.AMD64, sys.I386, sys.MIPS64)
add("runtime", "KeepAlive",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
data := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, args[0])
- s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, types.TypeMem, data, s.mem())
+ s.vars[memVar] = s.newValue2(ssa.OpKeepAlive, types.TypeMem, data, s.mem())
return nil
},
all...)
add("runtime", "getclosureptr",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue0(ssa.OpGetClosurePtr, s.f.Config.Types.Uintptr)
},
all...)
add("runtime", "getcallerpc",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue0(ssa.OpGetCallerPC, s.f.Config.Types.Uintptr)
},
all...)
add("runtime", "getcallersp",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue0(ssa.OpGetCallerSP, s.f.Config.Types.Uintptr)
},
all...)
/******** runtime/internal/sys ********/
addF("runtime/internal/sys", "Ctz32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpCtz32, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpCtz32, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64)
addF("runtime/internal/sys", "Ctz64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpCtz64, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpCtz64, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64)
addF("runtime/internal/sys", "Bswap32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBswap32, types.Types[TUINT32], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBswap32, types.Types[types.TUINT32], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X)
addF("runtime/internal/sys", "Bswap64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBswap64, types.Types[TUINT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBswap64, types.Types[types.TUINT64], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X)
/******** runtime/internal/atomic ********/
addF("runtime/internal/atomic", "Load",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue2(ssa.OpAtomicLoad32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue2(ssa.OpAtomicLoad32, types.NewTuple(types.Types[types.TUINT32], types.TypeMem), args[0], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT32], v)
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Load8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue2(ssa.OpAtomicLoad8, types.NewTuple(types.Types[TUINT8], types.TypeMem), args[0], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT8], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue2(ssa.OpAtomicLoad8, types.NewTuple(types.Types[types.TUINT8], types.TypeMem), args[0], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT8], v)
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Load64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue2(ssa.OpAtomicLoad64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue2(ssa.OpAtomicLoad64, types.NewTuple(types.Types[types.TUINT64], types.TypeMem), args[0], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT64], v)
},
sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "LoadAcq",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue2(ssa.OpAtomicLoadAcq32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue2(ssa.OpAtomicLoadAcq32, types.NewTuple(types.Types[types.TUINT32], types.TypeMem), args[0], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT32], v)
},
sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "LoadAcq64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue2(ssa.OpAtomicLoadAcq64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue2(ssa.OpAtomicLoadAcq64, types.NewTuple(types.Types[types.TUINT64], types.TypeMem), args[0], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT64], v)
},
sys.PPC64)
addF("runtime/internal/atomic", "Loadp",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
v := s.newValue2(ssa.OpAtomicLoadPtr, types.NewTuple(s.f.Config.Types.BytePtr, types.TypeMem), args[0], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, s.f.Config.Types.BytePtr, v)
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Store",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicStore32, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicStore32, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Store8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicStore8, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicStore8, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Store64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicStore64, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicStore64, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "StorepNoWB",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicStorePtrNoWB, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicStorePtrNoWB, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "StoreRel",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicStoreRel32, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicStoreRel32, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "StoreRel64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicStoreRel64, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicStoreRel64, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.PPC64)
addF("runtime/internal/atomic", "Xchg",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue3(ssa.OpAtomicExchange32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue3(ssa.OpAtomicExchange32, types.NewTuple(types.Types[types.TUINT32], types.TypeMem), args[0], args[1], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT32], v)
},
sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Xchg64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue3(ssa.OpAtomicExchange64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue3(ssa.OpAtomicExchange64, types.NewTuple(types.Types[types.TUINT64], types.TypeMem), args[0], args[1], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT64], v)
},
sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
- type atomicOpEmitter func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType)
+ type atomicOpEmitter func(s *state, n *ir.CallExpr, args []*ssa.Value, op ssa.Op, typ types.Kind)
- makeAtomicGuardedIntrinsicARM64 := func(op0, op1 ssa.Op, typ, rtyp types.EType, emit atomicOpEmitter) intrinsicBuilder {
+ makeAtomicGuardedIntrinsicARM64 := func(op0, op1 ssa.Op, typ, rtyp types.Kind, emit atomicOpEmitter) intrinsicBuilder {
- return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
// Target Atomic feature is identified by dynamic detection
- addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), arm64HasATOMICS, s.sb)
- v := s.load(types.Types[TBOOL], addr)
+ addr := s.entryNewValue1A(ssa.OpAddr, types.Types[types.TBOOL].PtrTo(), ir.Syms.ARM64HasATOMICS, s.sb)
+ v := s.load(types.Types[types.TBOOL], addr)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(v)
@@ -3556,7 +3816,7 @@ func init() {
// Merge results.
s.startBlock(bEnd)
- if rtyp == TNIL {
+ if rtyp == types.TNIL {
return nil
} else {
return s.variable(n, types.Types[rtyp])
@@ -3564,115 +3824,115 @@ func init() {
}
}
- atomicXchgXaddEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
+ atomicXchgXaddEmitterARM64 := func(s *state, n *ir.CallExpr, args []*ssa.Value, op ssa.Op, typ types.Kind) {
v := s.newValue3(op, types.NewTuple(types.Types[typ], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
}
addF("runtime/internal/atomic", "Xchg",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange32, ssa.OpAtomicExchange32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange32, ssa.OpAtomicExchange32Variant, types.TUINT32, types.TUINT32, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Xchg64",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange64, ssa.OpAtomicExchange64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange64, ssa.OpAtomicExchange64Variant, types.TUINT64, types.TUINT64, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Xadd",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue3(ssa.OpAtomicAdd32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue3(ssa.OpAtomicAdd32, types.NewTuple(types.Types[types.TUINT32], types.TypeMem), args[0], args[1], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT32], v)
},
sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Xadd64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue3(ssa.OpAtomicAdd64, types.NewTuple(types.Types[TUINT64], types.TypeMem), args[0], args[1], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue3(ssa.OpAtomicAdd64, types.NewTuple(types.Types[types.TUINT64], types.TypeMem), args[0], args[1], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT64], v)
},
sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Xadd",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, types.TUINT32, types.TUINT32, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Xadd64",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, types.TUINT64, types.TUINT64, atomicXchgXaddEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Cas",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[types.TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TBOOL], v)
},
sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "Cas64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue4(ssa.OpAtomicCompareAndSwap64, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue4(ssa.OpAtomicCompareAndSwap64, types.NewTuple(types.Types[types.TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TBOOL], v)
},
sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("runtime/internal/atomic", "CasRel",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
- return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[types.TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ return s.newValue1(ssa.OpSelect0, types.Types[types.TBOOL], v)
},
sys.PPC64)
- atomicCasEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
- v := s.newValue4(op, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
- s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
+ atomicCasEmitterARM64 := func(s *state, n *ir.CallExpr, args []*ssa.Value, op ssa.Op, typ types.Kind) {
+ v := s.newValue4(op, types.NewTuple(types.Types[types.TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
+ s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
}
addF("runtime/internal/atomic", "Cas",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap32, ssa.OpAtomicCompareAndSwap32Variant, TUINT32, TBOOL, atomicCasEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap32, ssa.OpAtomicCompareAndSwap32Variant, types.TUINT32, types.TBOOL, atomicCasEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Cas64",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap64, ssa.OpAtomicCompareAndSwap64Variant, TUINT64, TBOOL, atomicCasEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap64, ssa.OpAtomicCompareAndSwap64Variant, types.TUINT64, types.TBOOL, atomicCasEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "And8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd8, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicAnd8, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "And",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicAnd32, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "Or8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicOr8, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "Or",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem())
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue3(ssa.OpAtomicOr32, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.AMD64, sys.MIPS, sys.PPC64, sys.S390X)
- atomicAndOrEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
- s.vars[&memVar] = s.newValue3(op, types.TypeMem, args[0], args[1], s.mem())
+ atomicAndOrEmitterARM64 := func(s *state, n *ir.CallExpr, args []*ssa.Value, op ssa.Op, typ types.Kind) {
+ s.vars[memVar] = s.newValue3(op, types.TypeMem, args[0], args[1], s.mem())
}
addF("runtime/internal/atomic", "And8",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd8, ssa.OpAtomicAnd8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd8, ssa.OpAtomicAnd8Variant, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "And",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd32, ssa.OpAtomicAnd32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd32, ssa.OpAtomicAnd32Variant, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Or8",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr8, ssa.OpAtomicOr8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr8, ssa.OpAtomicOr8Variant, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
addF("runtime/internal/atomic", "Or",
- makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr32, ssa.OpAtomicOr32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
+ makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr32, ssa.OpAtomicOr32Variant, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
sys.ARM64)
alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...)
@@ -3707,57 +3967,57 @@ func init() {
/******** math ********/
addF("math", "Sqrt",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpSqrt, types.Types[TFLOAT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpSqrt, types.Types[types.TFLOAT64], args[0])
},
sys.I386, sys.AMD64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X, sys.Wasm)
addF("math", "Trunc",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpTrunc, types.Types[TFLOAT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpTrunc, types.Types[types.TFLOAT64], args[0])
},
sys.ARM64, sys.PPC64, sys.S390X, sys.Wasm)
addF("math", "Ceil",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpCeil, types.Types[TFLOAT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpCeil, types.Types[types.TFLOAT64], args[0])
},
sys.ARM64, sys.PPC64, sys.S390X, sys.Wasm)
addF("math", "Floor",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpFloor, types.Types[TFLOAT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpFloor, types.Types[types.TFLOAT64], args[0])
},
sys.ARM64, sys.PPC64, sys.S390X, sys.Wasm)
addF("math", "Round",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpRound, types.Types[TFLOAT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpRound, types.Types[types.TFLOAT64], args[0])
},
sys.ARM64, sys.PPC64, sys.S390X)
addF("math", "RoundToEven",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpRoundToEven, types.Types[TFLOAT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpRoundToEven, types.Types[types.TFLOAT64], args[0])
},
sys.ARM64, sys.S390X, sys.Wasm)
addF("math", "Abs",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpAbs, types.Types[TFLOAT64], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpAbs, types.Types[types.TFLOAT64], args[0])
},
sys.ARM64, sys.ARM, sys.PPC64, sys.Wasm)
addF("math", "Copysign",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue2(ssa.OpCopysign, types.Types[TFLOAT64], args[0], args[1])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue2(ssa.OpCopysign, types.Types[types.TFLOAT64], args[0], args[1])
},
sys.PPC64, sys.Wasm)
addF("math", "FMA",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue3(ssa.OpFMA, types.Types[TFLOAT64], args[0], args[1], args[2])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2])
},
sys.ARM64, sys.PPC64, sys.S390X)
addF("math", "FMA",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if !s.config.UseFMA {
s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
- return s.variable(n, types.Types[TFLOAT64])
+ return s.variable(n, types.Types[types.TFLOAT64])
}
- v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasFMA)
+ v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasFMA)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(v)
@@ -3770,7 +4030,7 @@ func init() {
// We have the intrinsic - use it directly.
s.startBlock(bTrue)
- s.vars[n] = s.newValue3(ssa.OpFMA, types.Types[TFLOAT64], args[0], args[1], args[2])
+ s.vars[n] = s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2])
s.endBlock().AddEdgeTo(bEnd)
// Call the pure Go version.
@@ -3780,17 +4040,17 @@ func init() {
// Merge results.
s.startBlock(bEnd)
- return s.variable(n, types.Types[TFLOAT64])
+ return s.variable(n, types.Types[types.TFLOAT64])
},
sys.AMD64)
addF("math", "FMA",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if !s.config.UseFMA {
s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
- return s.variable(n, types.Types[TFLOAT64])
+ return s.variable(n, types.Types[types.TFLOAT64])
}
- addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb)
- v := s.load(types.Types[TBOOL], addr)
+ addr := s.entryNewValue1A(ssa.OpAddr, types.Types[types.TBOOL].PtrTo(), ir.Syms.ARMHasVFPv4, s.sb)
+ v := s.load(types.Types[types.TBOOL], addr)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(v)
@@ -3803,7 +4063,7 @@ func init() {
// We have the intrinsic - use it directly.
s.startBlock(bTrue)
- s.vars[n] = s.newValue3(ssa.OpFMA, types.Types[TFLOAT64], args[0], args[1], args[2])
+ s.vars[n] = s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2])
s.endBlock().AddEdgeTo(bEnd)
// Call the pure Go version.
@@ -3813,13 +4073,13 @@ func init() {
// Merge results.
s.startBlock(bEnd)
- return s.variable(n, types.Types[TFLOAT64])
+ return s.variable(n, types.Types[types.TFLOAT64])
},
sys.ARM)
- makeRoundAMD64 := func(op ssa.Op) func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasSSE41)
+ makeRoundAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasSSE41)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(v)
@@ -3832,7 +4092,7 @@ func init() {
// We have the intrinsic - use it directly.
s.startBlock(bTrue)
- s.vars[n] = s.newValue1(op, types.Types[TFLOAT64], args[0])
+ s.vars[n] = s.newValue1(op, types.Types[types.TFLOAT64], args[0])
s.endBlock().AddEdgeTo(bEnd)
// Call the pure Go version.
@@ -3842,7 +4102,7 @@ func init() {
// Merge results.
s.startBlock(bEnd)
- return s.variable(n, types.Types[TFLOAT64])
+ return s.variable(n, types.Types[types.TFLOAT64])
}
}
addF("math", "RoundToEven",
@@ -3860,55 +4120,55 @@ func init() {
/******** math/bits ********/
addF("math/bits", "TrailingZeros64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpCtz64, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpCtz64, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
addF("math/bits", "TrailingZeros32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpCtz32, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpCtz32, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
addF("math/bits", "TrailingZeros16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- x := s.newValue1(ssa.OpZeroExt16to32, types.Types[TUINT32], args[0])
- c := s.constInt32(types.Types[TUINT32], 1<<16)
- y := s.newValue2(ssa.OpOr32, types.Types[TUINT32], x, c)
- return s.newValue1(ssa.OpCtz32, types.Types[TINT], y)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ x := s.newValue1(ssa.OpZeroExt16to32, types.Types[types.TUINT32], args[0])
+ c := s.constInt32(types.Types[types.TUINT32], 1<<16)
+ y := s.newValue2(ssa.OpOr32, types.Types[types.TUINT32], x, c)
+ return s.newValue1(ssa.OpCtz32, types.Types[types.TINT], y)
},
sys.MIPS)
addF("math/bits", "TrailingZeros16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpCtz16, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpCtz16, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.I386, sys.ARM, sys.ARM64, sys.Wasm)
addF("math/bits", "TrailingZeros16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- x := s.newValue1(ssa.OpZeroExt16to64, types.Types[TUINT64], args[0])
- c := s.constInt64(types.Types[TUINT64], 1<<16)
- y := s.newValue2(ssa.OpOr64, types.Types[TUINT64], x, c)
- return s.newValue1(ssa.OpCtz64, types.Types[TINT], y)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ x := s.newValue1(ssa.OpZeroExt16to64, types.Types[types.TUINT64], args[0])
+ c := s.constInt64(types.Types[types.TUINT64], 1<<16)
+ y := s.newValue2(ssa.OpOr64, types.Types[types.TUINT64], x, c)
+ return s.newValue1(ssa.OpCtz64, types.Types[types.TINT], y)
},
sys.S390X, sys.PPC64)
addF("math/bits", "TrailingZeros8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- x := s.newValue1(ssa.OpZeroExt8to32, types.Types[TUINT32], args[0])
- c := s.constInt32(types.Types[TUINT32], 1<<8)
- y := s.newValue2(ssa.OpOr32, types.Types[TUINT32], x, c)
- return s.newValue1(ssa.OpCtz32, types.Types[TINT], y)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ x := s.newValue1(ssa.OpZeroExt8to32, types.Types[types.TUINT32], args[0])
+ c := s.constInt32(types.Types[types.TUINT32], 1<<8)
+ y := s.newValue2(ssa.OpOr32, types.Types[types.TUINT32], x, c)
+ return s.newValue1(ssa.OpCtz32, types.Types[types.TINT], y)
},
sys.MIPS)
addF("math/bits", "TrailingZeros8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpCtz8, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpCtz8, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM, sys.ARM64, sys.Wasm)
addF("math/bits", "TrailingZeros8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- x := s.newValue1(ssa.OpZeroExt8to64, types.Types[TUINT64], args[0])
- c := s.constInt64(types.Types[TUINT64], 1<<8)
- y := s.newValue2(ssa.OpOr64, types.Types[TUINT64], x, c)
- return s.newValue1(ssa.OpCtz64, types.Types[TINT], y)
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ x := s.newValue1(ssa.OpZeroExt8to64, types.Types[types.TUINT64], args[0])
+ c := s.constInt64(types.Types[types.TUINT64], 1<<8)
+ y := s.newValue2(ssa.OpOr64, types.Types[types.TUINT64], x, c)
+ return s.newValue1(ssa.OpCtz64, types.Types[types.TINT], y)
},
sys.S390X)
alias("math/bits", "ReverseBytes64", "runtime/internal/sys", "Bswap64", all...)
@@ -3916,116 +4176,116 @@ func init() {
// ReverseBytes inlines correctly, no need to intrinsify it.
// ReverseBytes16 lowers to a rotate, no need for anything special here.
addF("math/bits", "Len64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitLen64, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitLen64, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
addF("math/bits", "Len32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitLen32, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM64)
addF("math/bits", "Len32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
- return s.newValue1(ssa.OpBitLen32, types.Types[TINT], args[0])
+ return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], args[0])
}
- x := s.newValue1(ssa.OpZeroExt32to64, types.Types[TUINT64], args[0])
- return s.newValue1(ssa.OpBitLen64, types.Types[TINT], x)
+ x := s.newValue1(ssa.OpZeroExt32to64, types.Types[types.TUINT64], args[0])
+ return s.newValue1(ssa.OpBitLen64, types.Types[types.TINT], x)
},
sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
addF("math/bits", "Len16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
- x := s.newValue1(ssa.OpZeroExt16to32, types.Types[TUINT32], args[0])
- return s.newValue1(ssa.OpBitLen32, types.Types[TINT], x)
+ x := s.newValue1(ssa.OpZeroExt16to32, types.Types[types.TUINT32], args[0])
+ return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], x)
}
- x := s.newValue1(ssa.OpZeroExt16to64, types.Types[TUINT64], args[0])
- return s.newValue1(ssa.OpBitLen64, types.Types[TINT], x)
+ x := s.newValue1(ssa.OpZeroExt16to64, types.Types[types.TUINT64], args[0])
+ return s.newValue1(ssa.OpBitLen64, types.Types[types.TINT], x)
},
sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
addF("math/bits", "Len16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitLen16, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitLen16, types.Types[types.TINT], args[0])
},
sys.AMD64)
addF("math/bits", "Len8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
- x := s.newValue1(ssa.OpZeroExt8to32, types.Types[TUINT32], args[0])
- return s.newValue1(ssa.OpBitLen32, types.Types[TINT], x)
+ x := s.newValue1(ssa.OpZeroExt8to32, types.Types[types.TUINT32], args[0])
+ return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], x)
}
- x := s.newValue1(ssa.OpZeroExt8to64, types.Types[TUINT64], args[0])
- return s.newValue1(ssa.OpBitLen64, types.Types[TINT], x)
+ x := s.newValue1(ssa.OpZeroExt8to64, types.Types[types.TUINT64], args[0])
+ return s.newValue1(ssa.OpBitLen64, types.Types[types.TINT], x)
},
sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
addF("math/bits", "Len8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitLen8, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitLen8, types.Types[types.TINT], args[0])
},
sys.AMD64)
addF("math/bits", "Len",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
- return s.newValue1(ssa.OpBitLen32, types.Types[TINT], args[0])
+ return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], args[0])
}
- return s.newValue1(ssa.OpBitLen64, types.Types[TINT], args[0])
+ return s.newValue1(ssa.OpBitLen64, types.Types[types.TINT], args[0])
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
// LeadingZeros is handled because it trivially calls Len.
addF("math/bits", "Reverse64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitRev64, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitRev64, types.Types[types.TINT], args[0])
},
sys.ARM64)
addF("math/bits", "Reverse32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitRev32, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitRev32, types.Types[types.TINT], args[0])
},
sys.ARM64)
addF("math/bits", "Reverse16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitRev16, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitRev16, types.Types[types.TINT], args[0])
},
sys.ARM64)
addF("math/bits", "Reverse8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpBitRev8, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpBitRev8, types.Types[types.TINT], args[0])
},
sys.ARM64)
addF("math/bits", "Reverse",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
- return s.newValue1(ssa.OpBitRev32, types.Types[TINT], args[0])
+ return s.newValue1(ssa.OpBitRev32, types.Types[types.TINT], args[0])
}
- return s.newValue1(ssa.OpBitRev64, types.Types[TINT], args[0])
+ return s.newValue1(ssa.OpBitRev64, types.Types[types.TINT], args[0])
},
sys.ARM64)
addF("math/bits", "RotateLeft8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue2(ssa.OpRotateLeft8, types.Types[TUINT8], args[0], args[1])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue2(ssa.OpRotateLeft8, types.Types[types.TUINT8], args[0], args[1])
},
sys.AMD64)
addF("math/bits", "RotateLeft16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue2(ssa.OpRotateLeft16, types.Types[TUINT16], args[0], args[1])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue2(ssa.OpRotateLeft16, types.Types[types.TUINT16], args[0], args[1])
},
sys.AMD64)
addF("math/bits", "RotateLeft32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue2(ssa.OpRotateLeft32, types.Types[TUINT32], args[0], args[1])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue2(ssa.OpRotateLeft32, types.Types[types.TUINT32], args[0], args[1])
},
sys.AMD64, sys.ARM, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm)
addF("math/bits", "RotateLeft64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue2(ssa.OpRotateLeft64, types.Types[TUINT64], args[0], args[1])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue2(ssa.OpRotateLeft64, types.Types[types.TUINT64], args[0], args[1])
},
sys.AMD64, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm)
alias("math/bits", "RotateLeft", "math/bits", "RotateLeft64", p8...)
- makeOnesCountAMD64 := func(op64 ssa.Op, op32 ssa.Op) func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasPOPCNT)
+ makeOnesCountAMD64 := func(op64 ssa.Op, op32 ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasPOPCNT)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(v)
@@ -4042,7 +4302,7 @@ func init() {
if s.config.PtrSize == 4 {
op = op32
}
- s.vars[n] = s.newValue1(op, types.Types[TINT], args[0])
+ s.vars[n] = s.newValue1(op, types.Types[types.TINT], args[0])
s.endBlock().AddEdgeTo(bEnd)
// Call the pure Go version.
@@ -4052,67 +4312,67 @@ func init() {
// Merge results.
s.startBlock(bEnd)
- return s.variable(n, types.Types[TINT])
+ return s.variable(n, types.Types[types.TINT])
}
}
addF("math/bits", "OnesCount64",
makeOnesCountAMD64(ssa.OpPopCount64, ssa.OpPopCount64),
sys.AMD64)
addF("math/bits", "OnesCount64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpPopCount64, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpPopCount64, types.Types[types.TINT], args[0])
},
sys.PPC64, sys.ARM64, sys.S390X, sys.Wasm)
addF("math/bits", "OnesCount32",
makeOnesCountAMD64(ssa.OpPopCount32, ssa.OpPopCount32),
sys.AMD64)
addF("math/bits", "OnesCount32",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpPopCount32, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpPopCount32, types.Types[types.TINT], args[0])
},
sys.PPC64, sys.ARM64, sys.S390X, sys.Wasm)
addF("math/bits", "OnesCount16",
makeOnesCountAMD64(ssa.OpPopCount16, ssa.OpPopCount16),
sys.AMD64)
addF("math/bits", "OnesCount16",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpPopCount16, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpPopCount16, types.Types[types.TINT], args[0])
},
sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm)
addF("math/bits", "OnesCount8",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue1(ssa.OpPopCount8, types.Types[TINT], args[0])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue1(ssa.OpPopCount8, types.Types[types.TINT], args[0])
},
sys.S390X, sys.PPC64, sys.Wasm)
addF("math/bits", "OnesCount",
makeOnesCountAMD64(ssa.OpPopCount64, ssa.OpPopCount32),
sys.AMD64)
addF("math/bits", "Mul64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1])
},
sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X, sys.MIPS64)
alias("math/bits", "Mul", "math/bits", "Mul64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchS390X, sys.ArchMIPS64, sys.ArchMIPS64LE)
addF("math/bits", "Add64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue3(ssa.OpAdd64carry, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1], args[2])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue3(ssa.OpAdd64carry, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1], args[2])
},
sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X)
alias("math/bits", "Add", "math/bits", "Add64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchS390X)
addF("math/bits", "Sub64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue3(ssa.OpSub64borrow, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1], args[2])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue3(ssa.OpSub64borrow, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1], args[2])
},
sys.AMD64, sys.ARM64, sys.S390X)
alias("math/bits", "Sub", "math/bits", "Sub64", sys.ArchAMD64, sys.ArchARM64, sys.ArchS390X)
addF("math/bits", "Div64",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
// check for divide-by-zero/overflow and panic with appropriate message
- cmpZero := s.newValue2(s.ssaOp(ONE, types.Types[TUINT64]), types.Types[TBOOL], args[2], s.zeroVal(types.Types[TUINT64]))
- s.check(cmpZero, panicdivide)
- cmpOverflow := s.newValue2(s.ssaOp(OLT, types.Types[TUINT64]), types.Types[TBOOL], args[0], args[2])
- s.check(cmpOverflow, panicoverflow)
- return s.newValue3(ssa.OpDiv128u, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1], args[2])
+ cmpZero := s.newValue2(s.ssaOp(ir.ONE, types.Types[types.TUINT64]), types.Types[types.TBOOL], args[2], s.zeroVal(types.Types[types.TUINT64]))
+ s.check(cmpZero, ir.Syms.Panicdivide)
+ cmpOverflow := s.newValue2(s.ssaOp(ir.OLT, types.Types[types.TUINT64]), types.Types[types.TBOOL], args[0], args[2])
+ s.check(cmpOverflow, ir.Syms.Panicoverflow)
+ return s.newValue3(ssa.OpDiv128u, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1], args[2])
},
sys.AMD64)
alias("math/bits", "Div", "math/bits", "Div64", sys.ArchAMD64)
@@ -4166,8 +4426,8 @@ func init() {
/******** math/big ********/
add("math/big", "mulWW",
- func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
- return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1])
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1])
},
sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64LE, sys.ArchPPC64, sys.ArchS390X)
}
@@ -4179,17 +4439,17 @@ func findIntrinsic(sym *types.Sym) intrinsicBuilder {
return nil
}
pkg := sym.Pkg.Path
- if sym.Pkg == localpkg {
- pkg = myimportpath
+ if sym.Pkg == types.LocalPkg {
+ pkg = base.Ctxt.Pkgpath
}
- if flag_race && pkg == "sync/atomic" {
+ if base.Flag.Race && pkg == "sync/atomic" {
// The race detector needs to be able to intercept these calls.
// We can't intrinsify them.
return nil
}
// Skip intrinsifying math functions (which may contain hard-float
// instructions) when soft-float
- if thearch.SoftFloat && pkg == "math" {
+ if Arch.SoftFloat && pkg == "math" {
return nil
}
@@ -4201,19 +4461,23 @@ func findIntrinsic(sym *types.Sym) intrinsicBuilder {
return nil
}
}
- return intrinsics[intrinsicKey{thearch.LinkArch.Arch, pkg, fn}]
+ return intrinsics[intrinsicKey{Arch.LinkArch.Arch, pkg, fn}]
}
-func isIntrinsicCall(n *Node) bool {
- if n == nil || n.Left == nil {
+func IsIntrinsicCall(n *ir.CallExpr) bool {
+ if n == nil {
+ return false
+ }
+ name, ok := n.X.(*ir.Name)
+ if !ok {
return false
}
- return findIntrinsic(n.Left.Sym) != nil
+ return findIntrinsic(name.Sym()) != nil
}
// intrinsicCall converts a call to a recognized intrinsic function into the intrinsic SSA operation.
-func (s *state) intrinsicCall(n *Node) *ssa.Value {
- v := findIntrinsic(n.Left.Sym)(s, n, s.intrinsicArgs(n))
+func (s *state) intrinsicCall(n *ir.CallExpr) *ssa.Value {
+ v := findIntrinsic(n.X.Sym())(s, n, s.intrinsicArgs(n))
if ssa.IntrinsicsDebug > 0 {
x := v
if x == nil {
@@ -4222,36 +4486,15 @@ func (s *state) intrinsicCall(n *Node) *ssa.Value {
if x.Op == ssa.OpSelect0 || x.Op == ssa.OpSelect1 {
x = x.Args[0]
}
- Warnl(n.Pos, "intrinsic substitution for %v with %s", n.Left.Sym.Name, x.LongString())
+ base.WarnfAt(n.Pos(), "intrinsic substitution for %v with %s", n.X.Sym().Name, x.LongString())
}
return v
}
// intrinsicArgs extracts args from n, evaluates them to SSA values, and returns them.
-func (s *state) intrinsicArgs(n *Node) []*ssa.Value {
- // Construct map of temps; see comments in s.call about the structure of n.
- temps := map[*Node]*ssa.Value{}
- for _, a := range n.List.Slice() {
- if a.Op != OAS {
- s.Fatalf("non-assignment as a temp function argument %v", a.Op)
- }
- l, r := a.Left, a.Right
- if l.Op != ONAME {
- s.Fatalf("non-ONAME temp function argument %v", a.Op)
- }
- // Evaluate and store to "temporary".
- // Walk ensures these temporaries are dead outside of n.
- temps[l] = s.expr(r)
- }
- args := make([]*ssa.Value, n.Rlist.Len())
- for i, n := range n.Rlist.Slice() {
- // Store a value to an argument slot.
- if x, ok := temps[n]; ok {
- // This is a previously computed temporary.
- args[i] = x
- continue
- }
- // This is an explicit value; evaluate it.
+func (s *state) intrinsicArgs(n *ir.CallExpr) []*ssa.Value {
+ args := make([]*ssa.Value, len(n.Args))
+ for i, n := range n.Args {
args[i] = s.expr(n)
}
return args
@@ -4263,62 +4506,48 @@ func (s *state) intrinsicArgs(n *Node) []*ssa.Value {
// call. We will also record funcdata information on where the args are stored
// (as well as the deferBits variable), and this will enable us to run the proper
// defer calls during panics.
-func (s *state) openDeferRecord(n *Node) {
- // Do any needed expression evaluation for the args (including the
- // receiver, if any). This may be evaluating something like 'autotmp_3 =
- // once.mutex'. Such a statement will create a mapping in s.vars[] from
- // the autotmp name to the evaluated SSA arg value, but won't do any
- // stores to the stack.
- s.stmtList(n.List)
-
+func (s *state) openDeferRecord(n *ir.CallExpr) {
var args []*ssa.Value
- var argNodes []*Node
+ var argNodes []*ir.Name
opendefer := &openDeferInfo{
n: n,
}
- fn := n.Left
- if n.Op == OCALLFUNC {
+ fn := n.X
+ if n.Op() == ir.OCALLFUNC {
// We must always store the function value in a stack slot for the
// runtime panic code to use. But in the defer exit code, we will
// call the function directly if it is a static function.
closureVal := s.expr(fn)
- closure := s.openDeferSave(nil, fn.Type, closureVal)
- opendefer.closureNode = closure.Aux.(*Node)
- if !(fn.Op == ONAME && fn.Class() == PFUNC) {
+ closure := s.openDeferSave(nil, fn.Type(), closureVal)
+ opendefer.closureNode = closure.Aux.(*ir.Name)
+ if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) {
opendefer.closure = closure
}
- } else if n.Op == OCALLMETH {
- if fn.Op != ODOTMETH {
- Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
- }
- closureVal := s.getMethodClosure(fn)
- // We must always store the function value in a stack slot for the
- // runtime panic code to use. But in the defer exit code, we will
- // call the method directly.
- closure := s.openDeferSave(nil, fn.Type, closureVal)
- opendefer.closureNode = closure.Aux.(*Node)
+ } else if n.Op() == ir.OCALLMETH {
+ base.Fatalf("OCALLMETH missed by walkCall")
} else {
- if fn.Op != ODOTINTER {
- Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
+ if fn.Op() != ir.ODOTINTER {
+ base.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op())
}
+ fn := fn.(*ir.SelectorExpr)
closure, rcvr := s.getClosureAndRcvr(fn)
opendefer.closure = s.openDeferSave(nil, closure.Type, closure)
// Important to get the receiver type correct, so it is recognized
// as a pointer for GC purposes.
- opendefer.rcvr = s.openDeferSave(nil, fn.Type.Recv().Type, rcvr)
- opendefer.closureNode = opendefer.closure.Aux.(*Node)
- opendefer.rcvrNode = opendefer.rcvr.Aux.(*Node)
+ opendefer.rcvr = s.openDeferSave(nil, fn.Type().Recv().Type, rcvr)
+ opendefer.closureNode = opendefer.closure.Aux.(*ir.Name)
+ opendefer.rcvrNode = opendefer.rcvr.Aux.(*ir.Name)
}
- for _, argn := range n.Rlist.Slice() {
+ for _, argn := range n.Args {
var v *ssa.Value
- if canSSAType(argn.Type) {
- v = s.openDeferSave(nil, argn.Type, s.expr(argn))
+ if TypeOK(argn.Type()) {
+ v = s.openDeferSave(nil, argn.Type(), s.expr(argn))
} else {
- v = s.openDeferSave(argn, argn.Type, nil)
+ v = s.openDeferSave(argn, argn.Type(), nil)
}
args = append(args, v)
- argNodes = append(argNodes, v.Aux.(*Node))
+ argNodes = append(argNodes, v.Aux.(*ir.Name))
}
opendefer.argVals = args
opendefer.argNodes = argNodes
@@ -4327,10 +4556,10 @@ func (s *state) openDeferRecord(n *Node) {
// Update deferBits only after evaluation and storage to stack of
// args/receiver/interface is successful.
- bitvalue := s.constInt8(types.Types[TUINT8], 1<= 0; i-- {
r := s.openDefers[i]
bCond := s.f.NewBlock(ssa.BlockPlain)
bEnd := s.f.NewBlock(ssa.BlockPlain)
- deferBits := s.variable(&deferBitsVar, types.Types[TUINT8])
+ deferBits := s.variable(deferBitsVar, types.Types[types.TUINT8])
// Generate code to check if the bit associated with the current
// defer is set.
- bitval := s.constInt8(types.Types[TUINT8], 1< int64(4*Widthptr) {
+// TypeOK reports whether variables of type t are SSA-able.
+func TypeOK(t *types.Type) bool {
+ types.CalcSize(t)
+ if t.Width > int64(4*types.PtrSize) {
// 4*Widthptr is an arbitrary constant. We want it
// to be at least 3*Widthptr so slices can be registerized.
// Too big and we'll introduce too much register pressure.
return false
}
- switch t.Etype {
- case TARRAY:
+ switch t.Kind() {
+ case types.TARRAY:
// We can't do larger arrays because dynamic indexing is
// not supported on SSA variables.
// TODO: allow if all indexes are constant.
if t.NumElem() <= 1 {
- return canSSAType(t.Elem())
+ return TypeOK(t.Elem())
}
return false
- case TSTRUCT:
+ case types.TSTRUCT:
if t.NumFields() > ssa.MaxStruct {
return false
}
for _, t1 := range t.Fields().Slice() {
- if !canSSAType(t1.Type) {
+ if !TypeOK(t1.Type) {
return false
}
}
@@ -5060,7 +5204,7 @@ func canSSAType(t *types.Type) bool {
}
// exprPtr evaluates n to a pointer and nil-checks it.
-func (s *state) exprPtr(n *Node, bounded bool, lineno src.XPos) *ssa.Value {
+func (s *state) exprPtr(n ir.Node, bounded bool, lineno src.XPos) *ssa.Value {
p := s.expr(n)
if bounded || n.NonNil() {
if s.f.Frontend().Debug_checknil() && lineno.Line() > 1 {
@@ -5076,7 +5220,7 @@ func (s *state) exprPtr(n *Node, bounded bool, lineno src.XPos) *ssa.Value {
// Used only for automatically inserted nil checks,
// not for user code like 'x != nil'.
func (s *state) nilCheck(ptr *ssa.Value) {
- if disable_checknil != 0 || s.curfn.Func.NilCheckDisabled() {
+ if base.Debug.DisableNil != 0 || s.curfn.NilCheckDisabled() {
return
}
s.newValue2(ssa.OpNilCheck, types.TypeVoid, ptr, s.mem())
@@ -5091,7 +5235,7 @@ func (s *state) nilCheck(ptr *ssa.Value) {
func (s *state) boundsCheck(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bool) *ssa.Value {
idx = s.extendIndex(idx, len, kind, bounded)
- if bounded || Debug.B != 0 {
+ if bounded || base.Flag.B != 0 {
// If bounded or bounds checking is flag-disabled, then no check necessary,
// just return the extended index.
//
@@ -5141,9 +5285,9 @@ func (s *state) boundsCheck(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bo
var cmp *ssa.Value
if kind == ssa.BoundsIndex || kind == ssa.BoundsIndexU {
- cmp = s.newValue2(ssa.OpIsInBounds, types.Types[TBOOL], idx, len)
+ cmp = s.newValue2(ssa.OpIsInBounds, types.Types[types.TBOOL], idx, len)
} else {
- cmp = s.newValue2(ssa.OpIsSliceInBounds, types.Types[TBOOL], idx, len)
+ cmp = s.newValue2(ssa.OpIsSliceInBounds, types.Types[types.TBOOL], idx, len)
}
b := s.endBlock()
b.Kind = ssa.BlockIf
@@ -5153,7 +5297,7 @@ func (s *state) boundsCheck(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bo
b.AddEdgeTo(bPanic)
s.startBlock(bPanic)
- if thearch.LinkArch.Family == sys.Wasm {
+ if Arch.LinkArch.Family == sys.Wasm {
// TODO(khr): figure out how to do "register" based calling convention for bounds checks.
// Should be similar to gcWriteBarrier, but I can't make it work.
s.rtcall(BoundsCheckFunc[kind], false, nil, idx, len)
@@ -5164,12 +5308,12 @@ func (s *state) boundsCheck(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bo
s.startBlock(bNext)
// In Spectre index mode, apply an appropriate mask to avoid speculative out-of-bounds accesses.
- if spectreIndex {
+ if base.Flag.Cfg.SpectreIndex {
op := ssa.OpSpectreIndex
if kind != ssa.BoundsIndex && kind != ssa.BoundsIndexU {
op = ssa.OpSpectreSliceIndex
}
- idx = s.newValue2(op, types.Types[TINT], idx, len)
+ idx = s.newValue2(op, types.Types[types.TINT], idx, len)
}
return idx
@@ -5183,7 +5327,7 @@ func (s *state) check(cmp *ssa.Value, fn *obj.LSym) {
b.Likely = ssa.BranchLikely
bNext := s.f.NewBlock(ssa.BlockPlain)
line := s.peekPos()
- pos := Ctxt.PosTable.Pos(line)
+ pos := base.Ctxt.PosTable.Pos(line)
fl := funcLine{f: fn, base: pos.Base(), line: pos.Line()}
bPanic := s.panics[fl]
if bPanic == nil {
@@ -5199,7 +5343,7 @@ func (s *state) check(cmp *ssa.Value, fn *obj.LSym) {
s.startBlock(bNext)
}
-func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value {
+func (s *state) intDivide(n ir.Node, a, b *ssa.Value) *ssa.Value {
needcheck := true
switch b.Op {
case ssa.OpConst8, ssa.OpConst16, ssa.OpConst32, ssa.OpConst64:
@@ -5209,10 +5353,10 @@ func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value {
}
if needcheck {
// do a size-appropriate check for zero
- cmp := s.newValue2(s.ssaOp(ONE, n.Type), types.Types[TBOOL], b, s.zeroVal(n.Type))
- s.check(cmp, panicdivide)
+ cmp := s.newValue2(s.ssaOp(ir.ONE, n.Type()), types.Types[types.TBOOL], b, s.zeroVal(n.Type()))
+ s.check(cmp, ir.Syms.Panicdivide)
}
- return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+ return s.newValue2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
}
// rtcall issues a call to the given runtime function fn with the listed args.
@@ -5222,31 +5366,25 @@ 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 {
s.prevCall = nil
// Write args to the stack
- off := Ctxt.FixedFrameSize()
- testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
+ off := base.Ctxt.FixedFrameSize()
var ACArgs []ssa.Param
var ACResults []ssa.Param
var callArgs []*ssa.Value
for _, arg := range args {
t := arg.Type
- off = Rnd(off, t.Alignment())
+ off = types.Rnd(off, t.Alignment())
size := t.Size()
ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
- if testLateExpansion {
- callArgs = append(callArgs, arg)
- } else {
- ptr := s.constOffPtrSP(t.PtrTo(), off)
- s.store(t, ptr, arg)
- }
+ callArgs = append(callArgs, arg)
off += size
}
- off = Rnd(off, int64(Widthreg))
+ off = types.Rnd(off, int64(types.RegSize))
// Accumulate results types and offsets
offR := off
for _, t := range results {
- offR = Rnd(offR, t.Alignment())
+ offR = types.Rnd(offR, t.Alignment())
ACResults = append(ACResults, ssa.Param{Type: t, Offset: int32(offR)})
offR += t.Size()
}
@@ -5254,22 +5392,17 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
// Issue call
var call *ssa.Value
aux := ssa.StaticAuxCall(fn, ACArgs, ACResults)
- if testLateExpansion {
- callArgs = append(callArgs, s.mem())
- call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
- call.AddArgs(callArgs...)
- s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
- } else {
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
- s.vars[&memVar] = call
- }
+ callArgs = append(callArgs, s.mem())
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ s.vars[memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
if !returns {
// Finish block
b := s.endBlock()
b.Kind = ssa.BlockExit
b.SetControl(call)
- call.AuxInt = off - Ctxt.FixedFrameSize()
+ call.AuxInt = off - base.Ctxt.FixedFrameSize()
if len(results) > 0 {
s.Fatalf("panic call can't have results")
}
@@ -5278,26 +5411,17 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
// Load results
res := make([]*ssa.Value, len(results))
- if testLateExpansion {
- for i, t := range results {
- off = Rnd(off, t.Alignment())
- if canSSAType(t) {
- res[i] = s.newValue1I(ssa.OpSelectN, t, int64(i), call)
- } else {
- addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(t), int64(i), call)
- res[i] = s.rawLoad(t, addr)
- }
- off += t.Size()
- }
- } else {
- for i, t := range results {
- off = Rnd(off, t.Alignment())
- ptr := s.constOffPtrSP(types.NewPtr(t), off)
- res[i] = s.load(t, ptr)
- off += t.Size()
+ for i, t := range results {
+ off = types.Rnd(off, t.Alignment())
+ if TypeOK(t) {
+ res[i] = s.newValue1I(ssa.OpSelectN, t, int64(i), call)
+ } else {
+ addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(t), int64(i), call)
+ res[i] = s.rawLoad(t, addr)
}
+ off += t.Size()
}
- off = Rnd(off, int64(Widthptr))
+ off = types.Rnd(off, int64(types.PtrSize))
// Remember how much callee stack space we needed.
call.AuxInt = off
@@ -5311,7 +5435,7 @@ func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask,
if skip == 0 && (!t.HasPointers() || ssa.IsStackAddr(left)) {
// Known to not have write barrier. Store the whole type.
- s.vars[&memVar] = s.newValue3Apos(ssa.OpStore, types.TypeMem, t, left, right, s.mem(), leftIsStmt)
+ s.vars[memVar] = s.newValue3Apos(ssa.OpStore, types.TypeMem, t, left, right, s.mem(), leftIsStmt)
return
}
@@ -5340,24 +5464,24 @@ func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip ski
if skip&skipLen != 0 {
return
}
- len := s.newValue1(ssa.OpStringLen, types.Types[TINT], right)
+ len := s.newValue1(ssa.OpStringLen, types.Types[types.TINT], right)
lenAddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, s.config.PtrSize, left)
- s.store(types.Types[TINT], lenAddr, len)
+ s.store(types.Types[types.TINT], lenAddr, len)
case t.IsSlice():
if skip&skipLen == 0 {
- len := s.newValue1(ssa.OpSliceLen, types.Types[TINT], right)
+ len := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], right)
lenAddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, s.config.PtrSize, left)
- s.store(types.Types[TINT], lenAddr, len)
+ s.store(types.Types[types.TINT], lenAddr, len)
}
if skip&skipCap == 0 {
- cap := s.newValue1(ssa.OpSliceCap, types.Types[TINT], right)
+ cap := s.newValue1(ssa.OpSliceCap, types.Types[types.TINT], right)
capAddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, 2*s.config.PtrSize, left)
- s.store(types.Types[TINT], capAddr, cap)
+ s.store(types.Types[types.TINT], capAddr, cap)
}
case t.IsInterface():
// itab field doesn't need a write barrier (even though it is a pointer).
itab := s.newValue1(ssa.OpITab, s.f.Config.Types.BytePtr, right)
- s.store(types.Types[TUINTPTR], left, itab)
+ s.store(types.Types[types.TUINTPTR], left, itab)
case t.IsStruct():
n := t.NumFields()
for i := 0; i < n; i++ {
@@ -5415,24 +5539,18 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
}
}
-// putArg evaluates n for the purpose of passing it as an argument to a function and returns the corresponding Param for the call.
-// If forLateExpandedCall is true, it returns the argument value to pass to the call operation.
-// If forLateExpandedCall is false, then the value is stored at the specified stack offset, and the returned value is nil.
-func (s *state) putArg(n *Node, t *types.Type, off int64, forLateExpandedCall bool) (ssa.Param, *ssa.Value) {
+// putArg evaluates n for the purpose of passing it as an argument to a function and returns the corresponding Param and value for the call.
+func (s *state) putArg(n ir.Node, t *types.Type, off int64) (ssa.Param, *ssa.Value) {
var a *ssa.Value
- if forLateExpandedCall {
- if !canSSAType(t) {
- a = s.newValue2(ssa.OpDereference, t, s.addr(n), s.mem())
- } else {
- a = s.expr(n)
- }
+ if !TypeOK(t) {
+ a = s.newValue2(ssa.OpDereference, t, s.addr(n), s.mem())
} else {
- s.storeArgWithBase(n, t, s.sp, off)
+ a = s.expr(n)
}
return ssa.Param{Type: t, Offset: int32(off)}, a
}
-func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) {
+func (s *state) storeArgWithBase(n ir.Node, t *types.Type, base *ssa.Value, off int64) {
pt := types.NewPtr(t)
var addr *ssa.Value
if base == s.sp {
@@ -5442,7 +5560,7 @@ func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off in
addr = s.newValue1I(ssa.OpOffPtr, pt, off, base)
}
- if !canSSAType(t) {
+ if !TypeOK(t) {
a := s.addr(n)
s.move(t, addr, a)
return
@@ -5461,11 +5579,11 @@ func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value)
switch {
case t.IsSlice():
ptr = s.newValue1(ssa.OpSlicePtr, types.NewPtr(t.Elem()), v)
- len = s.newValue1(ssa.OpSliceLen, types.Types[TINT], v)
- cap = s.newValue1(ssa.OpSliceCap, types.Types[TINT], v)
+ len = s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], v)
+ cap = s.newValue1(ssa.OpSliceCap, types.Types[types.TINT], v)
case t.IsString():
- ptr = s.newValue1(ssa.OpStringPtr, types.NewPtr(types.Types[TUINT8]), v)
- len = s.newValue1(ssa.OpStringLen, types.Types[TINT], v)
+ ptr = s.newValue1(ssa.OpStringPtr, types.NewPtr(types.Types[types.TUINT8]), v)
+ len = s.newValue1(ssa.OpStringLen, types.Types[types.TINT], v)
cap = len
case t.IsPtr():
if !t.Elem().IsArray() {
@@ -5473,7 +5591,7 @@ func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value)
}
s.nilCheck(v)
ptr = s.newValue1(ssa.OpCopy, types.NewPtr(t.Elem().Elem()), v)
- len = s.constInt(types.Types[TINT], t.Elem().NumElem())
+ len = s.constInt(types.Types[types.TINT], t.Elem().NumElem())
cap = len
default:
s.Fatalf("bad type in slice %v\n", t)
@@ -5481,7 +5599,7 @@ func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value)
// Set default values
if i == nil {
- i = s.constInt(types.Types[TINT], 0)
+ i = s.constInt(types.Types[types.TINT], 0)
}
if j == nil {
j = len
@@ -5519,18 +5637,18 @@ func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value)
}
// Word-sized integer operations.
- subOp := s.ssaOp(OSUB, types.Types[TINT])
- mulOp := s.ssaOp(OMUL, types.Types[TINT])
- andOp := s.ssaOp(OAND, types.Types[TINT])
+ subOp := s.ssaOp(ir.OSUB, types.Types[types.TINT])
+ mulOp := s.ssaOp(ir.OMUL, types.Types[types.TINT])
+ andOp := s.ssaOp(ir.OAND, types.Types[types.TINT])
// Calculate the length (rlen) and capacity (rcap) of the new slice.
// For strings the capacity of the result is unimportant. However,
// we use rcap to test if we've generated a zero-length slice.
// Use length of strings for that.
- rlen := s.newValue2(subOp, types.Types[TINT], j, i)
+ rlen := s.newValue2(subOp, types.Types[types.TINT], j, i)
rcap := rlen
if j != k && !t.IsString() {
- rcap = s.newValue2(subOp, types.Types[TINT], k, i)
+ rcap = s.newValue2(subOp, types.Types[types.TINT], k, i)
}
if (i.Op == ssa.OpConst64 || i.Op == ssa.OpConst32) && i.AuxInt == 0 {
@@ -5552,15 +5670,15 @@ func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value)
//
// Where mask(x) is 0 if x==0 and -1 if x>0 and stride is the width
// of the element type.
- stride := s.constInt(types.Types[TINT], ptr.Type.Elem().Width)
+ stride := s.constInt(types.Types[types.TINT], ptr.Type.Elem().Width)
// The delta is the number of bytes to offset ptr by.
- delta := s.newValue2(mulOp, types.Types[TINT], i, stride)
+ delta := s.newValue2(mulOp, types.Types[types.TINT], i, stride)
// If we're slicing to the point where the capacity is zero,
// zero out the delta.
- mask := s.newValue1(ssa.OpSlicemask, types.Types[TINT], rcap)
- delta = s.newValue2(andOp, types.Types[TINT], delta, mask)
+ mask := s.newValue1(ssa.OpSlicemask, types.Types[types.TINT], rcap)
+ delta = s.newValue2(andOp, types.Types[types.TINT], delta, mask)
// Compute rptr = ptr + delta.
rptr := s.newValue2(ssa.OpAddPtr, ptr.Type, ptr, delta)
@@ -5593,15 +5711,15 @@ var u64_f32 = u642fcvtTab{
one: (*state).constInt64,
}
-func (s *state) uint64Tofloat64(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) uint64Tofloat64(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.uint64Tofloat(&u64_f64, n, x, ft, tt)
}
-func (s *state) uint64Tofloat32(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) uint64Tofloat32(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.uint64Tofloat(&u64_f32, n, x, ft, tt)
}
-func (s *state) uint64Tofloat(cvttab *u642fcvtTab, n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) uint64Tofloat(cvttab *u642fcvtTab, n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
// if x >= 0 {
// result = (floatY) x
// } else {
@@ -5627,7 +5745,7 @@ func (s *state) uint64Tofloat(cvttab *u642fcvtTab, n *Node, x *ssa.Value, ft, tt
// equal to 10000000001; that rounds up, and the 1 cannot
// be lost else it would round down if the LSB of the
// candidate mantissa is 0.
- cmp := s.newValue2(cvttab.leq, types.Types[TBOOL], s.zeroVal(ft), x)
+ cmp := s.newValue2(cvttab.leq, types.Types[types.TBOOL], s.zeroVal(ft), x)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(cmp)
@@ -5657,7 +5775,7 @@ func (s *state) uint64Tofloat(cvttab *u642fcvtTab, n *Node, x *ssa.Value, ft, tt
bElse.AddEdgeTo(bAfter)
s.startBlock(bAfter)
- return s.variable(n, n.Type)
+ return s.variable(n, n.Type())
}
type u322fcvtTab struct {
@@ -5674,21 +5792,21 @@ var u32_f32 = u322fcvtTab{
cvtF2F: ssa.OpCvt64Fto32F,
}
-func (s *state) uint32Tofloat64(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) uint32Tofloat64(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.uint32Tofloat(&u32_f64, n, x, ft, tt)
}
-func (s *state) uint32Tofloat32(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) uint32Tofloat32(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.uint32Tofloat(&u32_f32, n, x, ft, tt)
}
-func (s *state) uint32Tofloat(cvttab *u322fcvtTab, n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) uint32Tofloat(cvttab *u322fcvtTab, n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
// if x >= 0 {
// result = floatY(x)
// } else {
// result = floatY(float64(x) + (1<<32))
// }
- cmp := s.newValue2(ssa.OpLeq32, types.Types[TBOOL], s.zeroVal(ft), x)
+ cmp := s.newValue2(ssa.OpLeq32, types.Types[types.TBOOL], s.zeroVal(ft), x)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(cmp)
@@ -5707,9 +5825,9 @@ func (s *state) uint32Tofloat(cvttab *u322fcvtTab, n *Node, x *ssa.Value, ft, tt
b.AddEdgeTo(bElse)
s.startBlock(bElse)
- a1 := s.newValue1(ssa.OpCvt32to64F, types.Types[TFLOAT64], x)
- twoToThe32 := s.constFloat64(types.Types[TFLOAT64], float64(1<<32))
- a2 := s.newValue2(ssa.OpAdd64F, types.Types[TFLOAT64], a1, twoToThe32)
+ a1 := s.newValue1(ssa.OpCvt32to64F, types.Types[types.TFLOAT64], x)
+ twoToThe32 := s.constFloat64(types.Types[types.TFLOAT64], float64(1<<32))
+ a2 := s.newValue2(ssa.OpAdd64F, types.Types[types.TFLOAT64], a1, twoToThe32)
a3 := s.newValue1(cvttab.cvtF2F, tt, a2)
s.vars[n] = a3
@@ -5717,12 +5835,12 @@ func (s *state) uint32Tofloat(cvttab *u322fcvtTab, n *Node, x *ssa.Value, ft, tt
bElse.AddEdgeTo(bAfter)
s.startBlock(bAfter)
- return s.variable(n, n.Type)
+ return s.variable(n, n.Type())
}
// referenceTypeBuiltin generates code for the len/cap builtins for maps and channels.
-func (s *state) referenceTypeBuiltin(n *Node, x *ssa.Value) *ssa.Value {
- if !n.Left.Type.IsMap() && !n.Left.Type.IsChan() {
+func (s *state) referenceTypeBuiltin(n *ir.UnaryExpr, x *ssa.Value) *ssa.Value {
+ if !n.X.Type().IsMap() && !n.X.Type().IsChan() {
s.Fatalf("node must be a map or a channel")
}
// if n == nil {
@@ -5733,9 +5851,9 @@ func (s *state) referenceTypeBuiltin(n *Node, x *ssa.Value) *ssa.Value {
// // cap
// return *(((*int)n)+1)
// }
- lenType := n.Type
- nilValue := s.constNil(types.Types[TUINTPTR])
- cmp := s.newValue2(ssa.OpEqPtr, types.Types[TBOOL], x, nilValue)
+ lenType := n.Type()
+ nilValue := s.constNil(types.Types[types.TUINTPTR])
+ cmp := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], x, nilValue)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(cmp)
@@ -5754,11 +5872,11 @@ func (s *state) referenceTypeBuiltin(n *Node, x *ssa.Value) *ssa.Value {
b.AddEdgeTo(bElse)
s.startBlock(bElse)
- switch n.Op {
- case OLEN:
+ switch n.Op() {
+ case ir.OLEN:
// length is stored in the first word for map/chan
s.vars[n] = s.load(lenType, x)
- case OCAP:
+ case ir.OCAP:
// capacity is stored in the second word for chan
sw := s.newValue1I(ssa.OpOffPtr, lenType.PtrTo(), lenType.Width, x)
s.vars[n] = s.load(lenType, sw)
@@ -5819,22 +5937,22 @@ var f64_u32 = f2uCvtTab{
cutoff: 1 << 31,
}
-func (s *state) float32ToUint64(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) float32ToUint64(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.floatToUint(&f32_u64, n, x, ft, tt)
}
-func (s *state) float64ToUint64(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) float64ToUint64(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.floatToUint(&f64_u64, n, x, ft, tt)
}
-func (s *state) float32ToUint32(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) float32ToUint32(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.floatToUint(&f32_u32, n, x, ft, tt)
}
-func (s *state) float64ToUint32(n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) float64ToUint32(n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
return s.floatToUint(&f64_u32, n, x, ft, tt)
}
-func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+func (s *state) floatToUint(cvttab *f2uCvtTab, n ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
// cutoff:=1<<(intY_Size-1)
// if x < floatX(cutoff) {
// result = uintY(x)
@@ -5844,7 +5962,7 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *ty
// result = z | -(cutoff)
// }
cutoff := cvttab.floatValue(s, ft, float64(cvttab.cutoff))
- cmp := s.newValue2(cvttab.ltf, types.Types[TBOOL], x, cutoff)
+ cmp := s.newValue2(cvttab.ltf, types.Types[types.TBOOL], x, cutoff)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(cmp)
@@ -5872,31 +5990,31 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *ty
bElse.AddEdgeTo(bAfter)
s.startBlock(bAfter)
- return s.variable(n, n.Type)
+ return s.variable(n, n.Type())
}
// dottype generates SSA for a type assertion node.
// commaok indicates whether to panic or return a bool.
// If commaok is false, resok will be nil.
-func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
- iface := s.expr(n.Left) // input interface
- target := s.expr(n.Right) // target type
+func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Value) {
+ iface := s.expr(n.X) // input interface
+ target := s.reflectType(n.Type()) // target type
byteptr := s.f.Config.Types.BytePtr
- if n.Type.IsInterface() {
- if n.Type.IsEmptyInterface() {
+ if n.Type().IsInterface() {
+ if n.Type().IsEmptyInterface() {
// Converting to an empty interface.
// Input could be an empty or nonempty interface.
- if Debug_typeassert > 0 {
- Warnl(n.Pos, "type assertion inlined")
+ if base.Debug.TypeAssert > 0 {
+ base.WarnfAt(n.Pos(), "type assertion inlined")
}
// Get itab/type field from input.
itab := s.newValue1(ssa.OpITab, byteptr, iface)
// Conversion succeeds iff that field is not nil.
- cond := s.newValue2(ssa.OpNeqPtr, types.Types[TBOOL], itab, s.constNil(byteptr))
+ cond := s.newValue2(ssa.OpNeqPtr, types.Types[types.TBOOL], itab, s.constNil(byteptr))
- if n.Left.Type.IsEmptyInterface() && commaok {
+ if n.X.Type().IsEmptyInterface() && commaok {
// Converting empty interface to empty interface with ,ok is just a nil check.
return iface, cond
}
@@ -5914,32 +6032,32 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
if !commaok {
// On failure, panic by calling panicnildottype.
s.startBlock(bFail)
- s.rtcall(panicnildottype, false, nil, target)
+ s.rtcall(ir.Syms.Panicnildottype, false, nil, target)
// On success, return (perhaps modified) input interface.
s.startBlock(bOk)
- if n.Left.Type.IsEmptyInterface() {
+ if n.X.Type().IsEmptyInterface() {
res = iface // Use input interface unchanged.
return
}
// Load type out of itab, build interface with existing idata.
- off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), itab)
+ off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab)
typ := s.load(byteptr, off)
idata := s.newValue1(ssa.OpIData, byteptr, iface)
- res = s.newValue2(ssa.OpIMake, n.Type, typ, idata)
+ res = s.newValue2(ssa.OpIMake, n.Type(), typ, idata)
return
}
s.startBlock(bOk)
// nonempty -> empty
// Need to load type from itab
- off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), itab)
- s.vars[&typVar] = s.load(byteptr, off)
+ off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab)
+ s.vars[typVar] = s.load(byteptr, off)
s.endBlock()
// itab is nil, might as well use that as the nil result.
s.startBlock(bFail)
- s.vars[&typVar] = itab
+ s.vars[typVar] = itab
s.endBlock()
// Merge point.
@@ -5948,59 +6066,59 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
bFail.AddEdgeTo(bEnd)
s.startBlock(bEnd)
idata := s.newValue1(ssa.OpIData, byteptr, iface)
- res = s.newValue2(ssa.OpIMake, n.Type, s.variable(&typVar, byteptr), idata)
+ res = s.newValue2(ssa.OpIMake, n.Type(), s.variable(typVar, byteptr), idata)
resok = cond
- delete(s.vars, &typVar)
+ delete(s.vars, typVar)
return
}
// converting to a nonempty interface needs a runtime call.
- if Debug_typeassert > 0 {
- Warnl(n.Pos, "type assertion not inlined")
+ if base.Debug.TypeAssert > 0 {
+ base.WarnfAt(n.Pos(), "type assertion not inlined")
}
- if n.Left.Type.IsEmptyInterface() {
+ if n.X.Type().IsEmptyInterface() {
if commaok {
- call := s.rtcall(assertE2I2, true, []*types.Type{n.Type, types.Types[TBOOL]}, target, iface)
+ call := s.rtcall(ir.Syms.AssertE2I2, true, []*types.Type{n.Type(), types.Types[types.TBOOL]}, target, iface)
return call[0], call[1]
}
- return s.rtcall(assertE2I, true, []*types.Type{n.Type}, target, iface)[0], nil
+ return s.rtcall(ir.Syms.AssertE2I, true, []*types.Type{n.Type()}, target, iface)[0], nil
}
if commaok {
- call := s.rtcall(assertI2I2, true, []*types.Type{n.Type, types.Types[TBOOL]}, target, iface)
+ call := s.rtcall(ir.Syms.AssertI2I2, true, []*types.Type{n.Type(), types.Types[types.TBOOL]}, target, iface)
return call[0], call[1]
}
- return s.rtcall(assertI2I, true, []*types.Type{n.Type}, target, iface)[0], nil
+ return s.rtcall(ir.Syms.AssertI2I, true, []*types.Type{n.Type()}, target, iface)[0], nil
}
- if Debug_typeassert > 0 {
- Warnl(n.Pos, "type assertion inlined")
+ if base.Debug.TypeAssert > 0 {
+ base.WarnfAt(n.Pos(), "type assertion inlined")
}
// Converting to a concrete type.
- direct := isdirectiface(n.Type)
+ direct := types.IsDirectIface(n.Type())
itab := s.newValue1(ssa.OpITab, byteptr, iface) // type word of interface
- if Debug_typeassert > 0 {
- Warnl(n.Pos, "type assertion inlined")
+ if base.Debug.TypeAssert > 0 {
+ base.WarnfAt(n.Pos(), "type assertion inlined")
}
var targetITab *ssa.Value
- if n.Left.Type.IsEmptyInterface() {
+ if n.X.Type().IsEmptyInterface() {
// Looking for pointer to target type.
targetITab = target
} else {
// Looking for pointer to itab for target type and source interface.
- targetITab = s.expr(n.List.First())
+ targetITab = s.expr(n.Itab)
}
- var tmp *Node // temporary for use with large types
+ var tmp ir.Node // temporary for use with large types
var addr *ssa.Value // address of tmp
- if commaok && !canSSAType(n.Type) {
+ if commaok && !TypeOK(n.Type()) {
// unSSAable type, use temporary.
// TODO: get rid of some of these temporaries.
- tmp = tempAt(n.Pos, s.curfn, n.Type)
- s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, tmp, s.mem())
+ tmp = typecheck.TempAt(n.Pos(), s.curfn, n.Type())
+ s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, tmp.(*ir.Name), s.mem())
addr = s.addr(tmp)
}
- cond := s.newValue2(ssa.OpEqPtr, types.Types[TBOOL], itab, targetITab)
+ cond := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], itab, targetITab)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(cond)
@@ -6014,20 +6132,20 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
if !commaok {
// on failure, panic by calling panicdottype
s.startBlock(bFail)
- taddr := s.expr(n.Right.Right)
- if n.Left.Type.IsEmptyInterface() {
- s.rtcall(panicdottypeE, false, nil, itab, target, taddr)
+ taddr := s.reflectType(n.X.Type())
+ if n.X.Type().IsEmptyInterface() {
+ s.rtcall(ir.Syms.PanicdottypeE, false, nil, itab, target, taddr)
} else {
- s.rtcall(panicdottypeI, false, nil, itab, target, taddr)
+ s.rtcall(ir.Syms.PanicdottypeI, false, nil, itab, target, taddr)
}
// on success, return data from interface
s.startBlock(bOk)
if direct {
- return s.newValue1(ssa.OpIData, n.Type, iface), nil
+ return s.newValue1(ssa.OpIData, n.Type(), iface), nil
}
- p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type), iface)
- return s.load(n.Type, p), nil
+ p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface)
+ return s.load(n.Type(), p), nil
}
// commaok is the more complicated case because we have
@@ -6035,95 +6153,94 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
bEnd := s.f.NewBlock(ssa.BlockPlain)
// Note that we need a new valVar each time (unlike okVar where we can
// reuse the variable) because it might have a different type every time.
- valVar := &Node{Op: ONAME, Sym: &types.Sym{Name: "val"}}
+ valVar := ssaMarker("val")
// type assertion succeeded
s.startBlock(bOk)
if tmp == nil {
if direct {
- s.vars[valVar] = s.newValue1(ssa.OpIData, n.Type, iface)
+ s.vars[valVar] = s.newValue1(ssa.OpIData, n.Type(), iface)
} else {
- p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type), iface)
- s.vars[valVar] = s.load(n.Type, p)
+ p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface)
+ s.vars[valVar] = s.load(n.Type(), p)
}
} else {
- p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type), iface)
- s.move(n.Type, addr, p)
+ p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface)
+ s.move(n.Type(), addr, p)
}
- s.vars[&okVar] = s.constBool(true)
+ s.vars[okVar] = s.constBool(true)
s.endBlock()
bOk.AddEdgeTo(bEnd)
// type assertion failed
s.startBlock(bFail)
if tmp == nil {
- s.vars[valVar] = s.zeroVal(n.Type)
+ s.vars[valVar] = s.zeroVal(n.Type())
} else {
- s.zero(n.Type, addr)
+ s.zero(n.Type(), addr)
}
- s.vars[&okVar] = s.constBool(false)
+ s.vars[okVar] = s.constBool(false)
s.endBlock()
bFail.AddEdgeTo(bEnd)
// merge point
s.startBlock(bEnd)
if tmp == nil {
- res = s.variable(valVar, n.Type)
+ res = s.variable(valVar, n.Type())
delete(s.vars, valVar)
} else {
- res = s.load(n.Type, addr)
- s.vars[&memVar] = s.newValue1A(ssa.OpVarKill, types.TypeMem, tmp, s.mem())
+ res = s.load(n.Type(), addr)
+ s.vars[memVar] = s.newValue1A(ssa.OpVarKill, types.TypeMem, tmp.(*ir.Name), s.mem())
}
- resok = s.variable(&okVar, types.Types[TBOOL])
- delete(s.vars, &okVar)
+ resok = s.variable(okVar, types.Types[types.TBOOL])
+ delete(s.vars, okVar)
return res, resok
}
// variable returns the value of a variable at the current location.
-func (s *state) variable(name *Node, t *types.Type) *ssa.Value {
- v := s.vars[name]
+func (s *state) variable(n ir.Node, t *types.Type) *ssa.Value {
+ v := s.vars[n]
if v != nil {
return v
}
- v = s.fwdVars[name]
+ v = s.fwdVars[n]
if v != nil {
return v
}
if s.curBlock == s.f.Entry {
// No variable should be live at entry.
- s.Fatalf("Value live at entry. It shouldn't be. func %s, node %v, value %v", s.f.Name, name, v)
+ s.Fatalf("Value live at entry. It shouldn't be. func %s, node %v, value %v", s.f.Name, n, v)
}
// Make a FwdRef, which records a value that's live on block input.
// We'll find the matching definition as part of insertPhis.
- v = s.newValue0A(ssa.OpFwdRef, t, name)
- s.fwdVars[name] = v
- s.addNamedValue(name, v)
+ v = s.newValue0A(ssa.OpFwdRef, t, fwdRefAux{N: n})
+ s.fwdVars[n] = v
+ if n.Op() == ir.ONAME {
+ s.addNamedValue(n.(*ir.Name), v)
+ }
return v
}
func (s *state) mem() *ssa.Value {
- return s.variable(&memVar, types.TypeMem)
+ return s.variable(memVar, types.TypeMem)
}
-func (s *state) addNamedValue(n *Node, v *ssa.Value) {
- if n.Class() == Pxxx {
- // Don't track our dummy nodes (&memVar etc.).
+func (s *state) addNamedValue(n *ir.Name, v *ssa.Value) {
+ if n.Class == ir.Pxxx {
+ // Don't track our marker nodes (memVar etc.).
return
}
- if n.IsAutoTmp() {
+ if ir.IsAutoTmp(n) {
// Don't track temporary variables.
return
}
- if n.Class() == PPARAMOUT {
+ if n.Class == ir.PPARAMOUT {
// Don't track named output values. This prevents return values
// from being assigned too early. See #14591 and #14762. TODO: allow this.
return
}
- if n.Class() == PAUTO && n.Xoffset != 0 {
- s.Fatalf("AUTO var with offset %v %d", n, n.Xoffset)
- }
- loc := ssa.LocalSlot{N: n, Type: n.Type, Off: 0}
+ loc := ssa.LocalSlot{N: n, Type: n.Type(), Off: 0}
values, ok := s.f.NamedValues[loc]
if !ok {
s.f.Names = append(s.f.Names, loc)
@@ -6132,7 +6249,7 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) {
}
// Generate a disconnected call to a runtime routine and a return.
-func gencallret(pp *Progs, sym *obj.LSym) *obj.Prog {
+func gencallret(pp *objw.Progs, sym *obj.LSym) *obj.Prog {
p := pp.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
@@ -6147,9 +6264,11 @@ type Branch struct {
B *ssa.Block // target
}
-// SSAGenState contains state needed during Prog generation.
-type SSAGenState struct {
- pp *Progs
+// State contains state needed during Prog generation.
+type State struct {
+ ABI obj.ABI
+
+ pp *objw.Progs
// Branches remembers all the branch instructions we've seen
// and where they would like to go.
@@ -6159,13 +6278,13 @@ type SSAGenState struct {
bstart []*obj.Prog
// Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8.
- ScratchFpMem *Node
+ ScratchFpMem *ir.Name
maxarg int64 // largest frame size for arguments to calls made by the function
// Map from GC safe points to liveness index, generated by
// liveness analysis.
- livenessMap LivenessMap
+ livenessMap liveness.Map
// lineRunStart records the beginning of the current run of instructions
// within a single block sharing the same line number
@@ -6177,9 +6296,9 @@ type SSAGenState struct {
}
// Prog appends a new Prog.
-func (s *SSAGenState) Prog(as obj.As) *obj.Prog {
+func (s *State) Prog(as obj.As) *obj.Prog {
p := s.pp.Prog(as)
- if ssa.LosesStmtMark(as) {
+ if objw.LosesStmtMark(as) {
return p
}
// Float a statement start to the beginning of any same-line run.
@@ -6194,19 +6313,19 @@ func (s *SSAGenState) Prog(as obj.As) *obj.Prog {
}
// Pc returns the current Prog.
-func (s *SSAGenState) Pc() *obj.Prog {
- return s.pp.next
+func (s *State) Pc() *obj.Prog {
+ return s.pp.Next
}
// SetPos sets the current source position.
-func (s *SSAGenState) SetPos(pos src.XPos) {
- s.pp.pos = pos
+func (s *State) SetPos(pos src.XPos) {
+ s.pp.Pos = pos
}
// Br emits a single branch instruction and returns the instruction.
// Not all architectures need the returned instruction, but otherwise
// the boilerplate is common to all.
-func (s *SSAGenState) Br(op obj.As, target *ssa.Block) *obj.Prog {
+func (s *State) Br(op obj.As, target *ssa.Block) *obj.Prog {
p := s.Prog(op)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, Branch{P: p, B: target})
@@ -6218,7 +6337,7 @@ func (s *SSAGenState) Br(op obj.As, target *ssa.Block) *obj.Prog {
// Spill/fill/copy instructions from the register allocator,
// phi functions, and instructions with a no-pos position
// are examples of instructions that can cause churn.
-func (s *SSAGenState) DebugFriendlySetPosFrom(v *ssa.Value) {
+func (s *State) DebugFriendlySetPosFrom(v *ssa.Value) {
switch v.Op {
case ssa.OpPhi, ssa.OpCopy, ssa.OpLoadReg, ssa.OpStoreReg:
// These are not statements
@@ -6232,81 +6351,30 @@ func (s *SSAGenState) DebugFriendlySetPosFrom(v *ssa.Value) {
// in the generated code.
if p.IsStmt() != src.PosIsStmt {
p = p.WithNotStmt()
- // Calls use the pos attached to v, but copy the statement mark from SSAGenState
+ // Calls use the pos attached to v, but copy the statement mark from State
}
s.SetPos(p)
} else {
- s.SetPos(s.pp.pos.WithNotStmt())
- }
- }
-}
-
-// byXoffset implements sort.Interface for []*Node using Xoffset as the ordering.
-type byXoffset []*Node
-
-func (s byXoffset) Len() int { return len(s) }
-func (s byXoffset) Less(i, j int) bool { return s[i].Xoffset < s[j].Xoffset }
-func (s byXoffset) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-func emitStackObjects(e *ssafn, pp *Progs) {
- var vars []*Node
- for _, n := range e.curfn.Func.Dcl {
- if livenessShouldTrack(n) && n.Name.Addrtaken() {
- vars = append(vars, n)
- }
- }
- if len(vars) == 0 {
- return
- }
-
- // Sort variables from lowest to highest address.
- sort.Sort(byXoffset(vars))
-
- // Populate the stack object data.
- // Format must match runtime/stack.go:stackObjectRecord.
- x := e.curfn.Func.lsym.Func().StackObjects
- off := 0
- off = duintptr(x, off, uint64(len(vars)))
- for _, v := range vars {
- // Note: arguments and return values have non-negative Xoffset,
- // in which case the offset is relative to argp.
- // Locals have a negative Xoffset, in which case the offset is relative to varp.
- off = duintptr(x, off, uint64(v.Xoffset))
- if !typesym(v.Type).Siggen() {
- e.Fatalf(v.Pos, "stack object's type symbol not generated for type %s", v.Type)
- }
- off = dsymptr(x, off, dtypesym(v.Type), 0)
- }
-
- // Emit a funcdata pointing at the stack object data.
- p := pp.Prog(obj.AFUNCDATA)
- Addrconst(&p.From, objabi.FUNCDATA_StackObjects)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = x
-
- if debuglive != 0 {
- for _, v := range vars {
- Warnl(v.Pos, "stack object %v %s", v, v.Type.String())
+ s.SetPos(s.pp.Pos.WithNotStmt())
}
}
}
// genssa appends entries to pp for each instruction in f.
-func genssa(f *ssa.Func, pp *Progs) {
- var s SSAGenState
+func genssa(f *ssa.Func, pp *objw.Progs) {
+ var s State
+ s.ABI = f.OwnAux.Fn.ABI()
e := f.Frontend().(*ssafn)
- s.livenessMap = liveness(e, f, pp)
- emitStackObjects(e, pp)
+ s.livenessMap = liveness.Compute(e.curfn, f, e.stkptrsize, pp)
- openDeferInfo := e.curfn.Func.lsym.Func().OpenCodedDeferInfo
+ openDeferInfo := e.curfn.LSym.Func().OpenCodedDeferInfo
if openDeferInfo != nil {
// This function uses open-coded defers -- write out the funcdata
// info that we computed at the end of genssa.
p := pp.Prog(obj.AFUNCDATA)
- Addrconst(&p.From, objabi.FUNCDATA_OpenCodedDeferInfo)
+ p.From.SetConst(objabi.FUNCDATA_OpenCodedDeferInfo)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = openDeferInfo
@@ -6322,12 +6390,12 @@ func genssa(f *ssa.Func, pp *Progs) {
progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
f.Logf("genssa %s\n", f.Name)
- progToBlock[s.pp.next] = f.Blocks[0]
+ progToBlock[s.pp.Next] = f.Blocks[0]
}
s.ScratchFpMem = e.scratchFpMem
- if Ctxt.Flag_locationlists {
+ if base.Ctxt.Flag_locationlists {
if cap(f.Cache.ValueToProgAfter) < f.NumValues() {
f.Cache.ValueToProgAfter = make([]*obj.Prog, f.NumValues())
}
@@ -6360,7 +6428,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// Emit basic blocks
for i, b := range f.Blocks {
- s.bstart[b.ID] = s.pp.next
+ s.bstart[b.ID] = s.pp.Next
s.lineRunStart = nil
// Attach a "default" liveness info. Normally this will be
@@ -6369,12 +6437,12 @@ func genssa(f *ssa.Func, pp *Progs) {
// instruction. We won't use the actual liveness map on a
// control instruction. Just mark it something that is
// preemptible, unless this function is "all unsafe".
- s.pp.nextLive = LivenessIndex{-1, allUnsafe(f)}
+ s.pp.NextLive = objw.LivenessIndex{StackMapIndex: -1, IsUnsafePoint: liveness.IsUnsafe(f)}
// Emit values in block
- thearch.SSAMarkMoves(&s, b)
+ Arch.SSAMarkMoves(&s, b)
for _, v := range b.Values {
- x := s.pp.next
+ x := s.pp.Next
s.DebugFriendlySetPosFrom(v)
switch v.Op {
@@ -6399,7 +6467,7 @@ func genssa(f *ssa.Func, pp *Progs) {
v.Fatalf("OpConvert should be a no-op: %s; %s", v.Args[0].LongString(), v.LongString())
}
case ssa.OpInlMark:
- p := thearch.Ginsnop(s.pp)
+ p := Arch.Ginsnop(s.pp)
if inlMarks == nil {
inlMarks = map[*obj.Prog]int32{}
inlMarksByPos = map[src.XPos][]*obj.Prog{}
@@ -6412,7 +6480,7 @@ func genssa(f *ssa.Func, pp *Progs) {
default:
// Attach this safe point to the next
// instruction.
- s.pp.nextLive = s.livenessMap.Get(v)
+ s.pp.NextLive = s.livenessMap.Get(v)
// Special case for first line in function; move it to the start.
if firstPos != src.NoXPos {
@@ -6420,22 +6488,22 @@ func genssa(f *ssa.Func, pp *Progs) {
firstPos = src.NoXPos
}
// let the backend handle it
- thearch.SSAGenValue(&s, v)
+ Arch.SSAGenValue(&s, v)
}
- if Ctxt.Flag_locationlists {
- valueToProgAfter[v.ID] = s.pp.next
+ if base.Ctxt.Flag_locationlists {
+ valueToProgAfter[v.ID] = s.pp.Next
}
if f.PrintOrHtmlSSA {
- for ; x != s.pp.next; x = x.Link {
+ for ; x != s.pp.Next; x = x.Link {
progToValue[x] = v
}
}
}
// If this is an empty infinite loop, stick a hardware NOP in there so that debuggers are less confused.
- if s.bstart[b.ID] == s.pp.next && len(b.Succs) == 1 && b.Succs[0].Block() == b {
- p := thearch.Ginsnop(s.pp)
+ if s.bstart[b.ID] == s.pp.Next && len(b.Succs) == 1 && b.Succs[0].Block() == b {
+ p := Arch.Ginsnop(s.pp)
p.Pos = p.Pos.WithIsStmt()
if b.Pos == src.NoXPos {
b.Pos = p.Pos // It needs a file, otherwise a no-file non-zero line causes confusion. See #35652.
@@ -6447,18 +6515,18 @@ func genssa(f *ssa.Func, pp *Progs) {
}
// Emit control flow instructions for block
var next *ssa.Block
- if i < len(f.Blocks)-1 && Debug.N == 0 {
+ if i < len(f.Blocks)-1 && base.Flag.N == 0 {
// If -N, leave next==nil so every block with successors
// ends in a JMP (except call blocks - plive doesn't like
// select{send,recv} followed by a JMP call). Helps keep
// line numbers for otherwise empty blocks.
next = f.Blocks[i+1]
}
- x := s.pp.next
+ x := s.pp.Next
s.SetPos(b.Pos)
- thearch.SSAGenBlock(&s, b, next)
+ Arch.SSAGenBlock(&s, b, next)
if f.PrintOrHtmlSSA {
- for ; x != s.pp.next; x = x.Link {
+ for ; x != s.pp.Next; x = x.Link {
progToBlock[x] = b
}
}
@@ -6468,14 +6536,14 @@ func genssa(f *ssa.Func, pp *Progs) {
// still be inside the function in question. So if
// it ends in a call which doesn't return, add a
// nop (which will never execute) after the call.
- thearch.Ginsnop(pp)
+ Arch.Ginsnop(pp)
}
if openDeferInfo != nil {
// When doing open-coded defers, generate a disconnected call to
// deferreturn and a return. This will be used to during panic
// recovery to unwind the stack and return back to the runtime.
- s.pp.nextLive = s.livenessMap.deferreturn
- gencallret(pp, Deferreturn)
+ s.pp.NextLive = s.livenessMap.DeferReturn
+ gencallret(pp, ir.Syms.Deferreturn)
}
if inlMarks != nil {
@@ -6483,7 +6551,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// going to emit anyway, and use those instructions instead of the
// inline marks.
for p := pp.Text; p != nil; p = p.Link {
- if p.As == obj.ANOP || p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT || p.As == obj.APCALIGN || thearch.LinkArch.Family == sys.Wasm {
+ if p.As == obj.ANOP || p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT || p.As == obj.APCALIGN || Arch.LinkArch.Family == sys.Wasm {
// Don't use 0-sized instructions as inline marks, because we need
// to identify inline mark instructions by pc offset.
// (Some of these instructions are sometimes zero-sized, sometimes not.
@@ -6506,7 +6574,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// some of the inline marks.
// Use this instruction instead.
p.Pos = p.Pos.WithIsStmt() // promote position to a statement
- pp.curfn.Func.lsym.Func().AddInlMark(p, inlMarks[m])
+ pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[m])
// Make the inline mark a real nop, so it doesn't generate any code.
m.As = obj.ANOP
m.Pos = src.NoXPos
@@ -6518,18 +6586,19 @@ func genssa(f *ssa.Func, pp *Progs) {
// Any unmatched inline marks now need to be added to the inlining tree (and will generate a nop instruction).
for _, p := range inlMarkList {
if p.As != obj.ANOP {
- pp.curfn.Func.lsym.Func().AddInlMark(p, inlMarks[p])
+ pp.CurFunc.LSym.Func().AddInlMark(p, inlMarks[p])
}
}
}
- if Ctxt.Flag_locationlists {
- e.curfn.Func.DebugInfo = ssa.BuildFuncDebug(Ctxt, f, Debug_locationlist > 1, stackOffset)
+ if base.Ctxt.Flag_locationlists {
+ debugInfo := ssa.BuildFuncDebug(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset)
+ e.curfn.DebugInfo = debugInfo
bstart := s.bstart
// Note that at this moment, Prog.Pc is a sequence number; it's
// not a real PC until after assembly, so this mapping has to
// be done later.
- e.curfn.Func.DebugInfo.GetPC = func(b, v ssa.ID) int64 {
+ debugInfo.GetPC = func(b, v ssa.ID) int64 {
switch v {
case ssa.BlockStart.ID:
if b == f.Entry.ID {
@@ -6538,7 +6607,7 @@ func genssa(f *ssa.Func, pp *Progs) {
}
return bstart[b].Pc
case ssa.BlockEnd.ID:
- return e.curfn.Func.lsym.Size
+ return e.curfn.LSym.Size
default:
return valueToProgAfter[v].Pc
}
@@ -6612,17 +6681,17 @@ func genssa(f *ssa.Func, pp *Progs) {
f.HTMLWriter = nil
}
-func defframe(s *SSAGenState, e *ssafn) {
+func defframe(s *State, e *ssafn) {
pp := s.pp
- frame := Rnd(s.maxarg+e.stksize, int64(Widthreg))
- if thearch.PadFrame != nil {
- frame = thearch.PadFrame(frame)
+ frame := types.Rnd(s.maxarg+e.stksize, int64(types.RegSize))
+ if Arch.PadFrame != nil {
+ frame = Arch.PadFrame(frame)
}
// Fill in argument and frame size.
pp.Text.To.Type = obj.TYPE_TEXTSIZE
- pp.Text.To.Val = int32(Rnd(e.curfn.Type.ArgWidth(), int64(Widthreg)))
+ pp.Text.To.Val = int32(types.Rnd(e.curfn.Type().ArgWidth(), int64(types.RegSize)))
pp.Text.To.Offset = frame
// Insert code to zero ambiguously live variables so that the
@@ -6636,33 +6705,33 @@ func defframe(s *SSAGenState, e *ssafn) {
var state uint32
// Iterate through declarations. They are sorted in decreasing Xoffset order.
- for _, n := range e.curfn.Func.Dcl {
- if !n.Name.Needzero() {
+ for _, n := range e.curfn.Dcl {
+ if !n.Needzero() {
continue
}
- if n.Class() != PAUTO {
- e.Fatalf(n.Pos, "needzero class %d", n.Class())
+ if n.Class != ir.PAUTO {
+ e.Fatalf(n.Pos(), "needzero class %d", n.Class)
}
- if n.Type.Size()%int64(Widthptr) != 0 || n.Xoffset%int64(Widthptr) != 0 || n.Type.Size() == 0 {
- e.Fatalf(n.Pos, "var %L has size %d offset %d", n, n.Type.Size(), n.Xoffset)
+ if n.Type().Size()%int64(types.PtrSize) != 0 || n.FrameOffset()%int64(types.PtrSize) != 0 || n.Type().Size() == 0 {
+ e.Fatalf(n.Pos(), "var %L has size %d offset %d", n, n.Type().Size(), n.Offset_)
}
- if lo != hi && n.Xoffset+n.Type.Size() >= lo-int64(2*Widthreg) {
+ if lo != hi && n.FrameOffset()+n.Type().Size() >= lo-int64(2*types.RegSize) {
// Merge with range we already have.
- lo = n.Xoffset
+ lo = n.FrameOffset()
continue
}
// Zero old range
- p = thearch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
+ p = Arch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
// Set new range.
- lo = n.Xoffset
- hi = lo + n.Type.Size()
+ lo = n.FrameOffset()
+ hi = lo + n.Type().Size()
}
// Zero final range.
- thearch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
+ Arch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
}
// For generating consecutive jump instructions to model a specific branching
@@ -6671,14 +6740,14 @@ type IndexJump struct {
Index int
}
-func (s *SSAGenState) oneJump(b *ssa.Block, jump *IndexJump) {
+func (s *State) oneJump(b *ssa.Block, jump *IndexJump) {
p := s.Br(jump.Jump, b.Succs[jump.Index].Block())
p.Pos = b.Pos
}
// CombJump generates combinational instructions (2 at present) for a block jump,
// thereby the behaviour of non-standard condition codes could be simulated
-func (s *SSAGenState) CombJump(b, next *ssa.Block, jumps *[2][2]IndexJump) {
+func (s *State) CombJump(b, next *ssa.Block, jumps *[2][2]IndexJump) {
switch next {
case b.Succs[0].Block():
s.oneJump(b, &jumps[0][0])
@@ -6724,16 +6793,16 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
case *obj.LSym:
a.Name = obj.NAME_EXTERN
a.Sym = n
- case *Node:
- if n.Class() == PPARAM || n.Class() == PPARAMOUT {
+ case *ir.Name:
+ if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
a.Name = obj.NAME_PARAM
- a.Sym = n.Orig.Sym.Linksym()
- a.Offset += n.Xoffset
+ a.Sym = ir.Orig(n).(*ir.Name).Linksym()
+ a.Offset += n.FrameOffset()
break
}
a.Name = obj.NAME_AUTO
- a.Sym = n.Sym.Linksym()
- a.Offset += n.Xoffset
+ a.Sym = n.Linksym()
+ a.Offset += n.FrameOffset()
default:
v.Fatalf("aux in %s not implemented %#v", v, v.Aux)
}
@@ -6751,17 +6820,17 @@ func (s *state) extendIndex(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bo
// high word and branch to out-of-bounds failure if it is not 0.
var lo *ssa.Value
if idx.Type.IsSigned() {
- lo = s.newValue1(ssa.OpInt64Lo, types.Types[TINT], idx)
+ lo = s.newValue1(ssa.OpInt64Lo, types.Types[types.TINT], idx)
} else {
- lo = s.newValue1(ssa.OpInt64Lo, types.Types[TUINT], idx)
+ lo = s.newValue1(ssa.OpInt64Lo, types.Types[types.TUINT], idx)
}
- if bounded || Debug.B != 0 {
+ if bounded || base.Flag.B != 0 {
return lo
}
bNext := s.f.NewBlock(ssa.BlockPlain)
bPanic := s.f.NewBlock(ssa.BlockExit)
- hi := s.newValue1(ssa.OpInt64Hi, types.Types[TUINT32], idx)
- cmp := s.newValue2(ssa.OpEq32, types.Types[TBOOL], hi, s.constInt32(types.Types[TUINT32], 0))
+ hi := s.newValue1(ssa.OpInt64Hi, types.Types[types.TUINT32], idx)
+ cmp := s.newValue2(ssa.OpEq32, types.Types[types.TBOOL], hi, s.constInt32(types.Types[types.TUINT32], 0))
if !idx.Type.IsSigned() {
switch kind {
case ssa.BoundsIndex:
@@ -6830,7 +6899,7 @@ func (s *state) extendIndex(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bo
s.Fatalf("bad unsigned index extension %s", idx.Type)
}
}
- return s.newValue1(op, types.Types[TINT], idx)
+ return s.newValue1(op, types.Types[types.TINT], idx)
}
// CheckLoweredPhi checks that regalloc and stackalloc correctly handled phi values.
@@ -6857,48 +6926,38 @@ func CheckLoweredPhi(v *ssa.Value) {
func CheckLoweredGetClosurePtr(v *ssa.Value) {
entry := v.Block.Func.Entry
if entry != v.Block || entry.Values[0] != v {
- Fatalf("in %s, badly placed LoweredGetClosurePtr: %v %v", v.Block.Func.Name, v.Block, v)
+ base.Fatalf("in %s, badly placed LoweredGetClosurePtr: %v %v", v.Block.Func.Name, v.Block, v)
}
}
-// AutoVar returns a *Node and int64 representing the auto variable and offset within it
-// where v should be spilled.
-func AutoVar(v *ssa.Value) (*Node, int64) {
- loc := v.Block.Func.RegAlloc[v.ID].(ssa.LocalSlot)
- if v.Type.Size() > loc.Type.Size() {
- v.Fatalf("spill/restore type %s doesn't fit in slot type %s", v.Type, loc.Type)
- }
- return loc.N.(*Node), loc.Off
-}
-
func AddrAuto(a *obj.Addr, v *ssa.Value) {
- n, off := AutoVar(v)
+ n, off := ssa.AutoVar(v)
a.Type = obj.TYPE_MEM
- a.Sym = n.Sym.Linksym()
- a.Reg = int16(thearch.REGSP)
- a.Offset = n.Xoffset + off
- if n.Class() == PPARAM || n.Class() == PPARAMOUT {
+ a.Sym = n.Linksym()
+ a.Reg = int16(Arch.REGSP)
+ a.Offset = n.FrameOffset() + off
+ if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
a.Name = obj.NAME_PARAM
} else {
a.Name = obj.NAME_AUTO
}
}
-func (s *SSAGenState) AddrScratch(a *obj.Addr) {
+func (s *State) AddrScratch(a *obj.Addr) {
if s.ScratchFpMem == nil {
panic("no scratch memory available; forgot to declare usesScratch for Op?")
}
a.Type = obj.TYPE_MEM
a.Name = obj.NAME_AUTO
- a.Sym = s.ScratchFpMem.Sym.Linksym()
- a.Reg = int16(thearch.REGSP)
- a.Offset = s.ScratchFpMem.Xoffset
+ a.Sym = s.ScratchFpMem.Linksym()
+ a.Reg = int16(Arch.REGSP)
+ a.Offset = s.ScratchFpMem.Offset_
}
// Call returns a new CALL instruction for the SSA value v.
// It uses PrepareCall to prepare the call.
-func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
- pPosIsStmt := s.pp.pos.IsStmt() // The statement-ness fo the call comes from ssaGenState
+func (s *State) Call(v *ssa.Value) *obj.Prog {
+ pPosIsStmt := s.pp.Pos.IsStmt() // The statement-ness fo the call comes from ssaGenState
s.PrepareCall(v)
p := s.Prog(obj.ACALL)
@@ -6913,13 +6972,13 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
p.To.Sym = sym.Fn
} else {
// TODO(mdempsky): Can these differences be eliminated?
- switch thearch.LinkArch.Family {
+ switch Arch.LinkArch.Family {
case sys.AMD64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X, sys.Wasm:
p.To.Type = obj.TYPE_REG
case sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
p.To.Type = obj.TYPE_MEM
default:
- Fatalf("unknown indirect call family")
+ base.Fatalf("unknown indirect call family")
}
p.To.Reg = v.Args[0].Reg()
}
@@ -6929,18 +6988,18 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
// PrepareCall prepares to emit a CALL instruction for v and does call-related bookkeeping.
// It must be called immediately before emitting the actual CALL instruction,
// since it emits PCDATA for the stack map at the call (calls are safe points).
-func (s *SSAGenState) PrepareCall(v *ssa.Value) {
+func (s *State) PrepareCall(v *ssa.Value) {
idx := s.livenessMap.Get(v)
if !idx.StackMapValid() {
// See Liveness.hasStackMap.
- 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, ok := v.Aux.(*ssa.AuxCall); !ok || !(sym.Fn == ir.Syms.Typedmemclr || sym.Fn == ir.Syms.Typedmemmove) {
+ base.Fatalf("missing stack map index for %v", v.LongString())
}
}
call, ok := v.Aux.(*ssa.AuxCall)
- if ok && call.Fn == Deferreturn {
+ if ok && call.Fn == ir.Syms.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
@@ -6949,14 +7008,14 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) {
// insert an actual hardware NOP that will have the right line number.
// This is different from obj.ANOP, which is a virtual no-op
// that doesn't make it into the instruction stream.
- thearch.Ginsnopdefer(s.pp)
+ Arch.Ginsnopdefer(s.pp)
}
if ok {
// Record call graph information for nowritebarrierrec
// analysis.
if nowritebarrierrecCheck != nil {
- nowritebarrierrecCheck.recordCall(s.pp.curfn, call.Fn, v.Pos)
+ nowritebarrierrecCheck.recordCall(s.pp.CurFunc, call.Fn, v.Pos)
}
}
@@ -6967,30 +7026,26 @@ func (s *SSAGenState) PrepareCall(v *ssa.Value) {
// UseArgs records the fact that an instruction needs a certain amount of
// callee args space for its use.
-func (s *SSAGenState) UseArgs(n int64) {
+func (s *State) UseArgs(n int64) {
if s.maxarg < n {
s.maxarg = n
}
}
// fieldIdx finds the index of the field referred to by the ODOT node n.
-func fieldIdx(n *Node) int {
- t := n.Left.Type
- f := n.Sym
+func fieldIdx(n *ir.SelectorExpr) int {
+ t := n.X.Type()
if !t.IsStruct() {
panic("ODOT's LHS is not a struct")
}
- var i int
- for _, t1 := range t.Fields().Slice() {
- if t1.Sym != f {
- i++
- continue
- }
- if t1.Offset != n.Xoffset {
- panic("field offset doesn't match")
+ for i, f := range t.Fields().Slice() {
+ if f.Sym == n.Sel {
+ if f.Offset != n.Offset() {
+ panic("field offset doesn't match")
+ }
+ return i
}
- return i
}
panic(fmt.Sprintf("can't find field in expr %v\n", n))
@@ -7001,9 +7056,9 @@ func fieldIdx(n *Node) int {
// ssafn holds frontend information about a function that the backend is processing.
// It also exports a bunch of compiler services for the ssa backend.
type ssafn struct {
- curfn *Node
+ curfn *ir.Func
strings map[string]*obj.LSym // map from constant string to data symbols
- scratchFpMem *Node // temp for floating point register / memory moves on some architectures
+ scratchFpMem *ir.Name // temp for floating point register / memory moves on some architectures
stksize int64 // stack size for current frame
stkptrsize int64 // prefix of stack containing pointers
log bool // print ssa debug to the stdout
@@ -7018,19 +7073,18 @@ func (e *ssafn) StringData(s string) *obj.LSym {
if e.strings == nil {
e.strings = make(map[string]*obj.LSym)
}
- data := stringsym(e.curfn.Pos, s)
+ data := staticdata.StringSym(e.curfn.Pos(), s)
e.strings[s] = data
return data
}
-func (e *ssafn) Auto(pos src.XPos, t *types.Type) ssa.GCNode {
- n := tempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list
- return n
+func (e *ssafn) Auto(pos src.XPos, t *types.Type) *ir.Name {
+ return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list
}
func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
- ptrType := types.NewPtr(types.Types[TUINT8])
- lenType := types.Types[TINT]
+ ptrType := types.NewPtr(types.Types[types.TUINT8])
+ lenType := types.Types[types.TINT]
// Split this string up into two separate variables.
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
@@ -7038,22 +7092,22 @@ func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
}
func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
- n := name.N.(*Node)
- u := types.Types[TUINTPTR]
- t := types.NewPtr(types.Types[TUINT8])
+ n := name.N
+ u := types.Types[types.TUINTPTR]
+ t := types.NewPtr(types.Types[types.TUINT8])
// Split this interface up into two separate variables.
f := ".itab"
- if n.Type.IsEmptyInterface() {
+ if n.Type().IsEmptyInterface() {
f = ".type"
}
- c := e.SplitSlot(&name, f, 0, u) // see comment in plive.go:onebitwalktype1.
+ c := e.SplitSlot(&name, f, 0, u) // see comment in typebits.Set
d := e.SplitSlot(&name, ".data", u.Size(), t)
return c, d
}
func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) {
ptrType := types.NewPtr(name.Type.Elem())
- lenType := types.Types[TINT]
+ lenType := types.Types[types.TINT]
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
c := e.SplitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType)
@@ -7064,9 +7118,9 @@ func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot)
s := name.Type.Size() / 2
var t *types.Type
if s == 8 {
- t = types.Types[TFLOAT64]
+ t = types.Types[types.TFLOAT64]
} else {
- t = types.Types[TFLOAT32]
+ t = types.Types[types.TFLOAT32]
}
r := e.SplitSlot(&name, ".real", 0, t)
i := e.SplitSlot(&name, ".imag", t.Size(), t)
@@ -7076,14 +7130,14 @@ func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot)
func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
var t *types.Type
if name.Type.IsSigned() {
- t = types.Types[TINT32]
+ t = types.Types[types.TINT32]
} else {
- t = types.Types[TUINT32]
+ t = types.Types[types.TUINT32]
}
- if thearch.LinkArch.ByteOrder == binary.BigEndian {
- return e.SplitSlot(&name, ".hi", 0, t), e.SplitSlot(&name, ".lo", t.Size(), types.Types[TUINT32])
+ if Arch.LinkArch.ByteOrder == binary.BigEndian {
+ return e.SplitSlot(&name, ".hi", 0, t), e.SplitSlot(&name, ".lo", t.Size(), types.Types[types.TUINT32])
}
- return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[TUINT32])
+ return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[types.TUINT32])
}
func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
@@ -7095,55 +7149,47 @@ func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
}
func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
- n := name.N.(*Node)
+ n := name.N
at := name.Type
if at.NumElem() != 1 {
- e.Fatalf(n.Pos, "bad array size")
+ e.Fatalf(n.Pos(), "bad array size")
}
et := at.Elem()
return e.SplitSlot(&name, "[0]", 0, et)
}
func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
- return itabsym(it, offset)
+ return reflectdata.ITabSym(it, offset)
}
// SplitSlot returns a slot representing the data of parent starting at offset.
func (e *ssafn) SplitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot {
- node := parent.N.(*Node)
+ node := parent.N
- if node.Class() != PAUTO || node.Name.Addrtaken() {
+ if node.Class != ir.PAUTO || node.Addrtaken() {
// addressed things and non-autos retain their parents (i.e., cannot truly be split)
return ssa.LocalSlot{N: node, Type: t, Off: parent.Off + offset}
}
- s := &types.Sym{Name: node.Sym.Name + suffix, Pkg: localpkg}
-
- n := &Node{
- Name: new(Name),
- Op: ONAME,
- Pos: parent.N.(*Node).Pos,
- }
- n.Orig = n
-
- s.Def = asTypesNode(n)
- asNode(s.Def).Name.SetUsed(true)
- n.Sym = s
- n.Type = t
- n.SetClass(PAUTO)
- n.Esc = EscNever
- n.Name.Curfn = e.curfn
- e.curfn.Func.Dcl = append(e.curfn.Func.Dcl, n)
- dowidth(t)
+ s := &types.Sym{Name: node.Sym().Name + suffix, Pkg: types.LocalPkg}
+ n := ir.NewNameAt(parent.N.Pos(), s)
+ s.Def = n
+ ir.AsNode(s.Def).Name().SetUsed(true)
+ n.SetType(t)
+ n.Class = ir.PAUTO
+ n.SetEsc(ir.EscNever)
+ n.Curfn = e.curfn
+ e.curfn.Dcl = append(e.curfn.Dcl, n)
+ types.CalcSize(t)
return ssa.LocalSlot{N: n, Type: t, Off: 0, SplitOf: parent, SplitOffset: offset}
}
func (e *ssafn) CanSSA(t *types.Type) bool {
- return canSSAType(t)
+ return TypeOK(t)
}
func (e *ssafn) Line(pos src.XPos) string {
- return linestr(pos)
+ return base.FmtPos(pos)
}
// Log logs a message from the compiler.
@@ -7159,73 +7205,165 @@ func (e *ssafn) Log() bool {
// Fatal reports a compiler error and exits.
func (e *ssafn) Fatalf(pos src.XPos, msg string, args ...interface{}) {
- lineno = pos
- nargs := append([]interface{}{e.curfn.funcname()}, args...)
- Fatalf("'%s': "+msg, nargs...)
+ base.Pos = pos
+ nargs := append([]interface{}{ir.FuncName(e.curfn)}, args...)
+ base.Fatalf("'%s': "+msg, nargs...)
}
// Warnl reports a "warning", which is usually flag-triggered
// logging output for the benefit of tests.
func (e *ssafn) Warnl(pos src.XPos, fmt_ string, args ...interface{}) {
- Warnl(pos, fmt_, args...)
+ base.WarnfAt(pos, fmt_, args...)
}
func (e *ssafn) Debug_checknil() bool {
- return Debug_checknil != 0
+ return base.Debug.Nil != 0
}
func (e *ssafn) UseWriteBarrier() bool {
- return use_writebarrier
+ return base.Flag.WB
}
func (e *ssafn) Syslook(name string) *obj.LSym {
switch name {
case "goschedguarded":
- return goschedguarded
+ return ir.Syms.Goschedguarded
case "writeBarrier":
- return writeBarrier
+ return ir.Syms.WriteBarrier
case "gcWriteBarrier":
- return gcWriteBarrier
+ return ir.Syms.GCWriteBarrier
case "typedmemmove":
- return typedmemmove
+ return ir.Syms.Typedmemmove
case "typedmemclr":
- return typedmemclr
+ return ir.Syms.Typedmemclr
}
e.Fatalf(src.NoXPos, "unknown Syslook func %v", name)
return nil
}
func (e *ssafn) SetWBPos(pos src.XPos) {
- e.curfn.Func.setWBPos(pos)
+ e.curfn.SetWBPos(pos)
}
func (e *ssafn) MyImportPath() string {
- return myimportpath
+ return base.Ctxt.Pkgpath
}
-func (n *Node) Typ() *types.Type {
- return n.Type
+func clobberBase(n ir.Node) ir.Node {
+ if n.Op() == ir.ODOT {
+ n := n.(*ir.SelectorExpr)
+ if n.X.Type().NumFields() == 1 {
+ return clobberBase(n.X)
+ }
+ }
+ if n.Op() == ir.OINDEX {
+ n := n.(*ir.IndexExpr)
+ if n.X.Type().IsArray() && n.X.Type().NumElem() == 1 {
+ return clobberBase(n.X)
+ }
+ }
+ return n
}
-func (n *Node) StorageClass() ssa.StorageClass {
- switch n.Class() {
- case PPARAM:
- return ssa.ClassParam
- case PPARAMOUT:
- return ssa.ClassParamOut
- case PAUTO:
- return ssa.ClassAuto
- default:
- Fatalf("untranslatable storage class for %v: %s", n, n.Class())
- return 0
+
+// callTargetLSym determines the correct LSym for 'callee' when called
+// from function 'caller'. There are a couple of different scenarios
+// to contend with here:
+//
+// 1. if 'caller' is an ABI wrapper, then we always want to use the
+// LSym from the Func for the callee.
+//
+// 2. if 'caller' is not an ABI wrapper, then we looked at the callee
+// to see if it corresponds to a "known" ABI0 symbol (e.g. assembly
+// routine defined in the current package); if so, we want the call to
+// directly target the ABI0 symbol (effectively bypassing the
+// ABIInternal->ABI0 wrapper for 'callee').
+//
+// 3. in all other cases, want the regular ABIInternal linksym
+//
+func callTargetLSym(callee *ir.Name, callerLSym *obj.LSym) *obj.LSym {
+ lsym := callee.Linksym()
+ if !base.Flag.ABIWrap {
+ return lsym
}
+ fn := callee.Func
+ if fn == nil {
+ return lsym
+ }
+
+ // check for case 1 above
+ if callerLSym.ABIWrapper() {
+ if nlsym := fn.LSym; nlsym != nil {
+ lsym = nlsym
+ }
+ } else {
+ // check for case 2 above
+ defABI, hasDefABI := symabiDefs[lsym.Name]
+ if hasDefABI && defABI == obj.ABI0 {
+ lsym = callee.LinksymABI(obj.ABI0)
+ }
+ }
+ return lsym
}
-func clobberBase(n *Node) *Node {
- if n.Op == ODOT && n.Left.Type.NumFields() == 1 {
- return clobberBase(n.Left)
+func min8(a, b int8) int8 {
+ if a < b {
+ return a
}
- if n.Op == OINDEX && n.Left.Type.IsArray() && n.Left.Type.NumElem() == 1 {
- return clobberBase(n.Left)
+ return b
+}
+
+func max8(a, b int8) int8 {
+ if a > b {
+ return a
}
- return n
+ return b
+}
+
+// deferstruct makes a runtime._defer structure, with additional space for
+// stksize bytes of args.
+func deferstruct(stksize int64) *types.Type {
+ makefield := func(name string, typ *types.Type) *types.Field {
+ // Unlike the global makefield function, this one needs to set Pkg
+ // because these types might be compared (in SSA CSE sorting).
+ // TODO: unify this makefield and the global one above.
+ sym := &types.Sym{Name: name, Pkg: types.LocalPkg}
+ return types.NewField(src.NoXPos, sym, typ)
+ }
+ argtype := types.NewArray(types.Types[types.TUINT8], stksize)
+ argtype.Width = stksize
+ argtype.Align = 1
+ // These fields must match the ones in runtime/runtime2.go:_defer and
+ // cmd/compile/internal/gc/ssa.go:(*state).call.
+ fields := []*types.Field{
+ makefield("siz", types.Types[types.TUINT32]),
+ makefield("started", types.Types[types.TBOOL]),
+ makefield("heap", types.Types[types.TBOOL]),
+ makefield("openDefer", types.Types[types.TBOOL]),
+ makefield("sp", types.Types[types.TUINTPTR]),
+ makefield("pc", types.Types[types.TUINTPTR]),
+ // Note: the types here don't really matter. Defer structures
+ // are always scanned explicitly during stack copying and GC,
+ // so we make them uintptr type even though they are real pointers.
+ makefield("fn", types.Types[types.TUINTPTR]),
+ makefield("_panic", types.Types[types.TUINTPTR]),
+ makefield("link", types.Types[types.TUINTPTR]),
+ makefield("framepc", types.Types[types.TUINTPTR]),
+ makefield("varp", types.Types[types.TUINTPTR]),
+ makefield("fd", types.Types[types.TUINTPTR]),
+ makefield("args", argtype),
+ }
+
+ // build struct holding the above fields
+ s := types.NewStruct(types.NoPkg, fields)
+ s.SetNoalg(true)
+ types.CalcStructSize(s)
+ return s
}
+
+var (
+ BoundsCheckFunc [ssa.BoundsKindCount]*obj.LSym
+ ExtendCheckFunc [ssa.BoundsKindCount]*obj.LSym
+)
+
+// GCWriteBarrierReg maps from registers to gcWriteBarrier implementation LSyms.
+var GCWriteBarrierReg map[int16]*obj.LSym
diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go
new file mode 100644
index 0000000000000..b06fd7aa4b246
--- /dev/null
+++ b/src/cmd/compile/internal/staticdata/data.go
@@ -0,0 +1,353 @@
+// Copyright 2009 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 staticdata
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "go/constant"
+ "io"
+ "io/ioutil"
+ "os"
+ "sort"
+ "strconv"
+ "sync"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+)
+
+// InitAddrOffset writes the static name symbol lsym to n, it does not modify n.
+// It's the caller responsibility to make sure lsym is from ONAME/PEXTERN node.
+func InitAddrOffset(n *ir.Name, noff int64, lsym *obj.LSym, off int64) {
+ if n.Op() != ir.ONAME {
+ base.Fatalf("InitAddr n op %v", n.Op())
+ }
+ if n.Sym() == nil {
+ base.Fatalf("InitAddr nil n sym")
+ }
+ s := n.Linksym()
+ s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, off)
+}
+
+// InitAddr is InitAddrOffset, with offset fixed to 0.
+func InitAddr(n *ir.Name, noff int64, lsym *obj.LSym) {
+ InitAddrOffset(n, noff, lsym, 0)
+}
+
+// InitSlice writes a static slice symbol {lsym, lencap, lencap} to n+noff, it does not modify n.
+// It's the caller responsibility to make sure lsym is from ONAME node.
+func InitSlice(n *ir.Name, noff int64, lsym *obj.LSym, lencap int64) {
+ s := n.Linksym()
+ s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, 0)
+ s.WriteInt(base.Ctxt, noff+types.SliceLenOffset, types.PtrSize, lencap)
+ s.WriteInt(base.Ctxt, noff+types.SliceCapOffset, types.PtrSize, lencap)
+}
+
+func InitSliceBytes(nam *ir.Name, off int64, s string) {
+ if nam.Op() != ir.ONAME {
+ base.Fatalf("InitSliceBytes %v", nam)
+ }
+ InitSlice(nam, off, slicedata(nam.Pos(), s).Linksym(), int64(len(s)))
+}
+
+const (
+ stringSymPrefix = "go.string."
+ stringSymPattern = ".gostring.%d.%x"
+)
+
+// StringSym returns a symbol containing the string s.
+// The symbol contains the string data, not a string header.
+func StringSym(pos src.XPos, s string) (data *obj.LSym) {
+ var symname string
+ if len(s) > 100 {
+ // Huge strings are hashed to avoid long names in object files.
+ // Indulge in some paranoia by writing the length of s, too,
+ // as protection against length extension attacks.
+ // Same pattern is known to fileStringSym below.
+ h := sha256.New()
+ io.WriteString(h, s)
+ symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil))
+ } else {
+ // Small strings get named directly by their contents.
+ symname = strconv.Quote(s)
+ }
+
+ symdata := base.Ctxt.Lookup(stringSymPrefix + symname)
+ if !symdata.OnList() {
+ off := dstringdata(symdata, 0, s, pos, "string")
+ objw.Global(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
+ symdata.Set(obj.AttrContentAddressable, true)
+ }
+
+ return symdata
+}
+
+// fileStringSym returns a symbol for the contents and the size of file.
+// If readonly is true, the symbol shares storage with any literal string
+// or other file with the same content and is placed in a read-only section.
+// If readonly is false, the symbol is a read-write copy separate from any other,
+// for use as the backing store of a []byte.
+// The content hash of file is copied into hash. (If hash is nil, nothing is copied.)
+// The returned symbol contains the data itself, not a string header.
+func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) {
+ f, err := os.Open(file)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer f.Close()
+ info, err := f.Stat()
+ if err != nil {
+ return nil, 0, err
+ }
+ if !info.Mode().IsRegular() {
+ return nil, 0, fmt.Errorf("not a regular file")
+ }
+ size := info.Size()
+ if size <= 1*1024 {
+ data, err := ioutil.ReadAll(f)
+ if err != nil {
+ return nil, 0, err
+ }
+ if int64(len(data)) != size {
+ return nil, 0, fmt.Errorf("file changed between reads")
+ }
+ var sym *obj.LSym
+ if readonly {
+ sym = StringSym(pos, string(data))
+ } else {
+ sym = slicedata(pos, string(data)).Linksym()
+ }
+ if len(hash) > 0 {
+ sum := sha256.Sum256(data)
+ copy(hash, sum[:])
+ }
+ return sym, size, nil
+ }
+ if size > 2e9 {
+ // ggloblsym takes an int32,
+ // and probably the rest of the toolchain
+ // can't handle such big symbols either.
+ // See golang.org/issue/9862.
+ return nil, 0, fmt.Errorf("file too large")
+ }
+
+ // File is too big to read and keep in memory.
+ // Compute hash if needed for read-only content hashing or if the caller wants it.
+ var sum []byte
+ if readonly || len(hash) > 0 {
+ h := sha256.New()
+ n, err := io.Copy(h, f)
+ if err != nil {
+ return nil, 0, err
+ }
+ if n != size {
+ return nil, 0, fmt.Errorf("file changed between reads")
+ }
+ sum = h.Sum(nil)
+ copy(hash, sum)
+ }
+
+ var symdata *obj.LSym
+ if readonly {
+ symname := fmt.Sprintf(stringSymPattern, size, sum)
+ symdata = base.Ctxt.Lookup(stringSymPrefix + symname)
+ if !symdata.OnList() {
+ info := symdata.NewFileInfo()
+ info.Name = file
+ info.Size = size
+ objw.Global(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL)
+ // Note: AttrContentAddressable cannot be set here,
+ // because the content-addressable-handling code
+ // does not know about file symbols.
+ }
+ } else {
+ // Emit a zero-length data symbol
+ // and then fix up length and content to use file.
+ symdata = slicedata(pos, "").Linksym()
+ symdata.Size = size
+ symdata.Type = objabi.SNOPTRDATA
+ info := symdata.NewFileInfo()
+ info.Name = file
+ info.Size = size
+ }
+
+ return symdata, size, nil
+}
+
+var slicedataGen int
+
+func slicedata(pos src.XPos, s string) *ir.Name {
+ slicedataGen++
+ symname := fmt.Sprintf(".gobytes.%d", slicedataGen)
+ sym := types.LocalPkg.Lookup(symname)
+ symnode := typecheck.NewName(sym)
+ sym.Def = symnode
+
+ lsym := symnode.Linksym()
+ off := dstringdata(lsym, 0, s, pos, "slice")
+ objw.Global(lsym, int32(off), obj.NOPTR|obj.LOCAL)
+
+ return symnode
+}
+
+func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
+ // Objects that are too large will cause the data section to overflow right away,
+ // causing a cryptic error message by the linker. Check for oversize objects here
+ // and provide a useful error message instead.
+ if int64(len(t)) > 2e9 {
+ base.ErrorfAt(pos, "%v with length %v is too big", what, len(t))
+ return 0
+ }
+
+ s.WriteString(base.Ctxt, int64(off), len(t), t)
+ return off + len(t)
+}
+
+var (
+ funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym)
+ funcsyms []*types.Sym
+)
+
+// FuncSym returns s·f.
+func FuncSym(s *types.Sym) *types.Sym {
+ // funcsymsmu here serves to protect not just mutations of funcsyms (below),
+ // but also the package lookup of the func sym name,
+ // since this function gets called concurrently from the backend.
+ // There are no other concurrent package lookups in the backend,
+ // except for the types package, which is protected separately.
+ // Reusing funcsymsmu to also cover this package lookup
+ // avoids a general, broader, expensive package lookup mutex.
+ // Note NeedFuncSym also does package look-up of func sym names,
+ // but that it is only called serially, from the front end.
+ funcsymsmu.Lock()
+ sf, existed := s.Pkg.LookupOK(ir.FuncSymName(s))
+ // Don't export s·f when compiling for dynamic linking.
+ // When dynamically linking, the necessary function
+ // symbols will be created explicitly with NeedFuncSym.
+ // See the NeedFuncSym comment for details.
+ if !base.Ctxt.Flag_dynlink && !existed {
+ funcsyms = append(funcsyms, s)
+ }
+ funcsymsmu.Unlock()
+ return sf
+}
+
+func FuncLinksym(n *ir.Name) *obj.LSym {
+ if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
+ base.Fatalf("expected func name: %v", n)
+ }
+ return FuncSym(n.Sym()).Linksym()
+}
+
+func GlobalLinksym(n *ir.Name) *obj.LSym {
+ if n.Op() != ir.ONAME || n.Class != ir.PEXTERN {
+ base.Fatalf("expected global variable: %v", n)
+ }
+ return n.Linksym()
+}
+
+// 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
+// the packages that use them.
+// Normally we emit the s·f stubs as DUPOK syms,
+// but DUPOK doesn't work across shared library boundaries.
+// 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 {
+ return
+ }
+ if s.IsBlank() {
+ return
+ }
+ if base.Flag.CompilingRuntime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") {
+ // runtime.getg(), getclosureptr(), getcallerpc(), and
+ // getcallersp() are not real functions and so do not
+ // get funcsyms.
+ return
+ }
+ funcsyms = append(funcsyms, s)
+}
+
+func WriteFuncSyms() {
+ sort.Slice(funcsyms, func(i, j int) bool {
+ return funcsyms[i].Linksym().Name < funcsyms[j].Linksym().Name
+ })
+ for _, s := range funcsyms {
+ sf := s.Pkg.Lookup(ir.FuncSymName(s)).Linksym()
+ objw.SymPtr(sf, 0, s.Linksym(), 0)
+ objw.Global(sf, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
+ }
+}
+
+// InitConst writes the static literal c to n.
+// Neither n nor c is modified.
+func InitConst(n *ir.Name, noff int64, c ir.Node, wid int) {
+ if n.Op() != ir.ONAME {
+ base.Fatalf("InitConst n op %v", n.Op())
+ }
+ if n.Sym() == nil {
+ base.Fatalf("InitConst nil n sym")
+ }
+ if c.Op() == ir.ONIL {
+ return
+ }
+ if c.Op() != ir.OLITERAL {
+ base.Fatalf("InitConst c op %v", c.Op())
+ }
+ s := n.Linksym()
+ switch u := c.Val(); u.Kind() {
+ case constant.Bool:
+ i := int64(obj.Bool2int(constant.BoolVal(u)))
+ s.WriteInt(base.Ctxt, noff, wid, i)
+
+ case constant.Int:
+ s.WriteInt(base.Ctxt, noff, wid, ir.IntVal(c.Type(), u))
+
+ case constant.Float:
+ f, _ := constant.Float64Val(u)
+ switch c.Type().Kind() {
+ case types.TFLOAT32:
+ s.WriteFloat32(base.Ctxt, noff, float32(f))
+ case types.TFLOAT64:
+ s.WriteFloat64(base.Ctxt, noff, f)
+ }
+
+ case constant.Complex:
+ re, _ := constant.Float64Val(constant.Real(u))
+ im, _ := constant.Float64Val(constant.Imag(u))
+ switch c.Type().Kind() {
+ case types.TCOMPLEX64:
+ s.WriteFloat32(base.Ctxt, noff, float32(re))
+ s.WriteFloat32(base.Ctxt, noff+4, float32(im))
+ case types.TCOMPLEX128:
+ s.WriteFloat64(base.Ctxt, noff, re)
+ s.WriteFloat64(base.Ctxt, noff+8, im)
+ }
+
+ case constant.String:
+ i := constant.StringVal(u)
+ symdata := StringSym(n.Pos(), i)
+ s.WriteAddr(base.Ctxt, noff, types.PtrSize, symdata, 0)
+ s.WriteInt(base.Ctxt, noff+int64(types.PtrSize), types.PtrSize, int64(len(i)))
+
+ default:
+ base.Fatalf("InitConst unhandled OLITERAL %v", c)
+ }
+}
diff --git a/src/cmd/compile/internal/staticdata/embed.go b/src/cmd/compile/internal/staticdata/embed.go
new file mode 100644
index 0000000000000..8936c4f5b44e4
--- /dev/null
+++ b/src/cmd/compile/internal/staticdata/embed.go
@@ -0,0 +1,181 @@
+// 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 staticdata
+
+import (
+ "path"
+ "sort"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+const (
+ embedUnknown = iota
+ embedBytes
+ embedString
+ embedFiles
+)
+
+func embedFileList(v *ir.Name, kind int) []string {
+ // Build list of files to store.
+ have := make(map[string]bool)
+ var list []string
+ for _, e := range *v.Embed {
+ for _, pattern := range e.Patterns {
+ files, ok := base.Flag.Cfg.Embed.Patterns[pattern]
+ if !ok {
+ base.ErrorfAt(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
+ }
+ for _, file := range files {
+ if base.Flag.Cfg.Embed.Files[file] == "" {
+ base.ErrorfAt(e.Pos, "invalid go:embed: build system did not map file: %s", file)
+ continue
+ }
+ if !have[file] {
+ have[file] = true
+ list = append(list, file)
+ }
+ if kind == embedFiles {
+ for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) {
+ have[dir] = true
+ list = append(list, dir+"/")
+ }
+ }
+ }
+ }
+ }
+ sort.Slice(list, func(i, j int) bool {
+ return embedFileLess(list[i], list[j])
+ })
+
+ if kind == embedString || kind == embedBytes {
+ if len(list) > 1 {
+ base.ErrorfAt(v.Pos(), "invalid go:embed: multiple files for type %v", v.Type())
+ return nil
+ }
+ }
+
+ return list
+}
+
+// embedKind determines the kind of embedding variable.
+func embedKind(typ *types.Type) int {
+ if typ.Sym() != nil && typ.Sym().Name == "FS" && (typ.Sym().Pkg.Path == "embed" || (typ.Sym().Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "embed")) {
+ return embedFiles
+ }
+ if typ.Kind() == types.TSTRING {
+ return embedString
+ }
+ if typ.Sym() == nil && typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 {
+ return embedBytes
+ }
+ return embedUnknown
+}
+
+func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
+ if name[len(name)-1] == '/' {
+ isDir = true
+ name = name[:len(name)-1]
+ }
+ i := len(name) - 1
+ for i >= 0 && name[i] != '/' {
+ i--
+ }
+ if i < 0 {
+ return ".", name, isDir
+ }
+ return name[:i], name[i+1:], isDir
+}
+
+// embedFileLess implements the sort order for a list of embedded files.
+// See the comment inside ../../../../embed/embed.go's Files struct for rationale.
+func embedFileLess(x, y string) bool {
+ xdir, xelem, _ := embedFileNameSplit(x)
+ ydir, yelem, _ := embedFileNameSplit(y)
+ return xdir < ydir || xdir == ydir && xelem < yelem
+}
+
+// WriteEmbed emits the init data for a //go:embed variable,
+// which is either a string, a []byte, or an embed.FS.
+func WriteEmbed(v *ir.Name) {
+ // TODO(mdempsky): User errors should be reported by the frontend.
+
+ commentPos := (*v.Embed)[0].Pos
+ if !types.AllowsGoVersion(types.LocalPkg, 1, 16) {
+ prevPos := base.Pos
+ base.Pos = commentPos
+ base.ErrorfVers("go1.16", "go:embed")
+ base.Pos = prevPos
+ return
+ }
+ if base.Flag.Cfg.Embed.Patterns == nil {
+ base.ErrorfAt(commentPos, "invalid go:embed: build system did not supply embed configuration")
+ return
+ }
+ kind := embedKind(v.Type())
+ if kind == embedUnknown {
+ base.ErrorfAt(v.Pos(), "go:embed cannot apply to var of type %v", v.Type())
+ return
+ }
+
+ files := embedFileList(v, kind)
+ switch kind {
+ case embedString, embedBytes:
+ file := files[0]
+ fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], kind == embedString, nil)
+ if err != nil {
+ base.ErrorfAt(v.Pos(), "embed %s: %v", file, err)
+ }
+ sym := v.Linksym()
+ off := 0
+ off = objw.SymPtr(sym, off, fsym, 0) // data string
+ off = objw.Uintptr(sym, off, uint64(size)) // len
+ if kind == embedBytes {
+ objw.Uintptr(sym, off, uint64(size)) // cap for slice
+ }
+
+ case embedFiles:
+ slicedata := base.Ctxt.Lookup(`"".` + v.Sym().Name + `.files`)
+ off := 0
+ // []files pointed at by Files
+ off = objw.SymPtr(slicedata, off, slicedata, 3*types.PtrSize) // []file, pointing just past slice
+ off = objw.Uintptr(slicedata, off, uint64(len(files)))
+ off = objw.Uintptr(slicedata, off, uint64(len(files)))
+
+ // embed/embed.go type file is:
+ // name string
+ // data string
+ // hash [16]byte
+ // Emit one of these per file in the set.
+ const hashSize = 16
+ hash := make([]byte, hashSize)
+ for _, file := range files {
+ off = objw.SymPtr(slicedata, off, StringSym(v.Pos(), file), 0) // file string
+ off = objw.Uintptr(slicedata, off, uint64(len(file)))
+ if strings.HasSuffix(file, "/") {
+ // entry for directory - no data
+ off = objw.Uintptr(slicedata, off, 0)
+ off = objw.Uintptr(slicedata, off, 0)
+ off += hashSize
+ } else {
+ fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], true, hash)
+ if err != nil {
+ base.ErrorfAt(v.Pos(), "embed %s: %v", file, err)
+ }
+ off = objw.SymPtr(slicedata, off, fsym, 0) // data string
+ off = objw.Uintptr(slicedata, off, uint64(size))
+ off = int(slicedata.WriteBytes(base.Ctxt, int64(off), hash))
+ }
+ }
+ objw.Global(slicedata, int32(off), obj.RODATA|obj.LOCAL)
+ sym := v.Linksym()
+ objw.SymPtr(sym, 0, slicedata, 0)
+ }
+}
diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go
new file mode 100644
index 0000000000000..f3ad82e7b60a1
--- /dev/null
+++ b/src/cmd/compile/internal/staticinit/sched.go
@@ -0,0 +1,610 @@
+// Copyright 2009 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 staticinit
+
+import (
+ "fmt"
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/staticdata"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+type Entry struct {
+ Xoffset int64 // struct, array only
+ Expr ir.Node // bytes of run-time computed expressions
+}
+
+type Plan struct {
+ E []Entry
+}
+
+// An Schedule is used to decompose assignment statements into
+// static and dynamic initialization parts. Static initializations are
+// handled by populating variables' linker symbol data, while dynamic
+// initializations are accumulated to be executed in order.
+type Schedule struct {
+ // Out is the ordered list of dynamic initialization
+ // statements.
+ Out []ir.Node
+
+ Plans map[ir.Node]*Plan
+ Temps map[ir.Node]*ir.Name
+}
+
+func (s *Schedule) append(n ir.Node) {
+ s.Out = append(s.Out, n)
+}
+
+// StaticInit adds an initialization statement n to the schedule.
+func (s *Schedule) StaticInit(n ir.Node) {
+ if !s.tryStaticInit(n) {
+ if base.Flag.Percent != 0 {
+ ir.Dump("nonstatic", n)
+ }
+ s.append(n)
+ }
+}
+
+// tryStaticInit attempts to statically execute an initialization
+// statement and reports whether it succeeded.
+func (s *Schedule) tryStaticInit(nn ir.Node) bool {
+ // Only worry about simple "l = r" assignments. Multiple
+ // variable/expression OAS2 assignments have already been
+ // replaced by multiple simple OAS assignments, and the other
+ // OAS2* assignments mostly necessitate dynamic execution
+ // anyway.
+ if nn.Op() != ir.OAS {
+ return false
+ }
+ n := nn.(*ir.AssignStmt)
+ if ir.IsBlank(n.X) && !AnySideEffects(n.Y) {
+ // Discard.
+ return true
+ }
+ lno := ir.SetPos(n)
+ defer func() { base.Pos = lno }()
+ nam := n.X.(*ir.Name)
+ return s.StaticAssign(nam, 0, n.Y, nam.Type())
+}
+
+// like staticassign but we are copying an already
+// initialized value r.
+func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool {
+ if rn.Class == ir.PFUNC {
+ // TODO if roff != 0 { panic }
+ staticdata.InitAddr(l, loff, staticdata.FuncLinksym(rn))
+ return true
+ }
+ if rn.Class != ir.PEXTERN || rn.Sym().Pkg != types.LocalPkg {
+ return false
+ }
+ if rn.Defn.Op() != ir.OAS {
+ return false
+ }
+ if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675)
+ return false
+ }
+ if rn.Embed != nil {
+ return false
+ }
+ orig := rn
+ r := rn.Defn.(*ir.AssignStmt).Y
+ if r == nil {
+ // No explicit initialization value. Probably zeroed but perhaps
+ // supplied externally and of unknown value.
+ return false
+ }
+
+ for r.Op() == ir.OCONVNOP && !types.Identical(r.Type(), typ) {
+ r = r.(*ir.ConvExpr).X
+ }
+
+ switch r.Op() {
+ case ir.OMETHEXPR:
+ r = r.(*ir.SelectorExpr).FuncName()
+ fallthrough
+ case ir.ONAME:
+ r := r.(*ir.Name)
+ if s.staticcopy(l, loff, r, typ) {
+ return true
+ }
+ // We may have skipped past one or more OCONVNOPs, so
+ // use conv to ensure r is assignable to l (#13263).
+ dst := ir.Node(l)
+ if loff != 0 || !types.Identical(typ, l.Type()) {
+ dst = ir.NewNameOffsetExpr(base.Pos, l, loff, typ)
+ }
+ s.append(ir.NewAssignStmt(base.Pos, dst, typecheck.Conv(r, typ)))
+ return true
+
+ case ir.ONIL:
+ return true
+
+ case ir.OLITERAL:
+ if ir.IsZero(r) {
+ return true
+ }
+ staticdata.InitConst(l, loff, r, int(typ.Width))
+ return true
+
+ case ir.OADDR:
+ r := r.(*ir.AddrExpr)
+ if a, ok := r.X.(*ir.Name); ok && a.Op() == ir.ONAME {
+ staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(a))
+ return true
+ }
+
+ case ir.OPTRLIT:
+ r := r.(*ir.AddrExpr)
+ switch r.X.Op() {
+ case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT, ir.OMAPLIT:
+ // copy pointer
+ staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(s.Temps[r]))
+ return true
+ }
+
+ case ir.OSLICELIT:
+ r := r.(*ir.CompLitExpr)
+ // copy slice
+ staticdata.InitSlice(l, loff, staticdata.GlobalLinksym(s.Temps[r]), r.Len)
+ return true
+
+ case ir.OARRAYLIT, ir.OSTRUCTLIT:
+ r := r.(*ir.CompLitExpr)
+ p := s.Plans[r]
+ for i := range p.E {
+ e := &p.E[i]
+ typ := e.Expr.Type()
+ if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
+ staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Width))
+ continue
+ }
+ x := e.Expr
+ if x.Op() == ir.OMETHEXPR {
+ x = x.(*ir.SelectorExpr).FuncName()
+ }
+ if x.Op() == ir.ONAME && s.staticcopy(l, loff+e.Xoffset, x.(*ir.Name), typ) {
+ continue
+ }
+ // Requires computation, but we're
+ // copying someone else's computation.
+ ll := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, typ)
+ rr := ir.NewNameOffsetExpr(base.Pos, orig, e.Xoffset, typ)
+ ir.SetPos(rr)
+ s.append(ir.NewAssignStmt(base.Pos, ll, rr))
+ }
+
+ return true
+ }
+
+ return false
+}
+
+func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool {
+ if r == nil {
+ // No explicit initialization value. Either zero or supplied
+ // externally.
+ return true
+ }
+ for r.Op() == ir.OCONVNOP {
+ r = r.(*ir.ConvExpr).X
+ }
+
+ assign := func(pos src.XPos, a *ir.Name, aoff int64, v ir.Node) {
+ if s.StaticAssign(a, aoff, v, v.Type()) {
+ return
+ }
+ var lhs ir.Node
+ if ir.IsBlank(a) {
+ // Don't use NameOffsetExpr with blank (#43677).
+ lhs = ir.BlankNode
+ } else {
+ lhs = ir.NewNameOffsetExpr(pos, a, aoff, v.Type())
+ }
+ s.append(ir.NewAssignStmt(pos, lhs, v))
+ }
+
+ switch r.Op() {
+ case ir.ONAME:
+ r := r.(*ir.Name)
+ return s.staticcopy(l, loff, r, typ)
+
+ case ir.OMETHEXPR:
+ r := r.(*ir.SelectorExpr)
+ return s.staticcopy(l, loff, r.FuncName(), typ)
+
+ case ir.ONIL:
+ return true
+
+ case ir.OLITERAL:
+ if ir.IsZero(r) {
+ return true
+ }
+ staticdata.InitConst(l, loff, r, int(typ.Width))
+ return true
+
+ case ir.OADDR:
+ r := r.(*ir.AddrExpr)
+ if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN {
+ staticdata.InitAddrOffset(l, loff, name.Linksym(), offset)
+ return true
+ }
+ fallthrough
+
+ case ir.OPTRLIT:
+ r := r.(*ir.AddrExpr)
+ switch r.X.Op() {
+ case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
+ // Init pointer.
+ a := StaticName(r.X.Type())
+
+ s.Temps[r] = a
+ staticdata.InitAddr(l, loff, a.Linksym())
+
+ // Init underlying literal.
+ assign(base.Pos, a, 0, r.X)
+ return true
+ }
+ //dump("not static ptrlit", r);
+
+ case ir.OSTR2BYTES:
+ r := r.(*ir.ConvExpr)
+ if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
+ sval := ir.StringVal(r.X)
+ staticdata.InitSliceBytes(l, loff, sval)
+ return true
+ }
+
+ case ir.OSLICELIT:
+ r := r.(*ir.CompLitExpr)
+ s.initplan(r)
+ // Init slice.
+ ta := types.NewArray(r.Type().Elem(), r.Len)
+ ta.SetNoalg(true)
+ a := StaticName(ta)
+ s.Temps[r] = a
+ staticdata.InitSlice(l, loff, a.Linksym(), r.Len)
+ // Fall through to init underlying array.
+ l = a
+ loff = 0
+ fallthrough
+
+ case ir.OARRAYLIT, ir.OSTRUCTLIT:
+ r := r.(*ir.CompLitExpr)
+ s.initplan(r)
+
+ p := s.Plans[r]
+ for i := range p.E {
+ e := &p.E[i]
+ if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
+ staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Width))
+ continue
+ }
+ ir.SetPos(e.Expr)
+ assign(base.Pos, l, loff+e.Xoffset, e.Expr)
+ }
+
+ return true
+
+ case ir.OMAPLIT:
+ break
+
+ case ir.OCLOSURE:
+ r := r.(*ir.ClosureExpr)
+ if ir.IsTrivialClosure(r) {
+ if base.Debug.Closure > 0 {
+ base.WarnfAt(r.Pos(), "closure converted to global")
+ }
+ // Closures with no captured variables are globals,
+ // so the assignment can be done at link time.
+ // TODO if roff != 0 { panic }
+ staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
+ return true
+ }
+ ir.ClosureDebugRuntimeCheck(r)
+
+ case ir.OCONVIFACE:
+ // This logic is mirrored in isStaticCompositeLiteral.
+ // If you change something here, change it there, and vice versa.
+
+ // Determine the underlying concrete type and value we are converting from.
+ r := r.(*ir.ConvExpr)
+ val := ir.Node(r)
+ for val.Op() == ir.OCONVIFACE {
+ val = val.(*ir.ConvExpr).X
+ }
+
+ if val.Type().IsInterface() {
+ // val is an interface type.
+ // If val is nil, we can statically initialize l;
+ // both words are zero and so there no work to do, so report success.
+ // If val is non-nil, we have no concrete type to record,
+ // and we won't be able to statically initialize its value, so report failure.
+ return val.Op() == ir.ONIL
+ }
+
+ reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym())
+
+ var itab *ir.AddrExpr
+ if typ.IsEmptyInterface() {
+ itab = reflectdata.TypePtr(val.Type())
+ } else {
+ itab = reflectdata.ITabAddr(val.Type(), typ)
+ }
+
+ // Create a copy of l to modify while we emit data.
+
+ // Emit itab, advance offset.
+ staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
+
+ // Emit data.
+ if types.IsDirectIface(val.Type()) {
+ if val.Op() == ir.ONIL {
+ // Nil is zero, nothing to do.
+ return true
+ }
+ // Copy val directly into n.
+ ir.SetPos(val)
+ assign(base.Pos, l, loff+int64(types.PtrSize), val)
+ } else {
+ // Construct temp to hold val, write pointer to temp into n.
+ a := StaticName(val.Type())
+ s.Temps[val] = a
+ assign(base.Pos, a, 0, val)
+ staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym())
+ }
+
+ return true
+ }
+
+ //dump("not static", r);
+ return false
+}
+
+func (s *Schedule) initplan(n ir.Node) {
+ if s.Plans[n] != nil {
+ return
+ }
+ p := new(Plan)
+ s.Plans[n] = p
+ switch n.Op() {
+ default:
+ base.Fatalf("initplan")
+
+ case ir.OARRAYLIT, ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ var k int64
+ for _, a := range n.List {
+ if a.Op() == ir.OKEY {
+ kv := a.(*ir.KeyExpr)
+ k = typecheck.IndexConst(kv.Key)
+ if k < 0 {
+ base.Fatalf("initplan arraylit: invalid index %v", kv.Key)
+ }
+ a = kv.Value
+ }
+ s.addvalue(p, k*n.Type().Elem().Width, a)
+ k++
+ }
+
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, a := range n.List {
+ if a.Op() != ir.OSTRUCTKEY {
+ base.Fatalf("initplan structlit")
+ }
+ a := a.(*ir.StructKeyExpr)
+ if a.Field.IsBlank() {
+ continue
+ }
+ s.addvalue(p, a.Offset, a.Value)
+ }
+
+ case ir.OMAPLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, a := range n.List {
+ if a.Op() != ir.OKEY {
+ base.Fatalf("initplan maplit")
+ }
+ a := a.(*ir.KeyExpr)
+ s.addvalue(p, -1, a.Value)
+ }
+ }
+}
+
+func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
+ // special case: zero can be dropped entirely
+ if ir.IsZero(n) {
+ return
+ }
+
+ // special case: inline struct and array (not slice) literals
+ if isvaluelit(n) {
+ s.initplan(n)
+ q := s.Plans[n]
+ for _, qe := range q.E {
+ // qe is a copy; we are not modifying entries in q.E
+ qe.Xoffset += xoffset
+ p.E = append(p.E, qe)
+ }
+ return
+ }
+
+ // add to plan
+ p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
+}
+
+// from here down is the walk analysis
+// of composite literals.
+// most of the work is to generate
+// data statements for the constant
+// part of the composite literal.
+
+var statuniqgen int // name generator for static temps
+
+// StaticName returns a name backed by a (writable) static data symbol.
+// Use readonlystaticname for read-only node.
+func StaticName(t *types.Type) *ir.Name {
+ // Don't use LookupNum; it interns the resulting string, but these are all unique.
+ n := typecheck.NewName(typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen)))
+ statuniqgen++
+ typecheck.Declare(n, ir.PEXTERN)
+ n.SetType(t)
+ n.Linksym().Set(obj.AttrLocal, true)
+ return n
+}
+
+// StaticLoc returns the static address of n, if n has one, or else nil.
+func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
+ if n == nil {
+ return nil, 0, false
+ }
+
+ switch n.Op() {
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ return n, 0, true
+
+ case ir.OMETHEXPR:
+ n := n.(*ir.SelectorExpr)
+ return StaticLoc(n.FuncName())
+
+ case ir.ODOT:
+ n := n.(*ir.SelectorExpr)
+ if name, offset, ok = StaticLoc(n.X); !ok {
+ break
+ }
+ offset += n.Offset()
+ return name, offset, true
+
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ if n.X.Type().IsSlice() {
+ break
+ }
+ if name, offset, ok = StaticLoc(n.X); !ok {
+ break
+ }
+ l := getlit(n.Index)
+ if l < 0 {
+ break
+ }
+
+ // Check for overflow.
+ if n.Type().Width != 0 && types.MaxWidth/n.Type().Width <= int64(l) {
+ break
+ }
+ offset += int64(l) * n.Type().Width
+ return name, offset, true
+ }
+
+ return nil, 0, false
+}
+
+// AnySideEffects reports whether n contains any operations that could have observable side effects.
+func AnySideEffects(n ir.Node) bool {
+ return ir.Any(n, func(n ir.Node) bool {
+ switch n.Op() {
+ // Assume side effects unless we know otherwise.
+ default:
+ return true
+
+ // No side effects here (arguments are checked separately).
+ case ir.ONAME,
+ ir.ONONAME,
+ ir.OTYPE,
+ ir.OPACK,
+ ir.OLITERAL,
+ ir.ONIL,
+ ir.OADD,
+ ir.OSUB,
+ ir.OOR,
+ ir.OXOR,
+ ir.OADDSTR,
+ ir.OADDR,
+ ir.OANDAND,
+ ir.OBYTES2STR,
+ ir.ORUNES2STR,
+ ir.OSTR2BYTES,
+ ir.OSTR2RUNES,
+ ir.OCAP,
+ ir.OCOMPLIT,
+ ir.OMAPLIT,
+ ir.OSTRUCTLIT,
+ ir.OARRAYLIT,
+ ir.OSLICELIT,
+ ir.OPTRLIT,
+ ir.OCONV,
+ ir.OCONVIFACE,
+ ir.OCONVNOP,
+ ir.ODOT,
+ ir.OEQ,
+ ir.ONE,
+ ir.OLT,
+ ir.OLE,
+ ir.OGT,
+ ir.OGE,
+ ir.OKEY,
+ ir.OSTRUCTKEY,
+ ir.OLEN,
+ ir.OMUL,
+ ir.OLSH,
+ ir.ORSH,
+ ir.OAND,
+ ir.OANDNOT,
+ ir.ONEW,
+ ir.ONOT,
+ ir.OBITNOT,
+ ir.OPLUS,
+ ir.ONEG,
+ ir.OOROR,
+ ir.OPAREN,
+ ir.ORUNESTR,
+ ir.OREAL,
+ ir.OIMAG,
+ ir.OCOMPLEX:
+ return false
+
+ // Only possible side effect is division by zero.
+ case ir.ODIV, ir.OMOD:
+ n := n.(*ir.BinaryExpr)
+ if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 {
+ return true
+ }
+
+ // Only possible side effect is panic on invalid size,
+ // but many makechan and makemap use size zero, which is definitely OK.
+ case ir.OMAKECHAN, ir.OMAKEMAP:
+ n := n.(*ir.MakeExpr)
+ if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 {
+ return true
+ }
+
+ // Only possible side effect is panic on invalid size.
+ // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
+ case ir.OMAKESLICE, ir.OMAKESLICECOPY:
+ return true
+ }
+ return false
+ })
+}
+
+func getlit(lit ir.Node) int {
+ if ir.IsSmallIntConst(lit) {
+ return int(ir.Int64Val(lit))
+ }
+ return -1
+}
+
+func isvaluelit(n ir.Node) bool {
+ return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
+}
diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go
index f84bd2d7056f0..22680dce786c5 100644
--- a/src/cmd/compile/internal/syntax/dumper_test.go
+++ b/src/cmd/compile/internal/syntax/dumper_test.go
@@ -13,7 +13,7 @@ func TestDump(t *testing.T) {
t.Skip("skipping test in short mode")
}
- // provide a dummy error handler so parsing doesn't stop after first error
+ // provide a no-op error handler so parsing doesn't stop after first error
ast, err := ParseFile(*src_, func(error) {}, nil, CheckBranches)
if err != nil {
t.Error(err)
diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go
index 815630fcd412e..487cab19fef9c 100644
--- a/src/cmd/compile/internal/syntax/nodes.go
+++ b/src/cmd/compile/internal/syntax/nodes.go
@@ -114,7 +114,7 @@ func (*decl) aDecl() {}
// All declarations belonging to the same group point to the same Group node.
type Group struct {
- dummy int // not empty so we are guaranteed different Group instances
+ _ int // not empty so we are guaranteed different Group instances
}
// ----------------------------------------------------------------------------
diff --git a/src/cmd/compile/internal/syntax/operator_string.go b/src/cmd/compile/internal/syntax/operator_string.go
index 3c759b2e9befb..a7cd40fb13518 100644
--- a/src/cmd/compile/internal/syntax/operator_string.go
+++ b/src/cmd/compile/internal/syntax/operator_string.go
@@ -1,9 +1,37 @@
-// Code generated by "stringer -type Operator -linecomment"; DO NOT EDIT.
+// Code generated by "stringer -type Operator -linecomment tokens.go"; DO NOT EDIT.
package syntax
import "strconv"
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[Def-1]
+ _ = x[Not-2]
+ _ = x[Recv-3]
+ _ = x[OrOr-4]
+ _ = x[AndAnd-5]
+ _ = x[Eql-6]
+ _ = x[Neq-7]
+ _ = x[Lss-8]
+ _ = x[Leq-9]
+ _ = x[Gtr-10]
+ _ = x[Geq-11]
+ _ = x[Add-12]
+ _ = x[Sub-13]
+ _ = x[Or-14]
+ _ = x[Xor-15]
+ _ = x[Mul-16]
+ _ = x[Div-17]
+ _ = x[Rem-18]
+ _ = x[And-19]
+ _ = x[AndNot-20]
+ _ = x[Shl-21]
+ _ = x[Shr-22]
+}
+
const _Operator_name = ":!<-||&&==!=<<=>>=+-|^*/%&&^<<>>"
var _Operator_index = [...]uint8{0, 1, 2, 4, 6, 8, 10, 12, 13, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 30, 32}
diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go
index c3b9aca229c33..fe72e7a374fb8 100644
--- a/src/cmd/compile/internal/syntax/printer_test.go
+++ b/src/cmd/compile/internal/syntax/printer_test.go
@@ -18,7 +18,7 @@ func TestPrint(t *testing.T) {
t.Skip("skipping test in short mode")
}
- // provide a dummy error handler so parsing doesn't stop after first error
+ // provide a no-op error handler so parsing doesn't stop after first error
ast, err := ParseFile(*src_, func(error) {}, nil, 0)
if err != nil {
t.Error(err)
diff --git a/src/cmd/compile/internal/syntax/token_string.go b/src/cmd/compile/internal/syntax/token_string.go
index 3cf5473febfd0..ef295eb24b2bc 100644
--- a/src/cmd/compile/internal/syntax/token_string.go
+++ b/src/cmd/compile/internal/syntax/token_string.go
@@ -1,9 +1,62 @@
-// Code generated by "stringer -type token -linecomment"; DO NOT EDIT.
+// Code generated by "stringer -type token -linecomment tokens.go"; DO NOT EDIT.
package syntax
import "strconv"
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[_EOF-1]
+ _ = x[_Name-2]
+ _ = x[_Literal-3]
+ _ = x[_Operator-4]
+ _ = x[_AssignOp-5]
+ _ = x[_IncOp-6]
+ _ = x[_Assign-7]
+ _ = x[_Define-8]
+ _ = x[_Arrow-9]
+ _ = x[_Star-10]
+ _ = x[_Lparen-11]
+ _ = x[_Lbrack-12]
+ _ = x[_Lbrace-13]
+ _ = x[_Rparen-14]
+ _ = x[_Rbrack-15]
+ _ = x[_Rbrace-16]
+ _ = x[_Comma-17]
+ _ = x[_Semi-18]
+ _ = x[_Colon-19]
+ _ = x[_Dot-20]
+ _ = x[_DotDotDot-21]
+ _ = x[_Break-22]
+ _ = x[_Case-23]
+ _ = x[_Chan-24]
+ _ = x[_Const-25]
+ _ = x[_Continue-26]
+ _ = x[_Default-27]
+ _ = x[_Defer-28]
+ _ = x[_Else-29]
+ _ = x[_Fallthrough-30]
+ _ = x[_For-31]
+ _ = x[_Func-32]
+ _ = x[_Go-33]
+ _ = x[_Goto-34]
+ _ = x[_If-35]
+ _ = x[_Import-36]
+ _ = x[_Interface-37]
+ _ = x[_Map-38]
+ _ = x[_Package-39]
+ _ = x[_Range-40]
+ _ = x[_Return-41]
+ _ = x[_Select-42]
+ _ = x[_Struct-43]
+ _ = x[_Switch-44]
+ _ = x[_Type-45]
+ _ = x[_Var-46]
+ _ = x[tokenCount-47]
+}
+
const _token_name = "EOFnameliteralopop=opop=:=<-*([{)]},;:....breakcasechanconstcontinuedefaultdeferelsefallthroughforfuncgogotoifimportinterfacemappackagerangereturnselectstructswitchtypevar"
var _token_index = [...]uint8{0, 3, 7, 14, 16, 19, 23, 24, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 42, 47, 51, 55, 60, 68, 75, 80, 84, 95, 98, 102, 104, 108, 110, 116, 125, 128, 135, 140, 146, 152, 158, 164, 168, 171, 171}
diff --git a/src/cmd/compile/internal/syntax/tokens.go b/src/cmd/compile/internal/syntax/tokens.go
index 3b97cb66f24b9..2936b6576bc7a 100644
--- a/src/cmd/compile/internal/syntax/tokens.go
+++ b/src/cmd/compile/internal/syntax/tokens.go
@@ -6,7 +6,7 @@ package syntax
type token uint
-//go:generate stringer -type token -linecomment
+//go:generate stringer -type token -linecomment tokens.go
const (
_ token = iota
@@ -105,7 +105,7 @@ const (
type Operator uint
-//go:generate stringer -type Operator -linecomment
+//go:generate stringer -type Operator -linecomment tokens.go
const (
_ Operator = iota
diff --git a/src/cmd/compile/internal/test/abiutils_test.go b/src/cmd/compile/internal/test/abiutils_test.go
new file mode 100644
index 0000000000000..decc29667e7d3
--- /dev/null
+++ b/src/cmd/compile/internal/test/abiutils_test.go
@@ -0,0 +1,295 @@
+// 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 test
+
+import (
+ "bufio"
+ "cmd/compile/internal/abi"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/obj/x86"
+ "cmd/internal/src"
+ "os"
+ "testing"
+)
+
+// AMD64 registers available:
+// - integer: RAX, RBX, RCX, RDI, RSI, R8, R9, r10, R11
+// - floating point: X0 - X14
+var configAMD64 = abi.NewABIConfig(9, 15)
+
+func TestMain(m *testing.M) {
+ ssagen.Arch.LinkArch = &x86.Linkamd64
+ ssagen.Arch.REGSP = x86.REGSP
+ ssagen.Arch.MAXWIDTH = 1 << 50
+ types.MaxWidth = ssagen.Arch.MAXWIDTH
+ base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch)
+ base.Ctxt.DiagFunc = base.Errorf
+ base.Ctxt.DiagFlush = base.FlushErrors
+ base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
+ types.PtrSize = ssagen.Arch.LinkArch.PtrSize
+ types.RegSize = ssagen.Arch.LinkArch.RegSize
+ typecheck.InitUniverse()
+ os.Exit(m.Run())
+}
+
+func TestABIUtilsBasic1(t *testing.T) {
+
+ // func(x int32) int32
+ i32 := types.Types[types.TINT32]
+ ft := mkFuncType(nil, []*types.Type{i32}, []*types.Type{i32})
+
+ // expected results
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 } spilloffset: 0 typ: int32
+ OUT 0: R{ I0 } spilloffset: -1 typ: int32
+ offsetToSpillArea: 0 spillAreaSize: 8
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABIUtilsBasic2(t *testing.T) {
+ // func(x int32, y float64) (int32, float64, float64)
+ i8 := types.Types[types.TINT8]
+ i16 := types.Types[types.TINT16]
+ i32 := types.Types[types.TINT32]
+ i64 := types.Types[types.TINT64]
+ f32 := types.Types[types.TFLOAT32]
+ f64 := types.Types[types.TFLOAT64]
+ c64 := types.Types[types.TCOMPLEX64]
+ c128 := types.Types[types.TCOMPLEX128]
+ ft := mkFuncType(nil,
+ []*types.Type{
+ i8, i16, i32, i64,
+ f32, f32, f64, f64,
+ i8, i16, i32, i64,
+ f32, f32, f64, f64,
+ c128, c128, c128, c128, c64,
+ i8, i16, i32, i64,
+ i8, i16, i32, i64},
+ []*types.Type{i32, f64, f64})
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 } spilloffset: 0 typ: int8
+ IN 1: R{ I1 } spilloffset: 2 typ: int16
+ IN 2: R{ I2 } spilloffset: 4 typ: int32
+ IN 3: R{ I3 } spilloffset: 8 typ: int64
+ IN 4: R{ F0 } spilloffset: 16 typ: float32
+ IN 5: R{ F1 } spilloffset: 20 typ: float32
+ IN 6: R{ F2 } spilloffset: 24 typ: float64
+ IN 7: R{ F3 } spilloffset: 32 typ: float64
+ IN 8: R{ I4 } spilloffset: 40 typ: int8
+ IN 9: R{ I5 } spilloffset: 42 typ: int16
+ IN 10: R{ I6 } spilloffset: 44 typ: int32
+ IN 11: R{ I7 } spilloffset: 48 typ: int64
+ IN 12: R{ F4 } spilloffset: 56 typ: float32
+ IN 13: R{ F5 } spilloffset: 60 typ: float32
+ IN 14: R{ F6 } spilloffset: 64 typ: float64
+ IN 15: R{ F7 } spilloffset: 72 typ: float64
+ IN 16: R{ F8 F9 } spilloffset: 80 typ: complex128
+ IN 17: R{ F10 F11 } spilloffset: 96 typ: complex128
+ IN 18: R{ F12 F13 } spilloffset: 112 typ: complex128
+ IN 19: R{ } offset: 0 typ: complex128
+ IN 20: R{ } offset: 16 typ: complex64
+ IN 21: R{ I8 } spilloffset: 128 typ: int8
+ IN 22: R{ } offset: 24 typ: int16
+ IN 23: R{ } offset: 28 typ: int32
+ IN 24: R{ } offset: 32 typ: int64
+ IN 25: R{ } offset: 40 typ: int8
+ IN 26: R{ } offset: 42 typ: int16
+ IN 27: R{ } offset: 44 typ: int32
+ IN 28: R{ } offset: 48 typ: int64
+ OUT 0: R{ I0 } spilloffset: -1 typ: int32
+ OUT 1: R{ F0 } spilloffset: -1 typ: float64
+ OUT 2: R{ F1 } spilloffset: -1 typ: float64
+ offsetToSpillArea: 56 spillAreaSize: 136
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABIUtilsArrays(t *testing.T) {
+ i32 := types.Types[types.TINT32]
+ ae := types.NewArray(i32, 0)
+ a1 := types.NewArray(i32, 1)
+ a2 := types.NewArray(i32, 2)
+ aa1 := types.NewArray(a1, 1)
+ ft := mkFuncType(nil, []*types.Type{a1, ae, aa1, a2},
+ []*types.Type{a2, a1, ae, aa1})
+
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 } spilloffset: 0 typ: [1]int32
+ IN 1: R{ } offset: 0 typ: [0]int32
+ IN 2: R{ I1 } spilloffset: 4 typ: [1][1]int32
+ IN 3: R{ } offset: 0 typ: [2]int32
+ OUT 0: R{ } offset: 8 typ: [2]int32
+ OUT 1: R{ I0 } spilloffset: -1 typ: [1]int32
+ OUT 2: R{ } offset: 16 typ: [0]int32
+ OUT 3: R{ I1 } spilloffset: -1 typ: [1][1]int32
+ offsetToSpillArea: 16 spillAreaSize: 8
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABIUtilsStruct1(t *testing.T) {
+ i8 := types.Types[types.TINT8]
+ i16 := types.Types[types.TINT16]
+ i32 := types.Types[types.TINT32]
+ i64 := types.Types[types.TINT64]
+ s := mkstruct([]*types.Type{i8, i8, mkstruct([]*types.Type{}), i8, i16})
+ ft := mkFuncType(nil, []*types.Type{i8, s, i64},
+ []*types.Type{s, i8, i32})
+
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 } spilloffset: 0 typ: int8
+ IN 1: R{ I1 I2 I3 I4 } spilloffset: 2 typ: struct { int8; int8; struct {}; int8; int16 }
+ IN 2: R{ I5 } spilloffset: 8 typ: int64
+ OUT 0: R{ I0 I1 I2 I3 } spilloffset: -1 typ: struct { int8; int8; struct {}; int8; int16 }
+ OUT 1: R{ I4 } spilloffset: -1 typ: int8
+ OUT 2: R{ I5 } spilloffset: -1 typ: int32
+ offsetToSpillArea: 0 spillAreaSize: 16
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABIUtilsStruct2(t *testing.T) {
+ f64 := types.Types[types.TFLOAT64]
+ i64 := types.Types[types.TINT64]
+ s := mkstruct([]*types.Type{i64, mkstruct([]*types.Type{})})
+ fs := mkstruct([]*types.Type{f64, s, mkstruct([]*types.Type{})})
+ ft := mkFuncType(nil, []*types.Type{s, s, fs},
+ []*types.Type{fs, fs})
+
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 } spilloffset: 0 typ: struct { int64; struct {} }
+ IN 1: R{ I1 } spilloffset: 16 typ: struct { int64; struct {} }
+ IN 2: R{ I2 F0 } spilloffset: 32 typ: struct { float64; struct { int64; struct {} }; struct {} }
+ OUT 0: R{ I0 F0 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
+ OUT 1: R{ I1 F1 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
+ offsetToSpillArea: 0 spillAreaSize: 64
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABIUtilsSliceString(t *testing.T) {
+ i32 := types.Types[types.TINT32]
+ sli32 := types.NewSlice(i32)
+ str := types.New(types.TSTRING)
+ i8 := types.Types[types.TINT8]
+ i64 := types.Types[types.TINT64]
+ ft := mkFuncType(nil, []*types.Type{sli32, i8, sli32, i8, str, i8, i64, sli32},
+ []*types.Type{str, i64, str, sli32})
+
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: []int32
+ IN 1: R{ I3 } spilloffset: 24 typ: int8
+ IN 2: R{ I4 I5 I6 } spilloffset: 32 typ: []int32
+ IN 3: R{ I7 } spilloffset: 56 typ: int8
+ IN 4: R{ } offset: 0 typ: string
+ IN 5: R{ I8 } spilloffset: 57 typ: int8
+ IN 6: R{ } offset: 16 typ: int64
+ IN 7: R{ } offset: 24 typ: []int32
+ OUT 0: R{ I0 I1 } spilloffset: -1 typ: string
+ OUT 1: R{ I2 } spilloffset: -1 typ: int64
+ OUT 2: R{ I3 I4 } spilloffset: -1 typ: string
+ OUT 3: R{ I5 I6 I7 } spilloffset: -1 typ: []int32
+ offsetToSpillArea: 48 spillAreaSize: 64
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABIUtilsMethod(t *testing.T) {
+ i16 := types.Types[types.TINT16]
+ i64 := types.Types[types.TINT64]
+ f64 := types.Types[types.TFLOAT64]
+
+ s1 := mkstruct([]*types.Type{i16, i16, i16})
+ ps1 := types.NewPtr(s1)
+ a7 := types.NewArray(ps1, 7)
+ ft := mkFuncType(s1, []*types.Type{ps1, a7, f64, i16, i16, i16},
+ []*types.Type{a7, f64, i64})
+
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: struct { int16; int16; int16 }
+ IN 1: R{ I3 } spilloffset: 8 typ: *struct { int16; int16; int16 }
+ IN 2: R{ } offset: 0 typ: [7]*struct { int16; int16; int16 }
+ IN 3: R{ F0 } spilloffset: 16 typ: float64
+ IN 4: R{ I4 } spilloffset: 24 typ: int16
+ IN 5: R{ I5 } spilloffset: 26 typ: int16
+ IN 6: R{ I6 } spilloffset: 28 typ: int16
+ OUT 0: R{ } offset: 56 typ: [7]*struct { int16; int16; int16 }
+ OUT 1: R{ F0 } spilloffset: -1 typ: float64
+ OUT 2: R{ I0 } spilloffset: -1 typ: int64
+ offsetToSpillArea: 112 spillAreaSize: 32
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABIUtilsInterfaces(t *testing.T) {
+ ei := types.Types[types.TINTER] // interface{}
+ pei := types.NewPtr(ei) // *interface{}
+ fldt := mkFuncType(types.FakeRecvType(), []*types.Type{},
+ []*types.Type{types.UntypedString})
+ field := types.NewField(src.NoXPos, nil, fldt)
+ // interface{ ...() string }
+ nei := types.NewInterface(types.LocalPkg, []*types.Field{field})
+
+ i16 := types.Types[types.TINT16]
+ tb := types.Types[types.TBOOL]
+ s1 := mkstruct([]*types.Type{i16, i16, tb})
+
+ ft := mkFuncType(nil, []*types.Type{s1, ei, ei, nei, pei, nei, i16},
+ []*types.Type{ei, nei, pei})
+
+ exp := makeExpectedDump(`
+ IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: struct { int16; int16; bool }
+ IN 1: R{ I3 I4 } spilloffset: 8 typ: interface {}
+ IN 2: R{ I5 I6 } spilloffset: 24 typ: interface {}
+ IN 3: R{ I7 I8 } spilloffset: 40 typ: interface { () untyped string }
+ IN 4: R{ } offset: 0 typ: *interface {}
+ IN 5: R{ } offset: 8 typ: interface { () untyped string }
+ IN 6: R{ } offset: 24 typ: int16
+ OUT 0: R{ I0 I1 } spilloffset: -1 typ: interface {}
+ OUT 1: R{ I2 I3 } spilloffset: -1 typ: interface { () untyped string }
+ OUT 2: R{ I4 } spilloffset: -1 typ: *interface {}
+ offsetToSpillArea: 32 spillAreaSize: 56
+`)
+
+ abitest(t, ft, exp)
+}
+
+func TestABINumParamRegs(t *testing.T) {
+ i8 := types.Types[types.TINT8]
+ i16 := types.Types[types.TINT16]
+ i32 := types.Types[types.TINT32]
+ i64 := types.Types[types.TINT64]
+ f32 := types.Types[types.TFLOAT32]
+ f64 := types.Types[types.TFLOAT64]
+ c64 := types.Types[types.TCOMPLEX64]
+ c128 := types.Types[types.TCOMPLEX128]
+
+ s := mkstruct([]*types.Type{i8, i8, mkstruct([]*types.Type{}), i8, i16})
+ a := types.NewArray(s, 3)
+
+ nrtest(t, i8, 1)
+ nrtest(t, i16, 1)
+ nrtest(t, i32, 1)
+ nrtest(t, i64, 1)
+ nrtest(t, f32, 1)
+ nrtest(t, f64, 1)
+ nrtest(t, c64, 2)
+ nrtest(t, c128, 2)
+ nrtest(t, s, 4)
+ nrtest(t, a, 12)
+
+}
\ No newline at end of file
diff --git a/src/cmd/compile/internal/test/abiutilsaux_test.go b/src/cmd/compile/internal/test/abiutilsaux_test.go
new file mode 100644
index 0000000000000..19dd3a51fd079
--- /dev/null
+++ b/src/cmd/compile/internal/test/abiutilsaux_test.go
@@ -0,0 +1,164 @@
+// 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 test
+
+// This file contains utility routines and harness infrastructure used
+// by the ABI tests in "abiutils_test.go".
+
+import (
+ "cmd/compile/internal/abi"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+ "fmt"
+ "strings"
+ "testing"
+ "text/scanner"
+)
+
+func mkParamResultField(t *types.Type, s *types.Sym, which ir.Class) *types.Field {
+ field := types.NewField(src.NoXPos, s, t)
+ n := typecheck.NewName(s)
+ n.Class = which
+ field.Nname = n
+ n.SetType(t)
+ return field
+}
+
+// mkstruct is a helper routine to create a struct type with fields
+// of the types specified in 'fieldtypes'.
+func mkstruct(fieldtypes []*types.Type) *types.Type {
+ fields := make([]*types.Field, len(fieldtypes))
+ for k, t := range fieldtypes {
+ if t == nil {
+ panic("bad -- field has no type")
+ }
+ f := types.NewField(src.NoXPos, nil, t)
+ fields[k] = f
+ }
+ s := types.NewStruct(types.LocalPkg, fields)
+ return s
+}
+
+func mkFuncType(rcvr *types.Type, ins []*types.Type, outs []*types.Type) *types.Type {
+ q := typecheck.Lookup("?")
+ inf := []*types.Field{}
+ for _, it := range ins {
+ inf = append(inf, mkParamResultField(it, q, ir.PPARAM))
+ }
+ outf := []*types.Field{}
+ for _, ot := range outs {
+ outf = append(outf, mkParamResultField(ot, q, ir.PPARAMOUT))
+ }
+ var rf *types.Field
+ if rcvr != nil {
+ rf = mkParamResultField(rcvr, q, ir.PPARAM)
+ }
+ return types.NewSignature(types.LocalPkg, rf, inf, outf)
+}
+
+type expectedDump struct {
+ dump string
+ file string
+ line int
+}
+
+func tokenize(src string) []string {
+ var s scanner.Scanner
+ s.Init(strings.NewReader(src))
+ res := []string{}
+ for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
+ res = append(res, s.TokenText())
+ }
+ return res
+}
+
+func verifyParamResultOffset(t *testing.T, f *types.Field, r abi.ABIParamAssignment, which string, idx int) int {
+ n := ir.AsNode(f.Nname).(*ir.Name)
+ if n.FrameOffset() != int64(r.Offset()) {
+ t.Errorf("%s %d: got offset %d wanted %d t=%v",
+ which, idx, r.Offset(), n.Offset_, f.Type)
+ return 1
+ }
+ return 0
+}
+
+func makeExpectedDump(e string) expectedDump {
+ return expectedDump{dump: e}
+}
+
+func difftokens(atoks []string, etoks []string) string {
+ if len(atoks) != len(etoks) {
+ return fmt.Sprintf("expected %d tokens got %d",
+ len(etoks), len(atoks))
+ }
+ for i := 0; i < len(etoks); i++ {
+ if etoks[i] == atoks[i] {
+ continue
+ }
+
+ return fmt.Sprintf("diff at token %d: expected %q got %q",
+ i, etoks[i], atoks[i])
+ }
+ return ""
+}
+
+func nrtest(t *testing.T, ft *types.Type, expected int) {
+ types.CalcSize(ft)
+ got := configAMD64.NumParamRegs(ft)
+ if got != expected {
+ t.Errorf("]\nexpected num regs = %d, got %d, type %v", expected, got, ft)
+ }
+}
+
+func abitest(t *testing.T, ft *types.Type, exp expectedDump) {
+
+ types.CalcSize(ft)
+
+ // Analyze with full set of registers.
+ regRes := configAMD64.ABIAnalyze(ft)
+ regResString := strings.TrimSpace(regRes.String())
+
+ // Check results.
+ reason := difftokens(tokenize(regResString), tokenize(exp.dump))
+ if reason != "" {
+ t.Errorf("\nexpected:\n%s\ngot:\n%s\nreason: %s",
+ strings.TrimSpace(exp.dump), regResString, reason)
+ }
+
+ // Analyze again with empty register set.
+ empty := abi.NewABIConfig(0, 0)
+ emptyRes := empty.ABIAnalyze(ft)
+ emptyResString := emptyRes.String()
+
+ // Walk the results and make sure the offsets assigned match
+ // up with those assiged by CalcSize. This checks to make sure that
+ // when we have no available registers the ABI assignment degenerates
+ // back to the original ABI0.
+
+ // receiver
+ failed := 0
+ rfsl := ft.Recvs().Fields().Slice()
+ poff := 0
+ if len(rfsl) != 0 {
+ failed |= verifyParamResultOffset(t, rfsl[0], emptyRes.InParams()[0], "receiver", 0)
+ poff = 1
+ }
+ // params
+ pfsl := ft.Params().Fields().Slice()
+ for k, f := range pfsl {
+ verifyParamResultOffset(t, f, emptyRes.InParams()[k+poff], "param", k)
+ }
+ // results
+ ofsl := ft.Results().Fields().Slice()
+ for k, f := range ofsl {
+ failed |= verifyParamResultOffset(t, f, emptyRes.OutParams()[k], "result", k)
+ }
+
+ if failed != 0 {
+ t.Logf("emptyres:\n%s\n", emptyResString)
+ }
+}
diff --git a/src/cmd/compile/internal/gc/bench_test.go b/src/cmd/compile/internal/test/bench_test.go
similarity index 98%
rename from src/cmd/compile/internal/gc/bench_test.go
rename to src/cmd/compile/internal/test/bench_test.go
index 8c4288128f25b..3fffe57d08203 100644
--- a/src/cmd/compile/internal/gc/bench_test.go
+++ b/src/cmd/compile/internal/test/bench_test.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.
-package gc
+package test
import "testing"
diff --git a/src/cmd/compile/internal/gc/constFold_test.go b/src/cmd/compile/internal/test/constFold_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/constFold_test.go
rename to src/cmd/compile/internal/test/constFold_test.go
index 59f905dad9623..7159f0ed33a37 100644
--- a/src/cmd/compile/internal/gc/constFold_test.go
+++ b/src/cmd/compile/internal/test/constFold_test.go
@@ -1,7 +1,7 @@
// run
// Code generated by gen/constFoldGen.go. DO NOT EDIT.
-package gc
+package test
import "testing"
diff --git a/src/cmd/compile/internal/gc/dep_test.go b/src/cmd/compile/internal/test/dep_test.go
similarity index 92%
rename from src/cmd/compile/internal/gc/dep_test.go
rename to src/cmd/compile/internal/test/dep_test.go
index c1dac9338652b..26122e6a5b70a 100644
--- a/src/cmd/compile/internal/gc/dep_test.go
+++ b/src/cmd/compile/internal/test/dep_test.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.
-package gc
+package test
import (
"internal/testenv"
@@ -18,7 +18,7 @@ func TestDeps(t *testing.T) {
}
for _, dep := range strings.Fields(strings.Trim(string(out), "[]")) {
switch dep {
- case "go/build", "go/token":
+ case "go/build", "go/scanner":
t.Errorf("undesired dependency on %q", dep)
}
}
diff --git a/src/cmd/compile/internal/gc/fixedbugs_test.go b/src/cmd/compile/internal/test/fixedbugs_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/fixedbugs_test.go
rename to src/cmd/compile/internal/test/fixedbugs_test.go
index 8ac4436947f78..e7e2f7e58eb29 100644
--- a/src/cmd/compile/internal/gc/fixedbugs_test.go
+++ b/src/cmd/compile/internal/test/fixedbugs_test.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.
-package gc
+package test
import (
"internal/testenv"
diff --git a/src/cmd/compile/internal/gc/float_test.go b/src/cmd/compile/internal/test/float_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/float_test.go
rename to src/cmd/compile/internal/test/float_test.go
index c619d2570507f..884a983bdd7a5 100644
--- a/src/cmd/compile/internal/gc/float_test.go
+++ b/src/cmd/compile/internal/test/float_test.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.
-package gc
+package test
import (
"math"
diff --git a/src/cmd/compile/internal/gc/global_test.go b/src/cmd/compile/internal/test/global_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/global_test.go
rename to src/cmd/compile/internal/test/global_test.go
index edad6d042a36d..5f5f7d619860f 100644
--- a/src/cmd/compile/internal/gc/global_test.go
+++ b/src/cmd/compile/internal/test/global_test.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.
-package gc
+package test
import (
"bytes"
diff --git a/src/cmd/compile/internal/gc/iface_test.go b/src/cmd/compile/internal/test/iface_test.go
similarity index 98%
rename from src/cmd/compile/internal/gc/iface_test.go
rename to src/cmd/compile/internal/test/iface_test.go
index 21c6587217f0a..ebc4f891c963a 100644
--- a/src/cmd/compile/internal/gc/iface_test.go
+++ b/src/cmd/compile/internal/test/iface_test.go
@@ -2,15 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package test
+
+import "testing"
// Test to make sure we make copies of the values we
// put in interfaces.
-import (
- "testing"
-)
-
var x int
func TestEfaceConv1(t *testing.T) {
diff --git a/src/cmd/compile/internal/gc/inl_test.go b/src/cmd/compile/internal/test/inl_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/inl_test.go
rename to src/cmd/compile/internal/test/inl_test.go
index 02735e50fb7f0..9d31975b310bf 100644
--- a/src/cmd/compile/internal/gc/inl_test.go
+++ b/src/cmd/compile/internal/test/inl_test.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.
-package gc
+package test
import (
"bufio"
diff --git a/src/cmd/compile/internal/gc/lang_test.go b/src/cmd/compile/internal/test/lang_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/lang_test.go
rename to src/cmd/compile/internal/test/lang_test.go
index 72e7f07a21c00..67c1551292298 100644
--- a/src/cmd/compile/internal/gc/lang_test.go
+++ b/src/cmd/compile/internal/test/lang_test.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.
-package gc
+package test
import (
"internal/testenv"
diff --git a/src/cmd/compile/internal/gc/logic_test.go b/src/cmd/compile/internal/test/logic_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/logic_test.go
rename to src/cmd/compile/internal/test/logic_test.go
index 78d2dd2fa81fa..1d7043ff605f1 100644
--- a/src/cmd/compile/internal/gc/logic_test.go
+++ b/src/cmd/compile/internal/test/logic_test.go
@@ -1,4 +1,4 @@
-package gc
+package test
import "testing"
diff --git a/src/cmd/compile/internal/gc/reproduciblebuilds_test.go b/src/cmd/compile/internal/test/reproduciblebuilds_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/reproduciblebuilds_test.go
rename to src/cmd/compile/internal/test/reproduciblebuilds_test.go
index 8101e440793ee..4d84f9cdeffc9 100644
--- a/src/cmd/compile/internal/gc/reproduciblebuilds_test.go
+++ b/src/cmd/compile/internal/test/reproduciblebuilds_test.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.
-package gc_test
+package test
import (
"bytes"
diff --git a/src/cmd/compile/internal/gc/shift_test.go b/src/cmd/compile/internal/test/shift_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/shift_test.go
rename to src/cmd/compile/internal/test/shift_test.go
index ce2eedf1521d9..ea88f0a70ae22 100644
--- a/src/cmd/compile/internal/gc/shift_test.go
+++ b/src/cmd/compile/internal/test/shift_test.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.
-package gc
+package test
import (
"reflect"
diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/test/ssa_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/ssa_test.go
rename to src/cmd/compile/internal/test/ssa_test.go
index 7f7c9464d44fa..2f3e24c2d37ca 100644
--- a/src/cmd/compile/internal/gc/ssa_test.go
+++ b/src/cmd/compile/internal/test/ssa_test.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.
-package gc
+package test
import (
"bytes"
diff --git a/src/cmd/compile/internal/gc/testdata/addressed_test.go b/src/cmd/compile/internal/test/testdata/addressed_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/addressed_test.go
rename to src/cmd/compile/internal/test/testdata/addressed_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/append_test.go b/src/cmd/compile/internal/test/testdata/append_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/append_test.go
rename to src/cmd/compile/internal/test/testdata/append_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/arithBoundary_test.go b/src/cmd/compile/internal/test/testdata/arithBoundary_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/arithBoundary_test.go
rename to src/cmd/compile/internal/test/testdata/arithBoundary_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/arithConst_test.go b/src/cmd/compile/internal/test/testdata/arithConst_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/arithConst_test.go
rename to src/cmd/compile/internal/test/testdata/arithConst_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/arith_test.go b/src/cmd/compile/internal/test/testdata/arith_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/arith_test.go
rename to src/cmd/compile/internal/test/testdata/arith_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/array_test.go b/src/cmd/compile/internal/test/testdata/array_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/array_test.go
rename to src/cmd/compile/internal/test/testdata/array_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/assert_test.go b/src/cmd/compile/internal/test/testdata/assert_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/assert_test.go
rename to src/cmd/compile/internal/test/testdata/assert_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/break_test.go b/src/cmd/compile/internal/test/testdata/break_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/break_test.go
rename to src/cmd/compile/internal/test/testdata/break_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/chan_test.go b/src/cmd/compile/internal/test/testdata/chan_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/chan_test.go
rename to src/cmd/compile/internal/test/testdata/chan_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/closure_test.go b/src/cmd/compile/internal/test/testdata/closure_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/closure_test.go
rename to src/cmd/compile/internal/test/testdata/closure_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/cmpConst_test.go b/src/cmd/compile/internal/test/testdata/cmpConst_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/cmpConst_test.go
rename to src/cmd/compile/internal/test/testdata/cmpConst_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/cmp_test.go b/src/cmd/compile/internal/test/testdata/cmp_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/cmp_test.go
rename to src/cmd/compile/internal/test/testdata/cmp_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/compound_test.go b/src/cmd/compile/internal/test/testdata/compound_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/compound_test.go
rename to src/cmd/compile/internal/test/testdata/compound_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/copy_test.go b/src/cmd/compile/internal/test/testdata/copy_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/copy_test.go
rename to src/cmd/compile/internal/test/testdata/copy_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/ctl_test.go b/src/cmd/compile/internal/test/testdata/ctl_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/ctl_test.go
rename to src/cmd/compile/internal/test/testdata/ctl_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/deferNoReturn_test.go b/src/cmd/compile/internal/test/testdata/deferNoReturn_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/deferNoReturn_test.go
rename to src/cmd/compile/internal/test/testdata/deferNoReturn_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/divbyzero_test.go b/src/cmd/compile/internal/test/testdata/divbyzero_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/divbyzero_test.go
rename to src/cmd/compile/internal/test/testdata/divbyzero_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/dupLoad_test.go b/src/cmd/compile/internal/test/testdata/dupLoad_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/dupLoad_test.go
rename to src/cmd/compile/internal/test/testdata/dupLoad_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/flowgraph_generator1.go b/src/cmd/compile/internal/test/testdata/flowgraph_generator1.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/flowgraph_generator1.go
rename to src/cmd/compile/internal/test/testdata/flowgraph_generator1.go
diff --git a/src/cmd/compile/internal/gc/testdata/fp_test.go b/src/cmd/compile/internal/test/testdata/fp_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/fp_test.go
rename to src/cmd/compile/internal/test/testdata/fp_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/gen/arithBoundaryGen.go b/src/cmd/compile/internal/test/testdata/gen/arithBoundaryGen.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/gen/arithBoundaryGen.go
rename to src/cmd/compile/internal/test/testdata/gen/arithBoundaryGen.go
diff --git a/src/cmd/compile/internal/gc/testdata/gen/arithConstGen.go b/src/cmd/compile/internal/test/testdata/gen/arithConstGen.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/gen/arithConstGen.go
rename to src/cmd/compile/internal/test/testdata/gen/arithConstGen.go
diff --git a/src/cmd/compile/internal/gc/testdata/gen/cmpConstGen.go b/src/cmd/compile/internal/test/testdata/gen/cmpConstGen.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/gen/cmpConstGen.go
rename to src/cmd/compile/internal/test/testdata/gen/cmpConstGen.go
diff --git a/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go b/src/cmd/compile/internal/test/testdata/gen/constFoldGen.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go
rename to src/cmd/compile/internal/test/testdata/gen/constFoldGen.go
diff --git a/src/cmd/compile/internal/gc/testdata/gen/copyGen.go b/src/cmd/compile/internal/test/testdata/gen/copyGen.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/gen/copyGen.go
rename to src/cmd/compile/internal/test/testdata/gen/copyGen.go
diff --git a/src/cmd/compile/internal/gc/testdata/gen/zeroGen.go b/src/cmd/compile/internal/test/testdata/gen/zeroGen.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/gen/zeroGen.go
rename to src/cmd/compile/internal/test/testdata/gen/zeroGen.go
diff --git a/src/cmd/compile/internal/gc/testdata/loadstore_test.go b/src/cmd/compile/internal/test/testdata/loadstore_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/loadstore_test.go
rename to src/cmd/compile/internal/test/testdata/loadstore_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/map_test.go b/src/cmd/compile/internal/test/testdata/map_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/map_test.go
rename to src/cmd/compile/internal/test/testdata/map_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/namedReturn_test.go b/src/cmd/compile/internal/test/testdata/namedReturn_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/namedReturn_test.go
rename to src/cmd/compile/internal/test/testdata/namedReturn_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/phi_test.go b/src/cmd/compile/internal/test/testdata/phi_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/phi_test.go
rename to src/cmd/compile/internal/test/testdata/phi_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/regalloc_test.go b/src/cmd/compile/internal/test/testdata/regalloc_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/regalloc_test.go
rename to src/cmd/compile/internal/test/testdata/regalloc_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/reproducible/issue20272.go b/src/cmd/compile/internal/test/testdata/reproducible/issue20272.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/reproducible/issue20272.go
rename to src/cmd/compile/internal/test/testdata/reproducible/issue20272.go
diff --git a/src/cmd/compile/internal/gc/testdata/reproducible/issue27013.go b/src/cmd/compile/internal/test/testdata/reproducible/issue27013.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/reproducible/issue27013.go
rename to src/cmd/compile/internal/test/testdata/reproducible/issue27013.go
diff --git a/src/cmd/compile/internal/gc/testdata/reproducible/issue30202.go b/src/cmd/compile/internal/test/testdata/reproducible/issue30202.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/reproducible/issue30202.go
rename to src/cmd/compile/internal/test/testdata/reproducible/issue30202.go
diff --git a/src/cmd/compile/internal/gc/testdata/reproducible/issue38068.go b/src/cmd/compile/internal/test/testdata/reproducible/issue38068.go
similarity index 95%
rename from src/cmd/compile/internal/gc/testdata/reproducible/issue38068.go
rename to src/cmd/compile/internal/test/testdata/reproducible/issue38068.go
index db5ca7dcbe7f6..b87daed8e9882 100644
--- a/src/cmd/compile/internal/gc/testdata/reproducible/issue38068.go
+++ b/src/cmd/compile/internal/test/testdata/reproducible/issue38068.go
@@ -53,7 +53,7 @@ func G(x *A, n int) {
return
}
// Address-taken local of type A, which will insure that the
- // compiler's dtypesym() routine will create a method wrapper.
+ // compiler's writeType() routine will create a method wrapper.
var a, b A
a.next = x
a.prev = &b
diff --git a/src/cmd/compile/internal/gc/testdata/short_test.go b/src/cmd/compile/internal/test/testdata/short_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/short_test.go
rename to src/cmd/compile/internal/test/testdata/short_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/slice_test.go b/src/cmd/compile/internal/test/testdata/slice_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/slice_test.go
rename to src/cmd/compile/internal/test/testdata/slice_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/sqrtConst_test.go b/src/cmd/compile/internal/test/testdata/sqrtConst_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/sqrtConst_test.go
rename to src/cmd/compile/internal/test/testdata/sqrtConst_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/string_test.go b/src/cmd/compile/internal/test/testdata/string_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/string_test.go
rename to src/cmd/compile/internal/test/testdata/string_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/unsafe_test.go b/src/cmd/compile/internal/test/testdata/unsafe_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/unsafe_test.go
rename to src/cmd/compile/internal/test/testdata/unsafe_test.go
diff --git a/src/cmd/compile/internal/gc/testdata/zero_test.go b/src/cmd/compile/internal/test/testdata/zero_test.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/zero_test.go
rename to src/cmd/compile/internal/test/testdata/zero_test.go
diff --git a/src/cmd/compile/internal/gc/truncconst_test.go b/src/cmd/compile/internal/test/truncconst_test.go
similarity index 99%
rename from src/cmd/compile/internal/gc/truncconst_test.go
rename to src/cmd/compile/internal/test/truncconst_test.go
index d1538180649df..7705042ca2c84 100644
--- a/src/cmd/compile/internal/gc/truncconst_test.go
+++ b/src/cmd/compile/internal/test/truncconst_test.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.
-package gc
+package test
import "testing"
diff --git a/src/cmd/compile/internal/gc/zerorange_test.go b/src/cmd/compile/internal/test/zerorange_test.go
similarity index 98%
rename from src/cmd/compile/internal/gc/zerorange_test.go
rename to src/cmd/compile/internal/test/zerorange_test.go
index 89f4cb9bcf6e4..cb1a6e04e4e77 100644
--- a/src/cmd/compile/internal/gc/zerorange_test.go
+++ b/src/cmd/compile/internal/test/zerorange_test.go
@@ -2,11 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package test
-import (
- "testing"
-)
+import "testing"
var glob = 3
var globp *int64
diff --git a/src/cmd/compile/internal/typebits/typebits.go b/src/cmd/compile/internal/typebits/typebits.go
new file mode 100644
index 0000000000000..1c1b077423dc9
--- /dev/null
+++ b/src/cmd/compile/internal/typebits/typebits.go
@@ -0,0 +1,87 @@
+// Copyright 2013 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 typebits
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/bitvec"
+ "cmd/compile/internal/types"
+)
+
+// NOTE: The bitmap for a specific type t could be cached in t after
+// the first run and then simply copied into bv at the correct offset
+// on future calls with the same type t.
+func Set(t *types.Type, off int64, bv bitvec.BitVec) {
+ if t.Align > 0 && off&int64(t.Align-1) != 0 {
+ base.Fatalf("typebits.Set: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
+ }
+ if !t.HasPointers() {
+ // Note: this case ensures that pointers to go:notinheap types
+ // are not considered pointers by garbage collection and stack copying.
+ return
+ }
+
+ switch t.Kind() {
+ case types.TPTR, types.TUNSAFEPTR, types.TFUNC, types.TCHAN, types.TMAP:
+ if off&int64(types.PtrSize-1) != 0 {
+ base.Fatalf("typebits.Set: invalid alignment, %v", t)
+ }
+ bv.Set(int32(off / int64(types.PtrSize))) // pointer
+
+ case types.TSTRING:
+ // struct { byte *str; intgo len; }
+ if off&int64(types.PtrSize-1) != 0 {
+ base.Fatalf("typebits.Set: invalid alignment, %v", t)
+ }
+ bv.Set(int32(off / int64(types.PtrSize))) //pointer in first slot
+
+ case types.TINTER:
+ // struct { Itab *tab; void *data; }
+ // or, when isnilinter(t)==true:
+ // struct { Type *type; void *data; }
+ if off&int64(types.PtrSize-1) != 0 {
+ base.Fatalf("typebits.Set: invalid alignment, %v", t)
+ }
+ // The first word of an interface is a pointer, but we don't
+ // treat it as such.
+ // 1. If it is a non-empty interface, the pointer points to an itab
+ // which is always in persistentalloc space.
+ // 2. If it is an empty interface, the pointer points to a _type.
+ // a. If it is a compile-time-allocated type, it points into
+ // the read-only data section.
+ // b. If it is a reflect-allocated type, it points into the Go heap.
+ // Reflect is responsible for keeping a reference to
+ // the underlying type so it won't be GCd.
+ // If we ever have a moving GC, we need to change this for 2b (as
+ // well as scan itabs to update their itab._type fields).
+ bv.Set(int32(off/int64(types.PtrSize) + 1)) // pointer in second slot
+
+ case types.TSLICE:
+ // struct { byte *array; uintgo len; uintgo cap; }
+ if off&int64(types.PtrSize-1) != 0 {
+ base.Fatalf("typebits.Set: invalid TARRAY alignment, %v", t)
+ }
+ bv.Set(int32(off / int64(types.PtrSize))) // pointer in first slot (BitsPointer)
+
+ case types.TARRAY:
+ elt := t.Elem()
+ if elt.Width == 0 {
+ // Short-circuit for #20739.
+ break
+ }
+ for i := int64(0); i < t.NumElem(); i++ {
+ Set(elt, off, bv)
+ off += elt.Width
+ }
+
+ case types.TSTRUCT:
+ for _, f := range t.Fields().Slice() {
+ Set(f.Type, off+f.Offset, bv)
+ }
+
+ default:
+ base.Fatalf("typebits.Set: unexpected type, %v", t)
+ }
+}
diff --git a/src/cmd/compile/internal/typecheck/bexport.go b/src/cmd/compile/internal/typecheck/bexport.go
new file mode 100644
index 0000000000000..4a84bb13fa48e
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/bexport.go
@@ -0,0 +1,102 @@
+// Copyright 2015 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 typecheck
+
+import "cmd/compile/internal/types"
+
+// ----------------------------------------------------------------------------
+// Export format
+
+// Tags. Must be < 0.
+const (
+ // Objects
+ packageTag = -(iota + 1)
+ constTag
+ typeTag
+ varTag
+ funcTag
+ endTag
+
+ // Types
+ namedTag
+ arrayTag
+ sliceTag
+ dddTag
+ structTag
+ pointerTag
+ signatureTag
+ interfaceTag
+ mapTag
+ chanTag
+
+ // Values
+ falseTag
+ trueTag
+ int64Tag
+ floatTag
+ fractionTag // not used by gc
+ complexTag
+ stringTag
+ nilTag
+ unknownTag // not used by gc (only appears in packages with errors)
+
+ // Type aliases
+ aliasTag
+)
+
+var predecl []*types.Type // initialized lazily
+
+func predeclared() []*types.Type {
+ if predecl == nil {
+ // initialize lazily to be sure that all
+ // elements have been initialized before
+ predecl = []*types.Type{
+ // basic types
+ types.Types[types.TBOOL],
+ types.Types[types.TINT],
+ types.Types[types.TINT8],
+ types.Types[types.TINT16],
+ types.Types[types.TINT32],
+ types.Types[types.TINT64],
+ types.Types[types.TUINT],
+ types.Types[types.TUINT8],
+ types.Types[types.TUINT16],
+ types.Types[types.TUINT32],
+ types.Types[types.TUINT64],
+ types.Types[types.TUINTPTR],
+ types.Types[types.TFLOAT32],
+ types.Types[types.TFLOAT64],
+ types.Types[types.TCOMPLEX64],
+ types.Types[types.TCOMPLEX128],
+ types.Types[types.TSTRING],
+
+ // basic type aliases
+ types.ByteType,
+ types.RuneType,
+
+ // error
+ types.ErrorType,
+
+ // untyped types
+ types.UntypedBool,
+ types.UntypedInt,
+ types.UntypedRune,
+ types.UntypedFloat,
+ types.UntypedComplex,
+ types.UntypedString,
+ types.Types[types.TNIL],
+
+ // package unsafe
+ types.Types[types.TUNSAFEPTR],
+
+ // invalid type (package contains errors)
+ types.Types[types.Txxx],
+
+ // any type, for builtin export data
+ types.Types[types.TANY],
+ }
+ }
+ return predecl
+}
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
new file mode 100644
index 0000000000000..0dee8525292ab
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -0,0 +1,343 @@
+// Code generated by mkbuiltin.go. DO NOT EDIT.
+
+package typecheck
+
+import (
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+var runtimeDecls = [...]struct {
+ name string
+ tag int
+ typ int
+}{
+ {"newobject", funcTag, 4},
+ {"mallocgc", funcTag, 8},
+ {"panicdivide", funcTag, 9},
+ {"panicshift", funcTag, 9},
+ {"panicmakeslicelen", funcTag, 9},
+ {"panicmakeslicecap", funcTag, 9},
+ {"throwinit", funcTag, 9},
+ {"panicwrap", funcTag, 9},
+ {"gopanic", funcTag, 11},
+ {"gorecover", funcTag, 14},
+ {"goschedguarded", funcTag, 9},
+ {"goPanicIndex", funcTag, 16},
+ {"goPanicIndexU", funcTag, 18},
+ {"goPanicSliceAlen", funcTag, 16},
+ {"goPanicSliceAlenU", funcTag, 18},
+ {"goPanicSliceAcap", funcTag, 16},
+ {"goPanicSliceAcapU", funcTag, 18},
+ {"goPanicSliceB", funcTag, 16},
+ {"goPanicSliceBU", funcTag, 18},
+ {"goPanicSlice3Alen", funcTag, 16},
+ {"goPanicSlice3AlenU", funcTag, 18},
+ {"goPanicSlice3Acap", funcTag, 16},
+ {"goPanicSlice3AcapU", funcTag, 18},
+ {"goPanicSlice3B", funcTag, 16},
+ {"goPanicSlice3BU", funcTag, 18},
+ {"goPanicSlice3C", funcTag, 16},
+ {"goPanicSlice3CU", funcTag, 18},
+ {"printbool", funcTag, 19},
+ {"printfloat", funcTag, 21},
+ {"printint", funcTag, 23},
+ {"printhex", funcTag, 25},
+ {"printuint", funcTag, 25},
+ {"printcomplex", funcTag, 27},
+ {"printstring", funcTag, 29},
+ {"printpointer", funcTag, 30},
+ {"printuintptr", funcTag, 31},
+ {"printiface", funcTag, 30},
+ {"printeface", funcTag, 30},
+ {"printslice", funcTag, 30},
+ {"printnl", funcTag, 9},
+ {"printsp", funcTag, 9},
+ {"printlock", funcTag, 9},
+ {"printunlock", funcTag, 9},
+ {"concatstring2", funcTag, 34},
+ {"concatstring3", funcTag, 35},
+ {"concatstring4", funcTag, 36},
+ {"concatstring5", funcTag, 37},
+ {"concatstrings", funcTag, 39},
+ {"cmpstring", funcTag, 40},
+ {"intstring", funcTag, 43},
+ {"slicebytetostring", funcTag, 44},
+ {"slicebytetostringtmp", funcTag, 45},
+ {"slicerunetostring", funcTag, 48},
+ {"stringtoslicebyte", funcTag, 50},
+ {"stringtoslicerune", funcTag, 53},
+ {"slicecopy", 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},
+ {"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},
+ {"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, 31},
+ {"racefuncenterfp", funcTag, 9},
+ {"racefuncexit", funcTag, 9},
+ {"raceread", funcTag, 31},
+ {"racewrite", funcTag, 31},
+ {"racereadrange", funcTag, 121},
+ {"racewriterange", funcTag, 121},
+ {"msanread", funcTag, 121},
+ {"msanwrite", funcTag, 121},
+ {"msanmove", 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},
+ {"x86HasPOPCNT", varTag, 6},
+ {"x86HasSSE41", varTag, 6},
+ {"x86HasFMA", varTag, 6},
+ {"armHasVFPv4", varTag, 6},
+ {"arm64HasATOMICS", varTag, 6},
+}
+
+func runtimeTypes() []*types.Type {
+ var typs [132]*types.Type
+ typs[0] = types.ByteType
+ typs[1] = types.NewPtr(typs[0])
+ typs[2] = types.Types[types.TANY]
+ typs[3] = types.NewPtr(typs[2])
+ typs[4] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
+ typs[5] = types.Types[types.TUINTPTR]
+ typs[6] = types.Types[types.TBOOL]
+ typs[7] = types.Types[types.TUNSAFEPTR]
+ typs[8] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[6])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
+ typs[9] = types.NewSignature(types.NoPkg, nil, nil, nil)
+ typs[10] = types.Types[types.TINTER]
+ typs[11] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[10])}, nil)
+ typs[12] = types.Types[types.TINT32]
+ typs[13] = types.NewPtr(typs[12])
+ typs[14] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[13])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[10])})
+ typs[15] = types.Types[types.TINT]
+ typs[16] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15])}, nil)
+ typs[17] = types.Types[types.TUINT]
+ typs[18] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[17]), types.NewField(src.NoXPos, nil, typs[15])}, nil)
+ typs[19] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])}, nil)
+ typs[20] = types.Types[types.TFLOAT64]
+ typs[21] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, nil)
+ typs[22] = types.Types[types.TINT64]
+ typs[23] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])}, nil)
+ typs[24] = types.Types[types.TUINT64]
+ typs[25] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])}, nil)
+ typs[26] = types.Types[types.TCOMPLEX128]
+ typs[27] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[26])}, nil)
+ typs[28] = types.Types[types.TSTRING]
+ typs[29] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])}, nil)
+ typs[30] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])}, nil)
+ typs[31] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5])}, nil)
+ typs[32] = types.NewArray(typs[0], 32)
+ typs[33] = types.NewPtr(typs[32])
+ typs[34] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[35] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[36] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[37] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[38] = types.NewSlice(typs[28])
+ typs[39] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[38])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[40] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
+ typs[41] = types.NewArray(typs[0], 4)
+ typs[42] = types.NewPtr(typs[41])
+ typs[43] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[42]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[44] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[45] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[46] = types.RuneType
+ typs[47] = types.NewSlice(typs[46])
+ typs[48] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[47])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
+ typs[49] = types.NewSlice(typs[0])
+ typs[50] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[49])})
+ typs[51] = types.NewArray(typs[46], 32)
+ typs[52] = types.NewPtr(typs[51])
+ typs[53] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[52]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[47])})
+ typs[54] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
+ typs[55] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[46]), types.NewField(src.NoXPos, nil, typs[15])})
+ typs[56] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
+ typs[57] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])})
+ typs[58] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
+ typs[59] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])})
+ typs[60] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[2]), types.NewField(src.NoXPos, nil, typs[6])})
+ typs[61] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[1])}, nil)
+ typs[62] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1])}, nil)
+ typs[63] = types.NewPtr(typs[5])
+ typs[64] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[63]), types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[7])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[65] = types.Types[types.TUINT32]
+ typs[66] = types.NewSignature(types.NoPkg, nil, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[65])})
+ typs[67] = types.NewMap(typs[2], typs[2])
+ typs[68] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[22]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[67])})
+ typs[69] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[67])})
+ typs[70] = types.NewSignature(types.NoPkg, nil, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[67])})
+ typs[71] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
+ typs[72] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
+ typs[73] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[1])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
+ typs[74] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[6])})
+ typs[75] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[6])})
+ typs[76] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[1])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[6])})
+ typs[77] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
+ typs[78] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[2])}, nil)
+ typs[79] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])}, nil)
+ typs[80] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67])}, nil)
+ typs[81] = types.NewChan(typs[2], types.Cboth)
+ typs[82] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[81])})
+ typs[83] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[81])})
+ typs[84] = types.NewChan(typs[2], types.Crecv)
+ typs[85] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[84]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
+ typs[86] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[84]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[87] = types.NewChan(typs[2], types.Csend)
+ typs[88] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[87]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
+ typs[89] = types.NewArray(typs[0], 3)
+ typs[90] = types.NewStruct(types.NoPkg, []*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[89]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
+ typs[91] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
+ typs[92] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
+ typs[93] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
+ typs[94] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[87]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[95] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[84])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[96] = types.NewPtr(typs[6])
+ typs[97] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[96]), types.NewField(src.NoXPos, nil, typs[84])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[98] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[63])}, nil)
+ typs[99] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[63]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[6])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[6])})
+ typs[100] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
+ typs[101] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[22]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
+ typs[102] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[7])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
+ typs[103] = types.NewSlice(typs[2])
+ typs[104] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[103]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[103])})
+ typs[105] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
+ typs[106] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
+ typs[107] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[108] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[109] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[7])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
+ typs[110] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[5])})
+ typs[111] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[5])})
+ typs[112] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[22]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])})
+ typs[113] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24]), types.NewField(src.NoXPos, nil, typs[24])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])})
+ typs[114] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])})
+ typs[115] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])})
+ typs[116] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[65])})
+ typs[117] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])})
+ typs[118] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])})
+ typs[119] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[65])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])})
+ typs[120] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[26]), types.NewField(src.NoXPos, nil, typs[26])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[26])})
+ typs[121] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
+ typs[122] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
+ typs[123] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
+ typs[124] = types.NewSlice(typs[7])
+ typs[125] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[124])}, nil)
+ typs[126] = types.Types[types.TUINT8]
+ typs[127] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[126]), types.NewField(src.NoXPos, nil, typs[126])}, nil)
+ typs[128] = types.Types[types.TUINT16]
+ typs[129] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[128]), types.NewField(src.NoXPos, nil, typs[128])}, nil)
+ typs[130] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[65]), types.NewField(src.NoXPos, nil, typs[65])}, nil)
+ typs[131] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24]), types.NewField(src.NoXPos, nil, typs[24])}, nil)
+ return typs[:]
+}
diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/typecheck/builtin/runtime.go
similarity index 100%
rename from src/cmd/compile/internal/gc/builtin/runtime.go
rename to src/cmd/compile/internal/typecheck/builtin/runtime.go
diff --git a/src/cmd/compile/internal/gc/builtin_test.go b/src/cmd/compile/internal/typecheck/builtin_test.go
similarity index 97%
rename from src/cmd/compile/internal/gc/builtin_test.go
rename to src/cmd/compile/internal/typecheck/builtin_test.go
index 57f24b2287978..fb9d3e393f10e 100644
--- a/src/cmd/compile/internal/gc/builtin_test.go
+++ b/src/cmd/compile/internal/typecheck/builtin_test.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.
-package gc_test
+package typecheck
import (
"bytes"
diff --git a/src/cmd/compile/internal/typecheck/const.go b/src/cmd/compile/internal/typecheck/const.go
new file mode 100644
index 0000000000000..1a8e58383ad34
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/const.go
@@ -0,0 +1,935 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "fmt"
+ "go/constant"
+ "go/token"
+ "math"
+ "math/big"
+ "strings"
+ "unicode"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+func roundFloat(v constant.Value, sz int64) constant.Value {
+ switch sz {
+ case 4:
+ f, _ := constant.Float32Val(v)
+ return makeFloat64(float64(f))
+ case 8:
+ f, _ := constant.Float64Val(v)
+ return makeFloat64(f)
+ }
+ base.Fatalf("unexpected size: %v", sz)
+ panic("unreachable")
+}
+
+// truncate float literal fv to 32-bit or 64-bit precision
+// according to type; return truncated value.
+func truncfltlit(v constant.Value, t *types.Type) constant.Value {
+ if t.IsUntyped() || overflow(v, t) {
+ // If there was overflow, simply continuing would set the
+ // value to Inf which in turn would lead to spurious follow-on
+ // errors. Avoid this by returning the existing value.
+ return v
+ }
+
+ return roundFloat(v, t.Size())
+}
+
+// truncate Real and Imag parts of Mpcplx to 32-bit or 64-bit
+// precision, according to type; return truncated value. In case of
+// overflow, calls Errorf but does not truncate the input value.
+func trunccmplxlit(v constant.Value, t *types.Type) constant.Value {
+ if t.IsUntyped() || overflow(v, t) {
+ // If there was overflow, simply continuing would set the
+ // value to Inf which in turn would lead to spurious follow-on
+ // errors. Avoid this by returning the existing value.
+ return v
+ }
+
+ fsz := t.Size() / 2
+ return makeComplex(roundFloat(constant.Real(v), fsz), roundFloat(constant.Imag(v), fsz))
+}
+
+// TODO(mdempsky): Replace these with better APIs.
+func convlit(n ir.Node, t *types.Type) ir.Node { return convlit1(n, t, false, nil) }
+func DefaultLit(n ir.Node, t *types.Type) ir.Node { return convlit1(n, t, false, nil) }
+
+// convlit1 converts an untyped expression n to type t. If n already
+// has a type, convlit1 has no effect.
+//
+// For explicit conversions, t must be non-nil, and integer-to-string
+// conversions are allowed.
+//
+// For implicit conversions (e.g., assignments), t may be nil; if so,
+// n is converted to its default type.
+//
+// If there's an error converting n to t, context is used in the error
+// message.
+func convlit1(n ir.Node, t *types.Type, explicit bool, context func() string) ir.Node {
+ if explicit && t == nil {
+ base.Fatalf("explicit conversion missing type")
+ }
+ if t != nil && t.IsUntyped() {
+ base.Fatalf("bad conversion to untyped: %v", t)
+ }
+
+ if n == nil || n.Type() == nil {
+ // Allow sloppy callers.
+ return n
+ }
+ if !n.Type().IsUntyped() {
+ // Already typed; nothing to do.
+ return n
+ }
+
+ // Nil is technically not a constant, so handle it specially.
+ if n.Type().Kind() == types.TNIL {
+ if n.Op() != ir.ONIL {
+ base.Fatalf("unexpected op: %v (%v)", n, n.Op())
+ }
+ n = ir.Copy(n)
+ if t == nil {
+ base.Errorf("use of untyped nil")
+ n.SetDiag(true)
+ n.SetType(nil)
+ return n
+ }
+
+ if !t.HasNil() {
+ // Leave for caller to handle.
+ return n
+ }
+
+ n.SetType(t)
+ return n
+ }
+
+ if t == nil || !ir.OKForConst[t.Kind()] {
+ t = defaultType(n.Type())
+ }
+
+ switch n.Op() {
+ default:
+ base.Fatalf("unexpected untyped expression: %v", n)
+
+ case ir.OLITERAL:
+ v := convertVal(n.Val(), t, explicit)
+ if v.Kind() == constant.Unknown {
+ n = ir.NewConstExpr(n.Val(), n)
+ break
+ }
+ n = ir.NewConstExpr(v, n)
+ n.SetType(t)
+ return n
+
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.OREAL, ir.OIMAG:
+ ot := operandType(n.Op(), t)
+ if ot == nil {
+ n = DefaultLit(n, nil)
+ break
+ }
+
+ n := n.(*ir.UnaryExpr)
+ n.X = convlit(n.X, ot)
+ if n.X.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ n.SetType(t)
+ return n
+
+ case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT, ir.OOROR, ir.OANDAND, ir.OCOMPLEX:
+ ot := operandType(n.Op(), t)
+ if ot == nil {
+ n = DefaultLit(n, nil)
+ break
+ }
+
+ var l, r ir.Node
+ switch n := n.(type) {
+ case *ir.BinaryExpr:
+ n.X = convlit(n.X, ot)
+ n.Y = convlit(n.Y, ot)
+ l, r = n.X, n.Y
+ case *ir.LogicalExpr:
+ n.X = convlit(n.X, ot)
+ n.Y = convlit(n.Y, ot)
+ l, r = n.X, n.Y
+ }
+
+ if l.Type() == nil || r.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !types.Identical(l.Type(), r.Type()) {
+ base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type())
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetType(t)
+ return n
+
+ case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ n := n.(*ir.BinaryExpr)
+ if !t.IsBoolean() {
+ break
+ }
+ n.SetType(t)
+ return n
+
+ case ir.OLSH, ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ n.X = convlit1(n.X, t, explicit, nil)
+ n.SetType(n.X.Type())
+ if n.Type() != nil && !n.Type().IsInteger() {
+ base.Errorf("invalid operation: %v (shift of type %v)", n, n.Type())
+ n.SetType(nil)
+ }
+ return n
+ }
+
+ if !n.Diag() {
+ if !t.Broke() {
+ if explicit {
+ base.Errorf("cannot convert %L to type %v", n, t)
+ } else if context != nil {
+ base.Errorf("cannot use %L as type %v in %s", n, t, context())
+ } else {
+ base.Errorf("cannot use %L as type %v", n, t)
+ }
+ }
+ n.SetDiag(true)
+ }
+ n.SetType(nil)
+ return n
+}
+
+func operandType(op ir.Op, t *types.Type) *types.Type {
+ switch op {
+ case ir.OCOMPLEX:
+ if t.IsComplex() {
+ return types.FloatForComplex(t)
+ }
+ case ir.OREAL, ir.OIMAG:
+ if t.IsFloat() {
+ return types.ComplexForFloat(t)
+ }
+ default:
+ if okfor[op][t.Kind()] {
+ return t
+ }
+ }
+ return nil
+}
+
+// convertVal converts v into a representation appropriate for t. If
+// no such representation exists, it returns Val{} instead.
+//
+// If explicit is true, then conversions from integer to string are
+// also allowed.
+func convertVal(v constant.Value, t *types.Type, explicit bool) constant.Value {
+ switch ct := v.Kind(); ct {
+ case constant.Bool:
+ if t.IsBoolean() {
+ return v
+ }
+
+ case constant.String:
+ if t.IsString() {
+ return v
+ }
+
+ case constant.Int:
+ if explicit && t.IsString() {
+ return tostr(v)
+ }
+ fallthrough
+ case constant.Float, constant.Complex:
+ switch {
+ case t.IsInteger():
+ v = toint(v)
+ overflow(v, t)
+ return v
+ case t.IsFloat():
+ v = toflt(v)
+ v = truncfltlit(v, t)
+ return v
+ case t.IsComplex():
+ v = tocplx(v)
+ v = trunccmplxlit(v, t)
+ return v
+ }
+ }
+
+ return constant.MakeUnknown()
+}
+
+func tocplx(v constant.Value) constant.Value {
+ return constant.ToComplex(v)
+}
+
+func toflt(v constant.Value) constant.Value {
+ if v.Kind() == constant.Complex {
+ if constant.Sign(constant.Imag(v)) != 0 {
+ base.Errorf("constant %v truncated to real", v)
+ }
+ v = constant.Real(v)
+ }
+
+ return constant.ToFloat(v)
+}
+
+func toint(v constant.Value) constant.Value {
+ if v.Kind() == constant.Complex {
+ if constant.Sign(constant.Imag(v)) != 0 {
+ base.Errorf("constant %v truncated to integer", v)
+ }
+ v = constant.Real(v)
+ }
+
+ if v := constant.ToInt(v); v.Kind() == constant.Int {
+ return v
+ }
+
+ // The value of v cannot be represented as an integer;
+ // so we need to print an error message.
+ // Unfortunately some float values cannot be
+ // reasonably formatted for inclusion in an error
+ // message (example: 1 + 1e-100), so first we try to
+ // format the float; if the truncation resulted in
+ // something that looks like an integer we omit the
+ // value from the error message.
+ // (See issue #11371).
+ f := ir.BigFloat(v)
+ if f.MantExp(nil) > 2*ir.ConstPrec {
+ base.Errorf("integer too large")
+ } else {
+ var t big.Float
+ t.Parse(fmt.Sprint(v), 0)
+ if t.IsInt() {
+ base.Errorf("constant truncated to integer")
+ } else {
+ base.Errorf("constant %v truncated to integer", v)
+ }
+ }
+
+ // Prevent follow-on errors.
+ // TODO(mdempsky): Use constant.MakeUnknown() instead.
+ return constant.MakeInt64(1)
+}
+
+// overflow reports whether constant value v is too large
+// to represent with type t, and emits an error message if so.
+func overflow(v constant.Value, t *types.Type) bool {
+ // v has already been converted
+ // to appropriate form for t.
+ if t.IsUntyped() {
+ return false
+ }
+ if v.Kind() == constant.Int && constant.BitLen(v) > ir.ConstPrec {
+ base.Errorf("integer too large")
+ return true
+ }
+ if ir.ConstOverflow(v, t) {
+ base.Errorf("constant %v overflows %v", types.FmtConst(v, false), t)
+ return true
+ }
+ return false
+}
+
+func tostr(v constant.Value) constant.Value {
+ if v.Kind() == constant.Int {
+ r := unicode.ReplacementChar
+ if x, ok := constant.Uint64Val(v); ok && x <= unicode.MaxRune {
+ r = rune(x)
+ }
+ v = constant.MakeString(string(r))
+ }
+ return v
+}
+
+var tokenForOp = [...]token.Token{
+ ir.OPLUS: token.ADD,
+ ir.ONEG: token.SUB,
+ ir.ONOT: token.NOT,
+ ir.OBITNOT: token.XOR,
+
+ ir.OADD: token.ADD,
+ ir.OSUB: token.SUB,
+ ir.OMUL: token.MUL,
+ ir.ODIV: token.QUO,
+ ir.OMOD: token.REM,
+ ir.OOR: token.OR,
+ ir.OXOR: token.XOR,
+ ir.OAND: token.AND,
+ ir.OANDNOT: token.AND_NOT,
+ ir.OOROR: token.LOR,
+ ir.OANDAND: token.LAND,
+
+ ir.OEQ: token.EQL,
+ ir.ONE: token.NEQ,
+ ir.OLT: token.LSS,
+ ir.OLE: token.LEQ,
+ ir.OGT: token.GTR,
+ ir.OGE: token.GEQ,
+
+ ir.OLSH: token.SHL,
+ ir.ORSH: token.SHR,
+}
+
+// EvalConst returns a constant-evaluated expression equivalent to n.
+// If n is not a constant, EvalConst returns n.
+// Otherwise, EvalConst returns a new OLITERAL with the same value as n,
+// and with .Orig pointing back to n.
+func EvalConst(n ir.Node) ir.Node {
+ // Pick off just the opcodes that can be constant evaluated.
+ switch n.Op() {
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT:
+ n := n.(*ir.UnaryExpr)
+ nl := n.X
+ if nl.Op() == ir.OLITERAL {
+ var prec uint
+ if n.Type().IsUnsigned() {
+ prec = uint(n.Type().Size() * 8)
+ }
+ return OrigConst(n, constant.UnaryOp(tokenForOp[n.Op()], nl.Val(), prec))
+ }
+
+ case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT:
+ n := n.(*ir.BinaryExpr)
+ nl, nr := n.X, n.Y
+ if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
+ rval := nr.Val()
+
+ // check for divisor underflow in complex division (see issue 20227)
+ if n.Op() == ir.ODIV && n.Type().IsComplex() && constant.Sign(square(constant.Real(rval))) == 0 && constant.Sign(square(constant.Imag(rval))) == 0 {
+ base.Errorf("complex division by zero")
+ n.SetType(nil)
+ return n
+ }
+ if (n.Op() == ir.ODIV || n.Op() == ir.OMOD) && constant.Sign(rval) == 0 {
+ base.Errorf("division by zero")
+ n.SetType(nil)
+ return n
+ }
+
+ tok := tokenForOp[n.Op()]
+ if n.Op() == ir.ODIV && n.Type().IsInteger() {
+ tok = token.QUO_ASSIGN // integer division
+ }
+ return OrigConst(n, constant.BinaryOp(nl.Val(), tok, rval))
+ }
+
+ case ir.OOROR, ir.OANDAND:
+ n := n.(*ir.LogicalExpr)
+ nl, nr := n.X, n.Y
+ if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
+ return OrigConst(n, constant.BinaryOp(nl.Val(), tokenForOp[n.Op()], nr.Val()))
+ }
+
+ case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ n := n.(*ir.BinaryExpr)
+ nl, nr := n.X, n.Y
+ if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
+ return OrigBool(n, constant.Compare(nl.Val(), tokenForOp[n.Op()], nr.Val()))
+ }
+
+ case ir.OLSH, ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ nl, nr := n.X, n.Y
+ if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
+ // shiftBound from go/types; "so we can express smallestFloat64"
+ const shiftBound = 1023 - 1 + 52
+ s, ok := constant.Uint64Val(nr.Val())
+ if !ok || s > shiftBound {
+ base.Errorf("invalid shift count %v", nr)
+ n.SetType(nil)
+ break
+ }
+ return OrigConst(n, constant.Shift(toint(nl.Val()), tokenForOp[n.Op()], uint(s)))
+ }
+
+ case ir.OCONV, ir.ORUNESTR:
+ n := n.(*ir.ConvExpr)
+ nl := n.X
+ if ir.OKForConst[n.Type().Kind()] && nl.Op() == ir.OLITERAL {
+ return OrigConst(n, convertVal(nl.Val(), n.Type(), true))
+ }
+
+ case ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ nl := n.X
+ if ir.OKForConst[n.Type().Kind()] && nl.Op() == ir.OLITERAL {
+ // set so n.Orig gets OCONV instead of OCONVNOP
+ n.SetOp(ir.OCONV)
+ return OrigConst(n, nl.Val())
+ }
+
+ case ir.OADDSTR:
+ // Merge adjacent constants in the argument list.
+ n := n.(*ir.AddStringExpr)
+ s := n.List
+ need := 0
+ for i := 0; i < len(s); i++ {
+ if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) {
+ // Can't merge s[i] into s[i-1]; need a slot in the list.
+ need++
+ }
+ }
+ if need == len(s) {
+ return n
+ }
+ if need == 1 {
+ var strs []string
+ for _, c := range s {
+ strs = append(strs, ir.StringVal(c))
+ }
+ return OrigConst(n, constant.MakeString(strings.Join(strs, "")))
+ }
+ newList := make([]ir.Node, 0, need)
+ for i := 0; i < len(s); i++ {
+ if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) {
+ // merge from i up to but not including i2
+ var strs []string
+ i2 := i
+ for i2 < len(s) && ir.IsConst(s[i2], constant.String) {
+ strs = append(strs, ir.StringVal(s[i2]))
+ i2++
+ }
+
+ nl := ir.Copy(n).(*ir.AddStringExpr)
+ nl.List = s[i:i2]
+ newList = append(newList, OrigConst(nl, constant.MakeString(strings.Join(strs, ""))))
+ i = i2 - 1
+ } else {
+ newList = append(newList, s[i])
+ }
+ }
+
+ nn := ir.Copy(n).(*ir.AddStringExpr)
+ nn.List = newList
+ return nn
+
+ case ir.OCAP, ir.OLEN:
+ n := n.(*ir.UnaryExpr)
+ nl := n.X
+ switch nl.Type().Kind() {
+ case types.TSTRING:
+ if ir.IsConst(nl, constant.String) {
+ return OrigInt(n, int64(len(ir.StringVal(nl))))
+ }
+ case types.TARRAY:
+ if !anyCallOrChan(nl) {
+ return OrigInt(n, nl.Type().NumElem())
+ }
+ }
+
+ case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+ n := n.(*ir.UnaryExpr)
+ return OrigInt(n, evalunsafe(n))
+
+ case ir.OREAL:
+ n := n.(*ir.UnaryExpr)
+ nl := n.X
+ if nl.Op() == ir.OLITERAL {
+ return OrigConst(n, constant.Real(nl.Val()))
+ }
+
+ case ir.OIMAG:
+ n := n.(*ir.UnaryExpr)
+ nl := n.X
+ if nl.Op() == ir.OLITERAL {
+ return OrigConst(n, constant.Imag(nl.Val()))
+ }
+
+ case ir.OCOMPLEX:
+ n := n.(*ir.BinaryExpr)
+ nl, nr := n.X, n.Y
+ if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
+ return OrigConst(n, makeComplex(nl.Val(), nr.Val()))
+ }
+ }
+
+ return n
+}
+
+func makeFloat64(f float64) constant.Value {
+ if math.IsInf(f, 0) {
+ base.Fatalf("infinity is not a valid constant")
+ }
+ return constant.MakeFloat64(f)
+}
+
+func makeComplex(real, imag constant.Value) constant.Value {
+ return constant.BinaryOp(constant.ToFloat(real), token.ADD, constant.MakeImag(constant.ToFloat(imag)))
+}
+
+func square(x constant.Value) constant.Value {
+ return constant.BinaryOp(x, token.MUL, x)
+}
+
+// For matching historical "constant OP overflow" error messages.
+// TODO(mdempsky): Replace with error messages like go/types uses.
+var overflowNames = [...]string{
+ ir.OADD: "addition",
+ ir.OSUB: "subtraction",
+ ir.OMUL: "multiplication",
+ ir.OLSH: "shift",
+ ir.OXOR: "bitwise XOR",
+ ir.OBITNOT: "bitwise complement",
+}
+
+// OrigConst returns an OLITERAL with orig n and value v.
+func OrigConst(n ir.Node, v constant.Value) ir.Node {
+ lno := ir.SetPos(n)
+ v = convertVal(v, n.Type(), false)
+ base.Pos = lno
+
+ switch v.Kind() {
+ case constant.Int:
+ if constant.BitLen(v) <= ir.ConstPrec {
+ break
+ }
+ fallthrough
+ case constant.Unknown:
+ what := overflowNames[n.Op()]
+ if what == "" {
+ base.Fatalf("unexpected overflow: %v", n.Op())
+ }
+ base.ErrorfAt(n.Pos(), "constant %v overflow", what)
+ n.SetType(nil)
+ return n
+ }
+
+ return ir.NewConstExpr(v, n)
+}
+
+func OrigBool(n ir.Node, v bool) ir.Node {
+ return OrigConst(n, constant.MakeBool(v))
+}
+
+func OrigInt(n ir.Node, v int64) ir.Node {
+ return OrigConst(n, constant.MakeInt64(v))
+}
+
+// DefaultLit on both nodes simultaneously;
+// if they're both ideal going in they better
+// get the same type going out.
+// force means must assign concrete (non-ideal) type.
+// The results of defaultlit2 MUST be assigned back to l and r, e.g.
+// n.Left, n.Right = defaultlit2(n.Left, n.Right, force)
+func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) {
+ if l.Type() == nil || r.Type() == nil {
+ return l, r
+ }
+ if !l.Type().IsUntyped() {
+ r = convlit(r, l.Type())
+ return l, r
+ }
+
+ if !r.Type().IsUntyped() {
+ l = convlit(l, r.Type())
+ return l, r
+ }
+
+ if !force {
+ return l, r
+ }
+
+ // Can't mix bool with non-bool, string with non-string, or nil with anything (untyped).
+ if l.Type().IsBoolean() != r.Type().IsBoolean() {
+ return l, r
+ }
+ if l.Type().IsString() != r.Type().IsString() {
+ return l, r
+ }
+ if ir.IsNil(l) || ir.IsNil(r) {
+ return l, r
+ }
+
+ t := defaultType(mixUntyped(l.Type(), r.Type()))
+ l = convlit(l, t)
+ r = convlit(r, t)
+ return l, r
+}
+
+func mixUntyped(t1, t2 *types.Type) *types.Type {
+ if t1 == t2 {
+ return t1
+ }
+
+ rank := func(t *types.Type) int {
+ switch t {
+ case types.UntypedInt:
+ return 0
+ case types.UntypedRune:
+ return 1
+ case types.UntypedFloat:
+ return 2
+ case types.UntypedComplex:
+ return 3
+ }
+ base.Fatalf("bad type %v", t)
+ panic("unreachable")
+ }
+
+ if rank(t2) > rank(t1) {
+ return t2
+ }
+ return t1
+}
+
+func defaultType(t *types.Type) *types.Type {
+ if !t.IsUntyped() || t.Kind() == types.TNIL {
+ return t
+ }
+
+ switch t {
+ case types.UntypedBool:
+ return types.Types[types.TBOOL]
+ case types.UntypedString:
+ return types.Types[types.TSTRING]
+ case types.UntypedInt:
+ return types.Types[types.TINT]
+ case types.UntypedRune:
+ return types.RuneType
+ case types.UntypedFloat:
+ return types.Types[types.TFLOAT64]
+ case types.UntypedComplex:
+ return types.Types[types.TCOMPLEX128]
+ }
+
+ base.Fatalf("bad type %v", t)
+ return nil
+}
+
+// IndexConst checks if Node n contains a constant expression
+// representable as a non-negative int and returns its value.
+// If n is not a constant expression, not representable as an
+// integer, or negative, it returns -1. If n is too large, it
+// returns -2.
+func IndexConst(n ir.Node) int64 {
+ if n.Op() != ir.OLITERAL {
+ return -1
+ }
+ if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL {
+ return -1
+ }
+
+ v := toint(n.Val())
+ if v.Kind() != constant.Int || constant.Sign(v) < 0 {
+ return -1
+ }
+ if ir.ConstOverflow(v, types.Types[types.TINT]) {
+ return -2
+ }
+ return ir.IntVal(types.Types[types.TINT], v)
+}
+
+// anyCallOrChan reports whether n contains any calls or channel operations.
+func anyCallOrChan(n ir.Node) bool {
+ return ir.Any(n, func(n ir.Node) bool {
+ switch n.Op() {
+ case ir.OAPPEND,
+ ir.OCALL,
+ ir.OCALLFUNC,
+ ir.OCALLINTER,
+ ir.OCALLMETH,
+ ir.OCAP,
+ ir.OCLOSE,
+ ir.OCOMPLEX,
+ ir.OCOPY,
+ ir.ODELETE,
+ ir.OIMAG,
+ ir.OLEN,
+ ir.OMAKE,
+ ir.ONEW,
+ ir.OPANIC,
+ ir.OPRINT,
+ ir.OPRINTN,
+ ir.OREAL,
+ ir.ORECOVER,
+ ir.ORECV:
+ return true
+ }
+ return false
+ })
+}
+
+// A constSet represents a set of Go constant expressions.
+type constSet struct {
+ m map[constSetKey]src.XPos
+}
+
+type constSetKey struct {
+ typ *types.Type
+ val interface{}
+}
+
+// add adds constant expression n to s. If a constant expression of
+// equal value and identical type has already been added, then add
+// reports an error about the duplicate value.
+//
+// pos provides position information for where expression n occurred
+// (in case n does not have its own position information). what and
+// where are used in the error message.
+//
+// n must not be an untyped constant.
+func (s *constSet) add(pos src.XPos, n ir.Node, what, where string) {
+ if conv := n; conv.Op() == ir.OCONVIFACE {
+ conv := conv.(*ir.ConvExpr)
+ if conv.Implicit() {
+ n = conv.X
+ }
+ }
+
+ if !ir.IsConstNode(n) {
+ return
+ }
+ if n.Type().IsUntyped() {
+ base.Fatalf("%v is untyped", n)
+ }
+
+ // Consts are only duplicates if they have the same value and
+ // identical types.
+ //
+ // In general, we have to use types.Identical to test type
+ // identity, because == gives false negatives for anonymous
+ // types and the byte/uint8 and rune/int32 builtin type
+ // aliases. However, this is not a problem here, because
+ // constant expressions are always untyped or have a named
+ // type, and we explicitly handle the builtin type aliases
+ // below.
+ //
+ // This approach may need to be revisited though if we fix
+ // #21866 by treating all type aliases like byte/uint8 and
+ // rune/int32.
+
+ typ := n.Type()
+ switch typ {
+ case types.ByteType:
+ typ = types.Types[types.TUINT8]
+ case types.RuneType:
+ typ = types.Types[types.TINT32]
+ }
+ k := constSetKey{typ, ir.ConstValue(n)}
+
+ if ir.HasUniquePos(n) {
+ pos = n.Pos()
+ }
+
+ if s.m == nil {
+ s.m = make(map[constSetKey]src.XPos)
+ }
+
+ if prevPos, isDup := s.m[k]; isDup {
+ base.ErrorfAt(pos, "duplicate %s %s in %s\n\tprevious %s at %v",
+ what, nodeAndVal(n), where,
+ what, base.FmtPos(prevPos))
+ } else {
+ s.m[k] = pos
+ }
+}
+
+// nodeAndVal reports both an expression and its constant value, if
+// the latter is non-obvious.
+//
+// TODO(mdempsky): This could probably be a fmt.go flag.
+func nodeAndVal(n ir.Node) string {
+ show := fmt.Sprint(n)
+ val := ir.ConstValue(n)
+ if s := fmt.Sprintf("%#v", val); show != s {
+ show += " (value " + s + ")"
+ }
+ return show
+}
+
+// evalunsafe evaluates a package unsafe operation and returns the result.
+func evalunsafe(n ir.Node) int64 {
+ switch n.Op() {
+ case ir.OALIGNOF, ir.OSIZEOF:
+ n := n.(*ir.UnaryExpr)
+ n.X = Expr(n.X)
+ n.X = DefaultLit(n.X, nil)
+ tr := n.X.Type()
+ if tr == nil {
+ return 0
+ }
+ types.CalcSize(tr)
+ if n.Op() == ir.OALIGNOF {
+ return int64(tr.Align)
+ }
+ return tr.Width
+
+ case ir.OOFFSETOF:
+ // must be a selector.
+ n := n.(*ir.UnaryExpr)
+ if n.X.Op() != ir.OXDOT {
+ base.Errorf("invalid expression %v", n)
+ return 0
+ }
+ sel := n.X.(*ir.SelectorExpr)
+
+ // Remember base of selector to find it back after dot insertion.
+ // Since r->left may be mutated by typechecking, check it explicitly
+ // first to track it correctly.
+ sel.X = Expr(sel.X)
+ sbase := sel.X
+
+ tsel := Expr(sel)
+ n.X = tsel
+ if tsel.Type() == nil {
+ return 0
+ }
+ switch tsel.Op() {
+ case ir.ODOT, ir.ODOTPTR:
+ break
+ case ir.OCALLPART:
+ base.Errorf("invalid expression %v: argument is a method value", n)
+ return 0
+ default:
+ base.Errorf("invalid expression %v", n)
+ return 0
+ }
+
+ // Sum offsets for dots until we reach sbase.
+ var v int64
+ var next ir.Node
+ for r := tsel; r != sbase; r = next {
+ switch r.Op() {
+ case ir.ODOTPTR:
+ // For Offsetof(s.f), s may itself be a pointer,
+ // but accessing f must not otherwise involve
+ // indirection via embedded pointer types.
+ r := r.(*ir.SelectorExpr)
+ if r.X != sbase {
+ base.Errorf("invalid expression %v: selector implies indirection of embedded %v", n, r.X)
+ return 0
+ }
+ fallthrough
+ case ir.ODOT:
+ r := r.(*ir.SelectorExpr)
+ v += r.Offset()
+ next = r.X
+ default:
+ ir.Dump("unsafenmagic", tsel)
+ base.Fatalf("impossible %v node after dot insertion", r.Op())
+ }
+ }
+ return v
+ }
+
+ base.Fatalf("unexpected op %v", n.Op())
+ return 0
+}
diff --git a/src/cmd/compile/internal/typecheck/dcl.go b/src/cmd/compile/internal/typecheck/dcl.go
new file mode 100644
index 0000000000000..eab0bb09b2950
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/dcl.go
@@ -0,0 +1,474 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "fmt"
+ "strconv"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+var DeclContext ir.Class = ir.PEXTERN // PEXTERN/PAUTO
+
+func DeclFunc(sym *types.Sym, tfn ir.Ntype) *ir.Func {
+ if tfn.Op() != ir.OTFUNC {
+ base.Fatalf("expected OTFUNC node, got %v", tfn)
+ }
+
+ fn := ir.NewFunc(base.Pos)
+ fn.Nname = ir.NewNameAt(base.Pos, sym)
+ fn.Nname.Func = fn
+ fn.Nname.Defn = fn
+ fn.Nname.Ntype = tfn
+ ir.MarkFunc(fn.Nname)
+ StartFuncBody(fn)
+ fn.Nname.Ntype = typecheckNtype(fn.Nname.Ntype)
+ return fn
+}
+
+// Declare records that Node n declares symbol n.Sym in the specified
+// declaration context.
+func Declare(n *ir.Name, ctxt ir.Class) {
+ if ir.IsBlank(n) {
+ return
+ }
+
+ s := n.Sym()
+
+ // kludgy: TypecheckAllowed means we're past parsing. Eg reflectdata.methodWrapper may declare out of package names later.
+ if !inimport && !TypecheckAllowed && s.Pkg != types.LocalPkg {
+ base.ErrorfAt(n.Pos(), "cannot declare name %v", s)
+ }
+
+ if ctxt == ir.PEXTERN {
+ if s.Name == "init" {
+ base.ErrorfAt(n.Pos(), "cannot declare init - must be func")
+ }
+ if s.Name == "main" && s.Pkg.Name == "main" {
+ base.ErrorfAt(n.Pos(), "cannot declare main - must be func")
+ }
+ Target.Externs = append(Target.Externs, n)
+ } else {
+ if ir.CurFunc == nil && ctxt == ir.PAUTO {
+ base.Pos = n.Pos()
+ base.Fatalf("automatic outside function")
+ }
+ if ir.CurFunc != nil && ctxt != ir.PFUNC && n.Op() == ir.ONAME {
+ ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
+ }
+ types.Pushdcl(s)
+ n.Curfn = ir.CurFunc
+ }
+
+ if ctxt == ir.PAUTO {
+ n.SetFrameOffset(0)
+ }
+
+ if s.Block == types.Block {
+ // functype will print errors about duplicate function arguments.
+ // Don't repeat the error here.
+ if ctxt != ir.PPARAM && ctxt != ir.PPARAMOUT {
+ Redeclared(n.Pos(), s, "in this block")
+ }
+ }
+
+ s.Block = types.Block
+ s.Lastlineno = base.Pos
+ s.Def = n
+ n.Class = ctxt
+ if ctxt == ir.PFUNC {
+ n.Sym().SetFunc(true)
+ }
+
+ autoexport(n, ctxt)
+}
+
+// Export marks n for export (or reexport).
+func Export(n *ir.Name) {
+ if n.Sym().OnExportList() {
+ return
+ }
+ n.Sym().SetOnExportList(true)
+
+ if base.Flag.E != 0 {
+ fmt.Printf("export symbol %v\n", n.Sym())
+ }
+
+ Target.Exports = append(Target.Exports, n)
+}
+
+// Redeclared emits a diagnostic about symbol s being redeclared at pos.
+func Redeclared(pos src.XPos, s *types.Sym, where string) {
+ if !s.Lastlineno.IsKnown() {
+ pkgName := DotImportRefs[s.Def.(*ir.Ident)]
+ base.ErrorfAt(pos, "%v redeclared %s\n"+
+ "\t%v: previous declaration during import %q", s, where, base.FmtPos(pkgName.Pos()), pkgName.Pkg.Path)
+ } else {
+ prevPos := s.Lastlineno
+
+ // When an import and a declaration collide in separate files,
+ // present the import as the "redeclared", because the declaration
+ // is visible where the import is, but not vice versa.
+ // See issue 4510.
+ if s.Def == nil {
+ pos, prevPos = prevPos, pos
+ }
+
+ base.ErrorfAt(pos, "%v redeclared %s\n"+
+ "\t%v: previous declaration", s, where, base.FmtPos(prevPos))
+ }
+}
+
+// declare the function proper
+// and declare the arguments.
+// called in extern-declaration context
+// returns in auto-declaration context.
+func StartFuncBody(fn *ir.Func) {
+ // change the declaration context from extern to auto
+ funcStack = append(funcStack, funcStackEnt{ir.CurFunc, DeclContext})
+ ir.CurFunc = fn
+ DeclContext = ir.PAUTO
+
+ types.Markdcl()
+
+ if fn.Nname.Ntype != nil {
+ funcargs(fn.Nname.Ntype.(*ir.FuncType))
+ } else {
+ funcargs2(fn.Type())
+ }
+}
+
+// finish the body.
+// called in auto-declaration context.
+// returns in extern-declaration context.
+func FinishFuncBody() {
+ // change the declaration context from auto to previous context
+ types.Popdcl()
+ var e funcStackEnt
+ funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1]
+ ir.CurFunc, DeclContext = e.curfn, e.dclcontext
+}
+
+func CheckFuncStack() {
+ if len(funcStack) != 0 {
+ base.Fatalf("funcStack is non-empty: %v", len(funcStack))
+ }
+}
+
+// Add a method, declared as a function.
+// - msym is the method symbol
+// - t is function type (with receiver)
+// Returns a pointer to the existing or added Field; or nil if there's an error.
+func addmethod(n *ir.Func, msym *types.Sym, t *types.Type, local, nointerface bool) *types.Field {
+ if msym == nil {
+ base.Fatalf("no method symbol")
+ }
+
+ // get parent type sym
+ rf := t.Recv() // ptr to this structure
+ if rf == nil {
+ base.Errorf("missing receiver")
+ return nil
+ }
+
+ mt := types.ReceiverBaseType(rf.Type)
+ if mt == nil || mt.Sym() == nil {
+ pa := rf.Type
+ t := pa
+ if t != nil && t.IsPtr() {
+ if t.Sym() != nil {
+ base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t)
+ return nil
+ }
+ t = t.Elem()
+ }
+
+ switch {
+ case t == nil || t.Broke():
+ // rely on typecheck having complained before
+ case t.Sym() == nil:
+ base.Errorf("invalid receiver type %v (%v is not a defined type)", pa, t)
+ case t.IsPtr():
+ base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t)
+ case t.IsInterface():
+ base.Errorf("invalid receiver type %v (%v is an interface type)", pa, t)
+ default:
+ // Should have picked off all the reasons above,
+ // but just in case, fall back to generic error.
+ base.Errorf("invalid receiver type %v (%L / %L)", pa, pa, t)
+ }
+ return nil
+ }
+
+ if local && mt.Sym().Pkg != types.LocalPkg {
+ base.Errorf("cannot define new methods on non-local type %v", mt)
+ return nil
+ }
+
+ if msym.IsBlank() {
+ return nil
+ }
+
+ if mt.IsStruct() {
+ for _, f := range mt.Fields().Slice() {
+ if f.Sym == msym {
+ base.Errorf("type %v has both field and method named %v", mt, msym)
+ f.SetBroke(true)
+ return nil
+ }
+ }
+ }
+
+ for _, f := range mt.Methods().Slice() {
+ if msym.Name != f.Sym.Name {
+ continue
+ }
+ // types.Identical only checks that incoming and result parameters match,
+ // so explicitly check that the receiver parameters match too.
+ if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) {
+ base.Errorf("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t)
+ }
+ return f
+ }
+
+ f := types.NewField(base.Pos, msym, t)
+ f.Nname = n.Nname
+ f.SetNointerface(nointerface)
+
+ mt.Methods().Append(f)
+ return f
+}
+
+func autoexport(n *ir.Name, ctxt ir.Class) {
+ if n.Sym().Pkg != types.LocalPkg {
+ return
+ }
+ if (ctxt != ir.PEXTERN && ctxt != ir.PFUNC) || DeclContext != ir.PEXTERN {
+ return
+ }
+ if n.Type() != nil && n.Type().IsKind(types.TFUNC) && ir.IsMethod(n) {
+ return
+ }
+
+ if types.IsExported(n.Sym().Name) || n.Sym().Name == "init" {
+ Export(n)
+ }
+ if base.Flag.AsmHdr != "" && !n.Sym().Asm() {
+ n.Sym().SetAsm(true)
+ Target.Asms = append(Target.Asms, n)
+ }
+}
+
+// checkdupfields emits errors for duplicately named fields or methods in
+// a list of struct or interface types.
+func checkdupfields(what string, fss ...[]*types.Field) {
+ seen := make(map[*types.Sym]bool)
+ for _, fs := range fss {
+ for _, f := range fs {
+ if f.Sym == nil || f.Sym.IsBlank() {
+ continue
+ }
+ if seen[f.Sym] {
+ base.ErrorfAt(f.Pos, "duplicate %s %s", what, f.Sym.Name)
+ continue
+ }
+ seen[f.Sym] = true
+ }
+ }
+}
+
+// structs, functions, and methods.
+// they don't belong here, but where do they belong?
+func checkembeddedtype(t *types.Type) {
+ if t == nil {
+ return
+ }
+
+ if t.Sym() == nil && t.IsPtr() {
+ t = t.Elem()
+ if t.IsInterface() {
+ base.Errorf("embedded type cannot be a pointer to interface")
+ }
+ }
+
+ if t.IsPtr() || t.IsUnsafePtr() {
+ base.Errorf("embedded type cannot be a pointer")
+ } else if t.Kind() == types.TFORW && !t.ForwardType().Embedlineno.IsKnown() {
+ t.ForwardType().Embedlineno = base.Pos
+ }
+}
+
+// TODO(mdempsky): Move to package types.
+func FakeRecv() *types.Field {
+ return types.NewField(src.NoXPos, nil, types.FakeRecvType())
+}
+
+var fakeRecvField = FakeRecv
+
+var funcStack []funcStackEnt // stack of previous values of ir.CurFunc/DeclContext
+
+type funcStackEnt struct {
+ curfn *ir.Func
+ dclcontext ir.Class
+}
+
+func funcarg(n *ir.Field, ctxt ir.Class) {
+ if n.Sym == nil {
+ return
+ }
+
+ name := ir.NewNameAt(n.Pos, n.Sym)
+ n.Decl = name
+ name.Ntype = n.Ntype
+ Declare(name, ctxt)
+}
+
+func funcarg2(f *types.Field, ctxt ir.Class) {
+ if f.Sym == nil {
+ return
+ }
+ n := ir.NewNameAt(f.Pos, f.Sym)
+ f.Nname = n
+ n.SetType(f.Type)
+ Declare(n, ctxt)
+}
+
+func funcargs(nt *ir.FuncType) {
+ if nt.Op() != ir.OTFUNC {
+ base.Fatalf("funcargs %v", nt.Op())
+ }
+
+ // declare the receiver and in arguments.
+ if nt.Recv != nil {
+ funcarg(nt.Recv, ir.PPARAM)
+ }
+ for _, n := range nt.Params {
+ funcarg(n, ir.PPARAM)
+ }
+
+ // declare the out arguments.
+ gen := len(nt.Params)
+ for _, n := range nt.Results {
+ if n.Sym == nil {
+ // Name so that escape analysis can track it. ~r stands for 'result'.
+ n.Sym = LookupNum("~r", gen)
+ gen++
+ }
+ if n.Sym.IsBlank() {
+ // Give it a name so we can assign to it during return. ~b stands for 'blank'.
+ // The name must be different from ~r above because if you have
+ // func f() (_ int)
+ // func g() int
+ // f is allowed to use a plain 'return' with no arguments, while g is not.
+ // So the two cases must be distinguished.
+ n.Sym = LookupNum("~b", gen)
+ gen++
+ }
+
+ funcarg(n, ir.PPARAMOUT)
+ }
+}
+
+// Same as funcargs, except run over an already constructed TFUNC.
+// This happens during import, where the hidden_fndcl rule has
+// used functype directly to parse the function's type.
+func funcargs2(t *types.Type) {
+ if t.Kind() != types.TFUNC {
+ base.Fatalf("funcargs2 %v", t)
+ }
+
+ for _, f := range t.Recvs().Fields().Slice() {
+ funcarg2(f, ir.PPARAM)
+ }
+ for _, f := range t.Params().Fields().Slice() {
+ funcarg2(f, ir.PPARAM)
+ }
+ for _, f := range t.Results().Fields().Slice() {
+ funcarg2(f, ir.PPARAMOUT)
+ }
+}
+
+func Temp(t *types.Type) *ir.Name {
+ return TempAt(base.Pos, ir.CurFunc, t)
+}
+
+// make a new Node off the books
+func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name {
+ if curfn == nil {
+ base.Fatalf("no curfn for TempAt")
+ }
+ if curfn.Op() == ir.OCLOSURE {
+ ir.Dump("TempAt", curfn)
+ base.Fatalf("adding TempAt to wrong closure function")
+ }
+ if t == nil {
+ base.Fatalf("TempAt called with nil type")
+ }
+ if t.Kind() == types.TFUNC && t.Recv() != nil {
+ base.Fatalf("misuse of method type: %v", t)
+ }
+
+ s := &types.Sym{
+ Name: autotmpname(len(curfn.Dcl)),
+ Pkg: types.LocalPkg,
+ }
+ n := ir.NewNameAt(pos, s)
+ s.Def = n
+ n.SetType(t)
+ n.Class = ir.PAUTO
+ n.SetEsc(ir.EscNever)
+ n.Curfn = curfn
+ n.SetUsed(true)
+ n.SetAutoTemp(true)
+ curfn.Dcl = append(curfn.Dcl, n)
+
+ types.CalcSize(t)
+
+ return n
+}
+
+// autotmpname returns the name for an autotmp variable numbered n.
+func autotmpname(n int) string {
+ // Give each tmp a different name so that they can be registerized.
+ // Add a preceding . to avoid clashing with legal names.
+ const prefix = ".autotmp_"
+ // Start with a buffer big enough to hold a large n.
+ b := []byte(prefix + " ")[:len(prefix)]
+ b = strconv.AppendInt(b, int64(n), 10)
+ return types.InternString(b)
+}
+
+// f is method type, with receiver.
+// return function type, receiver as first argument (or not).
+func NewMethodType(sig *types.Type, recv *types.Type) *types.Type {
+ nrecvs := 0
+ if recv != nil {
+ nrecvs++
+ }
+
+ // TODO(mdempsky): Move this function to types.
+ // TODO(mdempsky): Preserve positions, names, and package from sig+recv.
+
+ params := make([]*types.Field, nrecvs+sig.Params().Fields().Len())
+ if recv != nil {
+ params[0] = types.NewField(base.Pos, nil, recv)
+ }
+ for i, param := range sig.Params().Fields().Slice() {
+ d := types.NewField(base.Pos, nil, param.Type)
+ d.SetIsDDD(param.IsDDD())
+ params[nrecvs+i] = d
+ }
+
+ results := make([]*types.Field, sig.Results().Fields().Len())
+ for i, t := range sig.Results().Fields().Slice() {
+ results[i] = types.NewField(base.Pos, nil, t.Type)
+ }
+
+ return types.NewSignature(types.LocalPkg, nil, params, results)
+}
diff --git a/src/cmd/compile/internal/typecheck/export.go b/src/cmd/compile/internal/typecheck/export.go
new file mode 100644
index 0000000000000..63d0a1ec6c656
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/export.go
@@ -0,0 +1,74 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// importalias declares symbol s as an imported type alias with type t.
+// ipkg is the package being imported
+func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
+ return importobj(ipkg, pos, s, ir.OTYPE, ir.PEXTERN, t)
+}
+
+// importconst declares symbol s as an imported constant with type t and value val.
+// ipkg is the package being imported
+func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val constant.Value) *ir.Name {
+ n := importobj(ipkg, pos, s, ir.OLITERAL, ir.PEXTERN, t)
+ n.SetVal(val)
+ return n
+}
+
+// importfunc declares symbol s as an imported function with type t.
+// ipkg is the package being imported
+func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
+ n := importobj(ipkg, pos, s, ir.ONAME, ir.PFUNC, t)
+ n.Func = ir.NewFunc(pos)
+ n.Func.Nname = n
+ return n
+}
+
+// importobj declares symbol s as an imported object representable by op.
+// ipkg is the package being imported
+func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class, t *types.Type) *ir.Name {
+ n := importsym(ipkg, pos, s, op, ctxt)
+ n.SetType(t)
+ if ctxt == ir.PFUNC {
+ n.Sym().SetFunc(true)
+ }
+ return n
+}
+
+func importsym(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class) *ir.Name {
+ if n := s.PkgDef(); n != nil {
+ base.Fatalf("importsym of symbol that already exists: %v", n)
+ }
+
+ n := ir.NewDeclNameAt(pos, op, s)
+ n.Class = ctxt // TODO(mdempsky): Move this into NewDeclNameAt too?
+ s.SetPkgDef(n)
+ return n
+}
+
+// importtype returns the named type declared by symbol s.
+// If no such type has been declared yet, a forward declaration is returned.
+// ipkg is the package being imported
+func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *ir.Name {
+ n := importsym(ipkg, pos, s, ir.OTYPE, ir.PEXTERN)
+ n.SetType(types.NewNamed(n))
+ return n
+}
+
+// importvar declares symbol s as an imported variable with type t.
+// ipkg is the package being imported
+func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
+ return importobj(ipkg, pos, s, ir.ONAME, ir.PEXTERN, t)
+}
diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go
new file mode 100644
index 0000000000000..339fb00aa4496
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/expr.go
@@ -0,0 +1,877 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "fmt"
+ "go/constant"
+ "go/token"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+)
+
+// tcAddr typechecks an OADDR node.
+func tcAddr(n *ir.AddrExpr) ir.Node {
+ n.X = Expr(n.X)
+ if n.X.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+
+ switch n.X.Op() {
+ case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT:
+ n.SetOp(ir.OPTRLIT)
+
+ default:
+ checklvalue(n.X, "take the address of")
+ r := ir.OuterValue(n.X)
+ if r.Op() == ir.ONAME {
+ r := r.(*ir.Name)
+ if ir.Orig(r) != r {
+ base.Fatalf("found non-orig name node %v", r) // TODO(mdempsky): What does this mean?
+ }
+ }
+ n.X = DefaultLit(n.X, nil)
+ if n.X.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ }
+
+ n.SetType(types.NewPtr(n.X.Type()))
+ return n
+}
+
+func tcShift(n, l, r ir.Node) (ir.Node, ir.Node, *types.Type) {
+ if l.Type() == nil || l.Type() == nil {
+ return l, r, nil
+ }
+
+ r = DefaultLit(r, types.Types[types.TUINT])
+ t := r.Type()
+ if !t.IsInteger() {
+ base.Errorf("invalid operation: %v (shift count type %v, must be integer)", n, r.Type())
+ return l, r, nil
+ }
+ if t.IsSigned() && !types.AllowsGoVersion(curpkg(), 1, 13) {
+ base.ErrorfVers("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type())
+ return l, r, nil
+ }
+ t = l.Type()
+ if t != nil && t.Kind() != types.TIDEAL && !t.IsInteger() {
+ base.Errorf("invalid operation: %v (shift of type %v)", n, t)
+ return l, r, nil
+ }
+
+ // no DefaultLit for left
+ // the outer context gives the type
+ t = l.Type()
+ if (l.Type() == types.UntypedFloat || l.Type() == types.UntypedComplex) && r.Op() == ir.OLITERAL {
+ t = types.UntypedInt
+ }
+ return l, r, t
+}
+
+// tcArith typechecks operands of a binary arithmetic expression.
+// The result of tcArith MUST be assigned back to original operands,
+// t is the type of the expression, and should be set by the caller. e.g:
+// n.X, n.Y, t = tcArith(n, op, n.X, n.Y)
+// n.SetType(t)
+func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) {
+ l, r = defaultlit2(l, r, false)
+ if l.Type() == nil || r.Type() == nil {
+ return l, r, nil
+ }
+ t := l.Type()
+ if t.Kind() == types.TIDEAL {
+ t = r.Type()
+ }
+ aop := ir.OXXX
+ if iscmp[n.Op()] && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) {
+ // comparison is okay as long as one side is
+ // assignable to the other. convert so they have
+ // the same type.
+ //
+ // the only conversion that isn't a no-op is concrete == interface.
+ // in that case, check comparability of the concrete type.
+ // The conversion allocates, so only do it if the concrete type is huge.
+ converted := false
+ if r.Type().Kind() != types.TBLANK {
+ aop, _ = assignop(l.Type(), r.Type())
+ if aop != ir.OXXX {
+ if r.Type().IsInterface() && !l.Type().IsInterface() && !types.IsComparable(l.Type()) {
+ base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type()))
+ return l, r, nil
+ }
+
+ types.CalcSize(l.Type())
+ if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Width >= 1<<16 {
+ l = ir.NewConvExpr(base.Pos, aop, r.Type(), l)
+ l.SetTypecheck(1)
+ }
+
+ t = r.Type()
+ converted = true
+ }
+ }
+
+ if !converted && l.Type().Kind() != types.TBLANK {
+ aop, _ = assignop(r.Type(), l.Type())
+ if aop != ir.OXXX {
+ if l.Type().IsInterface() && !r.Type().IsInterface() && !types.IsComparable(r.Type()) {
+ base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type()))
+ return l, r, nil
+ }
+
+ types.CalcSize(r.Type())
+ if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Width >= 1<<16 {
+ r = ir.NewConvExpr(base.Pos, aop, l.Type(), r)
+ r.SetTypecheck(1)
+ }
+
+ t = l.Type()
+ }
+ }
+ }
+
+ if t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) {
+ l, r = defaultlit2(l, r, true)
+ if l.Type() == nil || r.Type() == nil {
+ return l, r, nil
+ }
+ if l.Type().IsInterface() == r.Type().IsInterface() || aop == 0 {
+ base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type())
+ return l, r, nil
+ }
+ }
+
+ if t.Kind() == types.TIDEAL {
+ t = mixUntyped(l.Type(), r.Type())
+ }
+ if dt := defaultType(t); !okfor[op][dt.Kind()] {
+ base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t))
+ return l, r, nil
+ }
+
+ // okfor allows any array == array, map == map, func == func.
+ // restrict to slice/map/func == nil and nil == slice/map/func.
+ if l.Type().IsArray() && !types.IsComparable(l.Type()) {
+ base.Errorf("invalid operation: %v (%v cannot be compared)", n, l.Type())
+ return l, r, nil
+ }
+
+ if l.Type().IsSlice() && !ir.IsNil(l) && !ir.IsNil(r) {
+ base.Errorf("invalid operation: %v (slice can only be compared to nil)", n)
+ return l, r, nil
+ }
+
+ if l.Type().IsMap() && !ir.IsNil(l) && !ir.IsNil(r) {
+ base.Errorf("invalid operation: %v (map can only be compared to nil)", n)
+ return l, r, nil
+ }
+
+ if l.Type().Kind() == types.TFUNC && !ir.IsNil(l) && !ir.IsNil(r) {
+ base.Errorf("invalid operation: %v (func can only be compared to nil)", n)
+ return l, r, nil
+ }
+
+ if l.Type().IsStruct() {
+ if f := types.IncomparableField(l.Type()); f != nil {
+ base.Errorf("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type)
+ return l, r, nil
+ }
+ }
+
+ if (op == ir.ODIV || op == ir.OMOD) && ir.IsConst(r, constant.Int) {
+ if constant.Sign(r.Val()) == 0 {
+ base.Errorf("division by zero")
+ return l, r, nil
+ }
+ }
+
+ return l, r, t
+}
+
+// The result of tcCompLit MUST be assigned back to n, e.g.
+// n.Left = tcCompLit(n.Left)
+func tcCompLit(n *ir.CompLitExpr) (res ir.Node) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("tcCompLit", n)(&res)
+ }
+
+ lno := base.Pos
+ defer func() {
+ base.Pos = lno
+ }()
+
+ if n.Ntype == nil {
+ base.ErrorfAt(n.Pos(), "missing type in composite literal")
+ n.SetType(nil)
+ return n
+ }
+
+ // Save original node (including n.Right)
+ n.SetOrig(ir.Copy(n))
+
+ ir.SetPos(n.Ntype)
+
+ // Need to handle [...]T arrays specially.
+ if array, ok := n.Ntype.(*ir.ArrayType); ok && array.Elem != nil && array.Len == nil {
+ array.Elem = typecheckNtype(array.Elem)
+ elemType := array.Elem.Type()
+ if elemType == nil {
+ n.SetType(nil)
+ return n
+ }
+ length := typecheckarraylit(elemType, -1, n.List, "array literal")
+ n.SetOp(ir.OARRAYLIT)
+ n.SetType(types.NewArray(elemType, length))
+ n.Ntype = nil
+ return n
+ }
+
+ n.Ntype = typecheckNtype(n.Ntype)
+ t := n.Ntype.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ n.SetType(t)
+
+ switch t.Kind() {
+ default:
+ base.Errorf("invalid composite literal type %v", t)
+ n.SetType(nil)
+
+ case types.TARRAY:
+ typecheckarraylit(t.Elem(), t.NumElem(), n.List, "array literal")
+ n.SetOp(ir.OARRAYLIT)
+ n.Ntype = nil
+
+ case types.TSLICE:
+ length := typecheckarraylit(t.Elem(), -1, n.List, "slice literal")
+ n.SetOp(ir.OSLICELIT)
+ n.Ntype = nil
+ n.Len = length
+
+ case types.TMAP:
+ var cs constSet
+ for i3, l := range n.List {
+ ir.SetPos(l)
+ if l.Op() != ir.OKEY {
+ n.List[i3] = Expr(l)
+ base.Errorf("missing key in map literal")
+ continue
+ }
+ l := l.(*ir.KeyExpr)
+
+ r := l.Key
+ r = pushtype(r, t.Key())
+ r = Expr(r)
+ l.Key = AssignConv(r, t.Key(), "map key")
+ cs.add(base.Pos, l.Key, "key", "map literal")
+
+ r = l.Value
+ r = pushtype(r, t.Elem())
+ r = Expr(r)
+ l.Value = AssignConv(r, t.Elem(), "map value")
+ }
+
+ n.SetOp(ir.OMAPLIT)
+ n.Ntype = nil
+
+ case types.TSTRUCT:
+ // Need valid field offsets for Xoffset below.
+ types.CalcSize(t)
+
+ errored := false
+ if len(n.List) != 0 && nokeys(n.List) {
+ // simple list of variables
+ ls := n.List
+ for i, n1 := range ls {
+ ir.SetPos(n1)
+ n1 = Expr(n1)
+ ls[i] = n1
+ if i >= t.NumFields() {
+ if !errored {
+ base.Errorf("too many values in %v", n)
+ errored = true
+ }
+ continue
+ }
+
+ f := t.Field(i)
+ s := f.Sym
+ if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
+ base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
+ }
+ // No pushtype allowed here. Must name fields for that.
+ n1 = AssignConv(n1, f.Type, "field value")
+ sk := ir.NewStructKeyExpr(base.Pos, f.Sym, n1)
+ sk.Offset = f.Offset
+ ls[i] = sk
+ }
+ if len(ls) < t.NumFields() {
+ base.Errorf("too few values in %v", n)
+ }
+ } else {
+ hash := make(map[string]bool)
+
+ // keyed list
+ ls := n.List
+ for i, l := range ls {
+ ir.SetPos(l)
+
+ if l.Op() == ir.OKEY {
+ kv := l.(*ir.KeyExpr)
+ key := kv.Key
+
+ // Sym might have resolved to name in other top-level
+ // package, because of import dot. Redirect to correct sym
+ // before we do the lookup.
+ s := key.Sym()
+ if id, ok := key.(*ir.Ident); ok && DotImportRefs[id] != nil {
+ s = Lookup(s.Name)
+ }
+
+ // An OXDOT uses the Sym field to hold
+ // the field to the right of the dot,
+ // so s will be non-nil, but an OXDOT
+ // is never a valid struct literal key.
+ if s == nil || s.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || s.IsBlank() {
+ base.Errorf("invalid field name %v in struct initializer", key)
+ continue
+ }
+
+ l = ir.NewStructKeyExpr(l.Pos(), s, kv.Value)
+ ls[i] = l
+ }
+
+ if l.Op() != ir.OSTRUCTKEY {
+ if !errored {
+ base.Errorf("mixture of field:value and value initializers")
+ errored = true
+ }
+ ls[i] = Expr(ls[i])
+ continue
+ }
+ l := l.(*ir.StructKeyExpr)
+
+ f := lookdot1(nil, l.Field, t, t.Fields(), 0)
+ if f == nil {
+ if ci := lookdot1(nil, l.Field, t, t.Fields(), 2); ci != nil { // Case-insensitive lookup.
+ if visible(ci.Sym) {
+ base.Errorf("unknown field '%v' in struct literal of type %v (but does have %v)", l.Field, t, ci.Sym)
+ } else if nonexported(l.Field) && l.Field.Name == ci.Sym.Name { // Ensure exactness before the suggestion.
+ base.Errorf("cannot refer to unexported field '%v' in struct literal of type %v", l.Field, t)
+ } else {
+ base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t)
+ }
+ continue
+ }
+ var f *types.Field
+ p, _ := dotpath(l.Field, t, &f, true)
+ if p == nil || f.IsMethod() {
+ base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t)
+ continue
+ }
+ // dotpath returns the parent embedded types in reverse order.
+ var ep []string
+ for ei := len(p) - 1; ei >= 0; ei-- {
+ ep = append(ep, p[ei].field.Sym.Name)
+ }
+ ep = append(ep, l.Field.Name)
+ base.Errorf("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), t)
+ continue
+ }
+ fielddup(f.Sym.Name, hash)
+ l.Offset = f.Offset
+
+ // No pushtype allowed here. Tried and rejected.
+ l.Value = Expr(l.Value)
+ l.Value = AssignConv(l.Value, f.Type, "field value")
+ }
+ }
+
+ n.SetOp(ir.OSTRUCTLIT)
+ n.Ntype = nil
+ }
+
+ return n
+}
+
+// tcConv typechecks an OCONV node.
+func tcConv(n *ir.ConvExpr) ir.Node {
+ types.CheckSize(n.Type()) // ensure width is calculated for backend
+ n.X = Expr(n.X)
+ n.X = convlit1(n.X, n.Type(), true, nil)
+ t := n.X.Type()
+ if t == nil || n.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ op, why := convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
+ if op == ir.OXXX {
+ if !n.Diag() && !n.Type().Broke() && !n.X.Diag() {
+ base.Errorf("cannot convert %L to type %v%s", n.X, n.Type(), why)
+ n.SetDiag(true)
+ }
+ n.SetOp(ir.OCONV)
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetOp(op)
+ switch n.Op() {
+ case ir.OCONVNOP:
+ if t.Kind() == n.Type().Kind() {
+ switch t.Kind() {
+ case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
+ // Floating point casts imply rounding and
+ // so the conversion must be kept.
+ n.SetOp(ir.OCONV)
+ }
+ }
+
+ // do not convert to []byte literal. See CL 125796.
+ // generated code and compiler memory footprint is better without it.
+ case ir.OSTR2BYTES:
+ // ok
+
+ case ir.OSTR2RUNES:
+ if n.X.Op() == ir.OLITERAL {
+ return stringtoruneslit(n)
+ }
+ }
+ return n
+}
+
+// tcDot typechecks an OXDOT or ODOT node.
+func tcDot(n *ir.SelectorExpr, top int) ir.Node {
+ if n.Op() == ir.OXDOT {
+ n = AddImplicitDots(n)
+ n.SetOp(ir.ODOT)
+ if n.X == nil {
+ n.SetType(nil)
+ return n
+ }
+ }
+
+ n.X = typecheck(n.X, ctxExpr|ctxType)
+ n.X = DefaultLit(n.X, nil)
+
+ t := n.X.Type()
+ if t == nil {
+ base.UpdateErrorDot(ir.Line(n), fmt.Sprint(n.X), fmt.Sprint(n))
+ n.SetType(nil)
+ return n
+ }
+
+ if n.X.Op() == ir.OTYPE {
+ return typecheckMethodExpr(n)
+ }
+
+ if t.IsPtr() && !t.Elem().IsInterface() {
+ t = t.Elem()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ n.SetOp(ir.ODOTPTR)
+ types.CheckSize(t)
+ }
+
+ if n.Sel.IsBlank() {
+ base.Errorf("cannot refer to blank field or method")
+ n.SetType(nil)
+ return n
+ }
+
+ if lookdot(n, t, 0) == nil {
+ // Legitimate field or method lookup failed, try to explain the error
+ switch {
+ case t.IsEmptyInterface():
+ base.Errorf("%v undefined (type %v is interface with no methods)", n, n.X.Type())
+
+ case t.IsPtr() && t.Elem().IsInterface():
+ // Pointer to interface is almost always a mistake.
+ base.Errorf("%v undefined (type %v is pointer to interface, not interface)", n, n.X.Type())
+
+ case lookdot(n, t, 1) != nil:
+ // Field or method matches by name, but it is not exported.
+ base.Errorf("%v undefined (cannot refer to unexported field or method %v)", n, n.Sel)
+
+ default:
+ if mt := lookdot(n, t, 2); mt != nil && visible(mt.Sym) { // Case-insensitive lookup.
+ base.Errorf("%v undefined (type %v has no field or method %v, but does have %v)", n, n.X.Type(), n.Sel, mt.Sym)
+ } else {
+ base.Errorf("%v undefined (type %v has no field or method %v)", n, n.X.Type(), n.Sel)
+ }
+ }
+ n.SetType(nil)
+ return n
+ }
+
+ if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && top&ctxCallee == 0 {
+ n.SetOp(ir.OCALLPART)
+ n.SetType(MethodValueWrapper(n).Type())
+ }
+ return n
+}
+
+// tcDotType typechecks an ODOTTYPE node.
+func tcDotType(n *ir.TypeAssertExpr) ir.Node {
+ n.X = Expr(n.X)
+ n.X = DefaultLit(n.X, nil)
+ l := n.X
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !t.IsInterface() {
+ base.Errorf("invalid type assertion: %v (non-interface type %v on left)", n, t)
+ n.SetType(nil)
+ return n
+ }
+
+ if n.Ntype != nil {
+ n.Ntype = typecheckNtype(n.Ntype)
+ n.SetType(n.Ntype.Type())
+ n.Ntype = nil
+ if n.Type() == nil {
+ return n
+ }
+ }
+
+ if n.Type() != nil && !n.Type().IsInterface() {
+ var missing, have *types.Field
+ var ptr int
+ if !implements(n.Type(), t, &missing, &have, &ptr) {
+ if have != nil && have.Sym == missing.Sym {
+ base.Errorf("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+
+ "\t\thave %v%S\n\t\twant %v%S", n.Type(), t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
+ } else if ptr != 0 {
+ base.Errorf("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type(), t, missing.Sym)
+ } else if have != nil {
+ base.Errorf("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+
+ "\t\thave %v%S\n\t\twant %v%S", n.Type(), t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
+ } else {
+ base.Errorf("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type(), t, missing.Sym)
+ }
+ n.SetType(nil)
+ return n
+ }
+ }
+ return n
+}
+
+// tcITab typechecks an OITAB node.
+func tcITab(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ t := n.X.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !t.IsInterface() {
+ base.Fatalf("OITAB of %v", t)
+ }
+ n.SetType(types.NewPtr(types.Types[types.TUINTPTR]))
+ return n
+}
+
+// tcIndex typechecks an OINDEX node.
+func tcIndex(n *ir.IndexExpr) ir.Node {
+ n.X = Expr(n.X)
+ n.X = DefaultLit(n.X, nil)
+ n.X = implicitstar(n.X)
+ l := n.X
+ n.Index = Expr(n.Index)
+ r := n.Index
+ t := l.Type()
+ if t == nil || r.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ switch t.Kind() {
+ default:
+ base.Errorf("invalid operation: %v (type %v does not support indexing)", n, t)
+ n.SetType(nil)
+ return n
+
+ case types.TSTRING, types.TARRAY, types.TSLICE:
+ n.Index = indexlit(n.Index)
+ if t.IsString() {
+ n.SetType(types.ByteType)
+ } else {
+ n.SetType(t.Elem())
+ }
+ why := "string"
+ if t.IsArray() {
+ why = "array"
+ } else if t.IsSlice() {
+ why = "slice"
+ }
+
+ if n.Index.Type() != nil && !n.Index.Type().IsInteger() {
+ base.Errorf("non-integer %s index %v", why, n.Index)
+ return n
+ }
+
+ if !n.Bounded() && ir.IsConst(n.Index, constant.Int) {
+ x := n.Index.Val()
+ if constant.Sign(x) < 0 {
+ base.Errorf("invalid %s index %v (index must be non-negative)", why, n.Index)
+ } else if t.IsArray() && constant.Compare(x, token.GEQ, constant.MakeInt64(t.NumElem())) {
+ base.Errorf("invalid array index %v (out of bounds for %d-element array)", n.Index, t.NumElem())
+ } else if ir.IsConst(n.X, constant.String) && constant.Compare(x, token.GEQ, constant.MakeInt64(int64(len(ir.StringVal(n.X))))) {
+ base.Errorf("invalid string index %v (out of bounds for %d-byte string)", n.Index, len(ir.StringVal(n.X)))
+ } else if ir.ConstOverflow(x, types.Types[types.TINT]) {
+ base.Errorf("invalid %s index %v (index too large)", why, n.Index)
+ }
+ }
+
+ case types.TMAP:
+ n.Index = AssignConv(n.Index, t.Key(), "map index")
+ n.SetType(t.Elem())
+ n.SetOp(ir.OINDEXMAP)
+ n.Assigned = false
+ }
+ return n
+}
+
+// tcLenCap typechecks an OLEN or OCAP node.
+func tcLenCap(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ n.X = DefaultLit(n.X, nil)
+ n.X = implicitstar(n.X)
+ l := n.X
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+
+ var ok bool
+ if n.Op() == ir.OLEN {
+ ok = okforlen[t.Kind()]
+ } else {
+ ok = okforcap[t.Kind()]
+ }
+ if !ok {
+ base.Errorf("invalid argument %L for %v", l, n.Op())
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetType(types.Types[types.TINT])
+ return n
+}
+
+// tcRecv typechecks an ORECV node.
+func tcRecv(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ n.X = DefaultLit(n.X, nil)
+ l := n.X
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !t.IsChan() {
+ base.Errorf("invalid operation: %v (receive from non-chan type %v)", n, t)
+ n.SetType(nil)
+ return n
+ }
+
+ if !t.ChanDir().CanRecv() {
+ base.Errorf("invalid operation: %v (receive from send-only type %v)", n, t)
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetType(t.Elem())
+ return n
+}
+
+// tcSPtr typechecks an OSPTR node.
+func tcSPtr(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ t := n.X.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !t.IsSlice() && !t.IsString() {
+ base.Fatalf("OSPTR of %v", t)
+ }
+ if t.IsString() {
+ n.SetType(types.NewPtr(types.Types[types.TUINT8]))
+ } else {
+ n.SetType(types.NewPtr(t.Elem()))
+ }
+ return n
+}
+
+// tcSlice typechecks an OSLICE or OSLICE3 node.
+func tcSlice(n *ir.SliceExpr) ir.Node {
+ n.X = DefaultLit(Expr(n.X), nil)
+ n.Low = indexlit(Expr(n.Low))
+ n.High = indexlit(Expr(n.High))
+ n.Max = indexlit(Expr(n.Max))
+ hasmax := n.Op().IsSlice3()
+ l := n.X
+ if l.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ if l.Type().IsArray() {
+ if !ir.IsAddressable(n.X) {
+ base.Errorf("invalid operation %v (slice of unaddressable value)", n)
+ n.SetType(nil)
+ return n
+ }
+
+ addr := NodAddr(n.X)
+ addr.SetImplicit(true)
+ n.X = Expr(addr)
+ l = n.X
+ }
+ t := l.Type()
+ var tp *types.Type
+ if t.IsString() {
+ if hasmax {
+ base.Errorf("invalid operation %v (3-index slice of string)", n)
+ n.SetType(nil)
+ return n
+ }
+ n.SetType(t)
+ n.SetOp(ir.OSLICESTR)
+ } else if t.IsPtr() && t.Elem().IsArray() {
+ tp = t.Elem()
+ n.SetType(types.NewSlice(tp.Elem()))
+ types.CalcSize(n.Type())
+ if hasmax {
+ n.SetOp(ir.OSLICE3ARR)
+ } else {
+ n.SetOp(ir.OSLICEARR)
+ }
+ } else if t.IsSlice() {
+ n.SetType(t)
+ } else {
+ base.Errorf("cannot slice %v (type %v)", l, t)
+ n.SetType(nil)
+ return n
+ }
+
+ if n.Low != nil && !checksliceindex(l, n.Low, tp) {
+ n.SetType(nil)
+ return n
+ }
+ if n.High != nil && !checksliceindex(l, n.High, tp) {
+ n.SetType(nil)
+ return n
+ }
+ if n.Max != nil && !checksliceindex(l, n.Max, tp) {
+ n.SetType(nil)
+ return n
+ }
+ if !checksliceconst(n.Low, n.High) || !checksliceconst(n.Low, n.Max) || !checksliceconst(n.High, n.Max) {
+ n.SetType(nil)
+ return n
+ }
+ return n
+}
+
+// tcSliceHeader typechecks an OSLICEHEADER node.
+func tcSliceHeader(n *ir.SliceHeaderExpr) ir.Node {
+ // Errors here are Fatalf instead of Errorf because only the compiler
+ // can construct an OSLICEHEADER node.
+ // Components used in OSLICEHEADER that are supplied by parsed source code
+ // have already been typechecked in e.g. OMAKESLICE earlier.
+ t := n.Type()
+ if t == nil {
+ base.Fatalf("no type specified for OSLICEHEADER")
+ }
+
+ if !t.IsSlice() {
+ base.Fatalf("invalid type %v for OSLICEHEADER", n.Type())
+ }
+
+ if n.Ptr == nil || n.Ptr.Type() == nil || !n.Ptr.Type().IsUnsafePtr() {
+ base.Fatalf("need unsafe.Pointer for OSLICEHEADER")
+ }
+
+ n.Ptr = Expr(n.Ptr)
+ n.Len = DefaultLit(Expr(n.Len), types.Types[types.TINT])
+ n.Cap = DefaultLit(Expr(n.Cap), types.Types[types.TINT])
+
+ if ir.IsConst(n.Len, constant.Int) && ir.Int64Val(n.Len) < 0 {
+ base.Fatalf("len for OSLICEHEADER must be non-negative")
+ }
+
+ if ir.IsConst(n.Cap, constant.Int) && ir.Int64Val(n.Cap) < 0 {
+ base.Fatalf("cap for OSLICEHEADER must be non-negative")
+ }
+
+ if ir.IsConst(n.Len, constant.Int) && ir.IsConst(n.Cap, constant.Int) && constant.Compare(n.Len.Val(), token.GTR, n.Cap.Val()) {
+ base.Fatalf("len larger than cap for OSLICEHEADER")
+ }
+
+ return n
+}
+
+// tcStar typechecks an ODEREF node, which may be an expression or a type.
+func tcStar(n *ir.StarExpr, top int) ir.Node {
+ n.X = typecheck(n.X, ctxExpr|ctxType)
+ l := n.X
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ if l.Op() == ir.OTYPE {
+ n.SetOTYPE(types.NewPtr(l.Type()))
+ // Ensure l.Type gets CalcSize'd for the backend. Issue 20174.
+ types.CheckSize(l.Type())
+ return n
+ }
+
+ if !t.IsPtr() {
+ if top&(ctxExpr|ctxStmt) != 0 {
+ base.Errorf("invalid indirect of %L", n.X)
+ n.SetType(nil)
+ return n
+ }
+ base.Errorf("%v is not a type", l)
+ return n
+ }
+
+ n.SetType(t.Elem())
+ return n
+}
+
+// tcUnaryArith typechecks a unary arithmetic expression.
+func tcUnaryArith(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ l := n.X
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !okfor[n.Op()][defaultType(t).Kind()] {
+ base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(t))
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetType(t)
+ return n
+}
diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go
new file mode 100644
index 0000000000000..7ab5f68ce301b
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/func.go
@@ -0,0 +1,972 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+
+ "fmt"
+ "go/constant"
+ "go/token"
+)
+
+// package all the arguments that match a ... T parameter into a []T.
+func MakeDotArgs(typ *types.Type, args []ir.Node) ir.Node {
+ var n ir.Node
+ if len(args) == 0 {
+ n = NodNil()
+ n.SetType(typ)
+ } else {
+ lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
+ lit.List.Append(args...)
+ lit.SetImplicit(true)
+ n = lit
+ }
+
+ n = Expr(n)
+ if n.Type() == nil {
+ base.Fatalf("mkdotargslice: typecheck failed")
+ }
+ return n
+}
+
+// FixVariadicCall rewrites calls to variadic functions to use an
+// explicit ... argument if one is not already present.
+func FixVariadicCall(call *ir.CallExpr) {
+ fntype := call.X.Type()
+ if !fntype.IsVariadic() || call.IsDDD {
+ return
+ }
+
+ vi := fntype.NumParams() - 1
+ vt := fntype.Params().Field(vi).Type
+
+ args := call.Args
+ extra := args[vi:]
+ slice := MakeDotArgs(vt, extra)
+ for i := range extra {
+ extra[i] = nil // allow GC
+ }
+
+ call.Args = append(args[:vi], slice)
+ call.IsDDD = true
+}
+
+// ClosureType returns the struct type used to hold all the information
+// needed in the closure for clo (clo must be a OCLOSURE node).
+// The address of a variable of the returned type can be cast to a func.
+func ClosureType(clo *ir.ClosureExpr) *types.Type {
+ // Create closure in the form of a composite literal.
+ // supposing the closure captures an int i and a string s
+ // and has one float64 argument and no results,
+ // the generated code looks like:
+ //
+ // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
+ //
+ // The use of the struct provides type information to the garbage
+ // collector so that it can walk the closure. We could use (in this case)
+ // [3]unsafe.Pointer instead, but that would leave the gc in the dark.
+ // The information appears in the binary in the form of type descriptors;
+ // the struct is unnamed so that closures in multiple packages with the
+ // same struct type can share the descriptor.
+ fields := []*types.Field{
+ types.NewField(base.Pos, Lookup(".F"), types.Types[types.TUINTPTR]),
+ }
+ for _, v := range clo.Func.ClosureVars {
+ typ := v.Type()
+ if !v.Byval() {
+ typ = types.NewPtr(typ)
+ }
+ fields = append(fields, types.NewField(base.Pos, v.Sym(), typ))
+ }
+ typ := types.NewStruct(types.NoPkg, fields)
+ typ.SetNoalg(true)
+ return typ
+}
+
+// PartialCallType returns the struct type used to hold all the information
+// needed in the closure for n (n must be a OCALLPART node).
+// The address of a variable of the returned type can be cast to a func.
+func PartialCallType(n *ir.SelectorExpr) *types.Type {
+ t := types.NewStruct(types.NoPkg, []*types.Field{
+ types.NewField(base.Pos, Lookup("F"), types.Types[types.TUINTPTR]),
+ types.NewField(base.Pos, Lookup("R"), n.X.Type()),
+ })
+ t.SetNoalg(true)
+ return t
+}
+
+// Lazy typechecking of imported bodies. For local functions, CanInline will set ->typecheck
+// because they're a copy of an already checked body.
+func ImportedBody(fn *ir.Func) {
+ lno := ir.SetPos(fn.Nname)
+
+ // When we load an inlined body, we need to allow OADDR
+ // operations on untyped expressions. We will fix the
+ // addrtaken flags on all the arguments of the OADDR with the
+ // computeAddrtaken call below (after we typecheck the body).
+ // TODO: export/import types and addrtaken marks along with inlined bodies,
+ // so this will be unnecessary.
+ IncrementalAddrtaken = false
+ defer func() {
+ if DirtyAddrtaken {
+ ComputeAddrtaken(fn.Inl.Body) // compute addrtaken marks once types are available
+ DirtyAddrtaken = false
+ }
+ IncrementalAddrtaken = true
+ }()
+
+ ImportBody(fn)
+
+ // Stmts(fn.Inl.Body) below is only for imported functions;
+ // their bodies may refer to unsafe as long as the package
+ // was marked safe during import (which was checked then).
+ // the ->inl of a local function has been typechecked before CanInline copied it.
+ pkg := fnpkg(fn.Nname)
+
+ if pkg == types.LocalPkg || pkg == nil {
+ return // ImportedBody on local function
+ }
+
+ if base.Flag.LowerM > 2 || base.Debug.Export != 0 {
+ fmt.Printf("typecheck import [%v] %L { %v }\n", fn.Sym(), fn, ir.Nodes(fn.Inl.Body))
+ }
+
+ savefn := ir.CurFunc
+ ir.CurFunc = fn
+ Stmts(fn.Inl.Body)
+ ir.CurFunc = savefn
+
+ // During ImportBody (which imports fn.Func.Inl.Body),
+ // declarations are added to fn.Func.Dcl by funcBody(). Move them
+ // to fn.Func.Inl.Dcl for consistency with how local functions
+ // behave. (Append because ImportedBody may be called multiple
+ // times on same fn.)
+ fn.Inl.Dcl = append(fn.Inl.Dcl, fn.Dcl...)
+ fn.Dcl = nil
+
+ base.Pos = lno
+}
+
+// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
+// the ->sym can be re-used in the local package, so peel it off the receiver's type.
+func fnpkg(fn *ir.Name) *types.Pkg {
+ if ir.IsMethod(fn) {
+ // method
+ rcvr := fn.Type().Recv().Type
+
+ if rcvr.IsPtr() {
+ rcvr = rcvr.Elem()
+ }
+ if rcvr.Sym() == nil {
+ base.Fatalf("receiver with no sym: [%v] %L (%v)", fn.Sym(), fn, rcvr)
+ }
+ return rcvr.Sym().Pkg
+ }
+
+ // non-method
+ return fn.Sym().Pkg
+}
+
+// closurename generates a new unique name for a closure within
+// outerfunc.
+func ClosureName(outerfunc *ir.Func) *types.Sym {
+ outer := "glob."
+ prefix := "func"
+ gen := &globClosgen
+
+ if outerfunc != nil {
+ if outerfunc.OClosure != nil {
+ prefix = ""
+ }
+
+ outer = ir.FuncName(outerfunc)
+
+ // There may be multiple functions named "_". In those
+ // cases, we can't use their individual Closgens as it
+ // would lead to name clashes.
+ if !ir.IsBlank(outerfunc.Nname) {
+ gen = &outerfunc.Closgen
+ }
+ }
+
+ *gen++
+ return Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
+}
+
+// globClosgen is like Func.Closgen, but for the global scope.
+var globClosgen int32
+
+// MethodValueWrapper returns the DCLFUNC node representing the
+// wrapper function (*-fm) needed for the given method value. If the
+// wrapper function hasn't already been created yet, it's created and
+// added to Target.Decls.
+//
+// TODO(mdempsky): Move into walk. This isn't part of type checking.
+func MethodValueWrapper(dot *ir.SelectorExpr) *ir.Func {
+ if dot.Op() != ir.OCALLPART {
+ base.Fatalf("MethodValueWrapper: unexpected %v (%v)", dot, dot.Op())
+ }
+
+ t0 := dot.Type()
+ meth := dot.Sel
+ rcvrtype := dot.X.Type()
+ sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm")
+
+ if sym.Uniq() {
+ return sym.Def.(*ir.Func)
+ }
+ sym.SetUniq(true)
+
+ savecurfn := ir.CurFunc
+ saveLineNo := base.Pos
+ ir.CurFunc = nil
+
+ // Set line number equal to the line number where the method is declared.
+ if pos := dot.Selection.Pos; pos.IsKnown() {
+ base.Pos = pos
+ }
+ // Note: !dot.Selection.Pos.IsKnown() happens for method expressions where
+ // the method is implicitly declared. The Error method of the
+ // built-in error type is one such method. We leave the line
+ // number at the use of the method expression in this
+ // case. See issue 29389.
+
+ tfn := ir.NewFuncType(base.Pos, nil,
+ NewFuncParams(t0.Params(), true),
+ NewFuncParams(t0.Results(), false))
+
+ fn := DeclFunc(sym, tfn)
+ fn.SetDupok(true)
+ fn.SetNeedctxt(true)
+ fn.SetWrapper(true)
+
+ // Declare and initialize variable holding receiver.
+ 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 = ret
+ }
+
+ fn.Body = []ir.Node{body}
+ FinishFuncBody()
+
+ Func(fn)
+ // Need to typecheck the body of the just-generated wrapper.
+ // typecheckslice() requires that Curfn is set when processing an ORETURN.
+ ir.CurFunc = fn
+ Stmts(fn.Body)
+ sym.Def = fn
+ Target.Decls = append(Target.Decls, fn)
+ ir.CurFunc = savecurfn
+ base.Pos = saveLineNo
+
+ return fn
+}
+
+// tcClosure typechecks an OCLOSURE node. It also creates the named
+// function associated with the closure.
+// TODO: This creation of the named function should probably really be done in a
+// separate pass from type-checking.
+func tcClosure(clo *ir.ClosureExpr, top int) {
+ fn := clo.Func
+ // Set current associated iota value, so iota can be used inside
+ // function in ConstSpec, see issue #22344
+ if x := getIotaValue(); x >= 0 {
+ fn.Iota = x
+ }
+
+ 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
+ }
+
+ // Don't give a name and add to xtop if we are typechecking an inlined
+ // body in ImportedBody(), since we only want to create the named function
+ // when the closure is actually inlined (and then we force a typecheck
+ // explicitly in (*inlsubst).node()).
+ inTypeCheckInl := ir.CurFunc != nil && ir.CurFunc.Body == nil
+ if !inTypeCheckInl {
+ 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
+ // ready to type check code yet; we'll check it later, because the
+ // underlying closure function we create is added to Target.Decls.
+ if ir.CurFunc != nil && clo.Type() != nil {
+ oldfn := ir.CurFunc
+ ir.CurFunc = fn
+ Stmts(fn.Body)
+ ir.CurFunc = oldfn
+ }
+
+ out := 0
+ for _, v := range fn.ClosureVars {
+ if v.Type() == nil {
+ // If v.Type is nil, it means v looked like it was going to be
+ // used in the closure, but isn't. This happens in struct
+ // literals like s{f: x} where we can't distinguish whether f is
+ // a field identifier or expression until resolving s.
+ continue
+ }
+
+ // type check closed variables outside the closure, so that the
+ // outer frame also captures them.
+ Expr(v.Outer)
+
+ fn.ClosureVars[out] = v
+ out++
+ }
+ fn.ClosureVars = fn.ClosureVars[:out]
+
+ if base.Flag.W > 1 {
+ s := fmt.Sprintf("New closure func: %s", ir.FuncName(fn))
+ ir.Dump(s, fn)
+ }
+ if !inTypeCheckInl {
+ // Add function to xtop once only when we give it a name
+ Target.Decls = append(Target.Decls, fn)
+ }
+}
+
+// type check function definition
+// To be called by typecheck, not directly.
+// (Call typecheck.Func instead.)
+func tcFunc(n *ir.Func) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("tcFunc", n)(nil)
+ }
+
+ n.Nname = AssignExpr(n.Nname).(*ir.Name)
+ t := n.Nname.Type()
+ if t == nil {
+ return
+ }
+ rcvr := t.Recv()
+ if rcvr != nil && n.Shortname != nil {
+ m := addmethod(n, n.Shortname, t, true, n.Pragma&ir.Nointerface != 0)
+ if m == nil {
+ return
+ }
+
+ n.Nname.SetSym(ir.MethodSym(rcvr.Type, n.Shortname))
+ Declare(n.Nname, ir.PFUNC)
+ }
+}
+
+// tcCall typechecks an OCALL node.
+func tcCall(n *ir.CallExpr, top int) ir.Node {
+ n.Use = ir.CallUseExpr
+ if top == ctxStmt {
+ n.Use = ir.CallUseStmt
+ }
+ Stmts(n.Init()) // imported rewritten f(g()) calls (#30907)
+ n.X = typecheck(n.X, ctxExpr|ctxType|ctxCallee)
+ if n.X.Diag() {
+ n.SetDiag(true)
+ }
+
+ l := n.X
+
+ if l.Op() == ir.ONAME && l.(*ir.Name).BuiltinOp != 0 {
+ l := l.(*ir.Name)
+ if n.IsDDD && l.BuiltinOp != ir.OAPPEND {
+ base.Errorf("invalid use of ... with builtin %v", l)
+ }
+
+ // builtin: OLEN, OCAP, etc.
+ switch l.BuiltinOp {
+ default:
+ base.Fatalf("unknown builtin %v", l)
+
+ case ir.OAPPEND, ir.ODELETE, ir.OMAKE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ n.SetOp(l.BuiltinOp)
+ n.X = nil
+ n.SetTypecheck(0) // re-typechecking new op is OK, not a loop
+ return typecheck(n, top)
+
+ case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL:
+ typecheckargs(n)
+ fallthrough
+ case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+ arg, ok := needOneArg(n, "%v", n.Op())
+ if !ok {
+ n.SetType(nil)
+ return n
+ }
+ u := ir.NewUnaryExpr(n.Pos(), l.BuiltinOp, arg)
+ return typecheck(ir.InitExpr(n.Init(), u), top) // typecheckargs can add to old.Init
+
+ case ir.OCOMPLEX, ir.OCOPY:
+ typecheckargs(n)
+ arg1, arg2, ok := needTwoArgs(n)
+ if !ok {
+ n.SetType(nil)
+ return n
+ }
+ b := ir.NewBinaryExpr(n.Pos(), l.BuiltinOp, arg1, arg2)
+ return typecheck(ir.InitExpr(n.Init(), b), top) // typecheckargs can add to old.Init
+ }
+ panic("unreachable")
+ }
+
+ n.X = DefaultLit(n.X, nil)
+ l = n.X
+ if l.Op() == ir.OTYPE {
+ if n.IsDDD {
+ if !l.Type().Broke() {
+ base.Errorf("invalid use of ... in type conversion to %v", l.Type())
+ }
+ n.SetDiag(true)
+ }
+
+ // pick off before type-checking arguments
+ arg, ok := needOneArg(n, "conversion to %v", l.Type())
+ if !ok {
+ n.SetType(nil)
+ return n
+ }
+
+ n := ir.NewConvExpr(n.Pos(), ir.OCONV, nil, arg)
+ n.SetType(l.Type())
+ return typecheck1(n, top)
+ }
+
+ typecheckargs(n)
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ types.CheckSize(t)
+
+ switch l.Op() {
+ case ir.ODOTINTER:
+ n.SetOp(ir.OCALLINTER)
+
+ case ir.ODOTMETH:
+ l := l.(*ir.SelectorExpr)
+ n.SetOp(ir.OCALLMETH)
+
+ // typecheckaste was used here but there wasn't enough
+ // information further down the call chain to know if we
+ // were testing a method receiver for unexported fields.
+ // It isn't necessary, so just do a sanity check.
+ tp := t.Recv().Type
+
+ if l.X == nil || !types.Identical(l.X.Type(), tp) {
+ base.Fatalf("method receiver")
+ }
+
+ default:
+ n.SetOp(ir.OCALLFUNC)
+ if t.Kind() != types.TFUNC {
+ if o := ir.Orig(l); o.Name() != nil && types.BuiltinPkg.Lookup(o.Sym().Name).Def != nil {
+ // be more specific when the non-function
+ // name matches a predeclared function
+ base.Errorf("cannot call non-function %L, declared at %s",
+ l, base.FmtPos(o.Name().Pos()))
+ } else {
+ base.Errorf("cannot call non-function %L", l)
+ }
+ n.SetType(nil)
+ return n
+ }
+ }
+
+ typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, func() string { return fmt.Sprintf("argument to %v", n.X) })
+ if t.NumResults() == 0 {
+ return n
+ }
+ if t.NumResults() == 1 {
+ n.SetType(l.Type().Results().Field(0).Type)
+
+ if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME {
+ if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" {
+ // Emit code for runtime.getg() directly instead of calling function.
+ // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk,
+ // so that the ordering pass can make sure to preserve the semantics of the original code
+ // (in particular, the exact time of the function call) by introducing temporaries.
+ // In this case, we know getg() always returns the same result within a given function
+ // and we want to avoid the temporaries, so we do the rewrite earlier than is typical.
+ n.SetOp(ir.OGETG)
+ }
+ }
+ return n
+ }
+
+ // multiple return
+ if top&(ctxMultiOK|ctxStmt) == 0 {
+ base.Errorf("multiple-value %v() in single-value context", l)
+ return n
+ }
+
+ n.SetType(l.Type().Results())
+ return n
+}
+
+// tcAppend typechecks an OAPPEND node.
+func tcAppend(n *ir.CallExpr) ir.Node {
+ typecheckargs(n)
+ args := n.Args
+ if len(args) == 0 {
+ base.Errorf("missing arguments to append")
+ n.SetType(nil)
+ return n
+ }
+
+ t := args[0].Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetType(t)
+ if !t.IsSlice() {
+ if ir.IsNil(args[0]) {
+ base.Errorf("first argument to append must be typed slice; have untyped nil")
+ n.SetType(nil)
+ return n
+ }
+
+ base.Errorf("first argument to append must be slice; have %L", t)
+ n.SetType(nil)
+ return n
+ }
+
+ if n.IsDDD {
+ if len(args) == 1 {
+ base.Errorf("cannot use ... on first argument to append")
+ n.SetType(nil)
+ return n
+ }
+
+ if len(args) != 2 {
+ base.Errorf("too many arguments to append")
+ n.SetType(nil)
+ return n
+ }
+
+ if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() {
+ args[1] = DefaultLit(args[1], types.Types[types.TSTRING])
+ return n
+ }
+
+ args[1] = AssignConv(args[1], t.Underlying(), "append")
+ return n
+ }
+
+ as := args[1:]
+ for i, n := range as {
+ if n.Type() == nil {
+ continue
+ }
+ as[i] = AssignConv(n, t.Elem(), "append")
+ types.CheckSize(as[i].Type()) // ensure width is calculated for backend
+ }
+ return n
+}
+
+// tcClose typechecks an OCLOSE node.
+func tcClose(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ n.X = DefaultLit(n.X, nil)
+ l := n.X
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !t.IsChan() {
+ base.Errorf("invalid operation: %v (non-chan type %v)", n, t)
+ n.SetType(nil)
+ return n
+ }
+
+ if !t.ChanDir().CanSend() {
+ base.Errorf("invalid operation: %v (cannot close receive-only channel)", n)
+ n.SetType(nil)
+ return n
+ }
+ return n
+}
+
+// tcComplex typechecks an OCOMPLEX node.
+func tcComplex(n *ir.BinaryExpr) ir.Node {
+ l := Expr(n.X)
+ r := Expr(n.Y)
+ if l.Type() == nil || r.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ l, r = defaultlit2(l, r, false)
+ if l.Type() == nil || r.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ n.X = l
+ n.Y = r
+
+ if !types.Identical(l.Type(), r.Type()) {
+ base.Errorf("invalid operation: %v (mismatched types %v and %v)", n, l.Type(), r.Type())
+ n.SetType(nil)
+ return n
+ }
+
+ var t *types.Type
+ switch l.Type().Kind() {
+ default:
+ base.Errorf("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type())
+ n.SetType(nil)
+ return n
+
+ case types.TIDEAL:
+ t = types.UntypedComplex
+
+ case types.TFLOAT32:
+ t = types.Types[types.TCOMPLEX64]
+
+ case types.TFLOAT64:
+ t = types.Types[types.TCOMPLEX128]
+ }
+ n.SetType(t)
+ return n
+}
+
+// tcCopy typechecks an OCOPY node.
+func tcCopy(n *ir.BinaryExpr) ir.Node {
+ n.SetType(types.Types[types.TINT])
+ n.X = Expr(n.X)
+ n.X = DefaultLit(n.X, nil)
+ n.Y = Expr(n.Y)
+ n.Y = DefaultLit(n.Y, nil)
+ if n.X.Type() == nil || n.Y.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+
+ // copy([]byte, string)
+ if n.X.Type().IsSlice() && n.Y.Type().IsString() {
+ if types.Identical(n.X.Type().Elem(), types.ByteType) {
+ return n
+ }
+ base.Errorf("arguments to copy have different element types: %L and string", n.X.Type())
+ n.SetType(nil)
+ return n
+ }
+
+ if !n.X.Type().IsSlice() || !n.Y.Type().IsSlice() {
+ if !n.X.Type().IsSlice() && !n.Y.Type().IsSlice() {
+ base.Errorf("arguments to copy must be slices; have %L, %L", n.X.Type(), n.Y.Type())
+ } else if !n.X.Type().IsSlice() {
+ base.Errorf("first argument to copy should be slice; have %L", n.X.Type())
+ } else {
+ base.Errorf("second argument to copy should be slice or string; have %L", n.Y.Type())
+ }
+ n.SetType(nil)
+ return n
+ }
+
+ if !types.Identical(n.X.Type().Elem(), n.Y.Type().Elem()) {
+ base.Errorf("arguments to copy have different element types: %L and %L", n.X.Type(), n.Y.Type())
+ n.SetType(nil)
+ return n
+ }
+ return n
+}
+
+// tcDelete typechecks an ODELETE node.
+func tcDelete(n *ir.CallExpr) ir.Node {
+ typecheckargs(n)
+ args := n.Args
+ if len(args) == 0 {
+ base.Errorf("missing arguments to delete")
+ n.SetType(nil)
+ return n
+ }
+
+ if len(args) == 1 {
+ base.Errorf("missing second (key) argument to delete")
+ n.SetType(nil)
+ return n
+ }
+
+ if len(args) != 2 {
+ base.Errorf("too many arguments to delete")
+ n.SetType(nil)
+ return n
+ }
+
+ l := args[0]
+ r := args[1]
+ if l.Type() != nil && !l.Type().IsMap() {
+ base.Errorf("first argument to delete must be map; have %L", l.Type())
+ n.SetType(nil)
+ return n
+ }
+
+ args[1] = AssignConv(r, l.Type().Key(), "delete")
+ return n
+}
+
+// tcMake typechecks an OMAKE node.
+func tcMake(n *ir.CallExpr) ir.Node {
+ args := n.Args
+ if len(args) == 0 {
+ base.Errorf("missing argument to make")
+ n.SetType(nil)
+ return n
+ }
+
+ n.Args = nil
+ l := args[0]
+ l = typecheck(l, ctxType)
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+
+ i := 1
+ var nn ir.Node
+ switch t.Kind() {
+ default:
+ base.Errorf("cannot make type %v", t)
+ n.SetType(nil)
+ return n
+
+ case types.TSLICE:
+ if i >= len(args) {
+ base.Errorf("missing len argument to make(%v)", t)
+ n.SetType(nil)
+ return n
+ }
+
+ l = args[i]
+ i++
+ l = Expr(l)
+ var r ir.Node
+ if i < len(args) {
+ r = args[i]
+ i++
+ r = Expr(r)
+ }
+
+ if l.Type() == nil || (r != nil && r.Type() == nil) {
+ n.SetType(nil)
+ return n
+ }
+ if !checkmake(t, "len", &l) || r != nil && !checkmake(t, "cap", &r) {
+ n.SetType(nil)
+ return n
+ }
+ if ir.IsConst(l, constant.Int) && r != nil && ir.IsConst(r, constant.Int) && constant.Compare(l.Val(), token.GTR, r.Val()) {
+ base.Errorf("len larger than cap in make(%v)", t)
+ n.SetType(nil)
+ return n
+ }
+ nn = ir.NewMakeExpr(n.Pos(), ir.OMAKESLICE, l, r)
+
+ case types.TMAP:
+ if i < len(args) {
+ l = args[i]
+ i++
+ l = Expr(l)
+ l = DefaultLit(l, types.Types[types.TINT])
+ if l.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !checkmake(t, "size", &l) {
+ n.SetType(nil)
+ return n
+ }
+ } else {
+ l = ir.NewInt(0)
+ }
+ nn = ir.NewMakeExpr(n.Pos(), ir.OMAKEMAP, l, nil)
+ nn.SetEsc(n.Esc())
+
+ case types.TCHAN:
+ l = nil
+ if i < len(args) {
+ l = args[i]
+ i++
+ l = Expr(l)
+ l = DefaultLit(l, types.Types[types.TINT])
+ if l.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ if !checkmake(t, "buffer", &l) {
+ n.SetType(nil)
+ return n
+ }
+ } else {
+ l = ir.NewInt(0)
+ }
+ nn = ir.NewMakeExpr(n.Pos(), ir.OMAKECHAN, l, nil)
+ }
+
+ if i < len(args) {
+ base.Errorf("too many arguments to make(%v)", t)
+ n.SetType(nil)
+ return n
+ }
+
+ nn.SetType(t)
+ return nn
+}
+
+// tcMakeSliceCopy typechecks an OMAKESLICECOPY node.
+func tcMakeSliceCopy(n *ir.MakeExpr) ir.Node {
+ // Errors here are Fatalf instead of Errorf because only the compiler
+ // can construct an OMAKESLICECOPY node.
+ // Components used in OMAKESCLICECOPY that are supplied by parsed source code
+ // have already been typechecked in OMAKE and OCOPY earlier.
+ t := n.Type()
+
+ if t == nil {
+ base.Fatalf("no type specified for OMAKESLICECOPY")
+ }
+
+ if !t.IsSlice() {
+ base.Fatalf("invalid type %v for OMAKESLICECOPY", n.Type())
+ }
+
+ if n.Len == nil {
+ base.Fatalf("missing len argument for OMAKESLICECOPY")
+ }
+
+ if n.Cap == nil {
+ base.Fatalf("missing slice argument to copy for OMAKESLICECOPY")
+ }
+
+ n.Len = Expr(n.Len)
+ n.Cap = Expr(n.Cap)
+
+ n.Len = DefaultLit(n.Len, types.Types[types.TINT])
+
+ if !n.Len.Type().IsInteger() && n.Type().Kind() != types.TIDEAL {
+ base.Errorf("non-integer len argument in OMAKESLICECOPY")
+ }
+
+ if ir.IsConst(n.Len, constant.Int) {
+ if ir.ConstOverflow(n.Len.Val(), types.Types[types.TINT]) {
+ base.Fatalf("len for OMAKESLICECOPY too large")
+ }
+ if constant.Sign(n.Len.Val()) < 0 {
+ base.Fatalf("len for OMAKESLICECOPY must be non-negative")
+ }
+ }
+ return n
+}
+
+// tcNew typechecks an ONEW node.
+func tcNew(n *ir.UnaryExpr) ir.Node {
+ if n.X == nil {
+ // Fatalf because the OCALL above checked for us,
+ // so this must be an internally-generated mistake.
+ base.Fatalf("missing argument to new")
+ }
+ l := n.X
+ l = typecheck(l, ctxType)
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+ n.X = l
+ n.SetType(types.NewPtr(t))
+ return n
+}
+
+// tcPanic typechecks an OPANIC node.
+func tcPanic(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ n.X = AssignConv(n.X, types.Types[types.TINTER], "argument to panic")
+ if n.X.Type() == nil {
+ n.SetType(nil)
+ return n
+ }
+ return n
+}
+
+// tcPrint typechecks an OPRINT or OPRINTN node.
+func tcPrint(n *ir.CallExpr) ir.Node {
+ typecheckargs(n)
+ ls := n.Args
+ for i1, n1 := range ls {
+ // Special case for print: int constant is int64, not int.
+ if ir.IsConst(n1, constant.Int) {
+ ls[i1] = DefaultLit(ls[i1], types.Types[types.TINT64])
+ } else {
+ ls[i1] = DefaultLit(ls[i1], nil)
+ }
+ }
+ return n
+}
+
+// tcRealImag typechecks an OREAL or OIMAG node.
+func tcRealImag(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ l := n.X
+ t := l.Type()
+ if t == nil {
+ n.SetType(nil)
+ return n
+ }
+
+ // Determine result type.
+ switch t.Kind() {
+ case types.TIDEAL:
+ n.SetType(types.UntypedFloat)
+ case types.TCOMPLEX64:
+ n.SetType(types.Types[types.TFLOAT32])
+ case types.TCOMPLEX128:
+ n.SetType(types.Types[types.TFLOAT64])
+ default:
+ base.Errorf("invalid argument %L for %v", l, n.Op())
+ n.SetType(nil)
+ return n
+ }
+ return n
+}
+
+// tcRecover typechecks an ORECOVER node.
+func tcRecover(n *ir.CallExpr) ir.Node {
+ if len(n.Args) != 0 {
+ base.Errorf("too many arguments to recover")
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetType(types.Types[types.TINTER])
+ return n
+}
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
similarity index 53%
rename from src/cmd/compile/internal/gc/iexport.go
rename to src/cmd/compile/internal/typecheck/iexport.go
index 1f53d8ca7dc19..6fab74e61fe77 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -199,21 +199,25 @@
// they're expected to change much more rapidly, so they're omitted
// here. See exportWriter's varExt/funcExt/etc methods for details.
-package gc
+package typecheck
import (
"bufio"
"bytes"
- "cmd/compile/internal/types"
- "cmd/internal/goobj"
- "cmd/internal/src"
"crypto/md5"
"encoding/binary"
"fmt"
+ "go/constant"
"io"
"math/big"
"sort"
"strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/goobj"
+ "cmd/internal/src"
)
// Current indexed export format version. Increase with each format change.
@@ -242,23 +246,12 @@ const (
interfaceType
)
-func iexport(out *bufio.Writer) {
- // Mark inline bodies that are reachable through exported types.
- // (Phase 0 of bexport.go.)
- {
- // TODO(mdempsky): Separate from bexport logic.
- p := &exporter{marked: make(map[*types.Type]bool)}
- for _, n := range exportlist {
- sym := n.Sym
- p.markType(asNode(sym.Def).Type)
- }
- }
-
+func WriteExports(out *bufio.Writer) {
p := iexporter{
allPkgs: map[*types.Pkg]bool{},
stringIndex: map[string]uint64{},
- declIndex: map[*Node]uint64{},
- inlineIndex: map[*Node]uint64{},
+ declIndex: map[*types.Sym]uint64{},
+ inlineIndex: map[*types.Sym]uint64{},
typIndex: map[*types.Type]uint64{},
}
@@ -266,19 +259,19 @@ func iexport(out *bufio.Writer) {
p.typIndex[pt] = uint64(i)
}
if len(p.typIndex) > predeclReserved {
- Fatalf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)
+ base.Fatalf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)
}
// Initialize work queue with exported declarations.
- for _, n := range exportlist {
+ for _, n := range Target.Exports {
p.pushDecl(n)
}
// Loop until no more work. We use a queue because while
// writing out inline bodies, we may discover additional
// declarations that are needed.
- for !p.declTodo.empty() {
- p.doDecl(p.declTodo.popLeft())
+ for !p.declTodo.Empty() {
+ p.doDecl(p.declTodo.PopLeft())
}
// Append indices to data0 section.
@@ -288,6 +281,10 @@ func iexport(out *bufio.Writer) {
w.writeIndex(p.inlineIndex, false)
w.flush()
+ if *base.Flag.LowerV {
+ fmt.Printf("export: hdr strings %v, data %v, index %v\n", p.strings.Len(), dataLen, p.data0.Len())
+ }
+
// Assemble header.
var hdr intWriter
hdr.WriteByte('i')
@@ -304,41 +301,38 @@ func iexport(out *bufio.Writer) {
// Add fingerprint (used by linker object file).
// Attach this to the end, so tools (e.g. gcimporter) don't care.
- copy(Ctxt.Fingerprint[:], h.Sum(nil)[:])
- out.Write(Ctxt.Fingerprint[:])
+ copy(base.Ctxt.Fingerprint[:], h.Sum(nil)[:])
+ out.Write(base.Ctxt.Fingerprint[:])
}
-// writeIndex writes out an object index. mainIndex indicates whether
+// writeIndex writes out a symbol index. mainIndex indicates whether
// we're writing out the main index, which is also read by
// non-compiler tools and includes a complete package description
// (i.e., name and height).
-func (w *exportWriter) writeIndex(index map[*Node]uint64, mainIndex bool) {
- // Build a map from packages to objects from that package.
- pkgObjs := map[*types.Pkg][]*Node{}
+func (w *exportWriter) writeIndex(index map[*types.Sym]uint64, mainIndex bool) {
+ // Build a map from packages to symbols from that package.
+ pkgSyms := map[*types.Pkg][]*types.Sym{}
// For the main index, make sure to include every package that
// we reference, even if we're not exporting (or reexporting)
// any symbols from it.
if mainIndex {
- pkgObjs[localpkg] = nil
+ pkgSyms[types.LocalPkg] = nil
for pkg := range w.p.allPkgs {
- pkgObjs[pkg] = nil
+ pkgSyms[pkg] = nil
}
}
- for n := range index {
- pkgObjs[n.Sym.Pkg] = append(pkgObjs[n.Sym.Pkg], n)
+ // Group symbols by package.
+ for sym := range index {
+ pkgSyms[sym.Pkg] = append(pkgSyms[sym.Pkg], sym)
}
+ // Sort packages by path.
var pkgs []*types.Pkg
- for pkg, objs := range pkgObjs {
+ for pkg := range pkgSyms {
pkgs = append(pkgs, pkg)
-
- sort.Slice(objs, func(i, j int) bool {
- return objs[i].Sym.Name < objs[j].Sym.Name
- })
}
-
sort.Slice(pkgs, func(i, j int) bool {
return pkgs[i].Path < pkgs[j].Path
})
@@ -351,11 +345,16 @@ func (w *exportWriter) writeIndex(index map[*Node]uint64, mainIndex bool) {
w.uint64(uint64(pkg.Height))
}
- objs := pkgObjs[pkg]
- w.uint64(uint64(len(objs)))
- for _, n := range objs {
- w.string(n.Sym.Name)
- w.uint64(index[n])
+ // Sort symbols within a package by name.
+ syms := pkgSyms[pkg]
+ sort.Slice(syms, func(i, j int) bool {
+ return syms[i].Name < syms[j].Name
+ })
+
+ w.uint64(uint64(len(syms)))
+ for _, sym := range syms {
+ w.string(sym.Name)
+ w.uint64(index[sym])
}
}
}
@@ -366,14 +365,14 @@ type iexporter struct {
// main index.
allPkgs map[*types.Pkg]bool
- declTodo nodeQueue
+ declTodo ir.NameQueue
strings intWriter
stringIndex map[string]uint64
data0 intWriter
- declIndex map[*Node]uint64
- inlineIndex map[*Node]uint64
+ declIndex map[*types.Sym]uint64
+ inlineIndex map[*types.Sym]uint64
typIndex map[*types.Type]uint64
}
@@ -385,6 +384,10 @@ func (p *iexporter) stringOff(s string) uint64 {
off = uint64(p.strings.Len())
p.stringIndex[s] = off
+ if *base.Flag.LowerV {
+ fmt.Printf("export: str %v %.40q\n", off, s)
+ }
+
p.strings.uint64(uint64(len(s)))
p.strings.WriteString(s)
}
@@ -392,22 +395,22 @@ func (p *iexporter) stringOff(s string) uint64 {
}
// pushDecl adds n to the declaration work queue, if not already present.
-func (p *iexporter) pushDecl(n *Node) {
- if n.Sym == nil || asNode(n.Sym.Def) != n && n.Op != OTYPE {
- Fatalf("weird Sym: %v, %v", n, n.Sym)
+func (p *iexporter) pushDecl(n *ir.Name) {
+ if n.Sym() == nil || n.Sym().Def != n && n.Op() != ir.OTYPE {
+ base.Fatalf("weird Sym: %v, %v", n, n.Sym())
}
// Don't export predeclared declarations.
- if n.Sym.Pkg == builtinpkg || n.Sym.Pkg == unsafepkg {
+ if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == ir.Pkgs.Unsafe {
return
}
- if _, ok := p.declIndex[n]; ok {
+ if _, ok := p.declIndex[n.Sym()]; ok {
return
}
- p.declIndex[n] = ^uint64(0) // mark n present in work queue
- p.declTodo.pushRight(n)
+ p.declIndex[n.Sym()] = ^uint64(0) // mark n present in work queue
+ p.declTodo.PushRight(n)
}
// exportWriter handles writing out individual data section chunks.
@@ -419,70 +422,83 @@ type exportWriter struct {
prevFile string
prevLine int64
prevColumn int64
+
+ // dclIndex maps function-scoped declarations to an int used to refer to
+ // them later in the function. For local variables/params, the int is
+ // non-negative and in order of the appearance in the Func's Dcl list. For
+ // closure variables, the index is negative starting at -2.
+ dclIndex map[*ir.Name]int
+ maxDclIndex int
+ maxClosureVarIndex int
}
-func (p *iexporter) doDecl(n *Node) {
+func (p *iexporter) doDecl(n *ir.Name) {
w := p.newWriter()
- w.setPkg(n.Sym.Pkg, false)
+ w.setPkg(n.Sym().Pkg, false)
- switch n.Op {
- case ONAME:
- switch n.Class() {
- case PEXTERN:
+ switch n.Op() {
+ case ir.ONAME:
+ switch n.Class {
+ case ir.PEXTERN:
// Variable.
w.tag('V')
- w.pos(n.Pos)
- w.typ(n.Type)
+ w.pos(n.Pos())
+ w.typ(n.Type())
w.varExt(n)
- case PFUNC:
- if n.IsMethod() {
- Fatalf("unexpected method: %v", n)
+ case ir.PFUNC:
+ if ir.IsMethod(n) {
+ base.Fatalf("unexpected method: %v", n)
}
// Function.
w.tag('F')
- w.pos(n.Pos)
- w.signature(n.Type)
+ w.pos(n.Pos())
+ w.signature(n.Type())
w.funcExt(n)
default:
- Fatalf("unexpected class: %v, %v", n, n.Class())
+ base.Fatalf("unexpected class: %v, %v", n, n.Class)
+ }
+
+ case ir.OLITERAL:
+ // TODO(mdempsky): Extend check to all declarations.
+ if n.Typecheck() == 0 {
+ base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
}
- case OLITERAL:
// Constant.
- n = typecheck(n, ctxExpr)
w.tag('C')
- w.pos(n.Pos)
- w.value(n.Type, n.Val())
+ w.pos(n.Pos())
+ w.value(n.Type(), n.Val())
+ w.constExt(n)
- case OTYPE:
- if IsAlias(n.Sym) {
+ case ir.OTYPE:
+ if types.IsDotAlias(n.Sym()) {
// Alias.
w.tag('A')
- w.pos(n.Pos)
- w.typ(n.Type)
+ w.pos(n.Pos())
+ w.typ(n.Type())
break
}
// Defined type.
w.tag('T')
- w.pos(n.Pos)
+ w.pos(n.Pos())
- underlying := n.Type.Orig
- if underlying == types.Errortype.Orig {
+ underlying := n.Type().Underlying()
+ if underlying == types.ErrorType.Underlying() {
// For "type T error", use error as the
// underlying type instead of error's own
// underlying anonymous interface. This
// ensures consistency with how importers may
// declare error (e.g., go/types uses nil Pkg
// for predeclared objects).
- underlying = types.Errortype
+ underlying = types.ErrorType
}
w.typ(underlying)
- t := n.Type
+ t := n.Type()
if t.IsInterface() {
w.typeExt(t)
break
@@ -503,27 +519,36 @@ func (p *iexporter) doDecl(n *Node) {
}
default:
- Fatalf("unexpected node: %v", n)
+ base.Fatalf("unexpected node: %v", n)
}
- p.declIndex[n] = w.flush()
+ w.finish("dcl", p.declIndex, n.Sym())
}
func (w *exportWriter) tag(tag byte) {
w.data.WriteByte(tag)
}
-func (p *iexporter) doInline(f *Node) {
+func (w *exportWriter) finish(what string, index map[*types.Sym]uint64, sym *types.Sym) {
+ off := w.flush()
+ if *base.Flag.LowerV {
+ fmt.Printf("export: %v %v %v\n", what, off, sym)
+ }
+ index[sym] = off
+}
+
+func (p *iexporter) doInline(f *ir.Name) {
w := p.newWriter()
w.setPkg(fnpkg(f), false)
- w.stmtList(asNodes(f.Func.Inl.Body))
+ w.dclIndex = make(map[*ir.Name]int, len(f.Func.Inl.Dcl))
+ w.funcBody(f.Func)
- p.inlineIndex[f] = w.flush()
+ w.finish("inl", p.inlineIndex, f.Sym())
}
func (w *exportWriter) pos(pos src.XPos) {
- p := Ctxt.PosTable.Pos(pos)
+ p := base.Ctxt.PosTable.Pos(pos)
file := p.Base().AbsFilename()
line := int64(p.RelLine())
column := int64(p.RelCol())
@@ -562,43 +587,40 @@ func (w *exportWriter) pos(pos src.XPos) {
}
func (w *exportWriter) pkg(pkg *types.Pkg) {
+ // TODO(mdempsky): Add flag to types.Pkg to mark pseudo-packages.
+ if pkg == ir.Pkgs.Go {
+ base.Fatalf("export of pseudo-package: %q", pkg.Path)
+ }
+
// Ensure any referenced packages are declared in the main index.
w.p.allPkgs[pkg] = true
w.string(pkg.Path)
}
-func (w *exportWriter) qualifiedIdent(n *Node) {
+func (w *exportWriter) qualifiedIdent(n ir.Node) {
// Ensure any referenced declarations are written out too.
- w.p.pushDecl(n)
+ w.p.pushDecl(n.Name())
- s := n.Sym
+ s := n.Sym()
w.string(s.Name)
w.pkg(s.Pkg)
}
func (w *exportWriter) selector(s *types.Sym) {
if w.currPkg == nil {
- Fatalf("missing currPkg")
+ base.Fatalf("missing currPkg")
}
- // Method selectors are rewritten into method symbols (of the
- // form T.M) during typechecking, but we want to write out
- // just the bare method name.
- name := s.Name
- if i := strings.LastIndex(name, "."); i >= 0 {
- name = name[i+1:]
- } else {
- pkg := w.currPkg
- if types.IsExported(name) {
- pkg = localpkg
- }
- if s.Pkg != pkg {
- Fatalf("package mismatch in selector: %v in package %q, but want %q", s, s.Pkg.Path, pkg.Path)
- }
+ pkg := w.currPkg
+ if types.IsExported(s.Name) {
+ pkg = types.LocalPkg
+ }
+ if s.Pkg != pkg {
+ base.Fatalf("package mismatch in selector: %v in package %q, but want %q", s, s.Pkg.Path, pkg.Path)
}
- w.string(name)
+ w.string(s.Name)
}
func (w *exportWriter) typ(t *types.Type) {
@@ -620,7 +642,11 @@ func (p *iexporter) typOff(t *types.Type) uint64 {
if !ok {
w := p.newWriter()
w.doTyp(t)
- off = predeclReserved + w.flush()
+ rawOff := w.flush()
+ if *base.Flag.LowerV {
+ fmt.Printf("export: typ %v %v\n", rawOff, t)
+ }
+ off = predeclReserved + rawOff
p.typIndex[t] = off
}
return off
@@ -631,46 +657,46 @@ func (w *exportWriter) startType(k itag) {
}
func (w *exportWriter) doTyp(t *types.Type) {
- if t.Sym != nil {
- if t.Sym.Pkg == builtinpkg || t.Sym.Pkg == unsafepkg {
- Fatalf("builtin type missing from typIndex: %v", t)
+ if t.Sym() != nil {
+ if t.Sym().Pkg == types.BuiltinPkg || t.Sym().Pkg == ir.Pkgs.Unsafe {
+ base.Fatalf("builtin type missing from typIndex: %v", t)
}
w.startType(definedType)
- w.qualifiedIdent(typenod(t))
+ w.qualifiedIdent(t.Obj().(*ir.Name))
return
}
- switch t.Etype {
- case TPTR:
+ switch t.Kind() {
+ case types.TPTR:
w.startType(pointerType)
w.typ(t.Elem())
- case TSLICE:
+ case types.TSLICE:
w.startType(sliceType)
w.typ(t.Elem())
- case TARRAY:
+ case types.TARRAY:
w.startType(arrayType)
w.uint64(uint64(t.NumElem()))
w.typ(t.Elem())
- case TCHAN:
+ case types.TCHAN:
w.startType(chanType)
w.uint64(uint64(t.ChanDir()))
w.typ(t.Elem())
- case TMAP:
+ case types.TMAP:
w.startType(mapType)
w.typ(t.Key())
w.typ(t.Elem())
- case TFUNC:
+ case types.TFUNC:
w.startType(signatureType)
w.setPkg(t.Pkg(), true)
w.signature(t)
- case TSTRUCT:
+ case types.TSTRUCT:
w.startType(structType)
w.setPkg(t.Pkg(), true)
@@ -683,7 +709,7 @@ func (w *exportWriter) doTyp(t *types.Type) {
w.string(f.Note)
}
- case TINTER:
+ case types.TINTER:
var embeddeds, methods []*types.Field
for _, m := range t.Methods().Slice() {
if m.Sym != nil {
@@ -710,15 +736,13 @@ func (w *exportWriter) doTyp(t *types.Type) {
}
default:
- Fatalf("unexpected type: %v", t)
+ base.Fatalf("unexpected type: %v", t)
}
}
func (w *exportWriter) setPkg(pkg *types.Pkg, write bool) {
- if pkg == nil {
- // TODO(mdempsky): Proactively set Pkg for types and
- // remove this fallback logic.
- pkg = localpkg
+ if pkg == types.NoPkg {
+ base.Fatalf("missing pkg")
}
if write {
@@ -745,44 +769,40 @@ func (w *exportWriter) paramList(fs []*types.Field) {
func (w *exportWriter) param(f *types.Field) {
w.pos(f.Pos)
- w.localIdent(origSym(f.Sym), 0)
+ w.localIdent(types.OrigSym(f.Sym))
w.typ(f.Type)
}
-func constTypeOf(typ *types.Type) Ctype {
+func constTypeOf(typ *types.Type) constant.Kind {
switch typ {
case types.UntypedInt, types.UntypedRune:
- return CTINT
+ return constant.Int
case types.UntypedFloat:
- return CTFLT
+ return constant.Float
case types.UntypedComplex:
- return CTCPLX
- }
-
- switch typ.Etype {
- case TCHAN, TFUNC, TMAP, TNIL, TINTER, TPTR, TSLICE, TUNSAFEPTR:
- return CTNIL
- case TBOOL:
- return CTBOOL
- case TSTRING:
- return CTSTR
- case TINT, TINT8, TINT16, TINT32, TINT64,
- TUINT, TUINT8, TUINT16, TUINT32, TUINT64, TUINTPTR:
- return CTINT
- case TFLOAT32, TFLOAT64:
- return CTFLT
- case TCOMPLEX64, TCOMPLEX128:
- return CTCPLX
- }
-
- Fatalf("unexpected constant type: %v", typ)
+ return constant.Complex
+ }
+
+ switch typ.Kind() {
+ case types.TBOOL:
+ return constant.Bool
+ case types.TSTRING:
+ return constant.String
+ case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
+ types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, types.TUINTPTR:
+ return constant.Int
+ case types.TFLOAT32, types.TFLOAT64:
+ return constant.Float
+ case types.TCOMPLEX64, types.TCOMPLEX128:
+ return constant.Complex
+ }
+
+ base.Fatalf("unexpected constant type: %v", typ)
return 0
}
-func (w *exportWriter) value(typ *types.Type, v Val) {
- if vt := idealType(v.Ctype()); typ.IsUntyped() && typ != vt {
- Fatalf("exporter: untyped type mismatch, have: %v, want: %v", typ, vt)
- }
+func (w *exportWriter) value(typ *types.Type, v constant.Value) {
+ ir.AssertValidTypeForConst(typ, v)
w.typ(typ)
// Each type has only one admissible constant representation,
@@ -791,33 +811,29 @@ func (w *exportWriter) value(typ *types.Type, v Val) {
// and provides a useful consistency check.
switch constTypeOf(typ) {
- case CTNIL:
- // Only one value; nothing to encode.
- _ = v.U.(*NilVal)
- case CTBOOL:
- w.bool(v.U.(bool))
- case CTSTR:
- w.string(v.U.(string))
- case CTINT:
- w.mpint(&v.U.(*Mpint).Val, typ)
- case CTFLT:
- w.mpfloat(&v.U.(*Mpflt).Val, typ)
- case CTCPLX:
- x := v.U.(*Mpcplx)
- w.mpfloat(&x.Real.Val, typ)
- w.mpfloat(&x.Imag.Val, typ)
+ case constant.Bool:
+ w.bool(constant.BoolVal(v))
+ case constant.String:
+ w.string(constant.StringVal(v))
+ case constant.Int:
+ w.mpint(v, typ)
+ case constant.Float:
+ w.mpfloat(v, typ)
+ case constant.Complex:
+ w.mpfloat(constant.Real(v), typ)
+ w.mpfloat(constant.Imag(v), typ)
}
}
func intSize(typ *types.Type) (signed bool, maxBytes uint) {
if typ.IsUntyped() {
- return true, Mpprec / 8
+ return true, ir.ConstPrec / 8
}
- switch typ.Etype {
- case TFLOAT32, TCOMPLEX64:
+ switch typ.Kind() {
+ case types.TFLOAT32, types.TCOMPLEX64:
return true, 3
- case TFLOAT64, TCOMPLEX128:
+ case types.TFLOAT64, types.TCOMPLEX128:
return true, 7
}
@@ -826,8 +842,8 @@ func intSize(typ *types.Type) (signed bool, maxBytes uint) {
// The go/types API doesn't expose sizes to importers, so they
// don't know how big these types are.
- switch typ.Etype {
- case TINT, TUINT, TUINTPTR:
+ switch typ.Kind() {
+ case types.TINT, types.TUINT, types.TUINTPTR:
maxBytes = 8
}
@@ -852,22 +868,24 @@ func intSize(typ *types.Type) (signed bool, maxBytes uint) {
// according to the maximum number of bytes needed to encode a value
// of type typ. As a special case, 8-bit types are always encoded as a
// single byte.
-//
-// TODO(mdempsky): Is this level of complexity really worthwhile?
-func (w *exportWriter) mpint(x *big.Int, typ *types.Type) {
+func (w *exportWriter) mpint(x constant.Value, typ *types.Type) {
signed, maxBytes := intSize(typ)
- negative := x.Sign() < 0
+ negative := constant.Sign(x) < 0
if !signed && negative {
- Fatalf("negative unsigned integer; type %v, value %v", typ, x)
+ base.Fatalf("negative unsigned integer; type %v, value %v", typ, x)
+ }
+
+ b := constant.Bytes(x) // little endian
+ for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
+ b[i], b[j] = b[j], b[i]
}
- b := x.Bytes()
if len(b) > 0 && b[0] == 0 {
- Fatalf("leading zeros")
+ base.Fatalf("leading zeros")
}
if uint(len(b)) > maxBytes {
- Fatalf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)
+ base.Fatalf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)
}
maxSmall := 256 - maxBytes
@@ -904,7 +922,7 @@ func (w *exportWriter) mpint(x *big.Int, typ *types.Type) {
}
}
if n < maxSmall || n >= 256 {
- Fatalf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)
+ base.Fatalf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)
}
w.data.WriteByte(byte(n))
@@ -917,9 +935,10 @@ func (w *exportWriter) mpint(x *big.Int, typ *types.Type) {
// mantissa is an integer. The value is written out as mantissa (as a
// multi-precision integer) and then the exponent, except exponent is
// omitted if mantissa is zero.
-func (w *exportWriter) mpfloat(f *big.Float, typ *types.Type) {
+func (w *exportWriter) mpfloat(v constant.Value, typ *types.Type) {
+ f := ir.BigFloat(v)
if f.IsInf() {
- Fatalf("infinite constant")
+ base.Fatalf("infinite constant")
}
// Break into f = mant × 2**exp, with 0.5 <= mant < 1.
@@ -933,14 +952,25 @@ func (w *exportWriter) mpfloat(f *big.Float, typ *types.Type) {
manti, acc := mant.Int(nil)
if acc != big.Exact {
- Fatalf("mantissa scaling failed for %f (%s)", f, acc)
+ base.Fatalf("mantissa scaling failed for %f (%s)", f, acc)
}
- w.mpint(manti, typ)
+ w.mpint(constant.Make(manti), typ)
if manti.Sign() != 0 {
w.int64(exp)
}
}
+func (w *exportWriter) mprat(v constant.Value) {
+ r, ok := constant.Val(v).(*big.Rat)
+ if !w.bool(ok) {
+ return
+ }
+ // TODO(mdempsky): Come up with a more efficient binary
+ // encoding before bumping iexportVersion to expose to
+ // gcimporter.
+ w.string(r.String())
+}
+
func (w *exportWriter) bool(b bool) bool {
var x uint64
if b {
@@ -956,18 +986,51 @@ func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) }
// Compiler-specific extensions.
-func (w *exportWriter) varExt(n *Node) {
- w.linkname(n.Sym)
- w.symIdx(n.Sym)
+func (w *exportWriter) constExt(n *ir.Name) {
+ // Internally, we now represent untyped float and complex
+ // constants with infinite-precision rational numbers using
+ // go/constant, but the "public" export data format known to
+ // gcimporter only supports 512-bit floating point constants.
+ // In case rationals turn out to be a bad idea and we want to
+ // switch back to fixed-precision constants, for now we
+ // continue writing out the 512-bit truncation in the public
+ // data section, and write the exact, rational constant in the
+ // compiler's extension data. Also, we only need to worry
+ // about exporting rationals for declared constants, because
+ // constants that appear in an expression will already have
+ // been coerced to a concrete, fixed-precision type.
+ //
+ // Eventually, assuming we stick with using rationals, we
+ // should bump iexportVersion to support rationals, and do the
+ // whole gcimporter update song-and-dance.
+ //
+ // TODO(mdempsky): Prepare vocals for that.
+
+ switch n.Type() {
+ case types.UntypedFloat:
+ w.mprat(n.Val())
+ case types.UntypedComplex:
+ v := n.Val()
+ w.mprat(constant.Real(v))
+ w.mprat(constant.Imag(v))
+ }
+}
+
+func (w *exportWriter) varExt(n *ir.Name) {
+ w.linkname(n.Sym())
+ w.symIdx(n.Sym())
}
-func (w *exportWriter) funcExt(n *Node) {
- w.linkname(n.Sym)
- w.symIdx(n.Sym)
+func (w *exportWriter) funcExt(n *ir.Name) {
+ w.linkname(n.Sym())
+ w.symIdx(n.Sym())
+
+ // TODO remove after register abi is working.
+ w.uint64(uint64(n.Func.Pragma))
// Escape analysis.
for _, fs := range &types.RecvsParams {
- for _, f := range fs(n.Type).FieldSlice() {
+ for _, f := range fs(n.Type()).FieldSlice() {
w.string(f.Note)
}
}
@@ -980,14 +1043,7 @@ func (w *exportWriter) funcExt(n *Node) {
}
// Endlineno for inlined function.
- if n.Name.Defn != nil {
- w.pos(n.Name.Defn.Func.Endlineno)
- } else {
- // When the exported node was defined externally,
- // e.g. io exports atomic.(*Value).Load or bytes exports errors.New.
- // Keep it as we don't distinguish this case in iimport.go.
- w.pos(n.Func.Endlineno)
- }
+ w.pos(n.Func.Endlineno)
} else {
w.uint64(0)
}
@@ -995,7 +1051,7 @@ func (w *exportWriter) funcExt(n *Node) {
func (w *exportWriter) methExt(m *types.Field) {
w.bool(m.Nointerface())
- w.funcExt(asNode(m.Type.Nname()))
+ w.funcExt(m.Nname.(*ir.Name))
}
func (w *exportWriter) linkname(s *types.Sym) {
@@ -1025,21 +1081,38 @@ func (w *exportWriter) typeExt(t *types.Type) {
w.int64(i[1])
return
}
- w.symIdx(typesym(t))
- w.symIdx(typesym(t.PtrTo()))
+ w.symIdx(types.TypeSym(t))
+ w.symIdx(types.TypeSym(t.PtrTo()))
}
// Inline bodies.
-func (w *exportWriter) stmtList(list Nodes) {
- for _, n := range list.Slice() {
+func (w *exportWriter) writeNames(dcl []*ir.Name) {
+ w.int64(int64(len(dcl)))
+ for i, n := range dcl {
+ w.pos(n.Pos())
+ w.localIdent(n.Sym())
+ w.typ(n.Type())
+ w.dclIndex[n] = w.maxDclIndex + i
+ }
+ w.maxDclIndex += len(dcl)
+}
+
+func (w *exportWriter) funcBody(fn *ir.Func) {
+ w.writeNames(fn.Inl.Dcl)
+
+ w.stmtList(fn.Inl.Body)
+}
+
+func (w *exportWriter) stmtList(list []ir.Node) {
+ for _, n := range list {
w.node(n)
}
- w.op(OEND)
+ w.op(ir.OEND)
}
-func (w *exportWriter) node(n *Node) {
- if opprec[n.Op] < 0 {
+func (w *exportWriter) node(n ir.Node) {
+ if ir.OpPrec[n.Op()] < 0 {
w.stmt(n)
} else {
w.expr(n)
@@ -1048,384 +1121,473 @@ func (w *exportWriter) node(n *Node) {
// Caution: stmt will emit more than one node for statement nodes n that have a non-empty
// n.Ninit and where n cannot have a natural init section (such as in "if", "for", etc.).
-func (w *exportWriter) stmt(n *Node) {
- if n.Ninit.Len() > 0 && !stmtwithinit(n.Op) {
+func (w *exportWriter) stmt(n ir.Node) {
+ if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) {
// can't use stmtList here since we don't want the final OEND
- for _, n := range n.Ninit.Slice() {
+ for _, n := range n.Init() {
w.stmt(n)
}
}
- switch op := n.Op; op {
- case ODCL:
- w.op(ODCL)
- w.pos(n.Left.Pos)
- w.localName(n.Left)
- w.typ(n.Left.Type)
+ switch n.Op() {
+ case ir.OBLOCK:
+ // No OBLOCK in export data.
+ // Inline content into this statement list,
+ // like the init list above.
+ // (At the moment neither the parser nor the typechecker
+ // generate OBLOCK nodes except to denote an empty
+ // function body, although that may change.)
+ n := n.(*ir.BlockStmt)
+ for _, n := range n.List {
+ w.stmt(n)
+ }
- // case ODCLFIELD:
- // unimplemented - handled by default case
+ case ir.ODCL:
+ n := n.(*ir.Decl)
+ if ir.IsBlank(n.X) {
+ return // blank declarations not useful to importers
+ }
+ w.op(ir.ODCL)
+ w.localName(n.X)
- case OAS:
+ case ir.OAS:
// Don't export "v = " initializing statements, hope they're always
// preceded by the DCL which will be re-parsed and typecheck to reproduce
// the "v = " again.
- if n.Right != nil {
- w.op(OAS)
- w.pos(n.Pos)
- w.expr(n.Left)
- w.expr(n.Right)
+ n := n.(*ir.AssignStmt)
+ if n.Y != nil {
+ w.op(ir.OAS)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.expr(n.Y)
}
- case OASOP:
- w.op(OASOP)
- w.pos(n.Pos)
- w.op(n.SubOp())
- w.expr(n.Left)
- if w.bool(!n.Implicit()) {
- w.expr(n.Right)
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ w.op(ir.OASOP)
+ w.pos(n.Pos())
+ w.op(n.AsOp)
+ w.expr(n.X)
+ if w.bool(!n.IncDec) {
+ w.expr(n.Y)
}
- case OAS2:
- w.op(OAS2)
- w.pos(n.Pos)
- w.exprList(n.List)
- w.exprList(n.Rlist)
+ case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
+ n := n.(*ir.AssignListStmt)
+ w.op(ir.OAS2)
+ w.pos(n.Pos())
+ w.exprList(n.Lhs)
+ w.exprList(n.Rhs)
- case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
- w.op(OAS2)
- w.pos(n.Pos)
- w.exprList(n.List)
- w.exprList(asNodes([]*Node{n.Right}))
-
- case ORETURN:
- w.op(ORETURN)
- w.pos(n.Pos)
- w.exprList(n.List)
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ w.op(ir.ORETURN)
+ w.pos(n.Pos())
+ w.exprList(n.Results)
// case ORETJMP:
// unreachable - generated by compiler for trampolin routines
- case OGO, ODEFER:
- w.op(op)
- w.pos(n.Pos)
- w.expr(n.Left)
-
- case OIF:
- w.op(OIF)
- w.pos(n.Pos)
- w.stmtList(n.Ninit)
- w.expr(n.Left)
- w.stmtList(n.Nbody)
- w.stmtList(n.Rlist)
-
- case OFOR:
- w.op(OFOR)
- w.pos(n.Pos)
- w.stmtList(n.Ninit)
- w.exprsOrNil(n.Left, n.Right)
- w.stmtList(n.Nbody)
-
- case ORANGE:
- w.op(ORANGE)
- w.pos(n.Pos)
- w.stmtList(n.List)
- w.expr(n.Right)
- w.stmtList(n.Nbody)
-
- case OSELECT, OSWITCH:
- w.op(op)
- w.pos(n.Pos)
- w.stmtList(n.Ninit)
- w.exprsOrNil(n.Left, nil)
- w.caseList(n)
+ case ir.OGO, ir.ODEFER:
+ n := n.(*ir.GoDeferStmt)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.Call)
+
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ w.op(ir.OIF)
+ w.pos(n.Pos())
+ w.stmtList(n.Init())
+ w.expr(n.Cond)
+ w.stmtList(n.Body)
+ w.stmtList(n.Else)
+
+ case ir.OFOR:
+ n := n.(*ir.ForStmt)
+ w.op(ir.OFOR)
+ w.pos(n.Pos())
+ w.stmtList(n.Init())
+ w.exprsOrNil(n.Cond, n.Post)
+ w.stmtList(n.Body)
+
+ case ir.ORANGE:
+ n := n.(*ir.RangeStmt)
+ w.op(ir.ORANGE)
+ w.pos(n.Pos())
+ w.exprsOrNil(n.Key, n.Value)
+ w.expr(n.X)
+ w.stmtList(n.Body)
+
+ case ir.OSELECT:
+ n := n.(*ir.SelectStmt)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.stmtList(n.Init())
+ w.commList(n.Cases)
+
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.stmtList(n.Init())
+ w.exprsOrNil(n.Tag, nil)
+ w.caseList(n.Cases, isNamedTypeSwitch(n.Tag))
// case OCASE:
// handled by caseList
- case OFALL:
- w.op(OFALL)
- w.pos(n.Pos)
-
- case OBREAK, OCONTINUE:
- w.op(op)
- w.pos(n.Pos)
- w.exprsOrNil(n.Left, nil)
-
- case OEMPTY:
- // nothing to emit
-
- case OGOTO, OLABEL:
- w.op(op)
- w.pos(n.Pos)
- w.string(n.Sym.Name)
+ case ir.OFALL:
+ n := n.(*ir.BranchStmt)
+ w.op(ir.OFALL)
+ w.pos(n.Pos())
+
+ case ir.OBREAK, ir.OCONTINUE, ir.OGOTO, ir.OLABEL:
+ w.op(n.Op())
+ w.pos(n.Pos())
+ label := ""
+ if sym := n.Sym(); sym != nil {
+ label = sym.Name
+ }
+ w.string(label)
default:
- Fatalf("exporter: CANNOT EXPORT: %v\nPlease notify gri@\n", n.Op)
+ base.Fatalf("exporter: CANNOT EXPORT: %v\nPlease notify gri@\n", n.Op())
}
}
-func (w *exportWriter) caseList(sw *Node) {
- namedTypeSwitch := sw.Op == OSWITCH && sw.Left != nil && sw.Left.Op == OTYPESW && sw.Left.Left != nil
+func isNamedTypeSwitch(x ir.Node) bool {
+ guard, ok := x.(*ir.TypeSwitchGuard)
+ return ok && guard.Tag != nil
+}
- cases := sw.List.Slice()
+func (w *exportWriter) caseList(cases []*ir.CaseClause, namedTypeSwitch bool) {
w.uint64(uint64(len(cases)))
for _, cas := range cases {
- if cas.Op != OCASE {
- Fatalf("expected OCASE, got %v", cas)
- }
- w.pos(cas.Pos)
+ w.pos(cas.Pos())
w.stmtList(cas.List)
if namedTypeSwitch {
- w.localName(cas.Rlist.First())
+ w.localName(cas.Var)
}
- w.stmtList(cas.Nbody)
+ w.stmtList(cas.Body)
}
}
-func (w *exportWriter) exprList(list Nodes) {
- for _, n := range list.Slice() {
- w.expr(n)
+func (w *exportWriter) commList(cases []*ir.CommClause) {
+ w.uint64(uint64(len(cases)))
+ for _, cas := range cases {
+ w.pos(cas.Pos())
+ w.node(cas.Comm)
+ w.stmtList(cas.Body)
}
- w.op(OEND)
}
-func (w *exportWriter) expr(n *Node) {
- // from nodefmt (fmt.go)
- //
- // nodefmt reverts nodes back to their original - we don't need to do
- // it because we are not bound to produce valid Go syntax when exporting
- //
- // if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil {
- // n = n.Orig
- // }
+func (w *exportWriter) exprList(list ir.Nodes) {
+ for _, n := range list {
+ w.expr(n)
+ }
+ w.op(ir.OEND)
+}
- // from exprfmt (fmt.go)
- for n.Op == OPAREN || n.Implicit() && (n.Op == ODEREF || n.Op == OADDR || n.Op == ODOT || n.Op == ODOTPTR) {
- n = n.Left
+func simplifyForExport(n ir.Node) ir.Node {
+ switch n.Op() {
+ case ir.OPAREN:
+ n := n.(*ir.ParenExpr)
+ return simplifyForExport(n.X)
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ if n.Implicit() {
+ return simplifyForExport(n.X)
+ }
+ case ir.OADDR:
+ n := n.(*ir.AddrExpr)
+ if n.Implicit() {
+ return simplifyForExport(n.X)
+ }
+ case ir.ODOT, ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ if n.Implicit() {
+ return simplifyForExport(n.X)
+ }
}
+ return n
+}
- switch op := n.Op; op {
+func (w *exportWriter) expr(n ir.Node) {
+ n = simplifyForExport(n)
+ switch n.Op() {
// expressions
// (somewhat closely following the structure of exprfmt in fmt.go)
- case OLITERAL:
- if n.Val().Ctype() == CTNIL && n.Orig != nil && n.Orig != n {
- w.expr(n.Orig)
- break
- }
- w.op(OLITERAL)
- w.pos(n.Pos)
- w.value(n.Type, n.Val())
-
- case ONAME:
- // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
- // but for export, this should be rendered as (*pkg.T).meth.
- // These nodes have the special property that they are names with a left OTYPE and a right ONAME.
- if n.isMethodExpression() {
- w.op(OXDOT)
- w.pos(n.Pos)
- w.expr(n.Left) // n.Left.Op == OTYPE
- w.selector(n.Right.Sym)
- break
+ case ir.ONIL:
+ n := n.(*ir.NilExpr)
+ if !n.Type().HasNil() {
+ base.Fatalf("unexpected type for nil: %v", n.Type())
}
+ w.op(ir.ONIL)
+ w.pos(n.Pos())
+ w.typ(n.Type())
+
+ case ir.OLITERAL:
+ w.op(ir.OLITERAL)
+ w.pos(n.Pos())
+ w.value(n.Type(), n.Val())
+ case ir.ONAME:
// Package scope name.
- if (n.Class() == PEXTERN || n.Class() == PFUNC) && !n.isBlank() {
- w.op(ONONAME)
+ n := n.(*ir.Name)
+ if (n.Class == ir.PEXTERN || n.Class == ir.PFUNC) && !ir.IsBlank(n) {
+ w.op(ir.ONONAME)
w.qualifiedIdent(n)
break
}
// Function scope name.
- w.op(ONAME)
+ w.op(ir.ONAME)
w.localName(n)
// case OPACK, ONONAME:
// should have been resolved by typechecking - handled by default case
- case OTYPE:
- w.op(OTYPE)
- w.typ(n.Type)
+ case ir.OTYPE:
+ w.op(ir.OTYPE)
+ w.typ(n.Type())
- case OTYPESW:
- w.op(OTYPESW)
- w.pos(n.Pos)
+ case ir.OTYPESW:
+ n := n.(*ir.TypeSwitchGuard)
+ w.op(ir.OTYPESW)
+ w.pos(n.Pos())
var s *types.Sym
- if n.Left != nil {
- if n.Left.Op != ONONAME {
- Fatalf("expected ONONAME, got %v", n.Left)
+ if n.Tag != nil {
+ if n.Tag.Op() != ir.ONONAME {
+ base.Fatalf("expected ONONAME, got %v", n.Tag)
}
- s = n.Left.Sym
+ s = n.Tag.Sym()
}
- w.localIdent(s, 0) // declared pseudo-variable, if any
- w.exprsOrNil(n.Right, nil)
+ w.localIdent(s) // declared pseudo-variable, if any
+ w.expr(n.X)
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// should have been resolved by typechecking - handled by default case
- // case OCLOSURE:
- // unimplemented - handled by default case
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ w.op(ir.OCLOSURE)
+ w.pos(n.Pos())
+ w.signature(n.Type())
+
+ // Write out id for the Outer of each conditional variable. The
+ // conditional variable itself for this closure will be re-created
+ // during import.
+ w.int64(int64(len(n.Func.ClosureVars)))
+ for i, cv := range n.Func.ClosureVars {
+ w.pos(cv.Pos())
+ w.localName(cv.Outer)
+ // Closure variable (which will be re-created during
+ // import) is given via a negative id, starting at -2,
+ // which is used to refer to it later in the function
+ // during export. -1 represents blanks.
+ w.dclIndex[cv] = -(i + 2) - w.maxClosureVarIndex
+ }
+ w.maxClosureVarIndex += len(n.Func.ClosureVars)
+
+ // like w.funcBody(n.Func), but not for .Inl
+ w.writeNames(n.Func.Dcl)
+ w.stmtList(n.Func.Body)
// case OCOMPLIT:
// should have been resolved by typechecking - handled by default case
- case OPTRLIT:
- w.op(OADDR)
- w.pos(n.Pos)
- w.expr(n.Left)
-
- case OSTRUCTLIT:
- w.op(OSTRUCTLIT)
- w.pos(n.Pos)
- w.typ(n.Type)
- w.elemList(n.List) // special handling of field names
-
- case OARRAYLIT, OSLICELIT, OMAPLIT:
- w.op(OCOMPLIT)
- w.pos(n.Pos)
- w.typ(n.Type)
+ case ir.OPTRLIT:
+ n := n.(*ir.AddrExpr)
+ w.op(ir.OADDR)
+ w.pos(n.Pos())
+ w.expr(n.X)
+
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ w.op(ir.OSTRUCTLIT)
+ w.pos(n.Pos())
+ w.typ(n.Type())
+ w.fieldList(n.List) // special handling of field names
+
+ case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
+ n := n.(*ir.CompLitExpr)
+ w.op(ir.OCOMPLIT)
+ w.pos(n.Pos())
+ w.typ(n.Type())
w.exprList(n.List)
- case OKEY:
- w.op(OKEY)
- w.pos(n.Pos)
- w.exprsOrNil(n.Left, n.Right)
+ case ir.OKEY:
+ n := n.(*ir.KeyExpr)
+ w.op(ir.OKEY)
+ w.pos(n.Pos())
+ w.expr(n.Key)
+ w.expr(n.Value)
// case OSTRUCTKEY:
// unreachable - handled in case OSTRUCTLIT by elemList
- case OCALLPART:
- // An OCALLPART is an OXDOT before type checking.
- w.op(OXDOT)
- w.pos(n.Pos)
- w.expr(n.Left)
- // Right node should be ONAME
- w.selector(n.Right.Sym)
-
- case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
- w.op(OXDOT)
- w.pos(n.Pos)
- w.expr(n.Left)
- w.selector(n.Sym)
-
- case ODOTTYPE, ODOTTYPE2:
- w.op(ODOTTYPE)
- w.pos(n.Pos)
- w.expr(n.Left)
- w.typ(n.Type)
-
- case OINDEX, OINDEXMAP:
- w.op(OINDEX)
- w.pos(n.Pos)
- w.expr(n.Left)
- w.expr(n.Right)
-
- case OSLICE, OSLICESTR, OSLICEARR:
- w.op(OSLICE)
- w.pos(n.Pos)
- w.expr(n.Left)
- low, high, _ := n.SliceBounds()
- w.exprsOrNil(low, high)
-
- case OSLICE3, OSLICE3ARR:
- w.op(OSLICE3)
- w.pos(n.Pos)
- w.expr(n.Left)
- low, high, max := n.SliceBounds()
- w.exprsOrNil(low, high)
- w.expr(max)
-
- case OCOPY, OCOMPLEX:
+ case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR:
+ n := n.(*ir.SelectorExpr)
+ w.op(ir.OXDOT)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.selector(n.Sel)
+
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ n := n.(*ir.TypeAssertExpr)
+ w.op(ir.ODOTTYPE)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.typ(n.Type())
+
+ case ir.OINDEX, ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ w.op(ir.OINDEX)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.expr(n.Index)
+
+ case ir.OSLICE, ir.OSLICESTR, ir.OSLICEARR:
+ n := n.(*ir.SliceExpr)
+ w.op(ir.OSLICE)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.exprsOrNil(n.Low, n.High)
+
+ case ir.OSLICE3, ir.OSLICE3ARR:
+ n := n.(*ir.SliceExpr)
+ w.op(ir.OSLICE3)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.exprsOrNil(n.Low, n.High)
+ w.expr(n.Max)
+
+ case ir.OCOPY, ir.OCOMPLEX:
// treated like other builtin calls (see e.g., OREAL)
- w.op(op)
- w.pos(n.Pos)
- w.expr(n.Left)
- w.expr(n.Right)
- w.op(OEND)
-
- case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, ORUNES2STR, OSTR2BYTES, OSTR2RUNES, ORUNESTR:
- w.op(OCONV)
- w.pos(n.Pos)
- w.expr(n.Left)
- w.typ(n.Type)
-
- case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN:
- w.op(op)
- w.pos(n.Pos)
- if n.Left != nil {
- w.expr(n.Left)
- w.op(OEND)
- } else {
- w.exprList(n.List) // emits terminating OEND
- }
+ n := n.(*ir.BinaryExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.expr(n.Y)
+ w.op(ir.OEND)
+
+ case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR:
+ n := n.(*ir.ConvExpr)
+ w.op(ir.OCONV)
+ w.pos(n.Pos())
+ w.typ(n.Type())
+ w.expr(n.X)
+
+ case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC:
+ n := n.(*ir.UnaryExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.op(ir.OEND)
+
+ case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN:
+ n := n.(*ir.CallExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.exprList(n.Args) // emits terminating OEND
// only append() calls may contain '...' arguments
- if op == OAPPEND {
- w.bool(n.IsDDD())
- } else if n.IsDDD() {
- Fatalf("exporter: unexpected '...' with %v call", op)
+ if n.Op() == ir.OAPPEND {
+ w.bool(n.IsDDD)
+ } else if n.IsDDD {
+ base.Fatalf("exporter: unexpected '...' with %v call", n.Op())
}
- case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
- w.op(OCALL)
- w.pos(n.Pos)
- w.stmtList(n.Ninit)
- w.expr(n.Left)
- w.exprList(n.List)
- w.bool(n.IsDDD())
-
- case OMAKEMAP, OMAKECHAN, OMAKESLICE:
- w.op(op) // must keep separate from OMAKE for importer
- w.pos(n.Pos)
- w.typ(n.Type)
+ case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OGETG:
+ n := n.(*ir.CallExpr)
+ w.op(ir.OCALL)
+ w.pos(n.Pos())
+ w.stmtList(n.Init())
+ w.expr(n.X)
+ w.exprList(n.Args)
+ w.bool(n.IsDDD)
+
+ case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
+ n := n.(*ir.MakeExpr)
+ w.op(n.Op()) // must keep separate from OMAKE for importer
+ w.pos(n.Pos())
+ w.typ(n.Type())
switch {
default:
// empty list
- w.op(OEND)
- case n.List.Len() != 0: // pre-typecheck
- w.exprList(n.List) // emits terminating OEND
- case n.Right != nil:
- w.expr(n.Left)
- w.expr(n.Right)
- w.op(OEND)
- case n.Left != nil && (n.Op == OMAKESLICE || !n.Left.Type.IsUntyped()):
- w.expr(n.Left)
- w.op(OEND)
+ w.op(ir.OEND)
+ case n.Cap != nil:
+ w.expr(n.Len)
+ w.expr(n.Cap)
+ w.op(ir.OEND)
+ case n.Len != nil && (n.Op() == ir.OMAKESLICE || !n.Len.Type().IsUntyped()):
+ w.expr(n.Len)
+ w.op(ir.OEND)
}
// unary expressions
- case OPLUS, ONEG, OADDR, OBITNOT, ODEREF, ONOT, ORECV:
- w.op(op)
- w.pos(n.Pos)
- w.expr(n.Left)
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+
+ case ir.OADDR:
+ n := n.(*ir.AddrExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.Chan)
+ w.expr(n.Value)
// binary expressions
- case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT,
- OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND, OSUB, OXOR:
- w.op(op)
- w.pos(n.Pos)
- w.expr(n.Left)
- w.expr(n.Right)
-
- case OADDSTR:
- w.op(OADDSTR)
- w.pos(n.Pos)
+ case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
+ ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR:
+ n := n.(*ir.BinaryExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.expr(n.Y)
+
+ case ir.OANDAND, ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.expr(n.Y)
+
+ case ir.OADDSTR:
+ n := n.(*ir.AddStringExpr)
+ w.op(ir.OADDSTR)
+ w.pos(n.Pos())
w.exprList(n.List)
- case ODCLCONST:
+ case ir.ODCLCONST:
// if exporting, DCLCONST should just be removed as its usage
// has already been replaced with literals
default:
- Fatalf("cannot export %v (%d) node\n"+
- "\t==> please file an issue and assign to gri@", n.Op, int(n.Op))
+ base.Fatalf("cannot export %v (%d) node\n"+
+ "\t==> please file an issue and assign to gri@", n.Op(), int(n.Op()))
}
}
-func (w *exportWriter) op(op Op) {
+func (w *exportWriter) op(op ir.Op) {
w.uint64(uint64(op))
}
-func (w *exportWriter) exprsOrNil(a, b *Node) {
+func (w *exportWriter) exprsOrNil(a, b ir.Node) {
ab := 0
if a != nil {
ab |= 1
@@ -1442,31 +1604,34 @@ func (w *exportWriter) exprsOrNil(a, b *Node) {
}
}
-func (w *exportWriter) elemList(list Nodes) {
- w.uint64(uint64(list.Len()))
- for _, n := range list.Slice() {
- w.selector(n.Sym)
- w.expr(n.Left)
+func (w *exportWriter) fieldList(list ir.Nodes) {
+ w.uint64(uint64(len(list)))
+ for _, n := range list {
+ n := n.(*ir.StructKeyExpr)
+ w.pos(n.Pos())
+ w.selector(n.Field)
+ w.expr(n.Value)
}
}
-func (w *exportWriter) localName(n *Node) {
- // 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() == PAUTO || (n.Class() == PAUTOHEAP && n.Name.Param.Stackcopy == nil) {
- v = n.Name.Vargen
+func (w *exportWriter) localName(n *ir.Name) {
+ 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")
+ }
+
// Anonymous parameters.
if s == nil {
w.string("")
@@ -1481,18 +1646,11 @@ func (w *exportWriter) localIdent(s *types.Sym, v int32) {
// TODO(mdempsky): Fix autotmp hack.
if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, ".autotmp_") {
- Fatalf("unexpected dot in identifier: %v", name)
- }
-
- if v > 0 {
- if strings.Contains(name, "·") {
- Fatalf("exporter: unexpected · in symbol name")
- }
- name = fmt.Sprintf("%s·%d", name, v)
+ base.Fatalf("unexpected dot in identifier: %v", name)
}
- if !types.IsExported(name) && s.Pkg != w.currPkg {
- Fatalf("weird package in name: %v => %v, not %q", s, name, w.currPkg.Path)
+ 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)
}
w.string(name)
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
new file mode 100644
index 0000000000000..b73ef5176b958
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -0,0 +1,1240 @@
+// 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.
+
+// Indexed package import.
+// See iexport.go for the export data format.
+
+package typecheck
+
+import (
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "io"
+ "math/big"
+ "os"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+// An iimporterAndOffset identifies an importer and an offset within
+// its data section.
+type iimporterAndOffset struct {
+ p *iimporter
+ off uint64
+}
+
+var (
+ // DeclImporter maps from imported identifiers to an importer
+ // and offset where that identifier's declaration can be read.
+ DeclImporter = map[*types.Sym]iimporterAndOffset{}
+
+ // inlineImporter is like DeclImporter, but for inline bodies
+ // for function and method symbols.
+ inlineImporter = map[*types.Sym]iimporterAndOffset{}
+)
+
+func expandDecl(n ir.Node) ir.Node {
+ if n, ok := n.(*ir.Name); ok {
+ return n
+ }
+
+ id := n.(*ir.Ident)
+ if n := id.Sym().PkgDef(); n != nil {
+ return n.(*ir.Name)
+ }
+
+ r := importReaderFor(id.Sym(), DeclImporter)
+ if r == nil {
+ // Can happen if user tries to reference an undeclared name.
+ return n
+ }
+
+ return r.doDecl(n.Sym())
+}
+
+func ImportBody(fn *ir.Func) {
+ if fn.Inl.Body != nil {
+ return
+ }
+
+ r := importReaderFor(fn.Nname.Sym(), inlineImporter)
+ if r == nil {
+ base.Fatalf("missing import reader for %v", fn)
+ }
+
+ if inimport {
+ base.Fatalf("recursive inimport")
+ }
+ inimport = true
+ r.doInline(fn)
+ inimport = false
+}
+
+func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset) *importReader {
+ x, ok := importers[sym]
+ if !ok {
+ return nil
+ }
+
+ return x.p.newReader(x.off, sym.Pkg)
+}
+
+type intReader struct {
+ *bio.Reader
+ pkg *types.Pkg
+}
+
+func (r *intReader) int64() int64 {
+ i, err := binary.ReadVarint(r.Reader)
+ if err != nil {
+ base.Errorf("import %q: read error: %v", r.pkg.Path, err)
+ base.ErrorExit()
+ }
+ return i
+}
+
+func (r *intReader) uint64() uint64 {
+ i, err := binary.ReadUvarint(r.Reader)
+ if err != nil {
+ base.Errorf("import %q: read error: %v", r.pkg.Path, err)
+ base.ErrorExit()
+ }
+ return i
+}
+
+func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintType) {
+ ird := &intReader{in, pkg}
+
+ version := ird.uint64()
+ if version != iexportVersion {
+ base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
+ base.ErrorExit()
+ }
+
+ sLen := ird.uint64()
+ dLen := ird.uint64()
+
+ // Map string (and data) section into memory as a single large
+ // string. This reduces heap fragmentation and allows
+ // returning individual substrings very efficiently.
+ data, err := mapFile(in.File(), in.Offset(), int64(sLen+dLen))
+ if err != nil {
+ base.Errorf("import %q: mapping input: %v", pkg.Path, err)
+ base.ErrorExit()
+ }
+ stringData := data[:sLen]
+ declData := data[sLen:]
+
+ in.MustSeek(int64(sLen+dLen), os.SEEK_CUR)
+
+ p := &iimporter{
+ ipkg: pkg,
+
+ pkgCache: map[uint64]*types.Pkg{},
+ posBaseCache: map[uint64]*src.PosBase{},
+ typCache: map[uint64]*types.Type{},
+
+ stringData: stringData,
+ declData: declData,
+ }
+
+ for i, pt := range predeclared() {
+ p.typCache[uint64(i)] = pt
+ }
+
+ // Declaration index.
+ for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
+ pkg := p.pkgAt(ird.uint64())
+ pkgName := p.stringAt(ird.uint64())
+ pkgHeight := int(ird.uint64())
+ if pkg.Name == "" {
+ pkg.Name = pkgName
+ pkg.Height = pkgHeight
+ types.NumImport[pkgName]++
+
+ // TODO(mdempsky): This belongs somewhere else.
+ pkg.Lookup("_").Def = ir.BlankNode
+ } else {
+ if pkg.Name != pkgName {
+ base.Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path)
+ }
+ if pkg.Height != pkgHeight {
+ base.Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path)
+ }
+ }
+
+ for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
+ s := pkg.Lookup(p.stringAt(ird.uint64()))
+ off := ird.uint64()
+
+ if _, ok := DeclImporter[s]; !ok {
+ DeclImporter[s] = iimporterAndOffset{p, off}
+ }
+ }
+ }
+
+ // Inline body index.
+ for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
+ pkg := p.pkgAt(ird.uint64())
+
+ for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
+ s := pkg.Lookup(p.stringAt(ird.uint64()))
+ off := ird.uint64()
+
+ if _, ok := inlineImporter[s]; !ok {
+ inlineImporter[s] = iimporterAndOffset{p, off}
+ }
+ }
+ }
+
+ // Fingerprint.
+ _, err = io.ReadFull(in, fingerprint[:])
+ if err != nil {
+ base.Errorf("import %s: error reading fingerprint", pkg.Path)
+ base.ErrorExit()
+ }
+ return fingerprint
+}
+
+type iimporter struct {
+ ipkg *types.Pkg
+
+ pkgCache map[uint64]*types.Pkg
+ posBaseCache map[uint64]*src.PosBase
+ typCache map[uint64]*types.Type
+
+ stringData string
+ declData string
+}
+
+func (p *iimporter) stringAt(off uint64) string {
+ var x [binary.MaxVarintLen64]byte
+ n := copy(x[:], p.stringData[off:])
+
+ slen, n := binary.Uvarint(x[:n])
+ if n <= 0 {
+ base.Fatalf("varint failed")
+ }
+ spos := off + uint64(n)
+ return p.stringData[spos : spos+slen]
+}
+
+func (p *iimporter) posBaseAt(off uint64) *src.PosBase {
+ if posBase, ok := p.posBaseCache[off]; ok {
+ return posBase
+ }
+
+ file := p.stringAt(off)
+ posBase := src.NewFileBase(file, file)
+ p.posBaseCache[off] = posBase
+ return posBase
+}
+
+func (p *iimporter) pkgAt(off uint64) *types.Pkg {
+ if pkg, ok := p.pkgCache[off]; ok {
+ return pkg
+ }
+
+ pkg := p.ipkg
+ if pkgPath := p.stringAt(off); pkgPath != "" {
+ pkg = types.NewPkg(pkgPath, "")
+ }
+ p.pkgCache[off] = pkg
+ return pkg
+}
+
+// An importReader keeps state for reading an individual imported
+// object (declaration or inline body).
+type importReader struct {
+ strings.Reader
+ p *iimporter
+
+ currPkg *types.Pkg
+ prevBase *src.PosBase
+ prevLine int64
+ prevColumn int64
+
+ // curfn is the current function we're importing into.
+ curfn *ir.Func
+ // Slice of all dcls for function, including any interior closures
+ allDcls []*ir.Name
+ allClosureVars []*ir.Name
+}
+
+func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
+ r := &importReader{
+ p: p,
+ currPkg: pkg,
+ }
+ // (*strings.Reader).Reset wasn't added until Go 1.7, and we
+ // need to build with Go 1.4.
+ r.Reader = *strings.NewReader(p.declData[off:])
+ return r
+}
+
+func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
+func (r *importReader) posBase() *src.PosBase { return r.p.posBaseAt(r.uint64()) }
+func (r *importReader) pkg() *types.Pkg { return r.p.pkgAt(r.uint64()) }
+
+func (r *importReader) setPkg() {
+ r.currPkg = r.pkg()
+}
+
+func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
+ tag := r.byte()
+ pos := r.pos()
+
+ switch tag {
+ case 'A':
+ typ := r.typ()
+
+ return importalias(r.p.ipkg, pos, sym, typ)
+
+ case 'C':
+ typ := r.typ()
+ val := r.value(typ)
+
+ n := importconst(r.p.ipkg, pos, sym, typ, val)
+ r.constExt(n)
+ return n
+
+ case 'F':
+ typ := r.signature(nil)
+
+ n := importfunc(r.p.ipkg, pos, sym, typ)
+ r.funcExt(n)
+ return n
+
+ case 'T':
+ // Types can be recursive. We need to setup a stub
+ // declaration before recursing.
+ n := importtype(r.p.ipkg, pos, sym)
+ t := n.Type()
+
+ // We also need to defer width calculations until
+ // after the underlying type has been assigned.
+ types.DeferCheckSize()
+ underlying := r.typ()
+ t.SetUnderlying(underlying)
+ types.ResumeCheckSize()
+
+ if underlying.IsInterface() {
+ r.typeExt(t)
+ return n
+ }
+
+ ms := make([]*types.Field, r.uint64())
+ for i := range ms {
+ mpos := r.pos()
+ msym := r.selector()
+ recv := r.param()
+ mtyp := r.signature(recv)
+
+ // MethodSym already marked m.Sym as a function.
+ m := ir.NewNameAt(mpos, ir.MethodSym(recv.Type, msym))
+ m.Class = ir.PFUNC
+ m.SetType(mtyp)
+
+ m.Func = ir.NewFunc(mpos)
+ m.Func.Nname = m
+
+ f := types.NewField(mpos, msym, mtyp)
+ f.Nname = m
+ ms[i] = f
+ }
+ t.Methods().Set(ms)
+
+ r.typeExt(t)
+ for _, m := range ms {
+ r.methExt(m)
+ }
+ return n
+
+ case 'V':
+ typ := r.typ()
+
+ n := importvar(r.p.ipkg, pos, sym, typ)
+ r.varExt(n)
+ return n
+
+ default:
+ base.Fatalf("unexpected tag: %v", tag)
+ panic("unreachable")
+ }
+}
+
+func (p *importReader) value(typ *types.Type) constant.Value {
+ switch constTypeOf(typ) {
+ case constant.Bool:
+ return constant.MakeBool(p.bool())
+ case constant.String:
+ return constant.MakeString(p.string())
+ case constant.Int:
+ var i big.Int
+ p.mpint(&i, typ)
+ return constant.Make(&i)
+ case constant.Float:
+ return p.float(typ)
+ case constant.Complex:
+ return makeComplex(p.float(typ), p.float(typ))
+ }
+
+ base.Fatalf("unexpected value type: %v", typ)
+ panic("unreachable")
+}
+
+func (p *importReader) mpint(x *big.Int, typ *types.Type) {
+ signed, maxBytes := intSize(typ)
+
+ maxSmall := 256 - maxBytes
+ if signed {
+ maxSmall = 256 - 2*maxBytes
+ }
+ if maxBytes == 1 {
+ maxSmall = 256
+ }
+
+ n, _ := p.ReadByte()
+ if uint(n) < maxSmall {
+ v := int64(n)
+ if signed {
+ v >>= 1
+ if n&1 != 0 {
+ v = ^v
+ }
+ }
+ x.SetInt64(v)
+ return
+ }
+
+ v := -n
+ if signed {
+ v = -(n &^ 1) >> 1
+ }
+ if v < 1 || uint(v) > maxBytes {
+ base.Fatalf("weird decoding: %v, %v => %v", n, signed, v)
+ }
+ b := make([]byte, v)
+ p.Read(b)
+ x.SetBytes(b)
+ if signed && n&1 != 0 {
+ x.Neg(x)
+ }
+}
+
+func (p *importReader) float(typ *types.Type) constant.Value {
+ var mant big.Int
+ p.mpint(&mant, typ)
+ var f big.Float
+ f.SetInt(&mant)
+ if f.Sign() != 0 {
+ f.SetMantExp(&f, int(p.int64()))
+ }
+ return constant.Make(&f)
+}
+
+func (p *importReader) mprat(orig constant.Value) constant.Value {
+ if !p.bool() {
+ return orig
+ }
+ var rat big.Rat
+ rat.SetString(p.string())
+ return constant.Make(&rat)
+}
+
+func (r *importReader) ident(selector bool) *types.Sym {
+ name := r.string()
+ if name == "" {
+ return nil
+ }
+ pkg := r.currPkg
+ if selector && types.IsExported(name) {
+ pkg = types.LocalPkg
+ }
+ return pkg.Lookup(name)
+}
+
+func (r *importReader) localIdent() *types.Sym { return r.ident(false) }
+func (r *importReader) selector() *types.Sym { return r.ident(true) }
+
+func (r *importReader) qualifiedIdent() *ir.Ident {
+ name := r.string()
+ pkg := r.pkg()
+ sym := pkg.Lookup(name)
+ return ir.NewIdent(src.NoXPos, sym)
+}
+
+func (r *importReader) pos() src.XPos {
+ delta := r.int64()
+ r.prevColumn += delta >> 1
+ if delta&1 != 0 {
+ delta = r.int64()
+ r.prevLine += delta >> 1
+ if delta&1 != 0 {
+ r.prevBase = r.posBase()
+ }
+ }
+
+ if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
+ // TODO(mdempsky): Remove once we reliably write
+ // position information for all nodes.
+ return src.NoXPos
+ }
+
+ if r.prevBase == nil {
+ base.Fatalf("missing posbase")
+ }
+ pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn))
+ return base.Ctxt.PosTable.XPos(pos)
+}
+
+func (r *importReader) typ() *types.Type {
+ return r.p.typAt(r.uint64())
+}
+
+func (p *iimporter) typAt(off uint64) *types.Type {
+ t, ok := p.typCache[off]
+ if !ok {
+ if off < predeclReserved {
+ base.Fatalf("predeclared type missing from cache: %d", off)
+ }
+ t = p.newReader(off-predeclReserved, nil).typ1()
+ p.typCache[off] = t
+ }
+ return t
+}
+
+func (r *importReader) typ1() *types.Type {
+ switch k := r.kind(); k {
+ default:
+ base.Fatalf("unexpected kind tag in %q: %v", r.p.ipkg.Path, k)
+ return nil
+
+ case definedType:
+ // We might be called from within doInline, in which
+ // case Sym.Def can point to declared parameters
+ // instead of the top-level types. Also, we don't
+ // support inlining functions with local defined
+ // types. Therefore, this must be a package-scope
+ // type.
+ n := expandDecl(r.qualifiedIdent())
+ if n.Op() != ir.OTYPE {
+ base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
+ }
+ return n.Type()
+ case pointerType:
+ return types.NewPtr(r.typ())
+ case sliceType:
+ return types.NewSlice(r.typ())
+ case arrayType:
+ n := r.uint64()
+ return types.NewArray(r.typ(), int64(n))
+ case chanType:
+ dir := types.ChanDir(r.uint64())
+ return types.NewChan(r.typ(), dir)
+ case mapType:
+ return types.NewMap(r.typ(), r.typ())
+
+ case signatureType:
+ r.setPkg()
+ return r.signature(nil)
+
+ case structType:
+ r.setPkg()
+
+ fs := make([]*types.Field, r.uint64())
+ for i := range fs {
+ pos := r.pos()
+ sym := r.selector()
+ typ := r.typ()
+ emb := r.bool()
+ note := r.string()
+
+ f := types.NewField(pos, sym, typ)
+ if emb {
+ f.Embedded = 1
+ }
+ f.Note = note
+ fs[i] = f
+ }
+
+ return types.NewStruct(r.currPkg, fs)
+
+ case interfaceType:
+ r.setPkg()
+
+ embeddeds := make([]*types.Field, r.uint64())
+ for i := range embeddeds {
+ pos := r.pos()
+ typ := r.typ()
+
+ embeddeds[i] = types.NewField(pos, nil, typ)
+ }
+
+ methods := make([]*types.Field, r.uint64())
+ for i := range methods {
+ pos := r.pos()
+ sym := r.selector()
+ typ := r.signature(fakeRecvField())
+
+ methods[i] = types.NewField(pos, sym, typ)
+ }
+
+ t := types.NewInterface(r.currPkg, append(embeddeds, methods...))
+
+ // Ensure we expand the interface in the frontend (#25055).
+ types.CheckSize(t)
+ return t
+ }
+}
+
+func (r *importReader) kind() itag {
+ return itag(r.uint64())
+}
+
+func (r *importReader) signature(recv *types.Field) *types.Type {
+ params := r.paramList()
+ results := r.paramList()
+ if n := len(params); n > 0 {
+ params[n-1].SetIsDDD(r.bool())
+ }
+ return types.NewSignature(r.currPkg, recv, params, results)
+}
+
+func (r *importReader) paramList() []*types.Field {
+ fs := make([]*types.Field, r.uint64())
+ for i := range fs {
+ fs[i] = r.param()
+ }
+ return fs
+}
+
+func (r *importReader) param() *types.Field {
+ return types.NewField(r.pos(), r.localIdent(), r.typ())
+}
+
+func (r *importReader) bool() bool {
+ return r.uint64() != 0
+}
+
+func (r *importReader) int64() int64 {
+ n, err := binary.ReadVarint(r)
+ if err != nil {
+ base.Fatalf("readVarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) uint64() uint64 {
+ n, err := binary.ReadUvarint(r)
+ if err != nil {
+ base.Fatalf("readVarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) byte() byte {
+ x, err := r.ReadByte()
+ if err != nil {
+ base.Fatalf("declReader.ReadByte: %v", err)
+ }
+ return x
+}
+
+// Compiler-specific extensions.
+
+func (r *importReader) constExt(n *ir.Name) {
+ switch n.Type() {
+ case types.UntypedFloat:
+ n.SetVal(r.mprat(n.Val()))
+ case types.UntypedComplex:
+ v := n.Val()
+ re := r.mprat(constant.Real(v))
+ im := r.mprat(constant.Imag(v))
+ n.SetVal(makeComplex(re, im))
+ }
+}
+
+func (r *importReader) varExt(n *ir.Name) {
+ r.linkname(n.Sym())
+ r.symIdx(n.Sym())
+}
+
+func (r *importReader) funcExt(n *ir.Name) {
+ r.linkname(n.Sym())
+ r.symIdx(n.Sym())
+
+ // TODO remove after register abi is working
+ n.SetPragma(ir.PragmaFlag(r.uint64()))
+
+ // Escape analysis.
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(n.Type()).FieldSlice() {
+ f.Note = r.string()
+ }
+ }
+
+ // Inline body.
+ if u := r.uint64(); u > 0 {
+ n.Func.Inl = &ir.Inline{
+ Cost: int32(u - 1),
+ }
+ n.Func.Endlineno = r.pos()
+ }
+}
+
+func (r *importReader) methExt(m *types.Field) {
+ if r.bool() {
+ m.SetNointerface(true)
+ }
+ r.funcExt(m.Nname.(*ir.Name))
+}
+
+func (r *importReader) linkname(s *types.Sym) {
+ s.Linkname = r.string()
+}
+
+func (r *importReader) symIdx(s *types.Sym) {
+ lsym := s.Linksym()
+ idx := int32(r.int64())
+ if idx != -1 {
+ if s.Linkname != "" {
+ base.Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
+ }
+ lsym.SymIdx = idx
+ lsym.Set(obj.AttrIndexed, true)
+ }
+}
+
+func (r *importReader) typeExt(t *types.Type) {
+ t.SetNotInHeap(r.bool())
+ i, pi := r.int64(), r.int64()
+ if i != -1 && pi != -1 {
+ typeSymIdx[t] = [2]int64{i, pi}
+ }
+}
+
+// Map imported type T to the index of type descriptor symbols of T and *T,
+// so we can use index to reference the symbol.
+var typeSymIdx = make(map[*types.Type][2]int64)
+
+func BaseTypeIndex(t *types.Type) int64 {
+ tbase := t
+ if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
+ tbase = t.Elem()
+ }
+ i, ok := typeSymIdx[tbase]
+ if !ok {
+ return -1
+ }
+ if t != tbase {
+ return i[1]
+ }
+ return i[0]
+}
+
+func (r *importReader) doInline(fn *ir.Func) {
+ if len(fn.Inl.Body) != 0 {
+ base.Fatalf("%v already has inline body", fn)
+ }
+
+ //fmt.Printf("Importing %v\n", n)
+ r.funcBody(fn)
+
+ importlist = append(importlist, fn)
+
+ if base.Flag.E > 0 && base.Flag.LowerM > 2 {
+ if base.Flag.LowerM > 3 {
+ fmt.Printf("inl body for %v %v: %+v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
+ } else {
+ fmt.Printf("inl body for %v %v: %v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Inlined function bodies
+
+// Approach: Read nodes and use them to create/declare the same data structures
+// as done originally by the (hidden) parser by closely following the parser's
+// original code. In other words, "parsing" the import data (which happens to
+// be encoded in binary rather textual form) is the best way at the moment to
+// re-establish the syntax tree's invariants. At some future point we might be
+// able to avoid this round-about way and create the rewritten nodes directly,
+// possibly avoiding a lot of duplicate work (name resolution, type checking).
+//
+// Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their
+// 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.
+ fn.Inl.Dcl = r.readFuncDcls(fn)
+
+ // 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) readNames(fn *ir.Func) []*ir.Name {
+ 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
+ }
+ r.allDcls = append(r.allDcls, dcls...)
+ return dcls
+}
+
+func (r *importReader) readFuncDcls(fn *ir.Func) []*ir.Name {
+ dcls := r.readNames(fn)
+
+ // 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)
+ }
+ return dcls
+}
+
+func (r *importReader) localName() *ir.Name {
+ i := r.int64()
+ if i == -1 {
+ return ir.BlankNode.(*ir.Name)
+ }
+ if i < 0 {
+ return r.allClosureVars[-i-2]
+ }
+ return r.allDcls[i]
+}
+
+func (r *importReader) stmtList() []ir.Node {
+ var list []ir.Node
+ for {
+ n := r.node()
+ if n == nil {
+ break
+ }
+ // OBLOCK nodes are not written to the import data directly,
+ // but the handling of ODCL calls liststmt, which creates one.
+ // Inline them into the statement list.
+ if n.Op() == ir.OBLOCK {
+ n := n.(*ir.BlockStmt)
+ list = append(list, n.List...)
+ } else {
+ list = append(list, n)
+ }
+
+ }
+ return list
+}
+
+func (r *importReader) caseList(switchExpr ir.Node) []*ir.CaseClause {
+ namedTypeSwitch := isNamedTypeSwitch(switchExpr)
+
+ cases := make([]*ir.CaseClause, r.uint64())
+ for i := range cases {
+ cas := ir.NewCaseStmt(r.pos(), nil, nil)
+ cas.List = r.stmtList()
+ if namedTypeSwitch {
+ cas.Var = r.localName()
+ cas.Var.Defn = switchExpr
+ }
+ cas.Body = r.stmtList()
+ cases[i] = cas
+ }
+ return cases
+}
+
+func (r *importReader) commList() []*ir.CommClause {
+ cases := make([]*ir.CommClause, r.uint64())
+ for i := range cases {
+ cases[i] = ir.NewCommStmt(r.pos(), r.node(), r.stmtList())
+ }
+ return cases
+}
+
+func (r *importReader) exprList() []ir.Node {
+ var list []ir.Node
+ for {
+ n := r.expr()
+ if n == nil {
+ break
+ }
+ list = append(list, n)
+ }
+ return list
+}
+
+func (r *importReader) expr() ir.Node {
+ n := r.node()
+ if n != nil && n.Op() == ir.OBLOCK {
+ n := n.(*ir.BlockStmt)
+ base.Fatalf("unexpected block node: %v", n)
+ }
+ return n
+}
+
+// TODO(gri) split into expr and stmt
+func (r *importReader) node() ir.Node {
+ switch op := r.op(); op {
+ // expressions
+ // case OPAREN:
+ // unreachable - unpacked by exporter
+
+ case ir.ONIL:
+ pos := r.pos()
+ typ := r.typ()
+
+ n := ir.NewNilExpr(pos)
+ n.SetType(typ)
+ return n
+
+ case ir.OLITERAL:
+ pos := r.pos()
+ typ := r.typ()
+
+ n := ir.NewBasicLit(pos, r.value(typ))
+ n.SetType(typ)
+ return n
+
+ case ir.ONONAME:
+ return r.qualifiedIdent()
+
+ case ir.ONAME:
+ return r.localName()
+
+ // case OPACK, ONONAME:
+ // unreachable - should have been resolved by typechecking
+
+ case ir.OTYPE:
+ return ir.TypeNode(r.typ())
+
+ case ir.OTYPESW:
+ pos := r.pos()
+ var tag *ir.Ident
+ if s := r.localIdent(); s != nil {
+ tag = ir.NewIdent(pos, s)
+ }
+ return ir.NewTypeSwitchGuard(pos, tag, r.expr())
+
+ // case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
+ // unreachable - should have been resolved by typechecking
+
+ case ir.OCLOSURE:
+ //println("Importing CLOSURE")
+ pos := r.pos()
+ typ := r.signature(nil)
+
+ // All the remaining code below is similar to (*noder).funcLit(), but
+ // with Dcls and ClosureVars lists already set up
+ fn := ir.NewFunc(pos)
+ fn.SetIsHiddenClosure(true)
+ fn.Nname = ir.NewNameAt(pos, ir.BlankNode.Sym())
+ fn.Nname.Func = fn
+ fn.Nname.Ntype = ir.TypeNode(typ)
+ fn.Nname.Defn = fn
+ fn.Nname.SetType(typ)
+
+ cvars := make([]*ir.Name, r.int64())
+ for i := range cvars {
+ cvars[i] = ir.CaptureName(r.pos(), fn, r.localName().Canonical())
+ }
+ fn.ClosureVars = cvars
+ r.allClosureVars = append(r.allClosureVars, cvars...)
+
+ fn.Dcl = r.readFuncDcls(fn)
+ body := r.stmtList()
+ ir.FinishCaptureNames(pos, r.curfn, fn)
+
+ clo := ir.NewClosureExpr(pos, fn)
+ fn.OClosure = clo
+
+ fn.Body = body
+
+ return clo
+
+ // case OPTRLIT:
+ // unreachable - mapped to case OADDR below by exporter
+
+ case ir.OSTRUCTLIT:
+ return ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()), r.fieldList())
+
+ // case OARRAYLIT, OSLICELIT, OMAPLIT:
+ // unreachable - mapped to case OCOMPLIT below by exporter
+
+ case ir.OCOMPLIT:
+ return ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()), r.exprList())
+
+ case ir.OKEY:
+ return ir.NewKeyExpr(r.pos(), r.expr(), r.expr())
+
+ // case OSTRUCTKEY:
+ // unreachable - handled in case OSTRUCTLIT by elemList
+
+ // case OCALLPART:
+ // unreachable - mapped to case OXDOT below by exporter
+
+ // case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
+ // unreachable - mapped to case OXDOT below by exporter
+
+ case ir.OXDOT:
+ // see parser.new_dotname
+ return ir.NewSelectorExpr(r.pos(), ir.OXDOT, r.expr(), r.selector())
+
+ // case ODOTTYPE, ODOTTYPE2:
+ // unreachable - mapped to case ODOTTYPE below by exporter
+
+ case ir.ODOTTYPE:
+ n := ir.NewTypeAssertExpr(r.pos(), r.expr(), nil)
+ n.SetType(r.typ())
+ return n
+
+ // case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
+ // unreachable - mapped to cases below by exporter
+
+ case ir.OINDEX:
+ return ir.NewIndexExpr(r.pos(), r.expr(), r.expr())
+
+ case ir.OSLICE, ir.OSLICE3:
+ pos, x := r.pos(), r.expr()
+ low, high := r.exprsOrNil()
+ var max ir.Node
+ if op.IsSlice3() {
+ max = r.expr()
+ }
+ return ir.NewSliceExpr(pos, op, x, low, high, max)
+
+ // case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, ORUNES2STR, OSTR2BYTES, OSTR2RUNES, ORUNESTR:
+ // unreachable - mapped to OCONV case below by exporter
+
+ case ir.OCONV:
+ return ir.NewConvExpr(r.pos(), ir.OCONV, r.typ(), r.expr())
+
+ case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN:
+ n := builtinCall(r.pos(), op)
+ n.Args = r.exprList()
+ if op == ir.OAPPEND {
+ n.IsDDD = r.bool()
+ }
+ return n
+
+ // case OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
+ // unreachable - mapped to OCALL case below by exporter
+
+ case ir.OCALL:
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewCallExpr(pos, ir.OCALL, r.expr(), r.exprList())
+ *n.PtrInit() = init
+ n.IsDDD = r.bool()
+ return n
+
+ case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
+ n := builtinCall(r.pos(), ir.OMAKE)
+ n.Args.Append(ir.TypeNode(r.typ()))
+ n.Args.Append(r.exprList()...)
+ return n
+
+ // unary expressions
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
+ return ir.NewUnaryExpr(r.pos(), op, r.expr())
+
+ case ir.OADDR:
+ return NodAddrAt(r.pos(), r.expr())
+
+ case ir.ODEREF:
+ return ir.NewStarExpr(r.pos(), r.expr())
+
+ // binary expressions
+ case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
+ ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR:
+ return ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
+
+ case ir.OANDAND, ir.OOROR:
+ return ir.NewLogicalExpr(r.pos(), op, r.expr(), r.expr())
+
+ case ir.OSEND:
+ return ir.NewSendStmt(r.pos(), r.expr(), r.expr())
+
+ case ir.OADDSTR:
+ pos := r.pos()
+ list := r.exprList()
+ x := list[0]
+ for _, y := range list[1:] {
+ x = ir.NewBinaryExpr(pos, ir.OADD, x, y)
+ }
+ return x
+
+ // --------------------------------------------------------------------
+ // statements
+ case ir.ODCL:
+ var stmts ir.Nodes
+ 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
+
+ case ir.OAS:
+ return ir.NewAssignStmt(r.pos(), r.expr(), r.expr())
+
+ case ir.OASOP:
+ n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil)
+ if !r.bool() {
+ n.Y = ir.NewInt(1)
+ n.IncDec = true
+ } else {
+ n.Y = r.expr()
+ }
+ return n
+
+ // case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+ // unreachable - mapped to OAS2 case below by exporter
+
+ case ir.OAS2:
+ return ir.NewAssignListStmt(r.pos(), ir.OAS2, r.exprList(), r.exprList())
+
+ case ir.ORETURN:
+ return ir.NewReturnStmt(r.pos(), r.exprList())
+
+ // case ORETJMP:
+ // unreachable - generated by compiler for trampolin routines (not exported)
+
+ case ir.OGO, ir.ODEFER:
+ return ir.NewGoDeferStmt(r.pos(), op, r.expr())
+
+ case ir.OIF:
+ pos, init := r.pos(), r.stmtList()
+ n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList())
+ *n.PtrInit() = init
+ return n
+
+ case ir.OFOR:
+ pos, init := r.pos(), r.stmtList()
+ cond, post := r.exprsOrNil()
+ n := ir.NewForStmt(pos, nil, cond, post, r.stmtList())
+ *n.PtrInit() = init
+ return n
+
+ case ir.ORANGE:
+ pos := r.pos()
+ k, v := r.exprsOrNil()
+ return ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
+
+ case ir.OSELECT:
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewSelectStmt(pos, r.commList())
+ *n.PtrInit() = init
+ return n
+
+ case ir.OSWITCH:
+ pos := r.pos()
+ init := r.stmtList()
+ x, _ := r.exprsOrNil()
+ n := ir.NewSwitchStmt(pos, x, r.caseList(x))
+ *n.PtrInit() = init
+ return n
+
+ // case OCASE:
+ // handled by caseList
+
+ case ir.OFALL:
+ return ir.NewBranchStmt(r.pos(), ir.OFALL, nil)
+
+ // case OEMPTY:
+ // unreachable - not emitted by exporter
+
+ case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
+ pos := r.pos()
+ var sym *types.Sym
+ if label := r.string(); label != "" {
+ sym = Lookup(label)
+ }
+ return ir.NewBranchStmt(pos, op, sym)
+
+ case ir.OLABEL:
+ return ir.NewLabelStmt(r.pos(), Lookup(r.string()))
+
+ case ir.OEND:
+ return nil
+
+ default:
+ base.Fatalf("cannot import %v (%d) node\n"+
+ "\t==> please file an issue and assign to gri@", op, int(op))
+ panic("unreachable") // satisfy compiler
+ }
+}
+
+func (r *importReader) op() ir.Op {
+ return ir.Op(r.uint64())
+}
+
+func (r *importReader) fieldList() []ir.Node {
+ list := make([]ir.Node, r.uint64())
+ for i := range list {
+ list[i] = ir.NewStructKeyExpr(r.pos(), r.selector(), r.expr())
+ }
+ return list
+}
+
+func (r *importReader) exprsOrNil() (a, b ir.Node) {
+ ab := r.uint64()
+ if ab&1 != 0 {
+ a = r.expr()
+ }
+ if ab&2 != 0 {
+ b = r.node()
+ }
+ return
+}
+
+func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr {
+ return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
+}
diff --git a/src/cmd/compile/internal/gc/mapfile_mmap.go b/src/cmd/compile/internal/typecheck/mapfile_mmap.go
similarity index 98%
rename from src/cmd/compile/internal/gc/mapfile_mmap.go
rename to src/cmd/compile/internal/typecheck/mapfile_mmap.go
index 9483688d68034..2f3aa16decfaa 100644
--- a/src/cmd/compile/internal/gc/mapfile_mmap.go
+++ b/src/cmd/compile/internal/typecheck/mapfile_mmap.go
@@ -4,7 +4,7 @@
// +build darwin dragonfly freebsd linux netbsd openbsd
-package gc
+package typecheck
import (
"os"
diff --git a/src/cmd/compile/internal/gc/mapfile_read.go b/src/cmd/compile/internal/typecheck/mapfile_read.go
similarity index 96%
rename from src/cmd/compile/internal/gc/mapfile_read.go
rename to src/cmd/compile/internal/typecheck/mapfile_read.go
index c6f68ed5df7a7..4059f261d4932 100644
--- a/src/cmd/compile/internal/gc/mapfile_read.go
+++ b/src/cmd/compile/internal/typecheck/mapfile_read.go
@@ -4,7 +4,7 @@
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
-package gc
+package typecheck
import (
"io"
diff --git a/src/cmd/compile/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/typecheck/mkbuiltin.go
similarity index 83%
rename from src/cmd/compile/internal/gc/mkbuiltin.go
rename to src/cmd/compile/internal/typecheck/mkbuiltin.go
index 63d2a12c079d6..07f4b767e8f8d 100644
--- a/src/cmd/compile/internal/gc/mkbuiltin.go
+++ b/src/cmd/compile/internal/typecheck/mkbuiltin.go
@@ -33,9 +33,12 @@ func main() {
var b bytes.Buffer
fmt.Fprintln(&b, "// Code generated by mkbuiltin.go. DO NOT EDIT.")
fmt.Fprintln(&b)
- fmt.Fprintln(&b, "package gc")
+ fmt.Fprintln(&b, "package typecheck")
fmt.Fprintln(&b)
- fmt.Fprintln(&b, `import "cmd/compile/internal/types"`)
+ fmt.Fprintln(&b, `import (`)
+ fmt.Fprintln(&b, ` "cmd/compile/internal/types"`)
+ fmt.Fprintln(&b, ` "cmd/internal/src"`)
+ fmt.Fprintln(&b, `)`)
mkbuiltin(&b, "runtime")
@@ -140,16 +143,16 @@ func (i *typeInterner) mktype(t ast.Expr) string {
case *ast.Ident:
switch t.Name {
case "byte":
- return "types.Bytetype"
+ return "types.ByteType"
case "rune":
- return "types.Runetype"
+ return "types.RuneType"
}
- return fmt.Sprintf("types.Types[T%s]", strings.ToUpper(t.Name))
+ return fmt.Sprintf("types.Types[types.T%s]", strings.ToUpper(t.Name))
case *ast.SelectorExpr:
if t.X.(*ast.Ident).Name != "unsafe" || t.Sel.Name != "Pointer" {
log.Fatalf("unhandled type: %#v", t)
}
- return "types.Types[TUNSAFEPTR]"
+ return "types.Types[types.TUNSAFEPTR]"
case *ast.ArrayType:
if t.Len == nil {
@@ -166,18 +169,18 @@ func (i *typeInterner) mktype(t ast.Expr) string {
}
return fmt.Sprintf("types.NewChan(%s, %s)", i.subtype(t.Value), dir)
case *ast.FuncType:
- return fmt.Sprintf("functype(nil, %s, %s)", i.fields(t.Params, false), i.fields(t.Results, false))
+ return fmt.Sprintf("types.NewSignature(types.NoPkg, nil, %s, %s)", i.fields(t.Params, false), i.fields(t.Results, false))
case *ast.InterfaceType:
if len(t.Methods.List) != 0 {
log.Fatal("non-empty interfaces unsupported")
}
- return "types.Types[TINTER]"
+ return "types.Types[types.TINTER]"
case *ast.MapType:
return fmt.Sprintf("types.NewMap(%s, %s)", i.subtype(t.Key), i.subtype(t.Value))
case *ast.StarExpr:
return fmt.Sprintf("types.NewPtr(%s)", i.subtype(t.X))
case *ast.StructType:
- return fmt.Sprintf("tostruct(%s)", i.fields(t.Fields, true))
+ return fmt.Sprintf("types.NewStruct(types.NoPkg, %s)", i.fields(t.Fields, true))
default:
log.Fatalf("unhandled type: %#v", t)
@@ -193,18 +196,18 @@ func (i *typeInterner) fields(fl *ast.FieldList, keepNames bool) string {
for _, f := range fl.List {
typ := i.subtype(f.Type)
if len(f.Names) == 0 {
- res = append(res, fmt.Sprintf("anonfield(%s)", typ))
+ res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, nil, %s)", typ))
} else {
for _, name := range f.Names {
if keepNames {
- res = append(res, fmt.Sprintf("namedfield(%q, %s)", name.Name, typ))
+ res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, Lookup(%q), %s)", name.Name, typ))
} else {
- res = append(res, fmt.Sprintf("anonfield(%s)", typ))
+ res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, nil, %s)", typ))
}
}
}
}
- return fmt.Sprintf("[]*Node{%s}", strings.Join(res, ", "))
+ return fmt.Sprintf("[]*types.Field{%s}", strings.Join(res, ", "))
}
func intconst(e ast.Expr) int64 {
diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go
new file mode 100644
index 0000000000000..14ed175be9c97
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/stmt.go
@@ -0,0 +1,669 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+func RangeExprType(t *types.Type) *types.Type {
+ if t.IsPtr() && t.Elem().IsArray() {
+ return t.Elem()
+ }
+ return t
+}
+
+func typecheckrangeExpr(n *ir.RangeStmt) {
+ n.X = Expr(n.X)
+ if n.X.Type() == nil {
+ return
+ }
+
+ t := RangeExprType(n.X.Type())
+ // delicate little dance. see tcAssignList
+ if n.Key != nil && !ir.DeclaredBy(n.Key, n) {
+ n.Key = AssignExpr(n.Key)
+ }
+ if n.Value != nil && !ir.DeclaredBy(n.Value, n) {
+ n.Value = AssignExpr(n.Value)
+ }
+
+ var tk, tv *types.Type
+ toomany := false
+ switch t.Kind() {
+ default:
+ base.ErrorfAt(n.Pos(), "cannot range over %L", n.X)
+ return
+
+ case types.TARRAY, types.TSLICE:
+ tk = types.Types[types.TINT]
+ tv = t.Elem()
+
+ case types.TMAP:
+ tk = t.Key()
+ tv = t.Elem()
+
+ case types.TCHAN:
+ if !t.ChanDir().CanRecv() {
+ base.ErrorfAt(n.Pos(), "invalid operation: range %v (receive from send-only type %v)", n.X, n.X.Type())
+ return
+ }
+
+ tk = t.Elem()
+ tv = nil
+ if n.Value != nil {
+ toomany = true
+ }
+
+ case types.TSTRING:
+ tk = types.Types[types.TINT]
+ tv = types.RuneType
+ }
+
+ if toomany {
+ base.ErrorfAt(n.Pos(), "too many variables in range")
+ }
+
+ do := func(nn ir.Node, t *types.Type) {
+ if nn != nil {
+ if ir.DeclaredBy(nn, n) {
+ nn.SetType(t)
+ } else if nn.Type() != nil {
+ if op, why := assignop(t, nn.Type()); op == ir.OXXX {
+ base.ErrorfAt(n.Pos(), "cannot assign type %v to %L in range%s", t, nn, why)
+ }
+ }
+ checkassign(n, nn)
+ }
+ }
+ do(n.Key, tk)
+ do(n.Value, tv)
+}
+
+// type check assignment.
+// if this assignment is the definition of a var on the left side,
+// fill in the var's type.
+func tcAssign(n *ir.AssignStmt) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("tcAssign", n)(nil)
+ }
+
+ if n.Y == nil {
+ n.X = AssignExpr(n.X)
+ return
+ }
+
+ lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
+ assign(n, lhs, rhs)
+ n.X, n.Y = lhs[0], rhs[0]
+
+ // TODO(mdempsky): This seems out of place.
+ if !ir.IsBlank(n.X) {
+ types.CheckSize(n.X.Type()) // ensure width is calculated for backend
+ }
+}
+
+func tcAssignList(n *ir.AssignListStmt) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("tcAssignList", n)(nil)
+ }
+
+ assign(n, n.Lhs, n.Rhs)
+}
+
+func assign(stmt ir.Node, lhs, rhs []ir.Node) {
+ // delicate little dance.
+ // the definition of lhs may refer to this assignment
+ // as its definition, in which case it will call tcAssign.
+ // in that case, do not call typecheck back, or it will cycle.
+ // if the variable has a type (ntype) then typechecking
+ // will not look at defn, so it is okay (and desirable,
+ // so that the conversion below happens).
+
+ checkLHS := func(i int, typ *types.Type) {
+ lhs[i] = Resolve(lhs[i])
+ if n := lhs[i]; typ != nil && ir.DeclaredBy(n, stmt) && n.Name().Ntype == nil {
+ if typ.Kind() != types.TNIL {
+ n.SetType(defaultType(typ))
+ } else {
+ base.Errorf("use of untyped nil")
+ }
+ }
+ if lhs[i].Typecheck() == 0 {
+ lhs[i] = AssignExpr(lhs[i])
+ }
+ checkassign(stmt, lhs[i])
+ }
+
+ assignType := func(i int, typ *types.Type) {
+ checkLHS(i, typ)
+ if typ != nil {
+ checkassignto(typ, lhs[i])
+ }
+ }
+
+ cr := len(rhs)
+ if len(rhs) == 1 {
+ rhs[0] = typecheck(rhs[0], ctxExpr|ctxMultiOK)
+ if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() {
+ cr = rtyp.NumFields()
+ }
+ } else {
+ Exprs(rhs)
+ }
+
+ // x, ok = y
+assignOK:
+ for len(lhs) == 2 && cr == 1 {
+ stmt := stmt.(*ir.AssignListStmt)
+ r := rhs[0]
+
+ switch r.Op() {
+ case ir.OINDEXMAP:
+ stmt.SetOp(ir.OAS2MAPR)
+ case ir.ORECV:
+ stmt.SetOp(ir.OAS2RECV)
+ case ir.ODOTTYPE:
+ r := r.(*ir.TypeAssertExpr)
+ stmt.SetOp(ir.OAS2DOTTYPE)
+ r.SetOp(ir.ODOTTYPE2)
+ default:
+ break assignOK
+ }
+
+ assignType(0, r.Type())
+ assignType(1, types.UntypedBool)
+ return
+ }
+
+ if len(lhs) != cr {
+ if r, ok := rhs[0].(*ir.CallExpr); ok && len(rhs) == 1 {
+ if r.Type() != nil {
+ base.ErrorfAt(stmt.Pos(), "assignment mismatch: %d variable%s but %v returns %d value%s", len(lhs), plural(len(lhs)), r.X, cr, plural(cr))
+ }
+ } else {
+ base.ErrorfAt(stmt.Pos(), "assignment mismatch: %d variable%s but %v value%s", len(lhs), plural(len(lhs)), len(rhs), plural(len(rhs)))
+ }
+
+ for i := range lhs {
+ checkLHS(i, nil)
+ }
+ return
+ }
+
+ // x,y,z = f()
+ if cr > len(rhs) {
+ stmt := stmt.(*ir.AssignListStmt)
+ stmt.SetOp(ir.OAS2FUNC)
+ r := rhs[0].(*ir.CallExpr)
+ r.Use = ir.CallUseList
+ rtyp := r.Type()
+
+ for i := range lhs {
+ assignType(i, rtyp.Field(i).Type)
+ }
+ return
+ }
+
+ for i, r := range rhs {
+ checkLHS(i, r.Type())
+ if lhs[i].Type() != nil {
+ rhs[i] = AssignConv(r, lhs[i].Type(), "assignment")
+ }
+ }
+}
+
+func plural(n int) string {
+ if n == 1 {
+ return ""
+ }
+ return "s"
+}
+
+// tcFor typechecks an OFOR node.
+func tcFor(n *ir.ForStmt) ir.Node {
+ Stmts(n.Init())
+ n.Cond = Expr(n.Cond)
+ n.Cond = DefaultLit(n.Cond, nil)
+ if n.Cond != nil {
+ t := n.Cond.Type()
+ if t != nil && !t.IsBoolean() {
+ base.Errorf("non-bool %L used as for condition", n.Cond)
+ }
+ }
+ n.Post = Stmt(n.Post)
+ if n.Op() == ir.OFORUNTIL {
+ Stmts(n.Late)
+ }
+ Stmts(n.Body)
+ return n
+}
+
+func tcGoDefer(n *ir.GoDeferStmt) {
+ what := "defer"
+ if n.Op() == ir.OGO {
+ what = "go"
+ }
+
+ switch n.Call.Op() {
+ // ok
+ case ir.OCALLINTER,
+ ir.OCALLMETH,
+ ir.OCALLFUNC,
+ ir.OCLOSE,
+ ir.OCOPY,
+ ir.ODELETE,
+ ir.OPANIC,
+ ir.OPRINT,
+ ir.OPRINTN,
+ ir.ORECOVER:
+ return
+
+ case ir.OAPPEND,
+ ir.OCAP,
+ ir.OCOMPLEX,
+ ir.OIMAG,
+ ir.OLEN,
+ ir.OMAKE,
+ ir.OMAKESLICE,
+ ir.OMAKECHAN,
+ ir.OMAKEMAP,
+ ir.ONEW,
+ ir.OREAL,
+ ir.OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof
+ if orig := ir.Orig(n.Call); orig.Op() == ir.OCONV {
+ break
+ }
+ base.ErrorfAt(n.Pos(), "%s discards result of %v", what, n.Call)
+ return
+ }
+
+ // type is broken or missing, most likely a method call on a broken type
+ // we will warn about the broken type elsewhere. no need to emit a potentially confusing error
+ if n.Call.Type() == nil || n.Call.Type().Broke() {
+ return
+ }
+
+ if !n.Diag() {
+ // The syntax made sure it was a call, so this must be
+ // a conversion.
+ n.SetDiag(true)
+ base.ErrorfAt(n.Pos(), "%s requires function call, not conversion", what)
+ }
+}
+
+// tcIf typechecks an OIF node.
+func tcIf(n *ir.IfStmt) ir.Node {
+ Stmts(n.Init())
+ n.Cond = Expr(n.Cond)
+ n.Cond = DefaultLit(n.Cond, nil)
+ if n.Cond != nil {
+ t := n.Cond.Type()
+ if t != nil && !t.IsBoolean() {
+ base.Errorf("non-bool %L used as if condition", n.Cond)
+ }
+ }
+ Stmts(n.Body)
+ Stmts(n.Else)
+ return n
+}
+
+// range
+func tcRange(n *ir.RangeStmt) {
+ // Typechecking order is important here:
+ // 0. first typecheck range expression (slice/map/chan),
+ // it is evaluated only once and so logically it is not part of the loop.
+ // 1. typecheck produced values,
+ // this part can declare new vars and so it must be typechecked before body,
+ // because body can contain a closure that captures the vars.
+ // 2. decldepth++ to denote loop body.
+ // 3. typecheck body.
+ // 4. decldepth--.
+ typecheckrangeExpr(n)
+
+ // second half of dance, the first half being typecheckrangeExpr
+ n.SetTypecheck(1)
+ if n.Key != nil && n.Key.Typecheck() == 0 {
+ n.Key = AssignExpr(n.Key)
+ }
+ if n.Value != nil && n.Value.Typecheck() == 0 {
+ n.Value = AssignExpr(n.Value)
+ }
+
+ Stmts(n.Body)
+}
+
+// tcReturn typechecks an ORETURN node.
+func tcReturn(n *ir.ReturnStmt) ir.Node {
+ typecheckargs(n)
+ if ir.CurFunc == nil {
+ base.Errorf("return outside function")
+ n.SetType(nil)
+ return n
+ }
+
+ if ir.HasNamedResults(ir.CurFunc) && len(n.Results) == 0 {
+ return n
+ }
+ typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, func() string { return "return argument" })
+ return n
+}
+
+// select
+func tcSelect(sel *ir.SelectStmt) {
+ var def *ir.CommClause
+ lno := ir.SetPos(sel)
+ Stmts(sel.Init())
+ for _, ncase := range sel.Cases {
+ if ncase.Comm == nil {
+ // default
+ if def != nil {
+ base.ErrorfAt(ncase.Pos(), "multiple defaults in select (first at %v)", ir.Line(def))
+ } else {
+ def = ncase
+ }
+ } else {
+ n := Stmt(ncase.Comm)
+ ncase.Comm = n
+ oselrecv2 := func(dst, recv ir.Node, def bool) {
+ n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
+ n.Def = def
+ n.SetTypecheck(1)
+ ncase.Comm = n
+ }
+ switch n.Op() {
+ default:
+ pos := n.Pos()
+ if n.Op() == ir.ONAME {
+ // We don't have the right position for ONAME nodes (see #15459 and
+ // others). Using ncase.Pos for now as it will provide the correct
+ // line number (assuming the expression follows the "case" keyword
+ // on the same line). This matches the approach before 1.10.
+ pos = ncase.Pos()
+ }
+ base.ErrorfAt(pos, "select case must be receive, send or assign recv")
+
+ case ir.OAS:
+ // convert x = <-c into x, _ = <-c
+ // remove implicit conversions; the eventual assignment
+ // will reintroduce them.
+ n := n.(*ir.AssignStmt)
+ if r := n.Y; r.Op() == ir.OCONVNOP || r.Op() == ir.OCONVIFACE {
+ r := r.(*ir.ConvExpr)
+ if r.Implicit() {
+ n.Y = r.X
+ }
+ }
+ if n.Y.Op() != ir.ORECV {
+ base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side")
+ break
+ }
+ oselrecv2(n.X, n.Y, n.Def)
+
+ case ir.OAS2RECV:
+ n := n.(*ir.AssignListStmt)
+ if n.Rhs[0].Op() != ir.ORECV {
+ base.ErrorfAt(n.Pos(), "select assignment must have receive on right hand side")
+ break
+ }
+ n.SetOp(ir.OSELRECV2)
+
+ case ir.ORECV:
+ // convert <-c into _, _ = <-c
+ n := n.(*ir.UnaryExpr)
+ oselrecv2(ir.BlankNode, n, false)
+
+ case ir.OSEND:
+ break
+ }
+ }
+
+ Stmts(ncase.Body)
+ }
+
+ base.Pos = lno
+}
+
+// tcSend typechecks an OSEND node.
+func tcSend(n *ir.SendStmt) ir.Node {
+ n.Chan = Expr(n.Chan)
+ n.Value = Expr(n.Value)
+ n.Chan = DefaultLit(n.Chan, nil)
+ t := n.Chan.Type()
+ if t == nil {
+ return n
+ }
+ if !t.IsChan() {
+ base.Errorf("invalid operation: %v (send to non-chan type %v)", n, t)
+ return n
+ }
+
+ if !t.ChanDir().CanSend() {
+ base.Errorf("invalid operation: %v (send to receive-only type %v)", n, t)
+ return n
+ }
+
+ n.Value = AssignConv(n.Value, t.Elem(), "send")
+ if n.Value.Type() == nil {
+ return n
+ }
+ return n
+}
+
+// tcSwitch typechecks a switch statement.
+func tcSwitch(n *ir.SwitchStmt) {
+ Stmts(n.Init())
+ if n.Tag != nil && n.Tag.Op() == ir.OTYPESW {
+ tcSwitchType(n)
+ } else {
+ tcSwitchExpr(n)
+ }
+}
+
+func tcSwitchExpr(n *ir.SwitchStmt) {
+ t := types.Types[types.TBOOL]
+ if n.Tag != nil {
+ n.Tag = Expr(n.Tag)
+ n.Tag = DefaultLit(n.Tag, nil)
+ t = n.Tag.Type()
+ }
+
+ var nilonly string
+ if t != nil {
+ switch {
+ case t.IsMap():
+ nilonly = "map"
+ case t.Kind() == types.TFUNC:
+ nilonly = "func"
+ case t.IsSlice():
+ nilonly = "slice"
+
+ case !types.IsComparable(t):
+ if t.IsStruct() {
+ base.ErrorfAt(n.Pos(), "cannot switch on %L (struct containing %v cannot be compared)", n.Tag, types.IncomparableField(t).Type)
+ } else {
+ base.ErrorfAt(n.Pos(), "cannot switch on %L", n.Tag)
+ }
+ t = nil
+ }
+ }
+
+ var defCase ir.Node
+ var cs constSet
+ for _, ncase := range n.Cases {
+ ls := ncase.List
+ if len(ls) == 0 { // default:
+ if defCase != nil {
+ base.ErrorfAt(ncase.Pos(), "multiple defaults in switch (first at %v)", ir.Line(defCase))
+ } else {
+ defCase = ncase
+ }
+ }
+
+ for i := range ls {
+ ir.SetPos(ncase)
+ ls[i] = Expr(ls[i])
+ ls[i] = DefaultLit(ls[i], t)
+ n1 := ls[i]
+ if t == nil || n1.Type() == nil {
+ continue
+ }
+
+ if nilonly != "" && !ir.IsNil(n1) {
+ base.ErrorfAt(ncase.Pos(), "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Tag)
+ } else if t.IsInterface() && !n1.Type().IsInterface() && !types.IsComparable(n1.Type()) {
+ base.ErrorfAt(ncase.Pos(), "invalid case %L in switch (incomparable type)", n1)
+ } else {
+ op1, _ := assignop(n1.Type(), t)
+ op2, _ := assignop(t, n1.Type())
+ if op1 == ir.OXXX && op2 == ir.OXXX {
+ if n.Tag != nil {
+ base.ErrorfAt(ncase.Pos(), "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Tag, n1.Type(), t)
+ } else {
+ base.ErrorfAt(ncase.Pos(), "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type())
+ }
+ }
+ }
+
+ // Don't check for duplicate bools. Although the spec allows it,
+ // (1) the compiler hasn't checked it in the past, so compatibility mandates it, and
+ // (2) it would disallow useful things like
+ // case GOARCH == "arm" && GOARM == "5":
+ // case GOARCH == "arm":
+ // which would both evaluate to false for non-ARM compiles.
+ if !n1.Type().IsBoolean() {
+ cs.add(ncase.Pos(), n1, "case", "switch")
+ }
+ }
+
+ Stmts(ncase.Body)
+ }
+}
+
+func tcSwitchType(n *ir.SwitchStmt) {
+ guard := n.Tag.(*ir.TypeSwitchGuard)
+ guard.X = Expr(guard.X)
+ t := guard.X.Type()
+ if t != nil && !t.IsInterface() {
+ base.ErrorfAt(n.Pos(), "cannot type switch on non-interface value %L", guard.X)
+ t = nil
+ }
+
+ // We don't actually declare the type switch's guarded
+ // declaration itself. So if there are no cases, we won't
+ // notice that it went unused.
+ if v := guard.Tag; v != nil && !ir.IsBlank(v) && len(n.Cases) == 0 {
+ base.ErrorfAt(v.Pos(), "%v declared but not used", v.Sym())
+ }
+
+ var defCase, nilCase ir.Node
+ var ts typeSet
+ for _, ncase := range n.Cases {
+ ls := ncase.List
+ if len(ls) == 0 { // default:
+ if defCase != nil {
+ base.ErrorfAt(ncase.Pos(), "multiple defaults in switch (first at %v)", ir.Line(defCase))
+ } else {
+ defCase = ncase
+ }
+ }
+
+ for i := range ls {
+ ls[i] = typecheck(ls[i], ctxExpr|ctxType)
+ n1 := ls[i]
+ if t == nil || n1.Type() == nil {
+ continue
+ }
+
+ var missing, have *types.Field
+ var ptr int
+ if ir.IsNil(n1) { // case nil:
+ if nilCase != nil {
+ base.ErrorfAt(ncase.Pos(), "multiple nil cases in type switch (first at %v)", ir.Line(nilCase))
+ } else {
+ nilCase = ncase
+ }
+ continue
+ }
+ if n1.Op() != ir.OTYPE {
+ base.ErrorfAt(ncase.Pos(), "%L is not a type", n1)
+ continue
+ }
+ if !n1.Type().IsInterface() && !implements(n1.Type(), t, &missing, &have, &ptr) && !missing.Broke() {
+ if have != nil && !have.Broke() {
+ base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
+ " (wrong type for %v method)\n\thave %v%S\n\twant %v%S", guard.X, n1.Type(), missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
+ } else if ptr != 0 {
+ base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
+ " (%v method has pointer receiver)", guard.X, n1.Type(), missing.Sym)
+ } else {
+ base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+
+ " (missing %v method)", guard.X, n1.Type(), missing.Sym)
+ }
+ continue
+ }
+
+ ts.add(ncase.Pos(), n1.Type())
+ }
+
+ if ncase.Var != nil {
+ // Assign the clause variable's type.
+ vt := t
+ if len(ls) == 1 {
+ if ls[0].Op() == ir.OTYPE {
+ vt = ls[0].Type()
+ } else if !ir.IsNil(ls[0]) {
+ // Invalid single-type case;
+ // mark variable as broken.
+ vt = nil
+ }
+ }
+
+ nvar := ncase.Var
+ nvar.SetType(vt)
+ if vt != nil {
+ nvar = AssignExpr(nvar).(*ir.Name)
+ } else {
+ // Clause variable is broken; prevent typechecking.
+ nvar.SetTypecheck(1)
+ nvar.SetWalkdef(1)
+ }
+ ncase.Var = nvar
+ }
+
+ Stmts(ncase.Body)
+ }
+}
+
+type typeSet struct {
+ m map[string][]typeSetEntry
+}
+
+type typeSetEntry struct {
+ pos src.XPos
+ typ *types.Type
+}
+
+func (s *typeSet) add(pos src.XPos, typ *types.Type) {
+ if s.m == nil {
+ s.m = make(map[string][]typeSetEntry)
+ }
+
+ // LongString does not uniquely identify types, so we need to
+ // disambiguate collisions with types.Identical.
+ // TODO(mdempsky): Add a method that *is* unique.
+ ls := typ.LongString()
+ prevs := s.m[ls]
+ for _, prev := range prevs {
+ if types.Identical(typ, prev.typ) {
+ base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev.pos))
+ return
+ }
+ }
+ s.m[ls] = append(prevs, typeSetEntry{pos, typ})
+}
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
new file mode 100644
index 0000000000000..b88a9f22839e7
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -0,0 +1,843 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+func AssignConv(n ir.Node, t *types.Type, context string) ir.Node {
+ return assignconvfn(n, t, func() string { return context })
+}
+
+// DotImportRefs maps idents introduced by importDot back to the
+// ir.PkgName they were dot-imported through.
+var DotImportRefs map[*ir.Ident]*ir.PkgName
+
+// LookupNum looks up the symbol starting with prefix and ending with
+// the decimal n. If prefix is too long, LookupNum panics.
+func LookupNum(prefix string, n int) *types.Sym {
+ var buf [20]byte // plenty long enough for all current users
+ copy(buf[:], prefix)
+ b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10)
+ return types.LocalPkg.LookupBytes(b)
+}
+
+// Given funarg struct list, return list of fn args.
+func NewFuncParams(tl *types.Type, mustname bool) []*ir.Field {
+ var args []*ir.Field
+ gen := 0
+ for _, t := range tl.Fields().Slice() {
+ s := t.Sym
+ if mustname && (s == nil || s.Name == "_") {
+ // invent a name so that we can refer to it in the trampoline
+ s = LookupNum(".anon", gen)
+ gen++
+ } else if s != nil && s.Pkg != types.LocalPkg {
+ // TODO(mdempsky): Preserve original position, name, and package.
+ s = Lookup(s.Name)
+ }
+ a := ir.NewField(base.Pos, s, nil, t.Type)
+ a.Pos = t.Pos
+ a.IsDDD = t.IsDDD()
+ args = append(args, a)
+ }
+
+ return args
+}
+
+// newname returns a new ONAME Node associated with symbol s.
+func NewName(s *types.Sym) *ir.Name {
+ n := ir.NewNameAt(base.Pos, s)
+ n.Curfn = ir.CurFunc
+ return n
+}
+
+// NodAddr returns a node representing &n at base.Pos.
+func NodAddr(n ir.Node) *ir.AddrExpr {
+ return NodAddrAt(base.Pos, n)
+}
+
+// nodAddrPos returns a node representing &n at position pos.
+func NodAddrAt(pos src.XPos, n ir.Node) *ir.AddrExpr {
+ n = markAddrOf(n)
+ return ir.NewAddrExpr(pos, n)
+}
+
+func markAddrOf(n ir.Node) ir.Node {
+ if IncrementalAddrtaken {
+ // We can only do incremental addrtaken computation when it is ok
+ // to typecheck the argument of the OADDR. That's only safe after the
+ // main typecheck has completed.
+ // The argument to OADDR needs to be typechecked because &x[i] takes
+ // the address of x if x is an array, but not if x is a slice.
+ // Note: OuterValue doesn't work correctly until n is typechecked.
+ n = typecheck(n, ctxExpr)
+ if x := ir.OuterValue(n); x.Op() == ir.ONAME {
+ x.Name().SetAddrtaken(true)
+ }
+ } else {
+ // Remember that we built an OADDR without computing the Addrtaken bit for
+ // its argument. We'll do that later in bulk using computeAddrtaken.
+ DirtyAddrtaken = true
+ }
+ return n
+}
+
+// If IncrementalAddrtaken is false, we do not compute Addrtaken for an OADDR Node
+// when it is built. The Addrtaken bits are set in bulk by computeAddrtaken.
+// If IncrementalAddrtaken is true, then when an OADDR Node is built the Addrtaken
+// field of its argument is updated immediately.
+var IncrementalAddrtaken = false
+
+// If DirtyAddrtaken is true, then there are OADDR whose corresponding arguments
+// have not yet been marked as Addrtaken.
+var DirtyAddrtaken = false
+
+func ComputeAddrtaken(top []ir.Node) {
+ for _, n := range top {
+ ir.Visit(n, func(n ir.Node) {
+ if n.Op() == ir.OADDR {
+ if x := ir.OuterValue(n.(*ir.AddrExpr).X); x.Op() == ir.ONAME {
+ x.Name().SetAddrtaken(true)
+ if x.Name().IsClosureVar() {
+ // Mark the original variable as Addrtaken so that capturevars
+ // knows not to pass it by value.
+ x.Name().Defn.Name().SetAddrtaken(true)
+ }
+ }
+ }
+ })
+ }
+}
+
+func NodNil() ir.Node {
+ n := ir.NewNilExpr(base.Pos)
+ n.SetType(types.Types[types.TNIL])
+ return n
+}
+
+// AddImplicitDots finds missing fields in obj.field that
+// will give the shortest unique addressing and
+// modifies the tree with missing field names.
+func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr {
+ n.X = typecheck(n.X, ctxType|ctxExpr)
+ if n.X.Diag() {
+ n.SetDiag(true)
+ }
+ t := n.X.Type()
+ if t == nil {
+ return n
+ }
+
+ if n.X.Op() == ir.OTYPE {
+ return n
+ }
+
+ s := n.Sel
+ if s == nil {
+ return n
+ }
+
+ switch path, ambig := dotpath(s, t, nil, false); {
+ case path != nil:
+ // rebuild elided dots
+ for c := len(path) - 1; c >= 0; c-- {
+ dot := ir.NewSelectorExpr(base.Pos, ir.ODOT, n.X, path[c].field.Sym)
+ dot.SetImplicit(true)
+ dot.SetType(path[c].field.Type)
+ n.X = dot
+ }
+ case ambig:
+ base.Errorf("ambiguous selector %v", n)
+ n.X = nil
+ }
+
+ return n
+}
+
+func CalcMethods(t *types.Type) {
+ if t == nil || t.AllMethods().Len() != 0 {
+ return
+ }
+
+ // mark top-level method symbols
+ // so that expand1 doesn't consider them.
+ for _, f := range t.Methods().Slice() {
+ f.Sym.SetUniq(true)
+ }
+
+ // generate all reachable methods
+ slist = slist[:0]
+ expand1(t, true)
+
+ // check each method to be uniquely reachable
+ var ms []*types.Field
+ for i, sl := range slist {
+ slist[i].field = nil
+ sl.field.Sym.SetUniq(false)
+
+ var f *types.Field
+ path, _ := dotpath(sl.field.Sym, t, &f, false)
+ if path == nil {
+ continue
+ }
+
+ // dotpath may have dug out arbitrary fields, we only want methods.
+ if !f.IsMethod() {
+ continue
+ }
+
+ // add it to the base type method list
+ f = f.Copy()
+ f.Embedded = 1 // needs a trampoline
+ for _, d := range path {
+ if d.field.Type.IsPtr() {
+ f.Embedded = 2
+ break
+ }
+ }
+ ms = append(ms, f)
+ }
+
+ for _, f := range t.Methods().Slice() {
+ f.Sym.SetUniq(false)
+ }
+
+ ms = append(ms, t.Methods().Slice()...)
+ sort.Sort(types.MethodsByName(ms))
+ t.AllMethods().Set(ms)
+}
+
+// adddot1 returns the number of fields or methods named s at depth d in Type t.
+// If exactly one exists, it will be returned in *save (if save is not nil),
+// and dotlist will contain the path of embedded fields traversed to find it,
+// in reverse order. If none exist, more will indicate whether t contains any
+// embedded fields at depth d, so callers can decide whether to retry at
+// a greater depth.
+func adddot1(s *types.Sym, t *types.Type, d int, save **types.Field, ignorecase bool) (c int, more bool) {
+ if t.Recur() {
+ return
+ }
+ t.SetRecur(true)
+ defer t.SetRecur(false)
+
+ var u *types.Type
+ d--
+ if d < 0 {
+ // We've reached our target depth. If t has any fields/methods
+ // named s, then we're done. Otherwise, we still need to check
+ // below for embedded fields.
+ c = lookdot0(s, t, save, ignorecase)
+ if c != 0 {
+ return c, false
+ }
+ }
+
+ u = t
+ if u.IsPtr() {
+ u = u.Elem()
+ }
+ if !u.IsStruct() && !u.IsInterface() {
+ return c, false
+ }
+
+ for _, f := range u.Fields().Slice() {
+ if f.Embedded == 0 || f.Sym == nil {
+ continue
+ }
+ if d < 0 {
+ // Found an embedded field at target depth.
+ return c, true
+ }
+ a, more1 := adddot1(s, f.Type, d, save, ignorecase)
+ if a != 0 && c == 0 {
+ dotlist[d].field = f
+ }
+ c += a
+ if more1 {
+ more = true
+ }
+ }
+
+ return c, more
+}
+
+// dotlist is used by adddot1 to record the path of embedded fields
+// used to access a target field or method.
+// Must be non-nil so that dotpath returns a non-nil slice even if d is zero.
+var dotlist = make([]dlist, 10)
+
+// Convert node n for assignment to type t.
+func assignconvfn(n ir.Node, t *types.Type, context func() string) ir.Node {
+ if n == nil || n.Type() == nil || n.Type().Broke() {
+ return n
+ }
+
+ if t.Kind() == types.TBLANK && n.Type().Kind() == types.TNIL {
+ base.Errorf("use of untyped nil")
+ }
+
+ n = convlit1(n, t, false, context)
+ if n.Type() == nil {
+ return n
+ }
+ if t.Kind() == types.TBLANK {
+ return n
+ }
+
+ // Convert ideal bool from comparison to plain bool
+ // if the next step is non-bool (like interface{}).
+ if n.Type() == types.UntypedBool && !t.IsBoolean() {
+ if n.Op() == ir.ONAME || n.Op() == ir.OLITERAL {
+ r := ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n)
+ r.SetType(types.Types[types.TBOOL])
+ r.SetTypecheck(1)
+ r.SetImplicit(true)
+ n = r
+ }
+ }
+
+ if types.Identical(n.Type(), t) {
+ return n
+ }
+
+ op, why := assignop(n.Type(), t)
+ if op == ir.OXXX {
+ base.Errorf("cannot use %L as type %v in %s%s", n, t, context(), why)
+ op = ir.OCONV
+ }
+
+ r := ir.NewConvExpr(base.Pos, op, t, n)
+ r.SetTypecheck(1)
+ r.SetImplicit(true)
+ return r
+}
+
+// Is type src assignment compatible to type dst?
+// If so, return op code to use in conversion.
+// If not, return OXXX. In this case, the string return parameter may
+// hold a reason why. In all other cases, it'll be the empty string.
+func assignop(src, dst *types.Type) (ir.Op, string) {
+ if src == dst {
+ return ir.OCONVNOP, ""
+ }
+ if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil {
+ return ir.OXXX, ""
+ }
+
+ // 1. src type is identical to dst.
+ if types.Identical(src, dst) {
+ return ir.OCONVNOP, ""
+ }
+
+ // 2. src and dst have identical underlying types
+ // and either src or dst is not a named type or
+ // both are empty interface types.
+ // For assignable but different non-empty interface types,
+ // we want to recompute the itab. Recomputing the itab ensures
+ // that itabs are unique (thus an interface with a compile-time
+ // type I has an itab with interface type I).
+ if types.Identical(src.Underlying(), dst.Underlying()) {
+ if src.IsEmptyInterface() {
+ // Conversion between two empty interfaces
+ // requires no code.
+ return ir.OCONVNOP, ""
+ }
+ if (src.Sym() == nil || dst.Sym() == nil) && !src.IsInterface() {
+ // Conversion between two types, at least one unnamed,
+ // needs no conversion. The exception is nonempty interfaces
+ // which need to have their itab updated.
+ return ir.OCONVNOP, ""
+ }
+ }
+
+ // 3. dst is an interface type and src implements dst.
+ if dst.IsInterface() && src.Kind() != types.TNIL {
+ var missing, have *types.Field
+ var ptr int
+ if implements(src, dst, &missing, &have, &ptr) {
+ // Call NeedITab/ITabAddr so that (src, dst)
+ // gets added to itabs early, which allows
+ // us to de-virtualize calls through this
+ // type/interface pair later. See CompileITabs in reflect.go
+ if types.IsDirectIface(src) && !dst.IsEmptyInterface() {
+ NeedITab(src, dst)
+ }
+
+ return ir.OCONVIFACE, ""
+ }
+
+ // we'll have complained about this method anyway, suppress spurious messages.
+ if have != nil && have.Sym == missing.Sym && (have.Type.Broke() || missing.Type.Broke()) {
+ return ir.OCONVIFACE, ""
+ }
+
+ var why string
+ if isptrto(src, types.TINTER) {
+ why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src)
+ } else if have != nil && have.Sym == missing.Sym && have.Nointerface() {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
+ } else if have != nil && have.Sym == missing.Sym {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
+ "\t\thave %v%S\n\t\twant %v%S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
+ } else if ptr != 0 {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
+ } else if have != nil {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
+ "\t\thave %v%S\n\t\twant %v%S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
+ } else {
+ why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
+ }
+
+ return ir.OXXX, why
+ }
+
+ if isptrto(dst, types.TINTER) {
+ why := fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst)
+ return ir.OXXX, why
+ }
+
+ if src.IsInterface() && dst.Kind() != types.TBLANK {
+ var missing, have *types.Field
+ var ptr int
+ var why string
+ if implements(dst, src, &missing, &have, &ptr) {
+ why = ": need type assertion"
+ }
+ return ir.OXXX, why
+ }
+
+ // 4. src is a bidirectional channel value, dst is a channel type,
+ // src and dst have identical element types, and
+ // either src or dst is not a named type.
+ if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
+ if types.Identical(src.Elem(), dst.Elem()) && (src.Sym() == nil || dst.Sym() == nil) {
+ return ir.OCONVNOP, ""
+ }
+ }
+
+ // 5. src is the predeclared identifier nil and dst is a nillable type.
+ if src.Kind() == types.TNIL {
+ switch dst.Kind() {
+ case types.TPTR,
+ types.TFUNC,
+ types.TMAP,
+ types.TCHAN,
+ types.TINTER,
+ types.TSLICE:
+ return ir.OCONVNOP, ""
+ }
+ }
+
+ // 6. rule about untyped constants - already converted by DefaultLit.
+
+ // 7. Any typed value can be assigned to the blank identifier.
+ if dst.Kind() == types.TBLANK {
+ return ir.OCONVNOP, ""
+ }
+
+ return ir.OXXX, ""
+}
+
+// Can we convert a value of type src to a value of type dst?
+// If so, return op code to use in conversion (maybe OCONVNOP).
+// If not, return OXXX. In this case, the string return parameter may
+// hold a reason why. In all other cases, it'll be the empty string.
+// srcConstant indicates whether the value of type src is a constant.
+func convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
+ if src == dst {
+ return ir.OCONVNOP, ""
+ }
+ if src == nil || dst == nil {
+ return ir.OXXX, ""
+ }
+
+ // Conversions from regular to go:notinheap are not allowed
+ // (unless it's unsafe.Pointer). These are runtime-specific
+ // rules.
+ // (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() {
+ why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem())
+ return ir.OXXX, why
+ }
+ // (b) Disallow string to []T where T is go:notinheap.
+ if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Kind() == types.ByteType.Kind() || dst.Elem().Kind() == types.RuneType.Kind()) {
+ why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem())
+ return ir.OXXX, why
+ }
+
+ // 1. src can be assigned to dst.
+ op, why := assignop(src, dst)
+ if op != ir.OXXX {
+ return op, why
+ }
+
+ // The rules for interfaces are no different in conversions
+ // than assignments. If interfaces are involved, stop now
+ // with the good message from assignop.
+ // Otherwise clear the error.
+ if src.IsInterface() || dst.IsInterface() {
+ return ir.OXXX, why
+ }
+
+ // 2. Ignoring struct tags, src and dst have identical underlying types.
+ if types.IdenticalIgnoreTags(src.Underlying(), dst.Underlying()) {
+ return ir.OCONVNOP, ""
+ }
+
+ // 3. src and dst are unnamed pointer types and, ignoring struct tags,
+ // their base types have identical underlying types.
+ if src.IsPtr() && dst.IsPtr() && src.Sym() == nil && dst.Sym() == nil {
+ if types.IdenticalIgnoreTags(src.Elem().Underlying(), dst.Elem().Underlying()) {
+ return ir.OCONVNOP, ""
+ }
+ }
+
+ // 4. src and dst are both integer or floating point types.
+ if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) {
+ if types.SimType[src.Kind()] == types.SimType[dst.Kind()] {
+ return ir.OCONVNOP, ""
+ }
+ return ir.OCONV, ""
+ }
+
+ // 5. src and dst are both complex types.
+ if src.IsComplex() && dst.IsComplex() {
+ if types.SimType[src.Kind()] == types.SimType[dst.Kind()] {
+ return ir.OCONVNOP, ""
+ }
+ return ir.OCONV, ""
+ }
+
+ // Special case for constant conversions: any numeric
+ // conversion is potentially okay. We'll validate further
+ // within evconst. See #38117.
+ if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
+ return ir.OCONV, ""
+ }
+
+ // 6. src is an integer or has type []byte or []rune
+ // and dst is a string type.
+ if src.IsInteger() && dst.IsString() {
+ return ir.ORUNESTR, ""
+ }
+
+ if src.IsSlice() && dst.IsString() {
+ if src.Elem().Kind() == types.ByteType.Kind() {
+ return ir.OBYTES2STR, ""
+ }
+ if src.Elem().Kind() == types.RuneType.Kind() {
+ return ir.ORUNES2STR, ""
+ }
+ }
+
+ // 7. src is a string and dst is []byte or []rune.
+ // String to slice.
+ if src.IsString() && dst.IsSlice() {
+ if dst.Elem().Kind() == types.ByteType.Kind() {
+ return ir.OSTR2BYTES, ""
+ }
+ if dst.Elem().Kind() == types.RuneType.Kind() {
+ return ir.OSTR2RUNES, ""
+ }
+ }
+
+ // 8. src is a pointer or uintptr and dst is unsafe.Pointer.
+ if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
+ return ir.OCONVNOP, ""
+ }
+
+ // 9. src is unsafe.Pointer and dst is a pointer or uintptr.
+ if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
+ return ir.OCONVNOP, ""
+ }
+
+ // src is map and dst is a pointer to corresponding hmap.
+ // This rule is needed for the implementation detail that
+ // go gc maps are implemented as a pointer to a hmap struct.
+ if src.Kind() == types.TMAP && dst.IsPtr() &&
+ src.MapType().Hmap == dst.Elem() {
+ return ir.OCONVNOP, ""
+ }
+
+ return ir.OXXX, ""
+}
+
+// Code to resolve elided DOTs in embedded types.
+
+// A dlist stores a pointer to a TFIELD Type embedded within
+// a TSTRUCT or TINTER Type.
+type dlist struct {
+ field *types.Field
+}
+
+// dotpath computes the unique shortest explicit selector path to fully qualify
+// a selection expression x.f, where x is of type t and f is the symbol s.
+// If no such path exists, dotpath returns nil.
+// If there are multiple shortest paths to the same depth, ambig is true.
+func dotpath(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) (path []dlist, ambig bool) {
+ // The embedding of types within structs imposes a tree structure onto
+ // types: structs parent the types they embed, and types parent their
+ // fields or methods. Our goal here is to find the shortest path to
+ // a field or method named s in the subtree rooted at t. To accomplish
+ // that, we iteratively perform depth-first searches of increasing depth
+ // until we either find the named field/method or exhaust the tree.
+ for d := 0; ; d++ {
+ if d > len(dotlist) {
+ dotlist = append(dotlist, dlist{})
+ }
+ if c, more := adddot1(s, t, d, save, ignorecase); c == 1 {
+ return dotlist[:d], false
+ } else if c > 1 {
+ return nil, true
+ } else if !more {
+ return nil, false
+ }
+ }
+}
+
+func expand0(t *types.Type) {
+ u := t
+ if u.IsPtr() {
+ u = u.Elem()
+ }
+
+ if u.IsInterface() {
+ for _, f := range u.Fields().Slice() {
+ if f.Sym.Uniq() {
+ continue
+ }
+ f.Sym.SetUniq(true)
+ slist = append(slist, symlink{field: f})
+ }
+
+ return
+ }
+
+ u = types.ReceiverBaseType(t)
+ if u != nil {
+ for _, f := range u.Methods().Slice() {
+ if f.Sym.Uniq() {
+ continue
+ }
+ f.Sym.SetUniq(true)
+ slist = append(slist, symlink{field: f})
+ }
+ }
+}
+
+func expand1(t *types.Type, top bool) {
+ if t.Recur() {
+ return
+ }
+ t.SetRecur(true)
+
+ if !top {
+ expand0(t)
+ }
+
+ u := t
+ if u.IsPtr() {
+ u = u.Elem()
+ }
+
+ if u.IsStruct() || u.IsInterface() {
+ for _, f := range u.Fields().Slice() {
+ if f.Embedded == 0 {
+ continue
+ }
+ if f.Sym == nil {
+ continue
+ }
+ expand1(f.Type, false)
+ }
+ }
+
+ t.SetRecur(false)
+}
+
+func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field, followptr bool) {
+ if t == nil {
+ return nil, false
+ }
+
+ path, ambig := dotpath(s, t, &m, ignorecase)
+ if path == nil {
+ if ambig {
+ base.Errorf("%v.%v is ambiguous", t, s)
+ }
+ return nil, false
+ }
+
+ for _, d := range path {
+ if d.field.Type.IsPtr() {
+ followptr = true
+ break
+ }
+ }
+
+ if !m.IsMethod() {
+ base.Errorf("%v.%v is a field, not a method", t, s)
+ return nil, followptr
+ }
+
+ return m, followptr
+}
+
+func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
+ t0 := t
+ if t == nil {
+ return false
+ }
+
+ if t.IsInterface() {
+ i := 0
+ tms := t.Fields().Slice()
+ for _, im := range iface.Fields().Slice() {
+ for i < len(tms) && tms[i].Sym != im.Sym {
+ i++
+ }
+ if i == len(tms) {
+ *m = im
+ *samename = nil
+ *ptr = 0
+ return false
+ }
+ tm := tms[i]
+ if !types.Identical(tm.Type, im.Type) {
+ *m = im
+ *samename = tm
+ *ptr = 0
+ return false
+ }
+ }
+
+ return true
+ }
+
+ t = types.ReceiverBaseType(t)
+ var tms []*types.Field
+ if t != nil {
+ CalcMethods(t)
+ tms = t.AllMethods().Slice()
+ }
+ i := 0
+ for _, im := range iface.Fields().Slice() {
+ if im.Broke() {
+ continue
+ }
+ for i < len(tms) && tms[i].Sym != im.Sym {
+ i++
+ }
+ if i == len(tms) {
+ *m = im
+ *samename, _ = ifacelookdot(im.Sym, t, true)
+ *ptr = 0
+ return false
+ }
+ tm := tms[i]
+ if tm.Nointerface() || !types.Identical(tm.Type, im.Type) {
+ *m = im
+ *samename = tm
+ *ptr = 0
+ return false
+ }
+ followptr := tm.Embedded == 2
+
+ // if pointer receiver in method,
+ // the method does not exist for value types.
+ rcvr := tm.Type.Recv().Type
+ if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !types.IsInterfaceMethod(tm.Type) {
+ if false && base.Flag.LowerR != 0 {
+ base.Errorf("interface pointer mismatch")
+ }
+
+ *m = im
+ *samename = nil
+ *ptr = 1
+ return false
+ }
+ }
+
+ return true
+}
+
+func isptrto(t *types.Type, et types.Kind) bool {
+ if t == nil {
+ return false
+ }
+ if !t.IsPtr() {
+ return false
+ }
+ t = t.Elem()
+ if t == nil {
+ return false
+ }
+ if t.Kind() != et {
+ return false
+ }
+ return true
+}
+
+// lookdot0 returns the number of fields or methods named s associated
+// with Type t. If exactly one exists, it will be returned in *save
+// (if save is not nil).
+func lookdot0(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) int {
+ u := t
+ if u.IsPtr() {
+ u = u.Elem()
+ }
+
+ c := 0
+ if u.IsStruct() || u.IsInterface() {
+ for _, f := range u.Fields().Slice() {
+ if f.Sym == s || (ignorecase && f.IsMethod() && strings.EqualFold(f.Sym.Name, s.Name)) {
+ if save != nil {
+ *save = f
+ }
+ c++
+ }
+ }
+ }
+
+ u = t
+ if t.Sym() != nil && t.IsPtr() && !t.Elem().IsPtr() {
+ // If t is a defined pointer type, then x.m is shorthand for (*x).m.
+ u = t.Elem()
+ }
+ u = types.ReceiverBaseType(u)
+ if u != nil {
+ for _, f := range u.Methods().Slice() {
+ if f.Embedded == 0 && (f.Sym == s || (ignorecase && strings.EqualFold(f.Sym.Name, s.Name))) {
+ if save != nil {
+ *save = f
+ }
+ c++
+ }
+ }
+ }
+
+ return c
+}
+
+var slist []symlink
+
+// Code to help generate trampoline functions for methods on embedded
+// types. These are approx the same as the corresponding AddImplicitDots
+// routines except that they expect to be called with unique tasks and
+// they return the actual methods.
+
+type symlink struct {
+ field *types.Field
+}
diff --git a/src/cmd/compile/internal/typecheck/syms.go b/src/cmd/compile/internal/typecheck/syms.go
new file mode 100644
index 0000000000000..202a932e6c96b
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/syms.go
@@ -0,0 +1,102 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+func LookupRuntime(name string) *ir.Name {
+ s := ir.Pkgs.Runtime.Lookup(name)
+ if s == nil || s.Def == nil {
+ base.Fatalf("LookupRuntime: can't find runtime.%s", name)
+ }
+ return ir.AsNode(s.Def).(*ir.Name)
+}
+
+// SubstArgTypes substitutes the given list of types for
+// successive occurrences of the "any" placeholder in the
+// type syntax expression n.Type.
+// The result of SubstArgTypes MUST be assigned back to old, e.g.
+// n.Left = SubstArgTypes(n.Left, t1, t2)
+func SubstArgTypes(old *ir.Name, types_ ...*types.Type) *ir.Name {
+ for _, t := range types_ {
+ types.CalcSize(t)
+ }
+ n := ir.NewNameAt(old.Pos(), old.Sym())
+ n.Class = old.Class
+ n.SetType(types.SubstAny(old.Type(), &types_))
+ if len(types_) > 0 {
+ base.Fatalf("SubstArgTypes: too many argument types")
+ }
+ return n
+}
+
+// AutoLabel generates a new Name node for use with
+// an automatically generated label.
+// prefix is a short mnemonic (e.g. ".s" for switch)
+// to help with debugging.
+// It should begin with "." to avoid conflicts with
+// user labels.
+func AutoLabel(prefix string) *types.Sym {
+ if prefix[0] != '.' {
+ base.Fatalf("autolabel prefix must start with '.', have %q", prefix)
+ }
+ fn := ir.CurFunc
+ if ir.CurFunc == nil {
+ base.Fatalf("autolabel outside function")
+ }
+ n := fn.Label
+ fn.Label++
+ return LookupNum(prefix, int(n))
+}
+
+func Lookup(name string) *types.Sym {
+ return types.LocalPkg.Lookup(name)
+}
+
+// InitRuntime loads the definitions for the low-level runtime functions,
+// so that the compiler can generate calls to them,
+// but does not make them visible to user code.
+func InitRuntime() {
+ base.Timer.Start("fe", "loadsys")
+ types.Block = 1
+
+ typs := runtimeTypes()
+ for _, d := range &runtimeDecls {
+ sym := ir.Pkgs.Runtime.Lookup(d.name)
+ typ := typs[d.typ]
+ switch d.tag {
+ case funcTag:
+ importfunc(ir.Pkgs.Runtime, src.NoXPos, sym, typ)
+ case varTag:
+ importvar(ir.Pkgs.Runtime, src.NoXPos, sym, typ)
+ default:
+ base.Fatalf("unhandled declaration tag %v", d.tag)
+ }
+ }
+}
+
+// LookupRuntimeFunc looks up Go function name in package runtime. This function
+// must follow the internal calling convention.
+func LookupRuntimeFunc(name string) *obj.LSym {
+ return LookupRuntimeABI(name, obj.ABIInternal)
+}
+
+// LookupRuntimeVar looks up a variable (or assembly function) name in package
+// runtime. If this is a function, it may have a special calling
+// convention.
+func LookupRuntimeVar(name string) *obj.LSym {
+ return LookupRuntimeABI(name, obj.ABI0)
+}
+
+// LookupRuntimeABI looks up a name in package runtime using the given ABI.
+func LookupRuntimeABI(name string, abi obj.ABI) *obj.LSym {
+ return base.PkgLinksym("runtime", name, abi)
+}
diff --git a/src/cmd/compile/internal/typecheck/target.go b/src/cmd/compile/internal/typecheck/target.go
new file mode 100644
index 0000000000000..018614d68bfc4
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/target.go
@@ -0,0 +1,12 @@
+// Copyright 2009 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.
+
+//go:generate go run mkbuiltin.go
+
+package typecheck
+
+import "cmd/compile/internal/ir"
+
+// Target is the package being compiled.
+var Target *ir.Package
diff --git a/src/cmd/compile/internal/typecheck/type.go b/src/cmd/compile/internal/typecheck/type.go
new file mode 100644
index 0000000000000..6fdafef77d926
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/type.go
@@ -0,0 +1,188 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+)
+
+// tcArrayType typechecks an OTARRAY node.
+func tcArrayType(n *ir.ArrayType) ir.Node {
+ n.Elem = typecheckNtype(n.Elem)
+ if n.Elem.Type() == nil {
+ return n
+ }
+ if n.Len == nil { // [...]T
+ if !n.Diag() {
+ n.SetDiag(true)
+ base.Errorf("use of [...] array outside of array literal")
+ }
+ return n
+ }
+ n.Len = indexlit(Expr(n.Len))
+ size := n.Len
+ if ir.ConstType(size) != constant.Int {
+ switch {
+ case size.Type() == nil:
+ // Error already reported elsewhere.
+ case size.Type().IsInteger() && size.Op() != ir.OLITERAL:
+ base.Errorf("non-constant array bound %v", size)
+ default:
+ base.Errorf("invalid array bound %v", size)
+ }
+ return n
+ }
+
+ v := size.Val()
+ if ir.ConstOverflow(v, types.Types[types.TINT]) {
+ base.Errorf("array bound is too large")
+ return n
+ }
+
+ if constant.Sign(v) < 0 {
+ base.Errorf("array bound must be non-negative")
+ return n
+ }
+
+ bound, _ := constant.Int64Val(v)
+ t := types.NewArray(n.Elem.Type(), bound)
+ n.SetOTYPE(t)
+ types.CheckSize(t)
+ return n
+}
+
+// tcChanType typechecks an OTCHAN node.
+func tcChanType(n *ir.ChanType) ir.Node {
+ n.Elem = typecheckNtype(n.Elem)
+ l := n.Elem
+ if l.Type() == nil {
+ return n
+ }
+ if l.Type().NotInHeap() {
+ base.Errorf("chan of incomplete (or unallocatable) type not allowed")
+ }
+ n.SetOTYPE(types.NewChan(l.Type(), n.Dir))
+ return n
+}
+
+// tcFuncType typechecks an OTFUNC node.
+func tcFuncType(n *ir.FuncType) ir.Node {
+ misc := func(f *types.Field, nf *ir.Field) {
+ f.SetIsDDD(nf.IsDDD)
+ if nf.Decl != nil {
+ nf.Decl.SetType(f.Type)
+ f.Nname = nf.Decl
+ }
+ }
+
+ lno := base.Pos
+
+ var recv *types.Field
+ if n.Recv != nil {
+ recv = tcField(n.Recv, misc)
+ }
+
+ t := types.NewSignature(types.LocalPkg, recv, tcFields(n.Params, misc), tcFields(n.Results, misc))
+ checkdupfields("argument", t.Recvs().FieldSlice(), t.Params().FieldSlice(), t.Results().FieldSlice())
+
+ base.Pos = lno
+
+ n.SetOTYPE(t)
+ return n
+}
+
+// tcInterfaceType typechecks an OTINTER node.
+func tcInterfaceType(n *ir.InterfaceType) ir.Node {
+ if len(n.Methods) == 0 {
+ n.SetOTYPE(types.Types[types.TINTER])
+ return n
+ }
+
+ lno := base.Pos
+ methods := tcFields(n.Methods, nil)
+ base.Pos = lno
+
+ n.SetOTYPE(types.NewInterface(types.LocalPkg, methods))
+ return n
+}
+
+// tcMapType typechecks an OTMAP node.
+func tcMapType(n *ir.MapType) ir.Node {
+ n.Key = typecheckNtype(n.Key)
+ n.Elem = typecheckNtype(n.Elem)
+ l := n.Key
+ r := n.Elem
+ if l.Type() == nil || r.Type() == nil {
+ return n
+ }
+ if l.Type().NotInHeap() {
+ base.Errorf("incomplete (or unallocatable) map key not allowed")
+ }
+ if r.Type().NotInHeap() {
+ base.Errorf("incomplete (or unallocatable) map value not allowed")
+ }
+ n.SetOTYPE(types.NewMap(l.Type(), r.Type()))
+ mapqueue = append(mapqueue, n) // check map keys when all types are settled
+ return n
+}
+
+// tcSliceType typechecks an OTSLICE node.
+func tcSliceType(n *ir.SliceType) ir.Node {
+ n.Elem = typecheckNtype(n.Elem)
+ if n.Elem.Type() == nil {
+ return n
+ }
+ t := types.NewSlice(n.Elem.Type())
+ n.SetOTYPE(t)
+ types.CheckSize(t)
+ return n
+}
+
+// tcStructType typechecks an OTSTRUCT node.
+func tcStructType(n *ir.StructType) ir.Node {
+ lno := base.Pos
+
+ fields := tcFields(n.Fields, func(f *types.Field, nf *ir.Field) {
+ if nf.Embedded {
+ checkembeddedtype(f.Type)
+ f.Embedded = 1
+ }
+ f.Note = nf.Note
+ })
+ checkdupfields("field", fields)
+
+ base.Pos = lno
+ n.SetOTYPE(types.NewStruct(types.LocalPkg, fields))
+ return n
+}
+
+// tcField typechecks a generic Field.
+// misc can be provided to handle specialized typechecking.
+func tcField(n *ir.Field, misc func(*types.Field, *ir.Field)) *types.Field {
+ base.Pos = n.Pos
+ if n.Ntype != nil {
+ n.Type = typecheckNtype(n.Ntype).Type()
+ n.Ntype = nil
+ }
+ f := types.NewField(n.Pos, n.Sym, n.Type)
+ if misc != nil {
+ misc(f, n)
+ }
+ return f
+}
+
+// tcFields typechecks a slice of generic Fields.
+// misc can be provided to handle specialized typechecking.
+func tcFields(l []*ir.Field, misc func(*types.Field, *ir.Field)) []*types.Field {
+ fields := make([]*types.Field, len(l))
+ for i, n := range l {
+ fields[i] = tcField(n, misc)
+ }
+ return fields
+}
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
new file mode 100644
index 0000000000000..cb434578dd37e
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -0,0 +1,2164 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "fmt"
+ "go/constant"
+ "go/token"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+)
+
+// Function collecting autotmps generated during typechecking,
+// to be included in the package-level init function.
+var InitTodoFunc = ir.NewFunc(base.Pos)
+
+var inimport bool // set during import
+
+var TypecheckAllowed bool
+
+var (
+ NeedITab = func(t, itype *types.Type) {}
+ NeedRuntimeType = func(*types.Type) {}
+)
+
+func AssignExpr(n ir.Node) ir.Node { return typecheck(n, ctxExpr|ctxAssign) }
+func Expr(n ir.Node) ir.Node { return typecheck(n, ctxExpr) }
+func Stmt(n ir.Node) ir.Node { return typecheck(n, ctxStmt) }
+
+func Exprs(exprs []ir.Node) { typecheckslice(exprs, ctxExpr) }
+func Stmts(stmts []ir.Node) { typecheckslice(stmts, ctxStmt) }
+
+func Call(call *ir.CallExpr) {
+ t := call.X.Type()
+ if t == nil {
+ panic("misuse of Call")
+ }
+ ctx := ctxStmt
+ if t.NumResults() > 0 {
+ ctx = ctxExpr | ctxMultiOK
+ }
+ if typecheck(call, ctx) != call {
+ panic("bad typecheck")
+ }
+}
+
+func Callee(n ir.Node) ir.Node {
+ return typecheck(n, ctxExpr|ctxCallee)
+}
+
+func FuncBody(n *ir.Func) {
+ ir.CurFunc = n
+ errorsBefore := base.Errors()
+ Stmts(n.Body)
+ CheckUnused(n)
+ CheckReturn(n)
+ if base.Errors() > errorsBefore {
+ n.Body = nil // type errors; do not compile
+ }
+}
+
+var importlist []*ir.Func
+
+func AllImportedBodies() {
+ for _, n := range importlist {
+ if n.Inl != nil {
+ ImportedBody(n)
+ }
+ }
+}
+
+var traceIndent []byte
+
+func tracePrint(title string, n ir.Node) func(np *ir.Node) {
+ indent := traceIndent
+
+ // guard against nil
+ var pos, op string
+ var tc uint8
+ if n != nil {
+ pos = base.FmtPos(n.Pos())
+ op = n.Op().String()
+ tc = n.Typecheck()
+ }
+
+ types.SkipSizeForTracing = true
+ defer func() { types.SkipSizeForTracing = false }()
+ fmt.Printf("%s: %s%s %p %s %v tc=%d\n", pos, indent, title, n, op, n, tc)
+ traceIndent = append(traceIndent, ". "...)
+
+ return func(np *ir.Node) {
+ traceIndent = traceIndent[:len(traceIndent)-2]
+
+ // if we have a result, use that
+ if np != nil {
+ n = *np
+ }
+
+ // guard against nil
+ // use outer pos, op so we don't get empty pos/op if n == nil (nicer output)
+ var tc uint8
+ var typ *types.Type
+ if n != nil {
+ pos = base.FmtPos(n.Pos())
+ op = n.Op().String()
+ tc = n.Typecheck()
+ typ = n.Type()
+ }
+
+ types.SkipSizeForTracing = true
+ defer func() { types.SkipSizeForTracing = false }()
+ fmt.Printf("%s: %s=> %p %s %v tc=%d type=%L\n", pos, indent, n, op, n, tc, typ)
+ }
+}
+
+const (
+ ctxStmt = 1 << iota // evaluated at statement level
+ ctxExpr // evaluated in value context
+ ctxType // evaluated in type context
+ ctxCallee // call-only expressions are ok
+ ctxMultiOK // multivalue function returns are ok
+ ctxAssign // assigning to expression
+)
+
+// type checks the whole tree of an expression.
+// calculates expression types.
+// evaluates compile time constants.
+// marks variables that escape the local frame.
+// rewrites n.Op to be more specific in some cases.
+
+var typecheckdefstack []*ir.Name
+
+// Resolve ONONAME to definition, if any.
+func Resolve(n ir.Node) (res ir.Node) {
+ if n == nil || n.Op() != ir.ONONAME {
+ return n
+ }
+
+ // only trace if there's work to do
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("resolve", n)(&res)
+ }
+
+ if sym := n.Sym(); sym.Pkg != types.LocalPkg {
+ // We might have an ir.Ident from oldname or importDot.
+ if id, ok := n.(*ir.Ident); ok {
+ if pkgName := DotImportRefs[id]; pkgName != nil {
+ pkgName.Used = true
+ }
+ }
+
+ return expandDecl(n)
+ }
+
+ r := ir.AsNode(n.Sym().Def)
+ if r == nil {
+ return n
+ }
+
+ if r.Op() == ir.OIOTA {
+ if x := getIotaValue(); x >= 0 {
+ return ir.NewInt(x)
+ }
+ return n
+ }
+
+ return r
+}
+
+func typecheckslice(l []ir.Node, top int) {
+ for i := range l {
+ l[i] = typecheck(l[i], top)
+ }
+}
+
+var _typekind = []string{
+ types.TINT: "int",
+ types.TUINT: "uint",
+ types.TINT8: "int8",
+ types.TUINT8: "uint8",
+ types.TINT16: "int16",
+ types.TUINT16: "uint16",
+ types.TINT32: "int32",
+ types.TUINT32: "uint32",
+ types.TINT64: "int64",
+ types.TUINT64: "uint64",
+ types.TUINTPTR: "uintptr",
+ types.TCOMPLEX64: "complex64",
+ types.TCOMPLEX128: "complex128",
+ types.TFLOAT32: "float32",
+ types.TFLOAT64: "float64",
+ types.TBOOL: "bool",
+ types.TSTRING: "string",
+ types.TPTR: "pointer",
+ types.TUNSAFEPTR: "unsafe.Pointer",
+ types.TSTRUCT: "struct",
+ types.TINTER: "interface",
+ types.TCHAN: "chan",
+ types.TMAP: "map",
+ types.TARRAY: "array",
+ types.TSLICE: "slice",
+ types.TFUNC: "func",
+ types.TNIL: "nil",
+ types.TIDEAL: "untyped number",
+}
+
+func typekind(t *types.Type) string {
+ if t.IsUntyped() {
+ return fmt.Sprintf("%v", t)
+ }
+ et := t.Kind()
+ if int(et) < len(_typekind) {
+ s := _typekind[et]
+ if s != "" {
+ return s
+ }
+ }
+ return fmt.Sprintf("etype=%d", et)
+}
+
+func cycleFor(start ir.Node) []ir.Node {
+ // Find the start node in typecheck_tcstack.
+ // We know that it must exist because each time we mark
+ // a node with n.SetTypecheck(2) we push it on the stack,
+ // and each time we mark a node with n.SetTypecheck(2) we
+ // pop it from the stack. We hit a cycle when we encounter
+ // a node marked 2 in which case is must be on the stack.
+ i := len(typecheck_tcstack) - 1
+ for i > 0 && typecheck_tcstack[i] != start {
+ i--
+ }
+
+ // collect all nodes with same Op
+ var cycle []ir.Node
+ for _, n := range typecheck_tcstack[i:] {
+ if n.Op() == start.Op() {
+ cycle = append(cycle, n)
+ }
+ }
+
+ return cycle
+}
+
+func cycleTrace(cycle []ir.Node) string {
+ var s string
+ for i, n := range cycle {
+ s += fmt.Sprintf("\n\t%v: %v uses %v", ir.Line(n), n, cycle[(i+1)%len(cycle)])
+ }
+ return s
+}
+
+var typecheck_tcstack []ir.Node
+
+func Func(fn *ir.Func) {
+ new := Stmt(fn)
+ if new != fn {
+ base.Fatalf("typecheck changed func")
+ }
+}
+
+func typecheckNtype(n ir.Ntype) ir.Ntype {
+ return typecheck(n, ctxType).(ir.Ntype)
+}
+
+// typecheck type checks node n.
+// The result of typecheck MUST be assigned back to n, e.g.
+// n.Left = typecheck(n.Left, top)
+func typecheck(n ir.Node, top int) (res ir.Node) {
+ // cannot type check until all the source has been parsed
+ if !TypecheckAllowed {
+ base.Fatalf("early typecheck")
+ }
+
+ if n == nil {
+ return nil
+ }
+
+ // only trace if there's work to do
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("typecheck", n)(&res)
+ }
+
+ lno := ir.SetPos(n)
+
+ // Skip over parens.
+ for n.Op() == ir.OPAREN {
+ n = n.(*ir.ParenExpr).X
+ }
+
+ // Resolve definition of name and value of iota lazily.
+ n = Resolve(n)
+
+ // Skip typecheck if already done.
+ // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed.
+ if n.Typecheck() == 1 {
+ switch n.Op() {
+ case ir.ONAME, ir.OTYPE, ir.OLITERAL, ir.OPACK:
+ break
+
+ default:
+ base.Pos = lno
+ return n
+ }
+ }
+
+ if n.Typecheck() == 2 {
+ // Typechecking loop. Trying printing a meaningful message,
+ // otherwise a stack trace of typechecking.
+ switch n.Op() {
+ // We can already diagnose variables used as types.
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ if top&(ctxExpr|ctxType) == ctxType {
+ base.Errorf("%v is not a type", n)
+ }
+
+ case ir.OTYPE:
+ // Only report a type cycle if we are expecting a type.
+ // Otherwise let other code report an error.
+ if top&ctxType == ctxType {
+ // A cycle containing only alias types is an error
+ // since it would expand indefinitely when aliases
+ // are substituted.
+ cycle := cycleFor(n)
+ for _, n1 := range cycle {
+ if n1.Name() != nil && !n1.Name().Alias() {
+ // Cycle is ok. But if n is an alias type and doesn't
+ // have a type yet, we have a recursive type declaration
+ // with aliases that we can't handle properly yet.
+ // Report an error rather than crashing later.
+ if n.Name() != nil && n.Name().Alias() && n.Type() == nil {
+ base.Pos = n.Pos()
+ base.Fatalf("cannot handle alias type declaration (issue #25838): %v", n)
+ }
+ base.Pos = lno
+ return n
+ }
+ }
+ base.ErrorfAt(n.Pos(), "invalid recursive type alias %v%s", n, cycleTrace(cycle))
+ }
+
+ case ir.OLITERAL:
+ if top&(ctxExpr|ctxType) == ctxType {
+ base.Errorf("%v is not a type", n)
+ break
+ }
+ base.ErrorfAt(n.Pos(), "constant definition loop%s", cycleTrace(cycleFor(n)))
+ }
+
+ if base.Errors() == 0 {
+ var trace string
+ for i := len(typecheck_tcstack) - 1; i >= 0; i-- {
+ x := typecheck_tcstack[i]
+ trace += fmt.Sprintf("\n\t%v %v", ir.Line(x), x)
+ }
+ base.Errorf("typechecking loop involving %v%s", n, trace)
+ }
+
+ base.Pos = lno
+ return n
+ }
+
+ typecheck_tcstack = append(typecheck_tcstack, n)
+
+ n.SetTypecheck(2)
+ n = typecheck1(n, top)
+ n.SetTypecheck(1)
+
+ last := len(typecheck_tcstack) - 1
+ typecheck_tcstack[last] = nil
+ typecheck_tcstack = typecheck_tcstack[:last]
+
+ _, isExpr := n.(ir.Expr)
+ _, isStmt := n.(ir.Stmt)
+ isMulti := false
+ switch n.Op() {
+ case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
+ n := n.(*ir.CallExpr)
+ if t := n.X.Type(); t != nil && t.Kind() == types.TFUNC {
+ nr := t.NumResults()
+ isMulti = nr > 1
+ if nr == 0 {
+ isExpr = false
+ }
+ }
+ case ir.OAPPEND:
+ // Must be used (and not BinaryExpr/UnaryExpr).
+ isStmt = false
+ case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.OVARKILL, ir.OVARLIVE:
+ // Must not be used.
+ isExpr = false
+ isStmt = true
+ case ir.OCOPY, ir.ORECOVER, ir.ORECV:
+ // Can be used or not.
+ isStmt = true
+ }
+
+ t := n.Type()
+ if t != nil && !t.IsFuncArgStruct() && n.Op() != ir.OTYPE {
+ switch t.Kind() {
+ case types.TFUNC, // might have TANY; wait until it's called
+ types.TANY, types.TFORW, types.TIDEAL, types.TNIL, types.TBLANK:
+ break
+
+ default:
+ types.CheckSize(t)
+ }
+ }
+ if t != nil {
+ n = EvalConst(n)
+ t = n.Type()
+ }
+
+ // TODO(rsc): Lots of the complexity here is because typecheck can
+ // see OTYPE, ONAME, and OLITERAL nodes multiple times.
+ // Once we make the IR a proper tree, we should be able to simplify
+ // this code a bit, especially the final case.
+ switch {
+ case top&(ctxStmt|ctxExpr) == ctxExpr && !isExpr && n.Op() != ir.OTYPE && !isMulti:
+ if !n.Diag() {
+ base.Errorf("%v used as value", n)
+ n.SetDiag(true)
+ }
+ if t != nil {
+ n.SetType(nil)
+ }
+
+ case top&ctxType == 0 && n.Op() == ir.OTYPE && t != nil:
+ if !n.Type().Broke() {
+ base.Errorf("type %v is not an expression", n.Type())
+ }
+ n.SetType(nil)
+
+ case top&(ctxStmt|ctxExpr) == ctxStmt && !isStmt && t != nil:
+ if !n.Diag() {
+ base.Errorf("%v evaluated but not used", n)
+ n.SetDiag(true)
+ }
+ n.SetType(nil)
+
+ case top&(ctxType|ctxExpr) == ctxType && n.Op() != ir.OTYPE && n.Op() != ir.ONONAME && (t != nil || n.Op() == ir.ONAME):
+ base.Errorf("%v is not a type", n)
+ if t != nil {
+ n.SetType(nil)
+ }
+
+ }
+
+ base.Pos = lno
+ return n
+}
+
+// indexlit implements typechecking of untyped values as
+// array/slice indexes. It is almost equivalent to DefaultLit
+// but also accepts untyped numeric values representable as
+// value of type int (see also checkmake for comparison).
+// The result of indexlit MUST be assigned back to n, e.g.
+// n.Left = indexlit(n.Left)
+func indexlit(n ir.Node) ir.Node {
+ if n != nil && n.Type() != nil && n.Type().Kind() == types.TIDEAL {
+ return DefaultLit(n, types.Types[types.TINT])
+ }
+ return n
+}
+
+// typecheck1 should ONLY be called from typecheck.
+func typecheck1(n ir.Node, top int) ir.Node {
+ if n, ok := n.(*ir.Name); ok {
+ typecheckdef(n)
+ }
+
+ switch n.Op() {
+ default:
+ ir.Dump("typecheck", n)
+ base.Fatalf("typecheck %v", n.Op())
+ panic("unreachable")
+
+ case ir.OLITERAL:
+ if n.Sym() == nil && n.Type() == nil {
+ base.Fatalf("literal missing type: %v", n)
+ }
+ return n
+
+ case ir.ONIL:
+ return n
+
+ // names
+ case ir.ONONAME:
+ if !n.Diag() {
+ // Note: adderrorname looks for this string and
+ // adds context about the outer expression
+ base.ErrorfAt(n.Pos(), "undefined: %v", n.Sym())
+ n.SetDiag(true)
+ }
+ n.SetType(nil)
+ return n
+
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ if n.BuiltinOp != 0 {
+ if top&ctxCallee == 0 {
+ base.Errorf("use of builtin %v not in function call", n.Sym())
+ n.SetType(nil)
+ return n
+ }
+ return n
+ }
+ if top&ctxAssign == 0 {
+ // not a write to the variable
+ if ir.IsBlank(n) {
+ base.Errorf("cannot use _ as value")
+ n.SetType(nil)
+ return n
+ }
+ n.SetUsed(true)
+ }
+ return n
+
+ case ir.OLINKSYMOFFSET:
+ // type already set
+ return n
+
+ case ir.OPACK:
+ n := n.(*ir.PkgName)
+ base.Errorf("use of package %v without selector", n.Sym())
+ n.SetType(nil)
+ return n
+
+ // types (ODEREF is with exprs)
+ case ir.OTYPE:
+ return n
+
+ case ir.OTSLICE:
+ n := n.(*ir.SliceType)
+ return tcSliceType(n)
+
+ case ir.OTARRAY:
+ n := n.(*ir.ArrayType)
+ return tcArrayType(n)
+
+ case ir.OTMAP:
+ n := n.(*ir.MapType)
+ return tcMapType(n)
+
+ case ir.OTCHAN:
+ n := n.(*ir.ChanType)
+ return tcChanType(n)
+
+ case ir.OTSTRUCT:
+ n := n.(*ir.StructType)
+ return tcStructType(n)
+
+ case ir.OTINTER:
+ n := n.(*ir.InterfaceType)
+ return tcInterfaceType(n)
+
+ case ir.OTFUNC:
+ n := n.(*ir.FuncType)
+ return tcFuncType(n)
+ // type or expr
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ return tcStar(n, top)
+
+ // x op= y
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ n.X, n.Y = Expr(n.X), Expr(n.Y)
+ checkassign(n, n.X)
+ if n.IncDec && !okforarith[n.X.Type().Kind()] {
+ base.Errorf("invalid operation: %v (non-numeric type %v)", n, n.X.Type())
+ return n
+ }
+ switch n.AsOp {
+ case ir.OLSH, ir.ORSH:
+ n.X, n.Y, _ = tcShift(n, n.X, n.Y)
+ case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD, ir.OMUL, ir.OOR, ir.OSUB, ir.OXOR:
+ n.X, n.Y, _ = tcArith(n, n.AsOp, n.X, n.Y)
+ default:
+ base.Fatalf("invalid assign op: %v", n.AsOp)
+ }
+ return n
+
+ // logical operators
+ case ir.OANDAND, ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ n.X, n.Y = Expr(n.X), Expr(n.Y)
+ // For "x == x && len(s)", it's better to report that "len(s)" (type int)
+ // can't be used with "&&" than to report that "x == x" (type untyped bool)
+ // can't be converted to int (see issue #41500).
+ if !n.X.Type().IsBoolean() {
+ base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(n.X.Type()))
+ n.SetType(nil)
+ return n
+ }
+ if !n.Y.Type().IsBoolean() {
+ base.Errorf("invalid operation: %v (operator %v not defined on %s)", n, n.Op(), typekind(n.Y.Type()))
+ n.SetType(nil)
+ return n
+ }
+ l, r, t := tcArith(n, n.Op(), n.X, n.Y)
+ n.X, n.Y = l, r
+ n.SetType(t)
+ return n
+
+ // shift operators
+ case ir.OLSH, ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ n.X, n.Y = Expr(n.X), Expr(n.Y)
+ l, r, t := tcShift(n, n.X, n.Y)
+ n.X, n.Y = l, r
+ n.SetType(t)
+ return n
+
+ // comparison operators
+ case ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT, ir.ONE:
+ n := n.(*ir.BinaryExpr)
+ n.X, n.Y = Expr(n.X), Expr(n.Y)
+ l, r, t := tcArith(n, n.Op(), n.X, n.Y)
+ if t != nil {
+ n.X, n.Y = l, r
+ n.SetType(types.UntypedBool)
+ if con := EvalConst(n); con.Op() == ir.OLITERAL {
+ return con
+ }
+ n.X, n.Y = defaultlit2(l, r, true)
+ }
+ return n
+
+ // binary operators
+ case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD, ir.OMUL, ir.OOR, ir.OSUB, ir.OXOR:
+ n := n.(*ir.BinaryExpr)
+ n.X, n.Y = Expr(n.X), Expr(n.Y)
+ l, r, t := tcArith(n, n.Op(), n.X, n.Y)
+ if t != nil && t.Kind() == types.TSTRING && n.Op() == ir.OADD {
+ // create or update OADDSTR node with list of strings in x + y + z + (w + v) + ...
+ var add *ir.AddStringExpr
+ if l.Op() == ir.OADDSTR {
+ add = l.(*ir.AddStringExpr)
+ add.SetPos(n.Pos())
+ } else {
+ add = ir.NewAddStringExpr(n.Pos(), []ir.Node{l})
+ }
+ if r.Op() == ir.OADDSTR {
+ r := r.(*ir.AddStringExpr)
+ add.List.Append(r.List.Take()...)
+ } else {
+ add.List.Append(r)
+ }
+ add.SetType(t)
+ return add
+ }
+ n.X, n.Y = l, r
+ n.SetType(t)
+ return n
+
+ case ir.OBITNOT, ir.ONEG, ir.ONOT, ir.OPLUS:
+ n := n.(*ir.UnaryExpr)
+ return tcUnaryArith(n)
+
+ // exprs
+ case ir.OADDR:
+ n := n.(*ir.AddrExpr)
+ return tcAddr(n)
+
+ case ir.OCOMPLIT:
+ return tcCompLit(n.(*ir.CompLitExpr))
+
+ case ir.OXDOT, ir.ODOT:
+ n := n.(*ir.SelectorExpr)
+ return tcDot(n, top)
+
+ case ir.ODOTTYPE:
+ n := n.(*ir.TypeAssertExpr)
+ return tcDotType(n)
+
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ return tcIndex(n)
+
+ case ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ return tcRecv(n)
+
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ return tcSend(n)
+
+ case ir.OSLICEHEADER:
+ n := n.(*ir.SliceHeaderExpr)
+ return tcSliceHeader(n)
+
+ case ir.OMAKESLICECOPY:
+ n := n.(*ir.MakeExpr)
+ return tcMakeSliceCopy(n)
+
+ case ir.OSLICE, ir.OSLICE3:
+ n := n.(*ir.SliceExpr)
+ return tcSlice(n)
+
+ // call and call like
+ case ir.OCALL:
+ n := n.(*ir.CallExpr)
+ return tcCall(n, top)
+
+ case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+ n := n.(*ir.UnaryExpr)
+ n.SetType(types.Types[types.TUINTPTR])
+ return n
+
+ case ir.OCAP, ir.OLEN:
+ n := n.(*ir.UnaryExpr)
+ return tcLenCap(n)
+
+ case ir.OREAL, ir.OIMAG:
+ n := n.(*ir.UnaryExpr)
+ return tcRealImag(n)
+
+ case ir.OCOMPLEX:
+ n := n.(*ir.BinaryExpr)
+ return tcComplex(n)
+
+ case ir.OCLOSE:
+ n := n.(*ir.UnaryExpr)
+ return tcClose(n)
+
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ return tcDelete(n)
+
+ case ir.OAPPEND:
+ n := n.(*ir.CallExpr)
+ return tcAppend(n)
+
+ case ir.OCOPY:
+ n := n.(*ir.BinaryExpr)
+ return tcCopy(n)
+
+ case ir.OCONV:
+ n := n.(*ir.ConvExpr)
+ return tcConv(n)
+
+ case ir.OMAKE:
+ n := n.(*ir.CallExpr)
+ return tcMake(n)
+
+ case ir.ONEW:
+ n := n.(*ir.UnaryExpr)
+ return tcNew(n)
+
+ case ir.OPRINT, ir.OPRINTN:
+ n := n.(*ir.CallExpr)
+ return tcPrint(n)
+
+ case ir.OPANIC:
+ n := n.(*ir.UnaryExpr)
+ return tcPanic(n)
+
+ case ir.ORECOVER:
+ n := n.(*ir.CallExpr)
+ return tcRecover(n)
+
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ tcClosure(n, top)
+ if n.Type() == nil {
+ return n
+ }
+ return n
+
+ case ir.OITAB:
+ n := n.(*ir.UnaryExpr)
+ return tcITab(n)
+
+ case ir.OIDATA:
+ // Whoever creates the OIDATA node must know a priori the concrete type at that moment,
+ // usually by just having checked the OITAB.
+ n := n.(*ir.UnaryExpr)
+ base.Fatalf("cannot typecheck interface data %v", n)
+ panic("unreachable")
+
+ case ir.OSPTR:
+ n := n.(*ir.UnaryExpr)
+ return tcSPtr(n)
+
+ case ir.OCFUNC:
+ n := n.(*ir.UnaryExpr)
+ n.X = Expr(n.X)
+ n.SetType(types.Types[types.TUINTPTR])
+ return n
+
+ case ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ n.X = Expr(n.X)
+ return n
+
+ // statements
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ tcAssign(n)
+
+ // Code that creates temps does not bother to set defn, so do it here.
+ if n.X.Op() == ir.ONAME && ir.IsAutoTmp(n.X) {
+ n.X.Name().Defn = n
+ }
+ return n
+
+ case ir.OAS2:
+ tcAssignList(n.(*ir.AssignListStmt))
+ return n
+
+ case ir.OBREAK,
+ ir.OCONTINUE,
+ ir.ODCL,
+ ir.OGOTO,
+ ir.OFALL,
+ ir.OVARKILL,
+ ir.OVARLIVE:
+ return n
+
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
+ Stmts(n.List)
+ return n
+
+ case ir.OLABEL:
+ if n.Sym().IsBlank() {
+ // Empty identifier is valid but useless.
+ // Eliminate now to simplify life later.
+ // See issues 7538, 11589, 11593.
+ n = ir.NewBlockStmt(n.Pos(), nil)
+ }
+ return n
+
+ case ir.ODEFER, ir.OGO:
+ n := n.(*ir.GoDeferStmt)
+ n.Call = typecheck(n.Call, ctxStmt|ctxExpr)
+ if !n.Call.Diag() {
+ tcGoDefer(n)
+ }
+ return n
+
+ case ir.OFOR, ir.OFORUNTIL:
+ n := n.(*ir.ForStmt)
+ return tcFor(n)
+
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ return tcIf(n)
+
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ return tcReturn(n)
+
+ case ir.OTAILCALL:
+ n := n.(*ir.TailCallStmt)
+ return n
+
+ case ir.OSELECT:
+ tcSelect(n.(*ir.SelectStmt))
+ return n
+
+ case ir.OSWITCH:
+ tcSwitch(n.(*ir.SwitchStmt))
+ return n
+
+ case ir.ORANGE:
+ tcRange(n.(*ir.RangeStmt))
+ return n
+
+ case ir.OTYPESW:
+ n := n.(*ir.TypeSwitchGuard)
+ base.Errorf("use of .(type) outside type switch")
+ n.SetType(nil)
+ return n
+
+ case ir.ODCLFUNC:
+ tcFunc(n.(*ir.Func))
+ return n
+
+ case ir.ODCLCONST:
+ n := n.(*ir.Decl)
+ n.X = Expr(n.X).(*ir.Name)
+ return n
+
+ case ir.ODCLTYPE:
+ n := n.(*ir.Decl)
+ n.X = typecheck(n.X, ctxType).(*ir.Name)
+ types.CheckSize(n.X.Type())
+ return n
+ }
+
+ // No return n here!
+ // Individual cases can type-assert n, introducing a new one.
+ // Each must execute its own return n.
+}
+
+func typecheckargs(n ir.InitNode) {
+ var list []ir.Node
+ switch n := n.(type) {
+ default:
+ base.Fatalf("typecheckargs %+v", n.Op())
+ case *ir.CallExpr:
+ list = n.Args
+ if n.IsDDD {
+ Exprs(list)
+ return
+ }
+ case *ir.ReturnStmt:
+ list = n.Results
+ }
+ if len(list) != 1 {
+ Exprs(list)
+ return
+ }
+
+ typecheckslice(list, ctxExpr|ctxMultiOK)
+ t := list[0].Type()
+ if t == nil || !t.IsFuncArgStruct() {
+ return
+ }
+
+ // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
+
+ // Save n as n.Orig for fmt.go.
+ if ir.Orig(n) == n {
+ n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
+ }
+
+ as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+ as.Rhs.Append(list...)
+
+ // If we're outside of function context, then this call will
+ // be executed during the generated init function. However,
+ // init.go hasn't yet created it. Instead, associate the
+ // temporary variables with InitTodoFunc for now, and init.go
+ // will reassociate them later when it's appropriate.
+ static := ir.CurFunc == nil
+ if static {
+ ir.CurFunc = InitTodoFunc
+ }
+ list = nil
+ for _, f := range t.FieldSlice() {
+ t := Temp(f.Type)
+ as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
+ as.Lhs.Append(t)
+ list = append(list, t)
+ }
+ if static {
+ ir.CurFunc = nil
+ }
+
+ switch n := n.(type) {
+ case *ir.CallExpr:
+ n.Args = list
+ case *ir.ReturnStmt:
+ n.Results = list
+ }
+
+ n.PtrInit().Append(Stmt(as))
+}
+
+func checksliceindex(l ir.Node, r ir.Node, tp *types.Type) bool {
+ t := r.Type()
+ if t == nil {
+ return false
+ }
+ if !t.IsInteger() {
+ base.Errorf("invalid slice index %v (type %v)", r, t)
+ return false
+ }
+
+ if r.Op() == ir.OLITERAL {
+ x := r.Val()
+ if constant.Sign(x) < 0 {
+ base.Errorf("invalid slice index %v (index must be non-negative)", r)
+ return false
+ } else if tp != nil && tp.NumElem() >= 0 && constant.Compare(x, token.GTR, constant.MakeInt64(tp.NumElem())) {
+ base.Errorf("invalid slice index %v (out of bounds for %d-element array)", r, tp.NumElem())
+ return false
+ } else if ir.IsConst(l, constant.String) && constant.Compare(x, token.GTR, constant.MakeInt64(int64(len(ir.StringVal(l))))) {
+ base.Errorf("invalid slice index %v (out of bounds for %d-byte string)", r, len(ir.StringVal(l)))
+ return false
+ } else if ir.ConstOverflow(x, types.Types[types.TINT]) {
+ base.Errorf("invalid slice index %v (index too large)", r)
+ return false
+ }
+ }
+
+ return true
+}
+
+func checksliceconst(lo ir.Node, hi ir.Node) bool {
+ if lo != nil && hi != nil && lo.Op() == ir.OLITERAL && hi.Op() == ir.OLITERAL && constant.Compare(lo.Val(), token.GTR, hi.Val()) {
+ base.Errorf("invalid slice index: %v > %v", lo, hi)
+ return false
+ }
+
+ return true
+}
+
+// The result of implicitstar MUST be assigned back to n, e.g.
+// n.Left = implicitstar(n.Left)
+func implicitstar(n ir.Node) ir.Node {
+ // insert implicit * if needed for fixed array
+ t := n.Type()
+ if t == nil || !t.IsPtr() {
+ return n
+ }
+ t = t.Elem()
+ if t == nil {
+ return n
+ }
+ if !t.IsArray() {
+ return n
+ }
+ star := ir.NewStarExpr(base.Pos, n)
+ star.SetImplicit(true)
+ return Expr(star)
+}
+
+func needOneArg(n *ir.CallExpr, f string, args ...interface{}) (ir.Node, bool) {
+ if len(n.Args) == 0 {
+ p := fmt.Sprintf(f, args...)
+ base.Errorf("missing argument to %s: %v", p, n)
+ return nil, false
+ }
+
+ if len(n.Args) > 1 {
+ p := fmt.Sprintf(f, args...)
+ base.Errorf("too many arguments to %s: %v", p, n)
+ return n.Args[0], false
+ }
+
+ return n.Args[0], true
+}
+
+func needTwoArgs(n *ir.CallExpr) (ir.Node, ir.Node, bool) {
+ if len(n.Args) != 2 {
+ if len(n.Args) < 2 {
+ base.Errorf("not enough arguments in call to %v", n)
+ } else {
+ base.Errorf("too many arguments in call to %v", n)
+ }
+ return nil, nil, false
+ }
+ return n.Args[0], n.Args[1], true
+}
+
+func lookdot1(errnode ir.Node, s *types.Sym, t *types.Type, fs *types.Fields, dostrcmp int) *types.Field {
+ var r *types.Field
+ for _, f := range fs.Slice() {
+ if dostrcmp != 0 && f.Sym.Name == s.Name {
+ return f
+ }
+ if dostrcmp == 2 && strings.EqualFold(f.Sym.Name, s.Name) {
+ return f
+ }
+ if f.Sym != s {
+ continue
+ }
+ if r != nil {
+ if errnode != nil {
+ base.Errorf("ambiguous selector %v", errnode)
+ } else if t.IsPtr() {
+ base.Errorf("ambiguous selector (%v).%v", t, s)
+ } else {
+ base.Errorf("ambiguous selector %v.%v", t, s)
+ }
+ break
+ }
+
+ r = f
+ }
+
+ return r
+}
+
+// typecheckMethodExpr checks selector expressions (ODOT) where the
+// base expression is a type expression (OTYPE).
+func typecheckMethodExpr(n *ir.SelectorExpr) (res ir.Node) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("typecheckMethodExpr", n)(&res)
+ }
+
+ t := n.X.Type()
+
+ // Compute the method set for t.
+ var ms *types.Fields
+ if t.IsInterface() {
+ ms = t.Fields()
+ } else {
+ mt := types.ReceiverBaseType(t)
+ if mt == nil {
+ base.Errorf("%v undefined (type %v has no method %v)", n, t, n.Sel)
+ n.SetType(nil)
+ return n
+ }
+ CalcMethods(mt)
+ ms = mt.AllMethods()
+
+ // The method expression T.m requires a wrapper when T
+ // is different from m's declared receiver type. We
+ // normally generate these wrappers while writing out
+ // runtime type descriptors, which is always done for
+ // types declared at package scope. However, we need
+ // to make sure to generate wrappers for anonymous
+ // receiver types too.
+ if mt.Sym() == nil {
+ NeedRuntimeType(t)
+ }
+ }
+
+ s := n.Sel
+ m := lookdot1(n, s, t, ms, 0)
+ if m == nil {
+ if lookdot1(n, s, t, ms, 1) != nil {
+ base.Errorf("%v undefined (cannot refer to unexported method %v)", n, s)
+ } else if _, ambig := dotpath(s, t, nil, false); ambig {
+ base.Errorf("%v undefined (ambiguous selector)", n) // method or field
+ } else {
+ base.Errorf("%v undefined (type %v has no method %v)", n, t, s)
+ }
+ n.SetType(nil)
+ return n
+ }
+
+ if !types.IsMethodApplicable(t, m) {
+ base.Errorf("invalid method expression %v (needs pointer receiver: (*%v).%S)", n, t, s)
+ n.SetType(nil)
+ return n
+ }
+
+ n.SetOp(ir.OMETHEXPR)
+ n.Selection = m
+ n.SetType(NewMethodType(m.Type, n.X.Type()))
+ return n
+}
+
+func derefall(t *types.Type) *types.Type {
+ for t != nil && t.IsPtr() {
+ t = t.Elem()
+ }
+ return t
+}
+
+func lookdot(n *ir.SelectorExpr, t *types.Type, dostrcmp int) *types.Field {
+ s := n.Sel
+
+ types.CalcSize(t)
+ var f1 *types.Field
+ if t.IsStruct() || t.IsInterface() {
+ f1 = lookdot1(n, s, t, t.Fields(), dostrcmp)
+ }
+
+ var f2 *types.Field
+ if n.X.Type() == t || n.X.Type().Sym() == nil {
+ mt := types.ReceiverBaseType(t)
+ if mt != nil {
+ f2 = lookdot1(n, s, mt, mt.Methods(), dostrcmp)
+ }
+ }
+
+ if f1 != nil {
+ if dostrcmp > 1 || f1.Broke() {
+ // Already in the process of diagnosing an error.
+ return f1
+ }
+ if f2 != nil {
+ base.Errorf("%v is both field and method", n.Sel)
+ }
+ if f1.Offset == types.BADWIDTH {
+ base.Fatalf("lookdot badwidth %v %p", f1, f1)
+ }
+ n.Selection = f1
+ n.SetType(f1.Type)
+ if t.IsInterface() {
+ if n.X.Type().IsPtr() {
+ star := ir.NewStarExpr(base.Pos, n.X)
+ star.SetImplicit(true)
+ n.X = Expr(star)
+ }
+
+ n.SetOp(ir.ODOTINTER)
+ }
+ return f1
+ }
+
+ if f2 != nil {
+ if dostrcmp > 1 {
+ // Already in the process of diagnosing an error.
+ return f2
+ }
+ orig := n.X
+ tt := n.X.Type()
+ types.CalcSize(tt)
+ rcvr := f2.Type.Recv().Type
+ if !types.Identical(rcvr, tt) {
+ if rcvr.IsPtr() && types.Identical(rcvr.Elem(), tt) {
+ checklvalue(n.X, "call pointer method on")
+ addr := NodAddr(n.X)
+ addr.SetImplicit(true)
+ n.X = typecheck(addr, ctxType|ctxExpr)
+ } else if tt.IsPtr() && (!rcvr.IsPtr() || rcvr.IsPtr() && rcvr.Elem().NotInHeap()) && types.Identical(tt.Elem(), rcvr) {
+ star := ir.NewStarExpr(base.Pos, n.X)
+ star.SetImplicit(true)
+ n.X = typecheck(star, ctxType|ctxExpr)
+ } else if tt.IsPtr() && tt.Elem().IsPtr() && types.Identical(derefall(tt), derefall(rcvr)) {
+ base.Errorf("calling method %v with receiver %L requires explicit dereference", n.Sel, n.X)
+ for tt.IsPtr() {
+ // Stop one level early for method with pointer receiver.
+ if rcvr.IsPtr() && !tt.Elem().IsPtr() {
+ break
+ }
+ star := ir.NewStarExpr(base.Pos, n.X)
+ star.SetImplicit(true)
+ n.X = typecheck(star, ctxType|ctxExpr)
+ tt = tt.Elem()
+ }
+ } else {
+ base.Fatalf("method mismatch: %v for %v", rcvr, tt)
+ }
+ }
+
+ // Check that we haven't implicitly dereferenced any defined pointer types.
+ for x := n.X; ; {
+ var inner ir.Node
+ implicit := false
+ switch x := x.(type) {
+ case *ir.AddrExpr:
+ inner, implicit = x.X, x.Implicit()
+ case *ir.SelectorExpr:
+ inner, implicit = x.X, x.Implicit()
+ case *ir.StarExpr:
+ inner, implicit = x.X, x.Implicit()
+ }
+ if !implicit {
+ break
+ }
+ if inner.Type().Sym() != nil && (x.Op() == ir.ODEREF || x.Op() == ir.ODOTPTR) {
+ // Found an implicit dereference of a defined pointer type.
+ // Restore n.X for better error message.
+ n.X = orig
+ return nil
+ }
+ x = inner
+ }
+
+ n.Selection = f2
+ n.SetType(f2.Type)
+ n.SetOp(ir.ODOTMETH)
+
+ return f2
+ }
+
+ return nil
+}
+
+func nokeys(l ir.Nodes) bool {
+ for _, n := range l {
+ if n.Op() == ir.OKEY || n.Op() == ir.OSTRUCTKEY {
+ return false
+ }
+ }
+ return true
+}
+
+func hasddd(t *types.Type) bool {
+ for _, tl := range t.Fields().Slice() {
+ if tl.IsDDD() {
+ return true
+ }
+ }
+
+ return false
+}
+
+// typecheck assignment: type list = expression list
+func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes, desc func() string) {
+ var t *types.Type
+ var i int
+
+ lno := base.Pos
+ defer func() { base.Pos = lno }()
+
+ if tstruct.Broke() {
+ return
+ }
+
+ var n ir.Node
+ if len(nl) == 1 {
+ n = nl[0]
+ }
+
+ n1 := tstruct.NumFields()
+ n2 := len(nl)
+ if !hasddd(tstruct) {
+ if n2 > n1 {
+ goto toomany
+ }
+ if n2 < n1 {
+ goto notenough
+ }
+ } else {
+ if !isddd {
+ if n2 < n1-1 {
+ goto notenough
+ }
+ } else {
+ if n2 > n1 {
+ goto toomany
+ }
+ if n2 < n1 {
+ goto notenough
+ }
+ }
+ }
+
+ i = 0
+ for _, tl := range tstruct.Fields().Slice() {
+ t = tl.Type
+ if tl.IsDDD() {
+ if isddd {
+ if i >= len(nl) {
+ goto notenough
+ }
+ if len(nl)-i > 1 {
+ goto toomany
+ }
+ n = nl[i]
+ ir.SetPos(n)
+ if n.Type() != nil {
+ nl[i] = assignconvfn(n, t, desc)
+ }
+ return
+ }
+
+ // TODO(mdempsky): Make into ... call with implicit slice.
+ for ; i < len(nl); i++ {
+ n = nl[i]
+ ir.SetPos(n)
+ if n.Type() != nil {
+ nl[i] = assignconvfn(n, t.Elem(), desc)
+ }
+ }
+ return
+ }
+
+ if i >= len(nl) {
+ goto notenough
+ }
+ n = nl[i]
+ ir.SetPos(n)
+ if n.Type() != nil {
+ nl[i] = assignconvfn(n, t, desc)
+ }
+ i++
+ }
+
+ if i < len(nl) {
+ goto toomany
+ }
+ if isddd {
+ if call != nil {
+ base.Errorf("invalid use of ... in call to %v", call)
+ } else {
+ base.Errorf("invalid use of ... in %v", op)
+ }
+ }
+ return
+
+notenough:
+ 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.
+ // Method expressions have the form T.M, and the compiler has
+ // rewritten those to ONAME nodes but left T in Left.
+ if call.Op() == ir.OMETHEXPR {
+ call := call.(*ir.SelectorExpr)
+ base.Errorf("not enough arguments in call to method expression %v%s", call, details)
+ } else {
+ base.Errorf("not enough arguments in call to %v%s", call, details)
+ }
+ } else {
+ base.Errorf("not enough arguments to %v%s", op, details)
+ }
+ if n != nil {
+ n.SetDiag(true)
+ }
+ }
+ return
+
+toomany:
+ details := errorDetails(nl, tstruct, isddd)
+ if call != nil {
+ base.Errorf("too many arguments in call to %v%s", call, details)
+ } else {
+ base.Errorf("too many arguments to %v%s", op, details)
+ }
+}
+
+func errorDetails(nl ir.Nodes, tstruct *types.Type, isddd bool) string {
+ // If we don't know any type at a call site, let's suppress any return
+ // message signatures. See Issue https://golang.org/issues/19012.
+ if tstruct == nil {
+ return ""
+ }
+ // If any node has an unknown type, suppress it as well
+ for _, n := range nl {
+ if n.Type() == nil {
+ return ""
+ }
+ }
+ return fmt.Sprintf("\n\thave %s\n\twant %v", fmtSignature(nl, isddd), tstruct)
+}
+
+// sigrepr is a type's representation to the outside world,
+// in string representations of return signatures
+// e.g in error messages about wrong arguments to return.
+func sigrepr(t *types.Type, isddd bool) string {
+ switch t {
+ case types.UntypedString:
+ return "string"
+ case types.UntypedBool:
+ return "bool"
+ }
+
+ if t.Kind() == types.TIDEAL {
+ // "untyped number" is not commonly used
+ // outside of the compiler, so let's use "number".
+ // TODO(mdempsky): Revisit this.
+ return "number"
+ }
+
+ // Turn []T... argument to ...T for clearer error message.
+ if isddd {
+ if !t.IsSlice() {
+ base.Fatalf("bad type for ... argument: %v", t)
+ }
+ return "..." + t.Elem().String()
+ }
+ return t.String()
+}
+
+// sigerr returns the signature of the types at the call or return.
+func fmtSignature(nl ir.Nodes, isddd bool) string {
+ if len(nl) < 1 {
+ return "()"
+ }
+
+ var typeStrings []string
+ for i, n := range nl {
+ isdddArg := isddd && i == len(nl)-1
+ typeStrings = append(typeStrings, sigrepr(n.Type(), isdddArg))
+ }
+
+ return fmt.Sprintf("(%s)", strings.Join(typeStrings, ", "))
+}
+
+// type check composite
+func fielddup(name string, hash map[string]bool) {
+ if hash[name] {
+ base.Errorf("duplicate field name in struct literal: %s", name)
+ return
+ }
+ hash[name] = true
+}
+
+// iscomptype reports whether type t is a composite literal type.
+func iscomptype(t *types.Type) bool {
+ switch t.Kind() {
+ case types.TARRAY, types.TSLICE, types.TSTRUCT, types.TMAP:
+ return true
+ default:
+ return false
+ }
+}
+
+// pushtype adds elided type information for composite literals if
+// appropriate, and returns the resulting expression.
+func pushtype(nn ir.Node, t *types.Type) ir.Node {
+ if nn == nil || nn.Op() != ir.OCOMPLIT {
+ return nn
+ }
+ n := nn.(*ir.CompLitExpr)
+ if n.Ntype != nil {
+ return n
+ }
+
+ switch {
+ case iscomptype(t):
+ // For T, return T{...}.
+ n.Ntype = ir.TypeNode(t)
+
+ case t.IsPtr() && iscomptype(t.Elem()):
+ // For *T, return &T{...}.
+ n.Ntype = ir.TypeNode(t.Elem())
+
+ addr := NodAddrAt(n.Pos(), n)
+ addr.SetImplicit(true)
+ return addr
+ }
+ return n
+}
+
+// typecheckarraylit type-checks a sequence of slice/array literal elements.
+func typecheckarraylit(elemType *types.Type, bound int64, elts []ir.Node, ctx string) int64 {
+ // If there are key/value pairs, create a map to keep seen
+ // keys so we can check for duplicate indices.
+ var indices map[int64]bool
+ for _, elt := range elts {
+ if elt.Op() == ir.OKEY {
+ indices = make(map[int64]bool)
+ break
+ }
+ }
+
+ var key, length int64
+ for i, elt := range elts {
+ ir.SetPos(elt)
+ r := elts[i]
+ var kv *ir.KeyExpr
+ if elt.Op() == ir.OKEY {
+ elt := elt.(*ir.KeyExpr)
+ elt.Key = Expr(elt.Key)
+ key = IndexConst(elt.Key)
+ if key < 0 {
+ if !elt.Key.Diag() {
+ if key == -2 {
+ base.Errorf("index too large")
+ } else {
+ base.Errorf("index must be non-negative integer constant")
+ }
+ elt.Key.SetDiag(true)
+ }
+ key = -(1 << 30) // stay negative for a while
+ }
+ kv = elt
+ r = elt.Value
+ }
+
+ r = pushtype(r, elemType)
+ r = Expr(r)
+ r = AssignConv(r, elemType, ctx)
+ if kv != nil {
+ kv.Value = r
+ } else {
+ elts[i] = r
+ }
+
+ if key >= 0 {
+ if indices != nil {
+ if indices[key] {
+ base.Errorf("duplicate index in %s: %d", ctx, key)
+ } else {
+ indices[key] = true
+ }
+ }
+
+ if bound >= 0 && key >= bound {
+ base.Errorf("array index %d out of bounds [0:%d]", key, bound)
+ bound = -1
+ }
+ }
+
+ key++
+ if key > length {
+ length = key
+ }
+ }
+
+ return length
+}
+
+// visible reports whether sym is exported or locally defined.
+func visible(sym *types.Sym) bool {
+ return sym != nil && (types.IsExported(sym.Name) || sym.Pkg == types.LocalPkg)
+}
+
+// nonexported reports whether sym is an unexported field.
+func nonexported(sym *types.Sym) bool {
+ return sym != nil && !types.IsExported(sym.Name)
+}
+
+func checklvalue(n ir.Node, verb string) {
+ if !ir.IsAddressable(n) {
+ base.Errorf("cannot %s %v", verb, n)
+ }
+}
+
+func checkassign(stmt ir.Node, n ir.Node) {
+ // have already complained about n being invalid
+ if n.Type() == nil {
+ if base.Errors() == 0 {
+ base.Fatalf("expected an error about %v", n)
+ }
+ return
+ }
+
+ if ir.IsAddressable(n) {
+ return
+ }
+ if n.Op() == ir.OINDEXMAP {
+ n := n.(*ir.IndexExpr)
+ n.Assigned = true
+ return
+ }
+
+ switch {
+ case n.Op() == ir.ODOT && n.(*ir.SelectorExpr).X.Op() == ir.OINDEXMAP:
+ base.Errorf("cannot assign to struct field %v in map", n)
+ case (n.Op() == ir.OINDEX && n.(*ir.IndexExpr).X.Type().IsString()) || n.Op() == ir.OSLICESTR:
+ base.Errorf("cannot assign to %v (strings are immutable)", n)
+ case n.Op() == ir.OLITERAL && n.Sym() != nil && ir.IsConstNode(n):
+ base.Errorf("cannot assign to %v (declared const)", n)
+ default:
+ base.Errorf("cannot assign to %v", n)
+ }
+ n.SetType(nil)
+}
+
+func checkassignlist(stmt ir.Node, l ir.Nodes) {
+ for _, n := range l {
+ checkassign(stmt, n)
+ }
+}
+
+func checkassignto(src *types.Type, dst ir.Node) {
+ // TODO(mdempsky): Handle all untyped types correctly.
+ if src == types.UntypedBool && dst.Type().IsBoolean() {
+ return
+ }
+
+ if op, why := assignop(src, dst.Type()); op == ir.OXXX {
+ base.Errorf("cannot assign %v to %L in multiple assignment%s", src, dst, why)
+ return
+ }
+}
+
+// The result of stringtoruneslit MUST be assigned back to n, e.g.
+// n.Left = stringtoruneslit(n.Left)
+func stringtoruneslit(n *ir.ConvExpr) ir.Node {
+ if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String {
+ base.Fatalf("stringtoarraylit %v", n)
+ }
+
+ var l []ir.Node
+ i := 0
+ for _, r := range ir.StringVal(n.X) {
+ l = append(l, ir.NewKeyExpr(base.Pos, ir.NewInt(int64(i)), ir.NewInt(int64(r))))
+ i++
+ }
+
+ nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil)
+ nn.List = l
+ return Expr(nn)
+}
+
+var mapqueue []*ir.MapType
+
+func CheckMapKeys() {
+ for _, n := range mapqueue {
+ k := n.Type().MapType().Key
+ if !k.Broke() && !types.IsComparable(k) {
+ base.ErrorfAt(n.Pos(), "invalid map key type %v", k)
+ }
+ }
+ 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)
+ if n.Curfn != nil {
+ TypeGen++
+ t.Vargen = TypeGen
+ }
+
+ if n.Pragma()&ir.NotInHeap != 0 {
+ t.SetNotInHeap(true)
+ }
+
+ n.SetType(t)
+ n.SetTypecheck(1)
+ n.SetWalkdef(1)
+
+ types.DeferCheckSize()
+ errorsBefore := base.Errors()
+ n.Ntype = typecheckNtype(n.Ntype)
+ if underlying := n.Ntype.Type(); underlying != nil {
+ t.SetUnderlying(underlying)
+ } else {
+ n.SetDiag(true)
+ n.SetType(nil)
+ }
+ if t.Kind() == types.TFORW && base.Errors() > errorsBefore {
+ // Something went wrong during type-checking,
+ // but it was reported. Silence future errors.
+ t.SetBroke(true)
+ }
+ types.ResumeCheckSize()
+}
+
+func typecheckdef(n *ir.Name) {
+ if base.EnableTrace && base.Flag.LowerT {
+ defer tracePrint("typecheckdef", n)(nil)
+ }
+
+ if n.Walkdef() == 1 {
+ return
+ }
+
+ if n.Type() != nil { // builtin
+ // Mark as Walkdef so that if n.SetType(nil) is called later, we
+ // won't try walking again.
+ if got := n.Walkdef(); got != 0 {
+ base.Fatalf("unexpected walkdef: %v", got)
+ }
+ n.SetWalkdef(1)
+ return
+ }
+
+ lno := ir.SetPos(n)
+ typecheckdefstack = append(typecheckdefstack, n)
+ if n.Walkdef() == 2 {
+ base.FlushErrors()
+ fmt.Printf("typecheckdef loop:")
+ for i := len(typecheckdefstack) - 1; i >= 0; i-- {
+ n := typecheckdefstack[i]
+ fmt.Printf(" %v", n.Sym())
+ }
+ fmt.Printf("\n")
+ base.Fatalf("typecheckdef loop")
+ }
+
+ n.SetWalkdef(2)
+
+ switch n.Op() {
+ default:
+ base.Fatalf("typecheckdef %v", n.Op())
+
+ case ir.OLITERAL:
+ if n.Ntype != nil {
+ n.Ntype = typecheckNtype(n.Ntype)
+ n.SetType(n.Ntype.Type())
+ n.Ntype = nil
+ if n.Type() == nil {
+ n.SetDiag(true)
+ goto ret
+ }
+ }
+
+ e := n.Defn
+ n.Defn = nil
+ if e == nil {
+ ir.Dump("typecheckdef nil defn", n)
+ base.ErrorfAt(n.Pos(), "xxx")
+ }
+
+ e = Expr(e)
+ if e.Type() == nil {
+ goto ret
+ }
+ if !ir.IsConstNode(e) {
+ if !e.Diag() {
+ if e.Op() == ir.ONIL {
+ base.ErrorfAt(n.Pos(), "const initializer cannot be nil")
+ } else {
+ base.ErrorfAt(n.Pos(), "const initializer %v is not a constant", e)
+ }
+ e.SetDiag(true)
+ }
+ goto ret
+ }
+
+ t := n.Type()
+ if t != nil {
+ if !ir.OKForConst[t.Kind()] {
+ base.ErrorfAt(n.Pos(), "invalid constant type %v", t)
+ goto ret
+ }
+
+ if !e.Type().IsUntyped() && !types.Identical(t, e.Type()) {
+ base.ErrorfAt(n.Pos(), "cannot use %L as type %v in const initializer", e, t)
+ goto ret
+ }
+
+ e = convlit(e, t)
+ }
+
+ n.SetType(e.Type())
+ if n.Type() != nil {
+ n.SetVal(e.Val())
+ }
+
+ case ir.ONAME:
+ if n.Ntype != nil {
+ n.Ntype = typecheckNtype(n.Ntype)
+ n.SetType(n.Ntype.Type())
+ if n.Type() == nil {
+ n.SetDiag(true)
+ goto ret
+ }
+ }
+
+ if n.Type() != nil {
+ break
+ }
+ if n.Defn == nil {
+ if n.BuiltinOp != 0 { // like OPRINTN
+ break
+ }
+ if base.Errors() > 0 {
+ // Can have undefined variables in x := foo
+ // that make x have an n.name.Defn == nil.
+ // If there are other errors anyway, don't
+ // bother adding to the noise.
+ break
+ }
+
+ base.Fatalf("var without type, init: %v", n.Sym())
+ }
+
+ if n.Defn.Op() == ir.ONAME {
+ n.Defn = Expr(n.Defn)
+ n.SetType(n.Defn.Type())
+ break
+ }
+
+ n.Defn = Stmt(n.Defn) // fills in n.Type
+
+ case ir.OTYPE:
+ if n.Alias() {
+ // Type alias declaration: Simply use the rhs type - no need
+ // to create a new type.
+ // If we have a syntax error, name.Ntype may be nil.
+ if n.Ntype != nil {
+ n.Ntype = typecheckNtype(n.Ntype)
+ n.SetType(n.Ntype.Type())
+ if n.Type() == nil {
+ n.SetDiag(true)
+ goto ret
+ }
+ // For package-level type aliases, set n.Sym.Def so we can identify
+ // it as a type alias during export. See also #31959.
+ if n.Curfn == nil {
+ n.Sym().Def = n.Ntype
+ }
+ }
+ break
+ }
+
+ // regular type declaration
+ typecheckdeftype(n)
+ }
+
+ret:
+ if n.Op() != ir.OLITERAL && n.Type() != nil && n.Type().IsUntyped() {
+ base.Fatalf("got %v for %v", n.Type(), n)
+ }
+ last := len(typecheckdefstack) - 1
+ if typecheckdefstack[last] != n {
+ base.Fatalf("typecheckdefstack mismatch")
+ }
+ typecheckdefstack[last] = nil
+ typecheckdefstack = typecheckdefstack[:last]
+
+ base.Pos = lno
+ n.SetWalkdef(1)
+}
+
+func checkmake(t *types.Type, arg string, np *ir.Node) bool {
+ n := *np
+ if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL {
+ base.Errorf("non-integer %s argument in make(%v) - %v", arg, t, n.Type())
+ return false
+ }
+
+ // Do range checks for constants before DefaultLit
+ // to avoid redundant "constant NNN overflows int" errors.
+ if n.Op() == ir.OLITERAL {
+ v := toint(n.Val())
+ if constant.Sign(v) < 0 {
+ base.Errorf("negative %s argument in make(%v)", arg, t)
+ return false
+ }
+ if ir.ConstOverflow(v, types.Types[types.TINT]) {
+ base.Errorf("%s argument too large in make(%v)", arg, t)
+ return false
+ }
+ }
+
+ // DefaultLit is necessary for non-constants too: n might be 1.1< 0 {
+ if x := typecheckdefstack[i-1]; x.Op() == ir.OLITERAL {
+ return x.Iota()
+ }
+ }
+
+ if ir.CurFunc != nil && ir.CurFunc.Iota >= 0 {
+ return ir.CurFunc.Iota
+ }
+
+ return -1
+}
+
+// curpkg returns the current package, based on Curfn.
+func curpkg() *types.Pkg {
+ fn := ir.CurFunc
+ if fn == nil {
+ // Initialization expressions for package-scope variables.
+ return types.LocalPkg
+ }
+ return fnpkg(fn.Nname)
+}
+
+func Conv(n ir.Node, t *types.Type) ir.Node {
+ if types.Identical(n.Type(), t) {
+ return n
+ }
+ n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
+ n.SetType(t)
+ n = Expr(n)
+ return n
+}
+
+// ConvNop converts node n to type t using the OCONVNOP op
+// and typechecks the result with ctxExpr.
+func ConvNop(n ir.Node, t *types.Type) ir.Node {
+ if types.Identical(n.Type(), t) {
+ return n
+ }
+ n = ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n)
+ n.SetType(t)
+ n = Expr(n)
+ return n
+}
diff --git a/src/cmd/compile/internal/typecheck/universe.go b/src/cmd/compile/internal/typecheck/universe.go
new file mode 100644
index 0000000000000..402b8deeb3889
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/universe.go
@@ -0,0 +1,362 @@
+// Copyright 2009 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 typecheck
+
+import (
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+var (
+ okfor [ir.OEND][]bool
+ iscmp [ir.OEND]bool
+)
+
+var (
+ okforeq [types.NTYPE]bool
+ okforadd [types.NTYPE]bool
+ okforand [types.NTYPE]bool
+ okfornone [types.NTYPE]bool
+ okforbool [types.NTYPE]bool
+ okforcap [types.NTYPE]bool
+ okforlen [types.NTYPE]bool
+ okforarith [types.NTYPE]bool
+)
+
+var basicTypes = [...]struct {
+ name string
+ etype types.Kind
+}{
+ {"int8", types.TINT8},
+ {"int16", types.TINT16},
+ {"int32", types.TINT32},
+ {"int64", types.TINT64},
+ {"uint8", types.TUINT8},
+ {"uint16", types.TUINT16},
+ {"uint32", types.TUINT32},
+ {"uint64", types.TUINT64},
+ {"float32", types.TFLOAT32},
+ {"float64", types.TFLOAT64},
+ {"complex64", types.TCOMPLEX64},
+ {"complex128", types.TCOMPLEX128},
+ {"bool", types.TBOOL},
+ {"string", types.TSTRING},
+}
+
+var typedefs = [...]struct {
+ name string
+ etype types.Kind
+ sameas32 types.Kind
+ sameas64 types.Kind
+}{
+ {"int", types.TINT, types.TINT32, types.TINT64},
+ {"uint", types.TUINT, types.TUINT32, types.TUINT64},
+ {"uintptr", types.TUINTPTR, types.TUINT32, types.TUINT64},
+}
+
+var builtinFuncs = [...]struct {
+ name string
+ op ir.Op
+}{
+ {"append", ir.OAPPEND},
+ {"cap", ir.OCAP},
+ {"close", ir.OCLOSE},
+ {"complex", ir.OCOMPLEX},
+ {"copy", ir.OCOPY},
+ {"delete", ir.ODELETE},
+ {"imag", ir.OIMAG},
+ {"len", ir.OLEN},
+ {"make", ir.OMAKE},
+ {"new", ir.ONEW},
+ {"panic", ir.OPANIC},
+ {"print", ir.OPRINT},
+ {"println", ir.OPRINTN},
+ {"real", ir.OREAL},
+ {"recover", ir.ORECOVER},
+}
+
+var unsafeFuncs = [...]struct {
+ name string
+ op ir.Op
+}{
+ {"Alignof", ir.OALIGNOF},
+ {"Offsetof", ir.OOFFSETOF},
+ {"Sizeof", ir.OSIZEOF},
+}
+
+// InitUniverse initializes the universe block.
+func InitUniverse() {
+ if types.PtrSize == 0 {
+ base.Fatalf("typeinit before betypeinit")
+ }
+
+ types.SlicePtrOffset = 0
+ types.SliceLenOffset = types.Rnd(types.SlicePtrOffset+int64(types.PtrSize), int64(types.PtrSize))
+ types.SliceCapOffset = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize))
+ types.SliceSize = types.Rnd(types.SliceCapOffset+int64(types.PtrSize), int64(types.PtrSize))
+
+ // string is same as slice wo the cap
+ types.StringSize = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize))
+
+ for et := types.Kind(0); et < types.NTYPE; et++ {
+ types.SimType[et] = et
+ }
+
+ types.Types[types.TANY] = types.New(types.TANY)
+ types.Types[types.TINTER] = types.NewInterface(types.LocalPkg, nil)
+
+ defBasic := func(kind types.Kind, pkg *types.Pkg, name string) *types.Type {
+ sym := pkg.Lookup(name)
+ n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym)
+ t := types.NewBasic(kind, n)
+ n.SetType(t)
+ sym.Def = n
+ if kind != types.TANY {
+ types.CalcSize(t)
+ }
+ return t
+ }
+
+ for _, s := range &basicTypes {
+ types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name)
+ }
+
+ for _, s := range &typedefs {
+ sameas := s.sameas32
+ if types.PtrSize == 8 {
+ sameas = s.sameas64
+ }
+ types.SimType[s.etype] = sameas
+
+ types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name)
+ }
+
+ // We create separate byte and rune types for better error messages
+ // rather than just creating type alias *types.Sym's for the uint8 and
+ // int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false.
+ // TODO(gri) Should we get rid of this special case (at the cost
+ // of less informative error messages involving bytes and runes)?
+ // (Alternatively, we could introduce an OTALIAS node representing
+ // type aliases, albeit at the cost of having to deal with it everywhere).
+ types.ByteType = defBasic(types.TUINT8, types.BuiltinPkg, "byte")
+ types.RuneType = defBasic(types.TINT32, types.BuiltinPkg, "rune")
+
+ // error type
+ s := types.BuiltinPkg.Lookup("error")
+ n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, s)
+ types.ErrorType = types.NewNamed(n)
+ types.ErrorType.SetUnderlying(makeErrorInterface())
+ n.SetType(types.ErrorType)
+ s.Def = n
+ types.CalcSize(types.ErrorType)
+
+ types.Types[types.TUNSAFEPTR] = defBasic(types.TUNSAFEPTR, ir.Pkgs.Unsafe, "Pointer")
+
+ // simple aliases
+ types.SimType[types.TMAP] = types.TPTR
+ types.SimType[types.TCHAN] = types.TPTR
+ types.SimType[types.TFUNC] = types.TPTR
+ types.SimType[types.TUNSAFEPTR] = types.TPTR
+
+ for _, s := range &builtinFuncs {
+ s2 := types.BuiltinPkg.Lookup(s.name)
+ def := NewName(s2)
+ def.BuiltinOp = s.op
+ s2.Def = def
+ }
+
+ for _, s := range &unsafeFuncs {
+ s2 := ir.Pkgs.Unsafe.Lookup(s.name)
+ def := NewName(s2)
+ def.BuiltinOp = s.op
+ s2.Def = def
+ }
+
+ s = types.BuiltinPkg.Lookup("true")
+ s.Def = ir.NewConstAt(src.NoXPos, s, types.UntypedBool, constant.MakeBool(true))
+
+ s = types.BuiltinPkg.Lookup("false")
+ s.Def = ir.NewConstAt(src.NoXPos, s, types.UntypedBool, constant.MakeBool(false))
+
+ s = Lookup("_")
+ types.BlankSym = s
+ s.Block = -100
+ s.Def = NewName(s)
+ types.Types[types.TBLANK] = types.New(types.TBLANK)
+ ir.AsNode(s.Def).SetType(types.Types[types.TBLANK])
+ ir.BlankNode = ir.AsNode(s.Def)
+ ir.BlankNode.SetTypecheck(1)
+
+ s = types.BuiltinPkg.Lookup("_")
+ s.Block = -100
+ s.Def = NewName(s)
+ types.Types[types.TBLANK] = types.New(types.TBLANK)
+ ir.AsNode(s.Def).SetType(types.Types[types.TBLANK])
+
+ types.Types[types.TNIL] = types.New(types.TNIL)
+ s = types.BuiltinPkg.Lookup("nil")
+ nnil := NodNil()
+ nnil.(*ir.NilExpr).SetSym(s)
+ s.Def = nnil
+
+ s = types.BuiltinPkg.Lookup("iota")
+ s.Def = ir.NewIota(base.Pos, s)
+
+ for et := types.TINT8; et <= types.TUINT64; et++ {
+ types.IsInt[et] = true
+ }
+ types.IsInt[types.TINT] = true
+ types.IsInt[types.TUINT] = true
+ types.IsInt[types.TUINTPTR] = true
+
+ types.IsFloat[types.TFLOAT32] = true
+ types.IsFloat[types.TFLOAT64] = true
+
+ types.IsComplex[types.TCOMPLEX64] = true
+ types.IsComplex[types.TCOMPLEX128] = true
+
+ // initialize okfor
+ for et := types.Kind(0); et < types.NTYPE; et++ {
+ if types.IsInt[et] || et == types.TIDEAL {
+ okforeq[et] = true
+ types.IsOrdered[et] = true
+ okforarith[et] = true
+ okforadd[et] = true
+ okforand[et] = true
+ ir.OKForConst[et] = true
+ types.IsSimple[et] = true
+ }
+
+ if types.IsFloat[et] {
+ okforeq[et] = true
+ types.IsOrdered[et] = true
+ okforadd[et] = true
+ okforarith[et] = true
+ ir.OKForConst[et] = true
+ types.IsSimple[et] = true
+ }
+
+ if types.IsComplex[et] {
+ okforeq[et] = true
+ okforadd[et] = true
+ okforarith[et] = true
+ ir.OKForConst[et] = true
+ types.IsSimple[et] = true
+ }
+ }
+
+ types.IsSimple[types.TBOOL] = true
+
+ okforadd[types.TSTRING] = true
+
+ okforbool[types.TBOOL] = true
+
+ okforcap[types.TARRAY] = true
+ okforcap[types.TCHAN] = true
+ okforcap[types.TSLICE] = true
+
+ ir.OKForConst[types.TBOOL] = true
+ ir.OKForConst[types.TSTRING] = true
+
+ okforlen[types.TARRAY] = true
+ okforlen[types.TCHAN] = true
+ okforlen[types.TMAP] = true
+ okforlen[types.TSLICE] = true
+ okforlen[types.TSTRING] = true
+
+ okforeq[types.TPTR] = true
+ okforeq[types.TUNSAFEPTR] = true
+ okforeq[types.TINTER] = true
+ okforeq[types.TCHAN] = true
+ okforeq[types.TSTRING] = true
+ okforeq[types.TBOOL] = true
+ okforeq[types.TMAP] = true // nil only; refined in typecheck
+ okforeq[types.TFUNC] = true // nil only; refined in typecheck
+ okforeq[types.TSLICE] = true // nil only; refined in typecheck
+ okforeq[types.TARRAY] = true // only if element type is comparable; refined in typecheck
+ okforeq[types.TSTRUCT] = true // only if all struct fields are comparable; refined in typecheck
+
+ types.IsOrdered[types.TSTRING] = true
+
+ for i := range okfor {
+ okfor[i] = okfornone[:]
+ }
+
+ // binary
+ okfor[ir.OADD] = okforadd[:]
+ okfor[ir.OAND] = okforand[:]
+ okfor[ir.OANDAND] = okforbool[:]
+ okfor[ir.OANDNOT] = okforand[:]
+ okfor[ir.ODIV] = okforarith[:]
+ okfor[ir.OEQ] = okforeq[:]
+ okfor[ir.OGE] = types.IsOrdered[:]
+ okfor[ir.OGT] = types.IsOrdered[:]
+ okfor[ir.OLE] = types.IsOrdered[:]
+ okfor[ir.OLT] = types.IsOrdered[:]
+ okfor[ir.OMOD] = okforand[:]
+ okfor[ir.OMUL] = okforarith[:]
+ okfor[ir.ONE] = okforeq[:]
+ okfor[ir.OOR] = okforand[:]
+ okfor[ir.OOROR] = okforbool[:]
+ okfor[ir.OSUB] = okforarith[:]
+ okfor[ir.OXOR] = okforand[:]
+ okfor[ir.OLSH] = okforand[:]
+ okfor[ir.ORSH] = okforand[:]
+
+ // unary
+ okfor[ir.OBITNOT] = okforand[:]
+ okfor[ir.ONEG] = okforarith[:]
+ okfor[ir.ONOT] = okforbool[:]
+ okfor[ir.OPLUS] = okforarith[:]
+
+ // special
+ okfor[ir.OCAP] = okforcap[:]
+ okfor[ir.OLEN] = okforlen[:]
+
+ // comparison
+ iscmp[ir.OLT] = true
+ iscmp[ir.OGT] = true
+ iscmp[ir.OGE] = true
+ iscmp[ir.OLE] = true
+ iscmp[ir.OEQ] = true
+ iscmp[ir.ONE] = true
+}
+
+func makeErrorInterface() *types.Type {
+ sig := types.NewSignature(types.NoPkg, fakeRecvField(), nil, []*types.Field{
+ types.NewField(src.NoXPos, nil, types.Types[types.TSTRING]),
+ })
+ method := types.NewField(src.NoXPos, Lookup("Error"), sig)
+ return types.NewInterface(types.NoPkg, []*types.Field{method})
+}
+
+// DeclareUniverse makes the universe block visible within the current package.
+func DeclareUniverse() {
+ // Operationally, this is similar to a dot import of builtinpkg, except
+ // that we silently skip symbols that are already declared in the
+ // package block rather than emitting a redeclared symbol error.
+
+ for _, s := range types.BuiltinPkg.Syms {
+ if s.Def == nil {
+ continue
+ }
+ s1 := Lookup(s.Name)
+ if s1.Def != nil {
+ continue
+ }
+
+ s1.Def = s.Def
+ s1.Block = s.Block
+ }
+
+ ir.RegFP = NewName(Lookup(".fp"))
+ ir.RegFP.SetType(types.Types[types.TINT32])
+ ir.RegFP.Class = ir.PPARAM
+ ir.RegFP.SetUsed(true)
+}
diff --git a/src/cmd/compile/internal/types/alg.go b/src/cmd/compile/internal/types/alg.go
new file mode 100644
index 0000000000000..2c2700f345730
--- /dev/null
+++ b/src/cmd/compile/internal/types/alg.go
@@ -0,0 +1,173 @@
+// 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.
+
+package types
+
+import "cmd/compile/internal/base"
+
+// AlgKind describes the kind of algorithms used for comparing and
+// hashing a Type.
+type AlgKind int
+
+//go:generate stringer -type AlgKind -trimprefix A alg.go
+
+const (
+ // These values are known by runtime.
+ ANOEQ AlgKind = iota
+ AMEM0
+ AMEM8
+ AMEM16
+ AMEM32
+ AMEM64
+ AMEM128
+ ASTRING
+ AINTER
+ ANILINTER
+ AFLOAT32
+ AFLOAT64
+ ACPLX64
+ ACPLX128
+
+ // Type can be compared/hashed as regular memory.
+ AMEM AlgKind = 100
+
+ // Type needs special comparison/hashing functions.
+ ASPECIAL AlgKind = -1
+)
+
+// AlgType returns the AlgKind used for comparing and hashing Type t.
+// If it returns ANOEQ, it also returns the component type of t that
+// makes it incomparable.
+func AlgType(t *Type) (AlgKind, *Type) {
+ if t.Broke() {
+ return AMEM, nil
+ }
+ if t.Noalg() {
+ return ANOEQ, t
+ }
+
+ switch t.Kind() {
+ case TANY, TFORW:
+ // will be defined later.
+ return ANOEQ, t
+
+ case TINT8, TUINT8, TINT16, TUINT16,
+ TINT32, TUINT32, TINT64, TUINT64,
+ TINT, TUINT, TUINTPTR,
+ TBOOL, TPTR,
+ TCHAN, TUNSAFEPTR:
+ return AMEM, nil
+
+ case TFUNC, TMAP:
+ return ANOEQ, t
+
+ case TFLOAT32:
+ return AFLOAT32, nil
+
+ case TFLOAT64:
+ return AFLOAT64, nil
+
+ case TCOMPLEX64:
+ return ACPLX64, nil
+
+ case TCOMPLEX128:
+ return ACPLX128, nil
+
+ case TSTRING:
+ return ASTRING, nil
+
+ case TINTER:
+ if t.IsEmptyInterface() {
+ return ANILINTER, nil
+ }
+ return AINTER, nil
+
+ case TSLICE:
+ return ANOEQ, t
+
+ case TARRAY:
+ a, bad := AlgType(t.Elem())
+ switch a {
+ case AMEM:
+ return AMEM, nil
+ case ANOEQ:
+ return ANOEQ, bad
+ }
+
+ switch t.NumElem() {
+ case 0:
+ // We checked above that the element type is comparable.
+ return AMEM, nil
+ case 1:
+ // Single-element array is same as its lone element.
+ return a, nil
+ }
+
+ return ASPECIAL, nil
+
+ case TSTRUCT:
+ fields := t.FieldSlice()
+
+ // One-field struct is same as that one field alone.
+ if len(fields) == 1 && !fields[0].Sym.IsBlank() {
+ return AlgType(fields[0].Type)
+ }
+
+ ret := AMEM
+ for i, f := range fields {
+ // All fields must be comparable.
+ a, bad := AlgType(f.Type)
+ if a == ANOEQ {
+ return ANOEQ, bad
+ }
+
+ // Blank fields, padded fields, fields with non-memory
+ // equality need special compare.
+ if a != AMEM || f.Sym.IsBlank() || IsPaddedField(t, i) {
+ ret = ASPECIAL
+ }
+ }
+
+ return ret, nil
+ }
+
+ base.Fatalf("AlgType: unexpected type %v", t)
+ return 0, nil
+}
+
+// TypeHasNoAlg reports whether t does not have any associated hash/eq
+// algorithms because t, or some component of t, is marked Noalg.
+func TypeHasNoAlg(t *Type) bool {
+ a, bad := AlgType(t)
+ return a == ANOEQ && bad.Noalg()
+}
+
+// IsComparable reports whether t is a comparable type.
+func IsComparable(t *Type) bool {
+ a, _ := AlgType(t)
+ return a != ANOEQ
+}
+
+// IncomparableField returns an incomparable Field of struct Type t, if any.
+func IncomparableField(t *Type) *Field {
+ for _, f := range t.FieldSlice() {
+ if !IsComparable(f.Type) {
+ return f
+ }
+ }
+ return nil
+}
+
+// IsPaddedField reports whether the i'th field of struct type t is followed
+// by padding.
+func IsPaddedField(t *Type, i int) bool {
+ if !t.IsStruct() {
+ base.Fatalf("IsPaddedField called non-struct %v", t)
+ }
+ end := t.Width
+ if i+1 < t.NumFields() {
+ end = t.Field(i + 1).Offset
+ }
+ return t.Field(i).End() != end
+}
diff --git a/src/cmd/compile/internal/gc/algkind_string.go b/src/cmd/compile/internal/types/algkind_string.go
similarity index 91%
rename from src/cmd/compile/internal/gc/algkind_string.go
rename to src/cmd/compile/internal/types/algkind_string.go
index 52b5399956170..a1b518e4dde26 100644
--- a/src/cmd/compile/internal/gc/algkind_string.go
+++ b/src/cmd/compile/internal/types/algkind_string.go
@@ -1,6 +1,6 @@
-// Code generated by "stringer -type AlgKind -trimprefix A"; DO NOT EDIT.
+// Code generated by "stringer -type AlgKind -trimprefix A alg.go"; DO NOT EDIT.
-package gc
+package types
import "strconv"
diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go
new file mode 100644
index 0000000000000..da224d40192a7
--- /dev/null
+++ b/src/cmd/compile/internal/types/fmt.go
@@ -0,0 +1,666 @@
+// Copyright 2009 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 types
+
+import (
+ "bytes"
+ "crypto/md5"
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "strconv"
+ "strings"
+ "sync"
+
+ "cmd/compile/internal/base"
+)
+
+// BuiltinPkg is a fake package that declares the universe block.
+var BuiltinPkg *Pkg
+
+// LocalPkg is the package being compiled.
+var LocalPkg *Pkg
+
+// BlankSym is the blank (_) symbol.
+var BlankSym *Sym
+
+// OrigSym returns the original symbol written by the user.
+func OrigSym(s *Sym) *Sym {
+ if s == nil {
+ return nil
+ }
+
+ if len(s.Name) > 1 && s.Name[0] == '~' {
+ switch s.Name[1] {
+ case 'r': // originally an unnamed result
+ return nil
+ case 'b': // originally the blank identifier _
+ // TODO(mdempsky): Does s.Pkg matter here?
+ return BlankSym
+ }
+ return s
+ }
+
+ if strings.HasPrefix(s.Name, ".anon") {
+ // originally an unnamed or _ name (see subr.go: NewFuncParams)
+ return nil
+ }
+
+ return s
+}
+
+// numImport tracks how often a package with a given name is imported.
+// It is used to provide a better error message (by using the package
+// path to disambiguate) if a package that appears multiple times with
+// the same name appears in an error message.
+var NumImport = make(map[string]int)
+
+// fmtMode represents the kind of printing being done.
+// The default is regular Go syntax (fmtGo).
+// fmtDebug is like fmtGo but for debugging dumps and prints the type kind too.
+// fmtTypeID and fmtTypeIDName are for generating various unique representations
+// of types used in hashes and the linker.
+type fmtMode int
+
+const (
+ fmtGo fmtMode = iota
+ fmtDebug
+ fmtTypeID
+ fmtTypeIDName
+)
+
+// Sym
+
+// Format implements formatting for a Sym.
+// The valid formats are:
+//
+// %v Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols.
+// %+v Debug syntax: always include PkgName. prefix even for local names.
+// %S Short syntax: Name only, no matter what.
+//
+func (s *Sym) Format(f fmt.State, verb rune) {
+ mode := fmtGo
+ switch verb {
+ case 'v', 'S':
+ if verb == 'v' && f.Flag('+') {
+ mode = fmtDebug
+ }
+ fmt.Fprint(f, sconv(s, verb, mode))
+
+ default:
+ fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s)
+ }
+}
+
+func (s *Sym) String() string {
+ return sconv(s, 0, fmtGo)
+}
+
+// See #16897 for details about performance implications
+// before changing the implementation of sconv.
+func sconv(s *Sym, verb rune, mode fmtMode) string {
+ if verb == 'L' {
+ panic("linksymfmt")
+ }
+
+ if s == nil {
+ return ""
+ }
+
+ if s.Name == "_" {
+ return "_"
+ }
+ buf := fmtBufferPool.Get().(*bytes.Buffer)
+ buf.Reset()
+ defer fmtBufferPool.Put(buf)
+
+ symfmt(buf, s, verb, mode)
+ return InternString(buf.Bytes())
+}
+
+func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
+ if verb == 'L' {
+ panic("linksymfmt")
+ }
+ if s == nil {
+ b.WriteString("")
+ return
+ }
+ if s.Name == "_" {
+ b.WriteString("_")
+ return
+ }
+
+ symfmt(b, s, verb, mode)
+}
+
+func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
+ if verb != 'S' {
+ switch mode {
+ case fmtGo: // This is for the user
+ if s.Pkg == BuiltinPkg || s.Pkg == LocalPkg {
+ b.WriteString(s.Name)
+ return
+ }
+
+ // If the name was used by multiple packages, display the full path,
+ if s.Pkg.Name != "" && NumImport[s.Pkg.Name] > 1 {
+ fmt.Fprintf(b, "%q.%s", s.Pkg.Path, s.Name)
+ return
+ }
+ b.WriteString(s.Pkg.Name)
+ b.WriteByte('.')
+ b.WriteString(s.Name)
+ return
+
+ case fmtDebug:
+ b.WriteString(s.Pkg.Name)
+ b.WriteByte('.')
+ b.WriteString(s.Name)
+ return
+
+ case fmtTypeIDName:
+ // dcommontype, typehash
+ b.WriteString(s.Pkg.Name)
+ b.WriteByte('.')
+ b.WriteString(s.Name)
+ return
+
+ case fmtTypeID:
+ // (methodsym), typesym, weaksym
+ b.WriteString(s.Pkg.Prefix)
+ b.WriteByte('.')
+ b.WriteString(s.Name)
+ return
+ }
+ }
+
+ b.WriteString(s.Name)
+}
+
+// Type
+
+var BasicTypeNames = []string{
+ TINT: "int",
+ TUINT: "uint",
+ TINT8: "int8",
+ TUINT8: "uint8",
+ TINT16: "int16",
+ TUINT16: "uint16",
+ TINT32: "int32",
+ TUINT32: "uint32",
+ TINT64: "int64",
+ TUINT64: "uint64",
+ TUINTPTR: "uintptr",
+ TFLOAT32: "float32",
+ TFLOAT64: "float64",
+ TCOMPLEX64: "complex64",
+ TCOMPLEX128: "complex128",
+ TBOOL: "bool",
+ TANY: "any",
+ TSTRING: "string",
+ TNIL: "nil",
+ TIDEAL: "untyped number",
+ TBLANK: "blank",
+}
+
+var fmtBufferPool = sync.Pool{
+ New: func() interface{} {
+ return new(bytes.Buffer)
+ },
+}
+
+// Format implements formatting for a Type.
+// The valid formats are:
+//
+// %v Go syntax
+// %+v Debug syntax: Go syntax with a KIND- prefix for all but builtins.
+// %L Go syntax for underlying type if t is named
+// %S short Go syntax: drop leading "func" in function type
+// %-S special case for method receiver symbol
+//
+func (t *Type) Format(s fmt.State, verb rune) {
+ mode := fmtGo
+ switch verb {
+ case 'v', 'S', 'L':
+ if verb == 'v' && s.Flag('+') { // %+v is debug format
+ mode = fmtDebug
+ }
+ if verb == 'S' && s.Flag('-') { // %-S is special case for receiver - short typeid format
+ mode = fmtTypeID
+ }
+ fmt.Fprint(s, tconv(t, verb, mode))
+ default:
+ fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t)
+ }
+}
+
+// String returns the Go syntax for the type t.
+func (t *Type) String() string {
+ return tconv(t, 0, fmtGo)
+}
+
+// ShortString generates a short description of t.
+// It is used in autogenerated method names, reflection,
+// and itab names.
+func (t *Type) ShortString() string {
+ return tconv(t, 0, fmtTypeID)
+}
+
+// LongString generates a complete description of t.
+// It is useful for reflection,
+// or when a unique fingerprint or hash of a type is required.
+func (t *Type) LongString() string {
+ return tconv(t, 0, fmtTypeIDName)
+}
+
+func tconv(t *Type, verb rune, mode fmtMode) string {
+ buf := fmtBufferPool.Get().(*bytes.Buffer)
+ buf.Reset()
+ defer fmtBufferPool.Put(buf)
+
+ tconv2(buf, t, verb, mode, nil)
+ return InternString(buf.Bytes())
+}
+
+// tconv2 writes a string representation of t to b.
+// flag and mode control exactly what is printed.
+// Any types x that are already in the visited map get printed as @%d where %d=visited[x].
+// See #16897 before changing the implementation of tconv.
+func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type]int) {
+ if off, ok := visited[t]; ok {
+ // We've seen this type before, so we're trying to print it recursively.
+ // Print a reference to it instead.
+ fmt.Fprintf(b, "@%d", off)
+ return
+ }
+ if t == nil {
+ b.WriteString("")
+ return
+ }
+ if t.Kind() == TSSA {
+ b.WriteString(t.Extra.(string))
+ return
+ }
+ if t.Kind() == TTUPLE {
+ b.WriteString(t.FieldType(0).String())
+ b.WriteByte(',')
+ b.WriteString(t.FieldType(1).String())
+ return
+ }
+
+ if t.Kind() == TRESULTS {
+ tys := t.Extra.(*Results).Types
+ for i, et := range tys {
+ if i > 0 {
+ b.WriteByte(',')
+ }
+ b.WriteString(et.String())
+ }
+ return
+ }
+
+ if t == ByteType || t == RuneType {
+ // in %-T mode collapse rune and byte with their originals.
+ switch mode {
+ case fmtTypeIDName, fmtTypeID:
+ t = Types[t.Kind()]
+ default:
+ sconv2(b, t.Sym(), 'S', mode)
+ return
+ }
+ }
+ if t == ErrorType {
+ b.WriteString("error")
+ return
+ }
+
+ // Unless the 'L' flag was specified, if the type has a name, just print that name.
+ if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
+ switch mode {
+ case fmtTypeID, fmtTypeIDName:
+ if verb == 'S' {
+ if t.Vargen != 0 {
+ sconv2(b, t.Sym(), 'S', mode)
+ fmt.Fprintf(b, "·%d", t.Vargen)
+ return
+ }
+ sconv2(b, t.Sym(), 'S', mode)
+ return
+ }
+
+ if mode == fmtTypeIDName {
+ sconv2(b, t.Sym(), 'v', fmtTypeIDName)
+ return
+ }
+
+ if t.Sym().Pkg == LocalPkg && t.Vargen != 0 {
+ sconv2(b, t.Sym(), 'v', mode)
+ fmt.Fprintf(b, "·%d", t.Vargen)
+ return
+ }
+ }
+
+ sconv2(b, t.Sym(), 'v', mode)
+ return
+ }
+
+ if int(t.Kind()) < len(BasicTypeNames) && BasicTypeNames[t.Kind()] != "" {
+ var name string
+ switch t {
+ case UntypedBool:
+ name = "untyped bool"
+ case UntypedString:
+ name = "untyped string"
+ case UntypedInt:
+ name = "untyped int"
+ case UntypedRune:
+ name = "untyped rune"
+ case UntypedFloat:
+ name = "untyped float"
+ case UntypedComplex:
+ name = "untyped complex"
+ default:
+ name = BasicTypeNames[t.Kind()]
+ }
+ b.WriteString(name)
+ return
+ }
+
+ if mode == fmtDebug {
+ b.WriteString(t.Kind().String())
+ b.WriteByte('-')
+ tconv2(b, t, 'v', fmtGo, visited)
+ return
+ }
+
+ // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't
+ // try to print it recursively.
+ // We record the offset in the result buffer where the type's text starts. This offset serves as a reference
+ // point for any later references to the same type.
+ // Note that we remove the type from the visited map as soon as the recursive call is done.
+ // This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work,
+ // but I'd like to use the @ notation only when strictly necessary.)
+ if visited == nil {
+ visited = map[*Type]int{}
+ }
+ visited[t] = b.Len()
+ defer delete(visited, t)
+
+ switch t.Kind() {
+ case TPTR:
+ b.WriteByte('*')
+ switch mode {
+ case fmtTypeID, fmtTypeIDName:
+ if verb == 'S' {
+ tconv2(b, t.Elem(), 'S', mode, visited)
+ return
+ }
+ }
+ tconv2(b, t.Elem(), 'v', mode, visited)
+
+ case TARRAY:
+ b.WriteByte('[')
+ b.WriteString(strconv.FormatInt(t.NumElem(), 10))
+ b.WriteByte(']')
+ tconv2(b, t.Elem(), 0, mode, visited)
+
+ case TSLICE:
+ b.WriteString("[]")
+ tconv2(b, t.Elem(), 0, mode, visited)
+
+ case TCHAN:
+ switch t.ChanDir() {
+ case Crecv:
+ b.WriteString("<-chan ")
+ tconv2(b, t.Elem(), 0, mode, visited)
+ case Csend:
+ b.WriteString("chan<- ")
+ tconv2(b, t.Elem(), 0, mode, visited)
+ default:
+ b.WriteString("chan ")
+ if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym() == nil && t.Elem().ChanDir() == Crecv {
+ b.WriteByte('(')
+ tconv2(b, t.Elem(), 0, mode, visited)
+ b.WriteByte(')')
+ } else {
+ tconv2(b, t.Elem(), 0, mode, visited)
+ }
+ }
+
+ case TMAP:
+ b.WriteString("map[")
+ tconv2(b, t.Key(), 0, mode, visited)
+ b.WriteByte(']')
+ tconv2(b, t.Elem(), 0, mode, visited)
+
+ case TINTER:
+ if t.IsEmptyInterface() {
+ b.WriteString("interface {}")
+ break
+ }
+ b.WriteString("interface {")
+ for i, f := range t.Fields().Slice() {
+ if i != 0 {
+ b.WriteByte(';')
+ }
+ b.WriteByte(' ')
+ switch {
+ case f.Sym == nil:
+ // Check first that a symbol is defined for this type.
+ // Wrong interface definitions may have types lacking a symbol.
+ break
+ case IsExported(f.Sym.Name):
+ sconv2(b, f.Sym, 'S', mode)
+ default:
+ if mode != fmtTypeIDName {
+ mode = fmtTypeID
+ }
+ sconv2(b, f.Sym, 'v', mode)
+ }
+ tconv2(b, f.Type, 'S', mode, visited)
+ }
+ if t.NumFields() != 0 {
+ b.WriteByte(' ')
+ }
+ b.WriteByte('}')
+
+ case TFUNC:
+ if verb == 'S' {
+ // no leading func
+ } else {
+ if t.Recv() != nil {
+ b.WriteString("method")
+ tconv2(b, t.Recvs(), 0, mode, visited)
+ b.WriteByte(' ')
+ }
+ b.WriteString("func")
+ }
+ tconv2(b, t.Params(), 0, mode, visited)
+
+ switch t.NumResults() {
+ case 0:
+ // nothing to do
+
+ case 1:
+ b.WriteByte(' ')
+ tconv2(b, t.Results().Field(0).Type, 0, mode, visited) // struct->field->field's type
+
+ default:
+ b.WriteByte(' ')
+ tconv2(b, t.Results(), 0, mode, visited)
+ }
+
+ case TSTRUCT:
+ if m := t.StructType().Map; m != nil {
+ mt := m.MapType()
+ // Format the bucket struct for map[x]y as map.bucket[x]y.
+ // This avoids a recursive print that generates very long names.
+ switch t {
+ case mt.Bucket:
+ b.WriteString("map.bucket[")
+ case mt.Hmap:
+ b.WriteString("map.hdr[")
+ case mt.Hiter:
+ b.WriteString("map.iter[")
+ default:
+ base.Fatalf("unknown internal map type")
+ }
+ tconv2(b, m.Key(), 0, mode, visited)
+ b.WriteByte(']')
+ tconv2(b, m.Elem(), 0, mode, visited)
+ break
+ }
+
+ if funarg := t.StructType().Funarg; funarg != FunargNone {
+ b.WriteByte('(')
+ fieldVerb := 'v'
+ switch mode {
+ case fmtTypeID, fmtTypeIDName, fmtGo:
+ // no argument names on function signature, and no "noescape"/"nosplit" tags
+ fieldVerb = 'S'
+ }
+ for i, f := range t.Fields().Slice() {
+ if i != 0 {
+ b.WriteString(", ")
+ }
+ fldconv(b, f, fieldVerb, mode, visited, funarg)
+ }
+ b.WriteByte(')')
+ } else {
+ b.WriteString("struct {")
+ for i, f := range t.Fields().Slice() {
+ if i != 0 {
+ b.WriteByte(';')
+ }
+ b.WriteByte(' ')
+ fldconv(b, f, 'L', mode, visited, funarg)
+ }
+ if t.NumFields() != 0 {
+ b.WriteByte(' ')
+ }
+ b.WriteByte('}')
+ }
+
+ case TFORW:
+ b.WriteString("undefined")
+ if t.Sym() != nil {
+ b.WriteByte(' ')
+ sconv2(b, t.Sym(), 'v', mode)
+ }
+
+ case TUNSAFEPTR:
+ b.WriteString("unsafe.Pointer")
+
+ case Txxx:
+ b.WriteString("Txxx")
+
+ default:
+ // Don't know how to handle - fall back to detailed prints
+ b.WriteString(t.Kind().String())
+ b.WriteString(" <")
+ sconv2(b, t.Sym(), 'v', mode)
+ b.WriteString(">")
+
+ }
+}
+
+func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Type]int, funarg Funarg) {
+ if f == nil {
+ b.WriteString("")
+ return
+ }
+
+ var name string
+ if verb != 'S' {
+ s := f.Sym
+
+ // Take the name from the original.
+ if mode == fmtGo {
+ s = OrigSym(s)
+ }
+
+ if s != nil && f.Embedded == 0 {
+ if funarg != FunargNone {
+ name = fmt.Sprint(f.Nname)
+ } else if verb == 'L' {
+ name = s.Name
+ if name == ".F" {
+ name = "F" // Hack for toolstash -cmp.
+ }
+ if !IsExported(name) && mode != fmtTypeIDName {
+ name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg)
+ }
+ } else {
+ name = sconv(s, 0, mode)
+ }
+ }
+ }
+
+ if name != "" {
+ b.WriteString(name)
+ b.WriteString(" ")
+ }
+
+ if f.IsDDD() {
+ var et *Type
+ if f.Type != nil {
+ et = f.Type.Elem()
+ }
+ b.WriteString("...")
+ tconv2(b, et, 0, mode, visited)
+ } else {
+ tconv2(b, f.Type, 0, mode, visited)
+ }
+
+ if verb != 'S' && funarg == FunargNone && f.Note != "" {
+ b.WriteString(" ")
+ b.WriteString(strconv.Quote(f.Note))
+ }
+}
+
+// Val
+
+func FmtConst(v constant.Value, sharp bool) string {
+ if !sharp && v.Kind() == constant.Complex {
+ real, imag := constant.Real(v), constant.Imag(v)
+
+ var re string
+ sre := constant.Sign(real)
+ if sre != 0 {
+ re = real.String()
+ }
+
+ var im string
+ sim := constant.Sign(imag)
+ if sim != 0 {
+ im = imag.String()
+ }
+
+ switch {
+ case sre == 0 && sim == 0:
+ return "0"
+ case sre == 0:
+ return im + "i"
+ case sim == 0:
+ return re
+ case sim < 0:
+ return fmt.Sprintf("(%s%si)", re, im)
+ default:
+ return fmt.Sprintf("(%s+%si)", re, im)
+ }
+ }
+
+ return v.String()
+}
+
+// TypeHash computes a hash value for type t to use in type switch statements.
+func TypeHash(t *Type) uint32 {
+ p := t.LongString()
+
+ // Using MD5 is overkill, but reduces accidental collisions.
+ h := md5.Sum([]byte(p))
+ return binary.LittleEndian.Uint32(h[:4])
+}
diff --git a/src/cmd/compile/internal/types/goversion.go b/src/cmd/compile/internal/types/goversion.go
new file mode 100644
index 0000000000000..1a324aa42fdb7
--- /dev/null
+++ b/src/cmd/compile/internal/types/goversion.go
@@ -0,0 +1,94 @@
+// Copyright 2009 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 types
+
+import (
+ "fmt"
+ "internal/goversion"
+ "log"
+ "regexp"
+ "strconv"
+
+ "cmd/compile/internal/base"
+)
+
+// A lang is a language version broken into major and minor numbers.
+type lang struct {
+ major, minor int
+}
+
+// langWant is the desired language version set by the -lang flag.
+// If the -lang flag is not set, this is the zero value, meaning that
+// any language version is supported.
+var langWant lang
+
+// AllowsGoVersion reports whether a particular package
+// is allowed to use Go version major.minor.
+// We assume the imported packages have all been checked,
+// so we only have to check the local package against the -lang flag.
+func AllowsGoVersion(pkg *Pkg, major, minor int) bool {
+ if pkg == nil {
+ // TODO(mdempsky): Set Pkg for local types earlier.
+ pkg = LocalPkg
+ }
+ if pkg != LocalPkg {
+ // Assume imported packages passed type-checking.
+ return true
+ }
+ if langWant.major == 0 && langWant.minor == 0 {
+ return true
+ }
+ return langWant.major > major || (langWant.major == major && langWant.minor >= minor)
+}
+
+// ParseLangFlag verifies that the -lang flag holds a valid value, and
+// exits if not. It initializes data used by langSupported.
+func ParseLangFlag() {
+ if base.Flag.Lang == "" {
+ return
+ }
+
+ var err error
+ langWant, err = parseLang(base.Flag.Lang)
+ if err != nil {
+ log.Fatalf("invalid value %q for -lang: %v", base.Flag.Lang, err)
+ }
+
+ if def := currentLang(); base.Flag.Lang != def {
+ defVers, err := parseLang(def)
+ if err != nil {
+ log.Fatalf("internal error parsing default lang %q: %v", def, err)
+ }
+ if langWant.major > defVers.major || (langWant.major == defVers.major && langWant.minor > defVers.minor) {
+ log.Fatalf("invalid value %q for -lang: max known version is %q", base.Flag.Lang, def)
+ }
+ }
+}
+
+// parseLang parses a -lang option into a langVer.
+func parseLang(s string) (lang, error) {
+ matches := goVersionRE.FindStringSubmatch(s)
+ if matches == nil {
+ return lang{}, fmt.Errorf(`should be something like "go1.12"`)
+ }
+ major, err := strconv.Atoi(matches[1])
+ if err != nil {
+ return lang{}, err
+ }
+ minor, err := strconv.Atoi(matches[2])
+ if err != nil {
+ return lang{}, err
+ }
+ return lang{major: major, minor: minor}, nil
+}
+
+// currentLang returns the current language version.
+func currentLang() string {
+ return fmt.Sprintf("go1.%d", goversion.Version)
+}
+
+// goVersionRE is a regular expression that matches the valid
+// arguments to the -lang flag.
+var goVersionRE = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
diff --git a/src/cmd/compile/internal/types/identity.go b/src/cmd/compile/internal/types/identity.go
index a77f514df96be..9bc636d7ffe41 100644
--- a/src/cmd/compile/internal/types/identity.go
+++ b/src/cmd/compile/internal/types/identity.go
@@ -25,17 +25,17 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
if t1 == t2 {
return true
}
- if t1 == nil || t2 == nil || t1.Etype != t2.Etype || t1.Broke() || t2.Broke() {
+ if t1 == nil || t2 == nil || t1.kind != t2.kind || t1.Broke() || t2.Broke() {
return false
}
- if t1.Sym != nil || t2.Sym != nil {
+ if t1.sym != nil || t2.sym != nil {
// Special case: we keep byte/uint8 and rune/int32
// separate for error messages. Treat them as equal.
- switch t1.Etype {
+ switch t1.kind {
case TUINT8:
- return (t1 == Types[TUINT8] || t1 == Bytetype) && (t2 == Types[TUINT8] || t2 == Bytetype)
+ return (t1 == Types[TUINT8] || t1 == ByteType) && (t2 == Types[TUINT8] || t2 == ByteType)
case TINT32:
- return (t1 == Types[TINT32] || t1 == Runetype) && (t2 == Types[TINT32] || t2 == Runetype)
+ return (t1 == Types[TINT32] || t1 == RuneType) && (t2 == Types[TINT32] || t2 == RuneType)
default:
return false
}
@@ -52,7 +52,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
}
assumedEqual[typePair{t1, t2}] = struct{}{}
- switch t1.Etype {
+ switch t1.kind {
case TIDEAL:
// Historically, cmd/compile used a single "untyped
// number" type, so all untyped number types were
diff --git a/src/cmd/compile/internal/types/etype_string.go b/src/cmd/compile/internal/types/kind_string.go
similarity index 57%
rename from src/cmd/compile/internal/types/etype_string.go
rename to src/cmd/compile/internal/types/kind_string.go
index 14fd5b71df801..1e1e84624080b 100644
--- a/src/cmd/compile/internal/types/etype_string.go
+++ b/src/cmd/compile/internal/types/kind_string.go
@@ -1,4 +1,4 @@
-// Code generated by "stringer -type EType -trimprefix T"; DO NOT EDIT.
+// Code generated by "stringer -type Kind -trimprefix T type.go"; DO NOT EDIT.
package types
@@ -48,13 +48,13 @@ func _() {
_ = x[NTYPE-37]
}
-const _EType_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
+const _Kind_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, 197, 202}
+var _Kind_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) {
- return "EType(" + strconv.FormatInt(int64(i), 10) + ")"
+func (i Kind) String() string {
+ if i >= Kind(len(_Kind_index)-1) {
+ return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _EType_name[_EType_index[i]:_EType_index[i+1]]
+ return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
}
diff --git a/src/cmd/compile/internal/types/pkg.go b/src/cmd/compile/internal/types/pkg.go
index bcc6789509581..a6d2e2007b042 100644
--- a/src/cmd/compile/internal/types/pkg.go
+++ b/src/cmd/compile/internal/types/pkg.go
@@ -31,8 +31,7 @@ type Pkg struct {
// height of their imported packages.
Height int
- Imported bool // export data of this package was parsed
- Direct bool // imported directly
+ Direct bool // imported directly
}
// NewPkg returns a new Pkg for the given package path and name.
@@ -84,9 +83,6 @@ func (pkg *Pkg) Lookup(name string) *Sym {
return s
}
-// List of .inittask entries in imported packages, in source code order.
-var InitSyms []*Sym
-
// LookupOK looks up name in pkg and reports whether it previously existed.
func (pkg *Pkg) LookupOK(name string) (s *Sym, existed bool) {
// TODO(gri) remove this check in favor of specialized lookup
@@ -101,9 +97,6 @@ func (pkg *Pkg) LookupOK(name string) (s *Sym, existed bool) {
Name: name,
Pkg: pkg,
}
- if name == ".inittask" {
- InitSyms = append(InitSyms, s)
- }
pkg.Syms[name] = s
return s, false
}
@@ -144,3 +137,7 @@ func CleanroomDo(f func()) {
f()
pkgMap = saved
}
+
+func IsDotAlias(sym *Sym) bool {
+ return sym.Def != nil && sym.Def.Sym() != sym
+}
diff --git a/src/cmd/compile/internal/types/scope.go b/src/cmd/compile/internal/types/scope.go
index 40d3d86ef11a4..d7c454f3795e4 100644
--- a/src/cmd/compile/internal/types/scope.go
+++ b/src/cmd/compile/internal/types/scope.go
@@ -4,18 +4,21 @@
package types
-import "cmd/internal/src"
+import (
+ "cmd/compile/internal/base"
+ "cmd/internal/src"
+)
// Declaration stack & operations
var blockgen int32 = 1 // max block number
-var Block int32 // current block number
+var Block int32 = 1 // current block number
// A dsym stores a symbol's shadowed declaration so that it can be
// restored once the block scope ends.
type dsym struct {
sym *Sym // sym == nil indicates stack mark
- def *Node
+ def Object
block int32
lastlineno src.XPos // last declaration for diagnostic
}
@@ -56,7 +59,7 @@ func Popdcl() {
d.sym = nil
d.def = nil
}
- Fatalf("popdcl: no stack mark")
+ base.Fatalf("popdcl: no stack mark")
}
// Markdcl records the start of a new block scope for declarations.
@@ -69,7 +72,7 @@ func Markdcl() {
Block = blockgen
}
-func IsDclstackValid() bool {
+func isDclstackValid() bool {
for _, d := range dclstack {
if d.sym == nil {
return false
@@ -79,19 +82,20 @@ func IsDclstackValid() bool {
}
// PkgDef returns the definition associated with s at package scope.
-func (s *Sym) PkgDef() *Node {
+func (s *Sym) PkgDef() Object {
return *s.pkgDefPtr()
}
// SetPkgDef sets the definition associated with s at package scope.
-func (s *Sym) SetPkgDef(n *Node) {
+func (s *Sym) SetPkgDef(n Object) {
*s.pkgDefPtr() = n
}
-func (s *Sym) pkgDefPtr() **Node {
+func (s *Sym) pkgDefPtr() *Object {
// Look for outermost saved declaration, which must be the
// package scope definition, if present.
- for _, d := range dclstack {
+ for i := range dclstack {
+ d := &dclstack[i]
if s == d.sym {
return &d.def
}
@@ -101,3 +105,9 @@ func (s *Sym) pkgDefPtr() **Node {
// function scope.
return &s.Def
}
+
+func CheckDclstack() {
+ if !isDclstackValid() {
+ base.Fatalf("mark left on the dclstack")
+ }
+}
diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/types/size.go
similarity index 52%
rename from src/cmd/compile/internal/gc/align.go
rename to src/cmd/compile/internal/types/size.go
index a3a0c8fce822e..98540eefb651d 100644
--- a/src/cmd/compile/internal/gc/align.go
+++ b/src/cmd/compile/internal/types/size.go
@@ -2,18 +2,64 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package gc
+package types
import (
"bytes"
- "cmd/compile/internal/types"
"fmt"
"sort"
+
+ "cmd/compile/internal/base"
+ "cmd/internal/src"
+)
+
+var PtrSize int
+
+var RegSize int
+
+// Slices in the runtime are represented by three components:
+//
+// type slice struct {
+// ptr unsafe.Pointer
+// len int
+// cap int
+// }
+//
+// Strings in the runtime are represented by two components:
+//
+// type string struct {
+// ptr unsafe.Pointer
+// len int
+// }
+//
+// These variables are the offsets of fields and sizes of these structs.
+var (
+ SlicePtrOffset int64
+ SliceLenOffset int64
+ SliceCapOffset int64
+
+ SliceSize int64
+ StringSize int64
)
-// sizeCalculationDisabled indicates whether it is safe
-// to calculate Types' widths and alignments. See dowidth.
-var sizeCalculationDisabled bool
+var SkipSizeForTracing bool
+
+// typePos returns the position associated with t.
+// This is where t was declared or where it appeared as a type expression.
+func typePos(t *Type) src.XPos {
+ if pos := t.Pos(); pos.IsKnown() {
+ return pos
+ }
+ base.Fatalf("bad type: %v", t)
+ panic("unreachable")
+}
+
+// MaxWidth is the maximum size of a value on the target architecture.
+var MaxWidth int64
+
+// CalcSizeDisabled indicates whether it is safe
+// to calculate Types' widths and alignments. See CalcSize.
+var CalcSizeDisabled bool
// machine size and rounding alignment is dictated around
// the size of a pointer, set in betypeinit (see ../amd64/galign.go).
@@ -21,25 +67,25 @@ var defercalc int
func Rnd(o int64, r int64) int64 {
if r < 1 || r > 8 || r&(r-1) != 0 {
- Fatalf("rnd %d", r)
+ base.Fatalf("rnd %d", r)
}
return (o + r - 1) &^ (r - 1)
}
// expandiface computes the method set for interface type t by
// expanding embedded interfaces.
-func expandiface(t *types.Type) {
- seen := make(map[*types.Sym]*types.Field)
- var methods []*types.Field
+func expandiface(t *Type) {
+ seen := make(map[*Sym]*Field)
+ var methods []*Field
- addMethod := func(m *types.Field, explicit bool) {
+ addMethod := func(m *Field, explicit bool) {
switch prev := seen[m.Sym]; {
case prev == nil:
seen[m.Sym] = m
- case langSupported(1, 14, t.Pkg()) && !explicit && types.Identical(m.Type, prev.Type):
+ case AllowsGoVersion(t.Pkg(), 1, 14) && !explicit && Identical(m.Type, prev.Type):
return
default:
- yyerrorl(m.Pos, "duplicate method %s", m.Sym.Name)
+ base.ErrorfAt(m.Pos, "duplicate method %s", m.Sym.Name)
}
methods = append(methods, m)
}
@@ -49,7 +95,7 @@ func expandiface(t *types.Type) {
continue
}
- checkwidth(m.Type)
+ CheckSize(m.Type)
addMethod(m, true)
}
@@ -59,7 +105,7 @@ func expandiface(t *types.Type) {
}
if !m.Type.IsInterface() {
- yyerrorl(m.Pos, "interface contains embedded non-interface %v", m.Type)
+ base.ErrorfAt(m.Pos, "interface contains embedded non-interface %v", m.Type)
m.SetBroke(true)
t.SetBroke(true)
// Add to fields so that error messages
@@ -74,30 +120,27 @@ func expandiface(t *types.Type) {
// (including broken ones, if any) and add to t's
// method set.
for _, t1 := range m.Type.Fields().Slice() {
- f := types.NewField()
- f.Pos = m.Pos // preserve embedding position
- f.Sym = t1.Sym
- f.Type = t1.Type
- f.SetBroke(t1.Broke())
+ // Use m.Pos rather than t1.Pos to preserve embedding position.
+ f := NewField(m.Pos, t1.Sym, t1.Type)
addMethod(f, false)
}
}
- sort.Sort(methcmp(methods))
+ sort.Sort(MethodsByName(methods))
- if int64(len(methods)) >= thearch.MAXWIDTH/int64(Widthptr) {
- yyerrorl(typePos(t), "interface too large")
+ if int64(len(methods)) >= MaxWidth/int64(PtrSize) {
+ base.ErrorfAt(typePos(t), "interface too large")
}
for i, m := range methods {
- m.Offset = int64(i) * int64(Widthptr)
+ m.Offset = int64(i) * int64(PtrSize)
}
- // Access fields directly to avoid recursively calling dowidth
+ // Access fields directly to avoid recursively calling CalcSize
// within Type.Fields().
- t.Extra.(*types.Interface).Fields.Set(methods)
+ t.Extra.(*Interface).Fields.Set(methods)
}
-func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
+func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
starto := o
maxalign := int32(flag)
if maxalign < 1 {
@@ -111,7 +154,7 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
continue
}
- dowidth(f.Type)
+ CalcSize(f.Type)
if int32(f.Type.Align) > maxalign {
maxalign = int32(f.Type.Align)
}
@@ -119,38 +162,33 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
o = Rnd(o, int64(f.Type.Align))
}
f.Offset = o
- if n := asNode(f.Nname); n != nil {
+ if f.Nname != nil {
// addrescapes has similar code to update these offsets.
- // Usually addrescapes runs after widstruct,
+ // Usually addrescapes runs after calcStructOffset,
// in which case we could drop this,
// but function closure functions are the exception.
// NOTE(rsc): This comment may be stale.
// It's possible the ordering has changed and this is
// now the common case. I'm not sure.
- if n.Name.Param.Stackcopy != nil {
- n.Name.Param.Stackcopy.Xoffset = o
- n.Xoffset = 0
- } else {
- n.Xoffset = o
- }
+ f.Nname.(VarObject).RecordFrameOffset(o)
}
w := f.Type.Width
if w < 0 {
- Fatalf("invalid width %d", f.Type.Width)
+ base.Fatalf("invalid width %d", f.Type.Width)
}
if w == 0 {
lastzero = o
}
o += w
- maxwidth := thearch.MAXWIDTH
+ maxwidth := MaxWidth
// On 32-bit systems, reflect tables impose an additional constraint
// that each field start offset must fit in 31 bits.
if maxwidth < 1<<32 {
maxwidth = 1<<31 - 1
}
if o >= maxwidth {
- yyerrorl(typePos(errtype), "type %L too large", errtype)
+ base.ErrorfAt(typePos(errtype), "type %L too large", errtype)
o = 8 // small but nonzero
}
}
@@ -182,15 +220,22 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
// path points to a slice used for tracking the sequence of types
// visited. Using a pointer to a slice allows the slice capacity to
// grow and limit reallocations.
-func findTypeLoop(t *types.Type, path *[]*types.Type) bool {
+func findTypeLoop(t *Type, path *[]*Type) bool {
// We implement a simple DFS loop-finding algorithm. This
// could be faster, but type cycles are rare.
- if t.Sym != nil {
+ if t.Sym() != nil {
// Declared type. Check for loops and otherwise
// recurse on the type expression used in the type
// declaration.
+ // Type imported from package, so it can't be part of
+ // a type loop (otherwise that package should have
+ // failed to compile).
+ if t.Sym().Pkg != LocalPkg {
+ return false
+ }
+
for i, x := range *path {
if x == t {
*path = (*path)[i:]
@@ -199,14 +244,14 @@ func findTypeLoop(t *types.Type, path *[]*types.Type) bool {
}
*path = append(*path, t)
- if p := asNode(t.Nod).Name.Param; p != nil && findTypeLoop(p.Ntype.Type, path) {
+ if findTypeLoop(t.Obj().(TypeObject).TypeDefn(), path) {
return true
}
*path = (*path)[:len(*path)-1]
} else {
// Anonymous type. Recurse on contained types.
- switch t.Etype {
+ switch t.Kind() {
case TARRAY:
if findTypeLoop(t.Elem(), path) {
return true
@@ -231,14 +276,14 @@ func findTypeLoop(t *types.Type, path *[]*types.Type) bool {
return false
}
-func reportTypeLoop(t *types.Type) {
+func reportTypeLoop(t *Type) {
if t.Broke() {
return
}
- var l []*types.Type
+ var l []*Type
if !findTypeLoop(t, &l) {
- Fatalf("failed to find type loop for: %v", t)
+ base.Fatalf("failed to find type loop for: %v", t)
}
// Rotate loop so that the earliest type declaration is first.
@@ -253,25 +298,26 @@ func reportTypeLoop(t *types.Type) {
var msg bytes.Buffer
fmt.Fprintf(&msg, "invalid recursive type %v\n", l[0])
for _, t := range l {
- fmt.Fprintf(&msg, "\t%v: %v refers to\n", linestr(typePos(t)), t)
+ fmt.Fprintf(&msg, "\t%v: %v refers to\n", base.FmtPos(typePos(t)), t)
t.SetBroke(true)
}
- fmt.Fprintf(&msg, "\t%v: %v", linestr(typePos(l[0])), l[0])
- yyerrorl(typePos(l[0]), msg.String())
+ fmt.Fprintf(&msg, "\t%v: %v", base.FmtPos(typePos(l[0])), l[0])
+ base.ErrorfAt(typePos(l[0]), msg.String())
}
-// dowidth calculates and stores the size and alignment for t.
-// If sizeCalculationDisabled is set, and the size/alignment
+// CalcSize calculates and stores the size and alignment for t.
+// If CalcSizeDisabled is set, and the size/alignment
// have not already been calculated, it calls Fatal.
// This is used to prevent data races in the back end.
-func dowidth(t *types.Type) {
- // Calling dowidth when typecheck tracing enabled is not safe.
+func CalcSize(t *Type) {
+ // Calling CalcSize when typecheck tracing enabled is not safe.
// See issue #33658.
- if enableTrace && skipDowidthForTracing {
+ if base.EnableTrace && SkipSizeForTracing {
return
}
- if Widthptr == 0 {
- Fatalf("dowidth without betypeinit")
+ if PtrSize == 0 {
+ // Assume this is a test.
+ return
}
if t == nil {
@@ -289,13 +335,13 @@ func dowidth(t *types.Type) {
return
}
- if sizeCalculationDisabled {
+ if CalcSizeDisabled {
if t.Broke() {
// break infinite recursion from Fatal call below
return
}
t.SetBroke(true)
- Fatalf("width not calculated: %v", t)
+ base.Fatalf("width not calculated: %v", t)
}
// break infinite recursion if the broken recursive type
@@ -304,33 +350,33 @@ func dowidth(t *types.Type) {
return
}
- // defer checkwidth calls until after we're done
- defercheckwidth()
+ // defer CheckSize calls until after we're done
+ DeferCheckSize()
- lno := lineno
- if asNode(t.Nod) != nil {
- lineno = asNode(t.Nod).Pos
+ lno := base.Pos
+ if pos := t.Pos(); pos.IsKnown() {
+ base.Pos = pos
}
t.Width = -2
t.Align = 0 // 0 means use t.Width, below
- et := t.Etype
+ et := t.Kind()
switch et {
case TFUNC, TCHAN, TMAP, TSTRING:
break
- // simtype == 0 during bootstrap
+ // SimType == 0 during bootstrap
default:
- if simtype[t.Etype] != 0 {
- et = simtype[t.Etype]
+ if SimType[t.Kind()] != 0 {
+ et = SimType[t.Kind()]
}
}
var w int64
switch et {
default:
- Fatalf("dowidth: unknown type: %v", t)
+ base.Fatalf("CalcSize: unknown type: %v", t)
// compiler-specific stuff
case TINT8, TUINT8, TBOOL:
@@ -345,7 +391,7 @@ func dowidth(t *types.Type) {
case TINT64, TUINT64, TFLOAT64:
w = 8
- t.Align = uint8(Widthreg)
+ t.Align = uint8(RegSize)
case TCOMPLEX64:
w = 8
@@ -353,68 +399,68 @@ func dowidth(t *types.Type) {
case TCOMPLEX128:
w = 16
- t.Align = uint8(Widthreg)
+ t.Align = uint8(RegSize)
case TPTR:
- w = int64(Widthptr)
- checkwidth(t.Elem())
+ w = int64(PtrSize)
+ CheckSize(t.Elem())
case TUNSAFEPTR:
- w = int64(Widthptr)
+ w = int64(PtrSize)
case TINTER: // implemented as 2 pointers
- w = 2 * int64(Widthptr)
- t.Align = uint8(Widthptr)
+ w = 2 * int64(PtrSize)
+ t.Align = uint8(PtrSize)
expandiface(t)
case TCHAN: // implemented as pointer
- w = int64(Widthptr)
+ w = int64(PtrSize)
- checkwidth(t.Elem())
+ CheckSize(t.Elem())
// make fake type to check later to
// trigger channel argument check.
- t1 := types.NewChanArgs(t)
- checkwidth(t1)
+ t1 := NewChanArgs(t)
+ CheckSize(t1)
case TCHANARGS:
t1 := t.ChanArgs()
- dowidth(t1) // just in case
+ CalcSize(t1) // just in case
if t1.Elem().Width >= 1<<16 {
- yyerrorl(typePos(t1), "channel element type too large (>64kB)")
+ base.ErrorfAt(typePos(t1), "channel element type too large (>64kB)")
}
w = 1 // anything will do
case TMAP: // implemented as pointer
- w = int64(Widthptr)
- checkwidth(t.Elem())
- checkwidth(t.Key())
+ w = int64(PtrSize)
+ CheckSize(t.Elem())
+ CheckSize(t.Key())
case TFORW: // should have been filled in
reportTypeLoop(t)
w = 1 // anything will do
case TANY:
- // dummy type; should be replaced before use.
- Fatalf("dowidth any")
+ // not a real type; should be replaced before use.
+ base.Fatalf("CalcSize any")
case TSTRING:
- if sizeofString == 0 {
- Fatalf("early dowidth string")
+ if StringSize == 0 {
+ base.Fatalf("early CalcSize string")
}
- w = sizeofString
- t.Align = uint8(Widthptr)
+ w = StringSize
+ t.Align = uint8(PtrSize)
case TARRAY:
if t.Elem() == nil {
break
}
- dowidth(t.Elem())
+ CalcSize(t.Elem())
if t.Elem().Width != 0 {
- cap := (uint64(thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
+ cap := (uint64(MaxWidth) - 1) / uint64(t.Elem().Width)
if uint64(t.NumElem()) > cap {
- yyerrorl(typePos(t), "type %L larger than address space", t)
+ base.ErrorfAt(typePos(t), "type %L larger than address space", t)
}
}
w = t.NumElem() * t.Elem().Width
@@ -424,55 +470,62 @@ func dowidth(t *types.Type) {
if t.Elem() == nil {
break
}
- w = sizeofSlice
- checkwidth(t.Elem())
- t.Align = uint8(Widthptr)
+ w = SliceSize
+ CheckSize(t.Elem())
+ t.Align = uint8(PtrSize)
case TSTRUCT:
if t.IsFuncArgStruct() {
- Fatalf("dowidth fn struct %v", t)
+ base.Fatalf("CalcSize fn struct %v", t)
}
- w = widstruct(t, t, 0, 1)
+ w = calcStructOffset(t, t, 0, 1)
// make fake type to check later to
// trigger function argument computation.
case TFUNC:
- t1 := types.NewFuncArgs(t)
- checkwidth(t1)
- w = int64(Widthptr) // width of func type is pointer
+ t1 := NewFuncArgs(t)
+ CheckSize(t1)
+ w = int64(PtrSize) // width of func type is pointer
// function is 3 cated structures;
// compute their widths as side-effect.
case TFUNCARGS:
t1 := t.FuncArgs()
- w = widstruct(t1, t1.Recvs(), 0, 0)
- w = widstruct(t1, t1.Params(), w, Widthreg)
- w = widstruct(t1, t1.Results(), w, Widthreg)
- t1.Extra.(*types.Func).Argwid = w
- if w%int64(Widthreg) != 0 {
- Warn("bad type %v %d\n", t1, w)
+ w = calcStructOffset(t1, t1.Recvs(), 0, 0)
+ w = calcStructOffset(t1, t1.Params(), w, RegSize)
+ w = calcStructOffset(t1, t1.Results(), w, RegSize)
+ t1.Extra.(*Func).Argwid = w
+ if w%int64(RegSize) != 0 {
+ base.Warn("bad type %v %d\n", t1, w)
}
t.Align = 1
}
- if Widthptr == 4 && w != int64(int32(w)) {
- yyerrorl(typePos(t), "type %v too large", t)
+ if PtrSize == 4 && w != int64(int32(w)) {
+ base.ErrorfAt(typePos(t), "type %v too large", t)
}
t.Width = w
if t.Align == 0 {
if w == 0 || w > 8 || w&(w-1) != 0 {
- Fatalf("invalid alignment for %v", t)
+ base.Fatalf("invalid alignment for %v", t)
}
t.Align = uint8(w)
}
- lineno = lno
+ base.Pos = lno
+
+ ResumeCheckSize()
+}
- resumecheckwidth()
+// CalcStructSize calculates the size of s,
+// filling in s.Width and s.Align,
+// even if size calculation is otherwise disabled.
+func CalcStructSize(s *Type) {
+ s.Width = calcStructOffset(s, s, 0, 1) // sets align
}
-// when a type's width should be known, we call checkwidth
+// when a type's width should be known, we call CheckSize
// to compute it. during a declaration like
//
// type T *struct { next T }
@@ -481,16 +534,16 @@ func dowidth(t *types.Type) {
// until after T has been initialized to be a pointer to that struct.
// similarly, during import processing structs may be used
// before their definition. in those situations, calling
-// defercheckwidth() stops width calculations until
-// resumecheckwidth() is called, at which point all the
-// checkwidths that were deferred are executed.
-// dowidth should only be called when the type's size
-// is needed immediately. checkwidth makes sure the
+// DeferCheckSize() stops width calculations until
+// ResumeCheckSize() is called, at which point all the
+// CalcSizes that were deferred are executed.
+// CalcSize should only be called when the type's size
+// is needed immediately. CheckSize makes sure the
// size is evaluated eventually.
-var deferredTypeStack []*types.Type
+var deferredTypeStack []*Type
-func checkwidth(t *types.Type) {
+func CheckSize(t *Type) {
if t == nil {
return
}
@@ -498,11 +551,11 @@ func checkwidth(t *types.Type) {
// function arg structs should not be checked
// outside of the enclosing function.
if t.IsFuncArgStruct() {
- Fatalf("checkwidth %v", t)
+ base.Fatalf("CheckSize %v", t)
}
if defercalc == 0 {
- dowidth(t)
+ CalcSize(t)
return
}
@@ -513,19 +566,68 @@ func checkwidth(t *types.Type) {
}
}
-func defercheckwidth() {
+func DeferCheckSize() {
defercalc++
}
-func resumecheckwidth() {
+func ResumeCheckSize() {
if defercalc == 1 {
for len(deferredTypeStack) > 0 {
t := deferredTypeStack[len(deferredTypeStack)-1]
deferredTypeStack = deferredTypeStack[:len(deferredTypeStack)-1]
t.SetDeferwidth(false)
- dowidth(t)
+ CalcSize(t)
}
}
defercalc--
}
+
+// PtrDataSize returns the length in bytes of the prefix of t
+// containing pointer data. Anything after this offset is scalar data.
+func PtrDataSize(t *Type) int64 {
+ if !t.HasPointers() {
+ return 0
+ }
+
+ switch t.Kind() {
+ case TPTR,
+ TUNSAFEPTR,
+ TFUNC,
+ TCHAN,
+ TMAP:
+ return int64(PtrSize)
+
+ case TSTRING:
+ // struct { byte *str; intgo len; }
+ return int64(PtrSize)
+
+ case TINTER:
+ // struct { Itab *tab; void *data; } or
+ // struct { Type *type; void *data; }
+ // Note: see comment in typebits.Set
+ return 2 * int64(PtrSize)
+
+ case TSLICE:
+ // struct { byte *array; uintgo len; uintgo cap; }
+ return int64(PtrSize)
+
+ case TARRAY:
+ // haspointers already eliminated t.NumElem() == 0.
+ return (t.NumElem()-1)*t.Elem().Width + PtrDataSize(t.Elem())
+
+ case TSTRUCT:
+ // Find the last field that has pointers.
+ var lastPtrField *Field
+ for _, t1 := range t.Fields().Slice() {
+ if t1.Type.HasPointers() {
+ lastPtrField = t1
+ }
+ }
+ return lastPtrField.Offset + PtrDataSize(lastPtrField.Type)
+
+ default:
+ base.Fatalf("PtrDataSize: unexpected type, %v", t)
+ return 0
+ }
+}
diff --git a/src/cmd/compile/internal/types/sizeof_test.go b/src/cmd/compile/internal/types/sizeof_test.go
index ea947d8f417c5..675739f7f6368 100644
--- a/src/cmd/compile/internal/types/sizeof_test.go
+++ b/src/cmd/compile/internal/types/sizeof_test.go
@@ -20,11 +20,11 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {Sym{}, 52, 88},
- {Type{}, 52, 88},
+ {Sym{}, 44, 72},
+ {Type{}, 56, 96},
{Map{}, 20, 40},
{Forward{}, 20, 32},
- {Func{}, 32, 56},
+ {Func{}, 24, 40},
{Struct{}, 16, 32},
{Interface{}, 8, 16},
{Chan{}, 8, 16},
diff --git a/src/cmd/compile/internal/types/sort.go b/src/cmd/compile/internal/types/sort.go
new file mode 100644
index 0000000000000..dc59b06415328
--- /dev/null
+++ b/src/cmd/compile/internal/types/sort.go
@@ -0,0 +1,14 @@
+// Copyright 2009 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 types
+
+// MethodsByName sorts methods by symbol.
+type MethodsByName []*Field
+
+func (x MethodsByName) Len() int { return len(x) }
+
+func (x MethodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (x MethodsByName) Less(i, j int) bool { return x[i].Sym.Less(x[j].Sym) }
diff --git a/src/cmd/compile/internal/types/sym.go b/src/cmd/compile/internal/types/sym.go
index 07bce4d5cdbb8..0e66ed348bfcf 100644
--- a/src/cmd/compile/internal/types/sym.go
+++ b/src/cmd/compile/internal/types/sym.go
@@ -5,6 +5,7 @@
package types
import (
+ "cmd/compile/internal/base"
"cmd/internal/obj"
"cmd/internal/src"
"unicode"
@@ -26,20 +27,17 @@ import (
// NOTE: In practice, things can be messier than the description above
// for various reasons (historical, convenience).
type Sym struct {
- Importdef *Pkg // where imported definition was found
- Linkname string // link name
+ Linkname string // link name
Pkg *Pkg
Name string // object name
// saved and restored by dcopy
- Def *Node // definition: ONAME OTYPE OPACK or OLITERAL
+ Def Object // definition: ONAME OTYPE OPACK or OLITERAL
Block int32 // blocknumber to catch redeclaration
Lastlineno src.XPos // last declaration for diagnostic
- flags bitset8
- Label *Node // corresponding label (ephemeral)
- Origpkg *Pkg // original package for . import
+ flags bitset8
}
const (
@@ -66,32 +64,30 @@ func (sym *Sym) IsBlank() bool {
return sym != nil && sym.Name == "_"
}
-func (sym *Sym) LinksymName() string {
- if sym.IsBlank() {
- return "_"
- }
- if sym.Linkname != "" {
- return sym.Linkname
+// Deprecated: This method should not be used directly. Instead, use a
+// higher-level abstraction that directly returns the linker symbol
+// for a named object. For example, reflectdata.TypeLinksym(t) instead
+// of reflectdata.TypeSym(t).Linksym().
+func (sym *Sym) Linksym() *obj.LSym {
+ abi := obj.ABI0
+ if sym.Func() {
+ abi = obj.ABIInternal
}
- return sym.Pkg.Prefix + "." + sym.Name
+ return sym.LinksymABI(abi)
}
-func (sym *Sym) Linksym() *obj.LSym {
+// Deprecated: This method should not be used directly. Instead, use a
+// higher-level abstraction that directly returns the linker symbol
+// for a named object. For example, (*ir.Name).LinksymABI(abi) instead
+// of (*ir.Name).Sym().LinksymABI(abi).
+func (sym *Sym) LinksymABI(abi obj.ABI) *obj.LSym {
if sym == nil {
- return nil
+ base.Fatalf("nil symbol")
}
- initPkg := func(r *obj.LSym) {
- if sym.Linkname != "" {
- r.Pkg = "_"
- } else {
- r.Pkg = sym.Pkg.Prefix
- }
- }
- if sym.Func() {
- // This is a function symbol. Mark it as "internal ABI".
- return Ctxt.LookupABIInit(sym.LinksymName(), obj.ABIInternal, initPkg)
+ if sym.Linkname != "" {
+ return base.Linkname(sym.Linkname, abi)
}
- return Ctxt.LookupInit(sym.LinksymName(), initPkg)
+ return base.PkgLinksym(sym.Pkg.Prefix, sym.Name, abi)
}
// Less reports whether symbol a is ordered before symbol b.
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index 023ab9af88aee..0dfbef8af1d62 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -5,23 +5,40 @@
package types
import (
- "cmd/internal/obj"
+ "cmd/compile/internal/base"
"cmd/internal/src"
"fmt"
+ "sync"
)
-// Dummy Node so we can refer to *Node without actually
-// having a gc.Node. Necessary to break import cycles.
-// TODO(gri) try to eliminate soon
-type Node struct{ _ int }
+// IRNode represents an ir.Node, but without needing to import cmd/compile/internal/ir,
+// which would cause an import cycle. The uses in other packages must type assert
+// values of type IRNode to ir.Node or a more specific type.
+type Object interface {
+ Pos() src.XPos
+ Sym() *Sym
+ Type() *Type
+}
+
+// A TypeObject is an Object representing a named type.
+type TypeObject interface {
+ Object
+ TypeDefn() *Type // for "type T Defn", returns Defn
+}
-//go:generate stringer -type EType -trimprefix T
+// A VarObject is an Object representing a function argument, variable, or struct field.
+type VarObject interface {
+ Object
+ RecordFrameOffset(int64) // save frame offset
+}
+
+//go:generate stringer -type Kind -trimprefix T type.go
-// EType describes a kind of type.
-type EType uint8
+// Kind describes a kind of type.
+type Kind uint8
const (
- Txxx EType = iota
+ Txxx Kind = iota
TINT8
TUINT8
@@ -90,7 +107,7 @@ const (
// Types stores pointers to predeclared named types.
//
// It also stores pointers to several special types:
-// - Types[TANY] is the placeholder "any" type recognized by substArgTypes.
+// - Types[TANY] is the placeholder "any" type recognized by SubstArgTypes.
// - Types[TBLANK] represents the blank variable's type.
// - Types[TNIL] represents the predeclared "nil" value's type.
// - Types[TUNSAFEPTR] is package unsafe's Pointer type.
@@ -98,15 +115,15 @@ var Types [NTYPE]*Type
var (
// Predeclared alias types. Kept separate for better error messages.
- Bytetype *Type
- Runetype *Type
+ ByteType *Type
+ RuneType *Type
// Predeclared error interface type.
- Errortype *Type
+ ErrorType *Type
// Types to represent untyped string and boolean constants.
- UntypedString *Type
- UntypedBool *Type
+ UntypedString = New(TSTRING)
+ UntypedBool = New(TBOOL)
// Types to represent untyped numeric constants.
UntypedInt = New(TIDEAL)
@@ -141,24 +158,26 @@ type Type struct {
methods Fields
allMethods Fields
- Nod *Node // canonical OTYPE node
- Orig *Type // original type (type literal or predefined type)
+ nod Object // canonical OTYPE node
+ underlying *Type // original type (type literal or predefined type)
// Cache of composite types, with this type being the element type.
- Cache struct {
+ cache struct {
ptr *Type // *T, or nil
slice *Type // []T, or nil
}
- Sym *Sym // symbol containing name, for named types
+ sym *Sym // symbol containing name, for named types
Vargen int32 // unique name for OTYPE/ONAME
- Etype EType // kind of type
+ kind Kind // kind of type
Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed)
flags bitset8
}
+func (*Type) CanBeAnSSAAux() {}
+
const (
typeNotInHeap = 1 << iota // type cannot be heap allocated
typeBroke // broken type definition
@@ -179,6 +198,38 @@ func (t *Type) SetNoalg(b bool) { t.flags.set(typeNoalg, b) }
func (t *Type) SetDeferwidth(b bool) { t.flags.set(typeDeferwidth, b) }
func (t *Type) SetRecur(b bool) { t.flags.set(typeRecur, b) }
+// Kind returns the kind of type t.
+func (t *Type) Kind() Kind { return t.kind }
+
+// Sym returns the name of type t.
+func (t *Type) Sym() *Sym { return t.sym }
+
+// Underlying returns the underlying type of type t.
+func (t *Type) Underlying() *Type { return t.underlying }
+
+// SetNod associates t with syntax node n.
+func (t *Type) SetNod(n Object) {
+ // t.nod can be non-nil already
+ // in the case of shared *Types, like []byte or interface{}.
+ if t.nod == nil {
+ t.nod = n
+ }
+}
+
+// Pos returns a position associated with t, if any.
+// This should only be used for diagnostics.
+func (t *Type) Pos() src.XPos {
+ if t.nod != nil {
+ return t.nod.Pos()
+ }
+ return src.NoXPos
+}
+
+// NoPkg is a nil *Pkg value for clarity.
+// It's intended for use when constructing types that aren't exported
+// and thus don't need to be associated with any package.
+var NoPkg *Pkg = nil
+
// Pkg returns the package that t appeared in.
//
// Pkg is only defined for function, struct, and interface types
@@ -186,7 +237,7 @@ func (t *Type) SetRecur(b bool) { t.flags.set(typeRecur, b) }
// cmd/compile itself, but we need to track it because it's exposed by
// the go/types API.
func (t *Type) Pkg() *Pkg {
- switch t.Etype {
+ switch t.kind {
case TFUNC:
return t.Extra.(*Func).pkg
case TSTRUCT:
@@ -194,25 +245,11 @@ func (t *Type) Pkg() *Pkg {
case TINTER:
return t.Extra.(*Interface).pkg
default:
- Fatalf("Pkg: unexpected kind: %v", t)
+ base.Fatalf("Pkg: unexpected kind: %v", t)
return nil
}
}
-// SetPkg sets the package that t appeared in.
-func (t *Type) SetPkg(pkg *Pkg) {
- switch t.Etype {
- case TFUNC:
- t.Extra.(*Func).pkg = pkg
- case TSTRUCT:
- t.Extra.(*Struct).pkg = pkg
- case TINTER:
- t.Extra.(*Interface).pkg = pkg
- default:
- Fatalf("Pkg: unexpected kind: %v", t)
- }
-}
-
// Map contains Type fields specific to maps.
type Map struct {
Key *Type // Key type
@@ -247,15 +284,12 @@ type Func struct {
Results *Type // function results
Params *Type // function params
- Nname *Node
- pkg *Pkg
+ pkg *Pkg
// Argwid is the total width of the function receiver, params, and results.
// It gets calculated via a temporary TFUNCARGS type.
// Note that TFUNC's Width is Widthptr.
Argwid int64
-
- Outnamed bool
}
// FuncType returns t's extra func-specific fields.
@@ -361,7 +395,7 @@ type Field struct {
// For fields that represent function parameters, Nname points
// to the associated ONAME Node.
- Nname *Node
+ Nname Object
// Offset in bytes of this field or method within its enclosing struct
// or interface Type.
@@ -389,7 +423,7 @@ func (f *Field) End() int64 {
// IsMethod reports whether f represents a method rather than a struct field.
func (f *Field) IsMethod() bool {
- return f.Type.Etype == TFUNC && f.Type.Recv() != nil
+ return f.Type.kind == TFUNC && f.Type.Recv() != nil
}
// Fields is a pointer to a slice of *Field.
@@ -444,14 +478,14 @@ func (f *Fields) Append(s ...*Field) {
}
// New returns a new Type of the specified kind.
-func New(et EType) *Type {
+func New(et Kind) *Type {
t := &Type{
- Etype: et,
+ kind: et,
Width: BADWIDTH,
}
- t.Orig = t
+ t.underlying = t
// TODO(josharian): lazily initialize some of these?
- switch t.Etype {
+ switch t.kind {
case TMAP:
t.Extra = new(Map)
case TFORW:
@@ -481,7 +515,7 @@ func New(et EType) *Type {
// NewArray returns a new fixed-length array Type.
func NewArray(elem *Type, bound int64) *Type {
if bound < 0 {
- Fatalf("NewArray: invalid bound %v", bound)
+ base.Fatalf("NewArray: invalid bound %v", bound)
}
t := New(TARRAY)
t.Extra = &Array{Elem: elem, Bound: bound}
@@ -491,16 +525,16 @@ func NewArray(elem *Type, bound int64) *Type {
// NewSlice returns the slice Type with element type elem.
func NewSlice(elem *Type) *Type {
- if t := elem.Cache.slice; t != nil {
+ if t := elem.cache.slice; t != nil {
if t.Elem() != elem {
- Fatalf("elem mismatch")
+ base.Fatalf("elem mismatch")
}
return t
}
t := New(TSLICE)
t.Extra = Slice{Elem: elem}
- elem.Cache.slice = t
+ elem.cache.slice = t
return t
}
@@ -549,22 +583,22 @@ var NewPtrCacheEnabled = true
// NewPtr returns the pointer type pointing to t.
func NewPtr(elem *Type) *Type {
if elem == nil {
- Fatalf("NewPtr: pointer to elem Type is nil")
+ base.Fatalf("NewPtr: pointer to elem Type is nil")
}
- if t := elem.Cache.ptr; t != nil {
+ if t := elem.cache.ptr; t != nil {
if t.Elem() != elem {
- Fatalf("NewPtr: elem mismatch")
+ base.Fatalf("NewPtr: elem mismatch")
}
return t
}
t := New(TPTR)
t.Extra = Ptr{Elem: elem}
- t.Width = int64(Widthptr)
- t.Align = uint8(Widthptr)
+ t.Width = int64(PtrSize)
+ t.Align = uint8(PtrSize)
if NewPtrCacheEnabled {
- elem.Cache.ptr = t
+ elem.cache.ptr = t
}
return t
}
@@ -583,10 +617,17 @@ func NewFuncArgs(f *Type) *Type {
return t
}
-func NewField() *Field {
- return &Field{
+func NewField(pos src.XPos, sym *Sym, typ *Type) *Field {
+ f := &Field{
+ Pos: pos,
+ Sym: sym,
+ Type: typ,
Offset: BADWIDTH,
}
+ if typ == nil {
+ f.SetBroke(true)
+ }
+ return f
}
// SubstAny walks t, replacing instances of "any" with successive
@@ -596,13 +637,13 @@ func SubstAny(t *Type, types *[]*Type) *Type {
return nil
}
- switch t.Etype {
+ switch t.kind {
default:
// Leave the type unchanged.
case TANY:
if len(*types) == 0 {
- Fatalf("substArgTypes: not enough argument types")
+ base.Fatalf("SubstArgTypes: not enough argument types")
}
t = (*types)[0]
*types = (*types)[1:]
@@ -680,7 +721,7 @@ func (t *Type) copy() *Type {
}
nt := *t
// copy any *T Extra fields, to avoid aliasing
- switch t.Etype {
+ switch t.kind {
case TMAP:
x := *t.Extra.(*Map)
nt.Extra = &x
@@ -703,11 +744,11 @@ func (t *Type) copy() *Type {
x := *t.Extra.(*Array)
nt.Extra = &x
case TTUPLE, TSSA, TRESULTS:
- Fatalf("ssa types cannot be copied")
+ base.Fatalf("ssa types cannot be copied")
}
// TODO(mdempsky): Find out why this is necessary and explain.
- if t.Orig == t {
- nt.Orig = &nt
+ if t.underlying == t {
+ nt.underlying = &nt
}
return &nt
}
@@ -717,9 +758,9 @@ func (f *Field) Copy() *Field {
return &nf
}
-func (t *Type) wantEtype(et EType) {
- if t.Etype != et {
- Fatalf("want %v, but have %v", et, t)
+func (t *Type) wantEtype(et Kind) {
+ if t.kind != et {
+ base.Fatalf("want %v, but have %v", et, t)
}
}
@@ -772,7 +813,7 @@ func (t *Type) Key() *Type {
// Elem returns the type of elements of t.
// Usable with pointers, channels, arrays, slices, and maps.
func (t *Type) Elem() *Type {
- switch t.Etype {
+ switch t.kind {
case TPTR:
return t.Extra.(Ptr).Elem
case TARRAY:
@@ -784,7 +825,7 @@ func (t *Type) Elem() *Type {
case TMAP:
return t.Extra.(*Map).Elem
}
- Fatalf("Type.Elem %s", t.Etype)
+ base.Fatalf("Type.Elem %s", t.kind)
return nil
}
@@ -800,29 +841,9 @@ func (t *Type) FuncArgs() *Type {
return t.Extra.(FuncArgs).T
}
-// Nname returns the associated function's nname.
-func (t *Type) Nname() *Node {
- switch t.Etype {
- case TFUNC:
- return t.Extra.(*Func).Nname
- }
- Fatalf("Type.Nname %v %v", t.Etype, t)
- return nil
-}
-
-// Nname sets the associated function's nname.
-func (t *Type) SetNname(n *Node) {
- switch t.Etype {
- case TFUNC:
- t.Extra.(*Func).Nname = n
- default:
- Fatalf("Type.SetNname %v %v", t.Etype, t)
- }
-}
-
// IsFuncArgStruct reports whether t is a struct representing function parameters.
func (t *Type) IsFuncArgStruct() bool {
- return t.Etype == TSTRUCT && t.Extra.(*Struct).Funarg != FunargNone
+ return t.kind == TSTRUCT && t.Extra.(*Struct).Funarg != FunargNone
}
func (t *Type) Methods() *Fields {
@@ -836,14 +857,14 @@ func (t *Type) AllMethods() *Fields {
}
func (t *Type) Fields() *Fields {
- switch t.Etype {
+ switch t.kind {
case TSTRUCT:
return &t.Extra.(*Struct).fields
case TINTER:
- Dowidth(t)
+ CalcSize(t)
return &t.Extra.(*Interface).Fields
}
- Fatalf("Fields: type %v does not have fields", t)
+ base.Fatalf("Fields: type %v does not have fields", t)
return nil
}
@@ -867,7 +888,7 @@ func (t *Type) SetFields(fields []*Field) {
// enforce that SetFields cannot be called once
// t's width has been calculated.
if t.WidthCalculated() {
- Fatalf("SetFields of %v: width previously calculated", t)
+ base.Fatalf("SetFields of %v: width previously calculated", t)
}
t.wantEtype(TSTRUCT)
for _, f := range fields {
@@ -901,23 +922,23 @@ func (t *Type) ArgWidth() int64 {
}
func (t *Type) Size() int64 {
- if t.Etype == TSSA {
+ if t.kind == TSSA {
if t == TypeInt128 {
return 16
}
return 0
}
- Dowidth(t)
+ CalcSize(t)
return t.Width
}
func (t *Type) Alignment() int64 {
- Dowidth(t)
+ CalcSize(t)
return int64(t.Align)
}
func (t *Type) SimpleString() string {
- return t.Etype.String()
+ return t.kind.String()
}
// Cmp is a comparison between values a and b.
@@ -1001,31 +1022,31 @@ func (t *Type) cmp(x *Type) Cmp {
return CMPgt
}
- if t.Etype != x.Etype {
- return cmpForNe(t.Etype < x.Etype)
+ if t.kind != x.kind {
+ return cmpForNe(t.kind < x.kind)
}
- if t.Sym != nil || x.Sym != nil {
+ if t.sym != nil || x.sym != nil {
// Special case: we keep byte and uint8 separate
// for error messages. Treat them as equal.
- switch t.Etype {
+ switch t.kind {
case TUINT8:
- if (t == Types[TUINT8] || t == Bytetype) && (x == Types[TUINT8] || x == Bytetype) {
+ if (t == Types[TUINT8] || t == ByteType) && (x == Types[TUINT8] || x == ByteType) {
return CMPeq
}
case TINT32:
- if (t == Types[Runetype.Etype] || t == Runetype) && (x == Types[Runetype.Etype] || x == Runetype) {
+ if (t == Types[RuneType.kind] || t == RuneType) && (x == Types[RuneType.kind] || x == RuneType) {
return CMPeq
}
}
}
- if c := t.Sym.cmpsym(x.Sym); c != CMPeq {
+ if c := t.sym.cmpsym(x.sym); c != CMPeq {
return c
}
- if x.Sym != nil {
+ if x.sym != nil {
// Syms non-nil, if vargens match then equal.
if t.Vargen != x.Vargen {
return cmpForNe(t.Vargen < x.Vargen)
@@ -1034,7 +1055,7 @@ func (t *Type) cmp(x *Type) Cmp {
}
// both syms nil, look at structure below.
- switch t.Etype {
+ switch t.kind {
case TBOOL, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TUNSAFEPTR, TUINTPTR,
TINT8, TINT16, TINT32, TINT64, TINT, TUINT8, TUINT16, TUINT32, TUINT64, TUINT:
return CMPeq
@@ -1191,15 +1212,15 @@ func (t *Type) cmp(x *Type) Cmp {
}
// IsKind reports whether t is a Type of the specified kind.
-func (t *Type) IsKind(et EType) bool {
- return t != nil && t.Etype == et
+func (t *Type) IsKind(et Kind) bool {
+ return t != nil && t.kind == et
}
func (t *Type) IsBoolean() bool {
- return t.Etype == TBOOL
+ return t.kind == TBOOL
}
-var unsignedEType = [...]EType{
+var unsignedEType = [...]Kind{
TINT8: TUINT8,
TUINT8: TUINT8,
TINT16: TUINT16,
@@ -1216,54 +1237,62 @@ var unsignedEType = [...]EType{
// ToUnsigned returns the unsigned equivalent of integer type t.
func (t *Type) ToUnsigned() *Type {
if !t.IsInteger() {
- Fatalf("unsignedType(%v)", t)
+ base.Fatalf("unsignedType(%v)", t)
}
- return Types[unsignedEType[t.Etype]]
+ return Types[unsignedEType[t.kind]]
}
func (t *Type) IsInteger() bool {
- switch t.Etype {
+ switch t.kind {
case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR:
return true
}
- return false
+ return t == UntypedInt || t == UntypedRune
}
func (t *Type) IsSigned() bool {
- switch t.Etype {
+ switch t.kind {
case TINT8, TINT16, TINT32, TINT64, TINT:
return true
}
return false
}
+func (t *Type) IsUnsigned() bool {
+ switch t.kind {
+ case TUINT8, TUINT16, TUINT32, TUINT64, TUINT, TUINTPTR:
+ return true
+ }
+ return false
+}
+
func (t *Type) IsFloat() bool {
- return t.Etype == TFLOAT32 || t.Etype == TFLOAT64
+ return t.kind == TFLOAT32 || t.kind == TFLOAT64 || t == UntypedFloat
}
func (t *Type) IsComplex() bool {
- return t.Etype == TCOMPLEX64 || t.Etype == TCOMPLEX128
+ return t.kind == TCOMPLEX64 || t.kind == TCOMPLEX128 || t == UntypedComplex
}
// IsPtr reports whether t is a regular Go pointer type.
// This does not include unsafe.Pointer.
func (t *Type) IsPtr() bool {
- return t.Etype == TPTR
+ return t.kind == TPTR
}
// IsPtrElem reports whether t is the element of a pointer (to t).
func (t *Type) IsPtrElem() bool {
- return t.Cache.ptr != nil
+ return t.cache.ptr != nil
}
// IsUnsafePtr reports whether t is an unsafe pointer.
func (t *Type) IsUnsafePtr() bool {
- return t.Etype == TUNSAFEPTR
+ return t.kind == TUNSAFEPTR
}
// IsUintptr reports whether t is an uintptr.
func (t *Type) IsUintptr() bool {
- return t.Etype == TUINTPTR
+ return t.kind == TUINTPTR
}
// IsPtrShaped reports whether t is represented by a single machine pointer.
@@ -1272,45 +1301,45 @@ func (t *Type) IsUintptr() bool {
// that consist of a single pointer shaped type.
// TODO(mdempsky): Should it? See golang.org/issue/15028.
func (t *Type) IsPtrShaped() bool {
- return t.Etype == TPTR || t.Etype == TUNSAFEPTR ||
- t.Etype == TMAP || t.Etype == TCHAN || t.Etype == TFUNC
+ return t.kind == TPTR || t.kind == TUNSAFEPTR ||
+ t.kind == TMAP || t.kind == TCHAN || t.kind == TFUNC
}
// HasNil reports whether the set of values determined by t includes nil.
func (t *Type) HasNil() bool {
- switch t.Etype {
- case TCHAN, TFUNC, TINTER, TMAP, TPTR, TSLICE, TUNSAFEPTR:
+ switch t.kind {
+ case TCHAN, TFUNC, TINTER, TMAP, TNIL, TPTR, TSLICE, TUNSAFEPTR:
return true
}
return false
}
func (t *Type) IsString() bool {
- return t.Etype == TSTRING
+ return t.kind == TSTRING
}
func (t *Type) IsMap() bool {
- return t.Etype == TMAP
+ return t.kind == TMAP
}
func (t *Type) IsChan() bool {
- return t.Etype == TCHAN
+ return t.kind == TCHAN
}
func (t *Type) IsSlice() bool {
- return t.Etype == TSLICE
+ return t.kind == TSLICE
}
func (t *Type) IsArray() bool {
- return t.Etype == TARRAY
+ return t.kind == TARRAY
}
func (t *Type) IsStruct() bool {
- return t.Etype == TSTRUCT
+ return t.kind == TSTRUCT
}
func (t *Type) IsInterface() bool {
- return t.Etype == TINTER
+ return t.kind == TINTER
}
// IsEmptyInterface reports whether t is an empty interface type.
@@ -1318,6 +1347,20 @@ func (t *Type) IsEmptyInterface() bool {
return t.IsInterface() && t.NumFields() == 0
}
+// IsScalar reports whether 't' is a scalar Go type, e.g.
+// bool/int/float/complex. Note that struct and array types consisting
+// of a single scalar element are not considered scalar, likewise
+// pointer types are also not considered scalar.
+func (t *Type) IsScalar() bool {
+ switch t.kind {
+ case TBOOL, TINT8, TUINT8, TINT16, TUINT16, TINT32,
+ TUINT32, TINT64, TUINT64, TINT, TUINT,
+ TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64:
+ return true
+ }
+ return false
+}
+
func (t *Type) PtrTo() *Type {
return NewPtr(t)
}
@@ -1326,7 +1369,7 @@ func (t *Type) NumFields() int {
return t.Fields().Len()
}
func (t *Type) FieldType(i int) *Type {
- if t.Etype == TTUPLE {
+ if t.kind == TTUPLE {
switch i {
case 0:
return t.Extra.(*Tuple).first
@@ -1336,7 +1379,7 @@ func (t *Type) FieldType(i int) *Type {
panic("bad tuple index")
}
}
- if t.Etype == TRESULTS {
+ if t.kind == TRESULTS {
return t.Extra.(*Results).Types[i]
}
return t.Field(i).Type
@@ -1367,10 +1410,10 @@ const (
// (and their comprised elements) are excluded from the count.
// struct { x, y [3]int } has six components; [10]struct{ x, y string } has twenty.
func (t *Type) NumComponents(countBlank componentsIncludeBlankFields) int64 {
- switch t.Etype {
+ switch t.kind {
case TSTRUCT:
if t.IsFuncArgStruct() {
- Fatalf("NumComponents func arg struct")
+ base.Fatalf("NumComponents func arg struct")
}
var n int64
for _, f := range t.FieldSlice() {
@@ -1390,10 +1433,10 @@ func (t *Type) NumComponents(countBlank componentsIncludeBlankFields) int64 {
// if there is exactly one. Otherwise, it returns nil.
// Components are counted as in NumComponents, including blank fields.
func (t *Type) SoleComponent() *Type {
- switch t.Etype {
+ switch t.kind {
case TSTRUCT:
if t.IsFuncArgStruct() {
- Fatalf("SoleComponent func arg struct")
+ base.Fatalf("SoleComponent func arg struct")
}
if t.NumFields() != 1 {
return nil
@@ -1416,10 +1459,10 @@ func (t *Type) ChanDir() ChanDir {
}
func (t *Type) IsMemory() bool {
- if t == TypeMem || t.Etype == TTUPLE && t.Extra.(*Tuple).second == TypeMem {
+ if t == TypeMem || t.kind == TTUPLE && t.Extra.(*Tuple).second == TypeMem {
return true
}
- if t.Etype == TRESULTS {
+ if t.kind == TRESULTS {
if types := t.Extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem {
return true
}
@@ -1428,8 +1471,8 @@ func (t *Type) IsMemory() bool {
}
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 }
+func (t *Type) IsTuple() bool { return t.kind == TTUPLE }
+func (t *Type) IsResults() bool { return t.kind == TRESULTS }
// IsUntyped reports whether t is an untyped type.
func (t *Type) IsUntyped() bool {
@@ -1439,7 +1482,7 @@ func (t *Type) IsUntyped() bool {
if t == UntypedString || t == UntypedBool {
return true
}
- switch t.Etype {
+ switch t.kind {
case TNIL, TIDEAL:
return true
}
@@ -1449,7 +1492,7 @@ func (t *Type) IsUntyped() bool {
// HasPointers reports whether t contains a heap pointer.
// Note that this function ignores pointers to go:notinheap types.
func (t *Type) HasPointers() bool {
- switch t.Etype {
+ switch t.kind {
case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64,
TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL, TSSA:
return false
@@ -1488,10 +1531,6 @@ func (t *Type) HasPointers() bool {
return true
}
-func (t *Type) Symbol() *obj.LSym {
- return TypeLinkSym(t)
-}
-
// Tie returns 'T' if t is a concrete type,
// 'I' if t is an interface type, and 'E' if t is an empty interface type.
// It is used to build calls to the conv* and assert* runtime routines.
@@ -1523,3 +1562,333 @@ var (
TypeVoid = newSSA("void")
TypeInt128 = newSSA("int128")
)
+
+// NewNamed returns a new named type for the given type name.
+func NewNamed(obj Object) *Type {
+ t := New(TFORW)
+ t.sym = obj.Sym()
+ t.nod = obj
+ return t
+}
+
+// Obj returns the type name for the named type t.
+func (t *Type) Obj() Object {
+ if t.sym != nil {
+ return t.nod
+ }
+ return nil
+}
+
+// SetUnderlying sets the underlying type.
+func (t *Type) SetUnderlying(underlying *Type) {
+ if underlying.kind == TFORW {
+ // This type isn't computed yet; when it is, update n.
+ underlying.ForwardType().Copyto = append(underlying.ForwardType().Copyto, t)
+ return
+ }
+
+ ft := t.ForwardType()
+
+ // TODO(mdempsky): Fix Type rekinding.
+ t.kind = underlying.kind
+ t.Extra = underlying.Extra
+ t.Width = underlying.Width
+ t.Align = underlying.Align
+ t.underlying = underlying.underlying
+
+ if underlying.NotInHeap() {
+ t.SetNotInHeap(true)
+ }
+ if underlying.Broke() {
+ t.SetBroke(true)
+ }
+
+ // spec: "The declared type does not inherit any methods bound
+ // to the existing type, but the method set of an interface
+ // type [...] remains unchanged."
+ if t.IsInterface() {
+ t.methods = underlying.methods
+ t.allMethods = underlying.allMethods
+ }
+
+ // Update types waiting on this type.
+ for _, w := range ft.Copyto {
+ w.SetUnderlying(t)
+ }
+
+ // Double-check use of type as embedded type.
+ if ft.Embedlineno.IsKnown() {
+ if t.IsPtr() || t.IsUnsafePtr() {
+ base.ErrorfAt(ft.Embedlineno, "embedded type cannot be a pointer")
+ }
+ }
+}
+
+// NewBasic returns a new basic type of the given kind.
+func NewBasic(kind Kind, obj Object) *Type {
+ t := New(kind)
+ t.sym = obj.Sym()
+ t.nod = obj
+ return t
+}
+
+// NewInterface returns a new interface for the given methods and
+// embedded types. Embedded types are specified as fields with no Sym.
+func NewInterface(pkg *Pkg, methods []*Field) *Type {
+ t := New(TINTER)
+ t.SetInterface(methods)
+ if anyBroke(methods) {
+ t.SetBroke(true)
+ }
+ t.Extra.(*Interface).pkg = pkg
+ return t
+}
+
+// NewSignature returns a new function type for the given receiver,
+// parameters, and results, any of which may be nil.
+func NewSignature(pkg *Pkg, recv *Field, params, results []*Field) *Type {
+ var recvs []*Field
+ if recv != nil {
+ recvs = []*Field{recv}
+ }
+
+ t := New(TFUNC)
+ ft := t.FuncType()
+
+ funargs := func(fields []*Field, funarg Funarg) *Type {
+ s := NewStruct(NoPkg, fields)
+ s.StructType().Funarg = funarg
+ if s.Broke() {
+ t.SetBroke(true)
+ }
+ return s
+ }
+
+ ft.Receiver = funargs(recvs, FunargRcvr)
+ ft.Params = funargs(params, FunargParams)
+ ft.Results = funargs(results, FunargResults)
+ ft.pkg = pkg
+
+ return t
+}
+
+// NewStruct returns a new struct with the given fields.
+func NewStruct(pkg *Pkg, fields []*Field) *Type {
+ t := New(TSTRUCT)
+ t.SetFields(fields)
+ if anyBroke(fields) {
+ t.SetBroke(true)
+ }
+ t.Extra.(*Struct).pkg = pkg
+ return t
+}
+
+func anyBroke(fields []*Field) bool {
+ for _, f := range fields {
+ if f.Broke() {
+ return true
+ }
+ }
+ return false
+}
+
+var (
+ IsInt [NTYPE]bool
+ IsFloat [NTYPE]bool
+ IsComplex [NTYPE]bool
+ IsSimple [NTYPE]bool
+)
+
+var IsOrdered [NTYPE]bool
+
+// IsReflexive reports whether t has a reflexive equality operator.
+// That is, if x==x for all x of type t.
+func IsReflexive(t *Type) bool {
+ switch t.Kind() {
+ case TBOOL,
+ TINT,
+ TUINT,
+ TINT8,
+ TUINT8,
+ TINT16,
+ TUINT16,
+ TINT32,
+ TUINT32,
+ TINT64,
+ TUINT64,
+ TUINTPTR,
+ TPTR,
+ TUNSAFEPTR,
+ TSTRING,
+ TCHAN:
+ return true
+
+ case TFLOAT32,
+ TFLOAT64,
+ TCOMPLEX64,
+ TCOMPLEX128,
+ TINTER:
+ return false
+
+ case TARRAY:
+ return IsReflexive(t.Elem())
+
+ case TSTRUCT:
+ for _, t1 := range t.Fields().Slice() {
+ if !IsReflexive(t1.Type) {
+ return false
+ }
+ }
+ return true
+
+ default:
+ base.Fatalf("bad type for map key: %v", t)
+ return false
+ }
+}
+
+// Can this type be stored directly in an interface word?
+// Yes, if the representation is a single pointer.
+func IsDirectIface(t *Type) bool {
+ if t.Broke() {
+ return false
+ }
+
+ switch t.Kind() {
+ case TPTR:
+ // Pointers to notinheap types must be stored indirectly. See issue 42076.
+ return !t.Elem().NotInHeap()
+ case TCHAN,
+ TMAP,
+ TFUNC,
+ TUNSAFEPTR:
+ return true
+
+ case TARRAY:
+ // Array of 1 direct iface type can be direct.
+ return t.NumElem() == 1 && IsDirectIface(t.Elem())
+
+ case TSTRUCT:
+ // Struct with 1 field of direct iface type can be direct.
+ return t.NumFields() == 1 && IsDirectIface(t.Field(0).Type)
+ }
+
+ return false
+}
+
+// IsInterfaceMethod reports whether (field) m is
+// an interface method. Such methods have the
+// special receiver type types.FakeRecvType().
+func IsInterfaceMethod(f *Type) bool {
+ return f.Recv().Type == FakeRecvType()
+}
+
+// IsMethodApplicable reports whether method m can be called on a
+// value of type t. This is necessary because we compute a single
+// method set for both T and *T, but some *T methods are not
+// applicable to T receivers.
+func IsMethodApplicable(t *Type, m *Field) bool {
+ return t.IsPtr() || !m.Type.Recv().Type.IsPtr() || IsInterfaceMethod(m.Type) || m.Embedded == 2
+}
+
+// IsRuntimePkg reports whether p is package runtime.
+func IsRuntimePkg(p *Pkg) bool {
+ if base.Flag.CompilingRuntime && p == LocalPkg {
+ return true
+ }
+ return p.Path == "runtime"
+}
+
+// IsReflectPkg reports whether p is package reflect.
+func IsReflectPkg(p *Pkg) bool {
+ if p == LocalPkg {
+ return base.Ctxt.Pkgpath == "reflect"
+ }
+ return p.Path == "reflect"
+}
+
+// ReceiverBaseType returns the underlying type, if any,
+// that owns methods with receiver parameter t.
+// The result is either a named type or an anonymous struct.
+func ReceiverBaseType(t *Type) *Type {
+ if t == nil {
+ return nil
+ }
+
+ // Strip away pointer if it's there.
+ if t.IsPtr() {
+ if t.Sym() != nil {
+ return nil
+ }
+ t = t.Elem()
+ if t == nil {
+ return nil
+ }
+ }
+
+ // Must be a named type or anonymous struct.
+ if t.Sym() == nil && !t.IsStruct() {
+ return nil
+ }
+
+ // Check types.
+ if IsSimple[t.Kind()] {
+ return t
+ }
+ switch t.Kind() {
+ case TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRING, TSTRUCT:
+ return t
+ }
+ return nil
+}
+
+func FloatForComplex(t *Type) *Type {
+ switch t.Kind() {
+ case TCOMPLEX64:
+ return Types[TFLOAT32]
+ case TCOMPLEX128:
+ return Types[TFLOAT64]
+ }
+ base.Fatalf("unexpected type: %v", t)
+ return nil
+}
+
+func ComplexForFloat(t *Type) *Type {
+ switch t.Kind() {
+ case TFLOAT32:
+ return Types[TCOMPLEX64]
+ case TFLOAT64:
+ return Types[TCOMPLEX128]
+ }
+ base.Fatalf("unexpected type: %v", t)
+ return nil
+}
+
+func TypeSym(t *Type) *Sym {
+ return TypeSymLookup(TypeSymName(t))
+}
+
+func TypeSymLookup(name string) *Sym {
+ typepkgmu.Lock()
+ s := typepkg.Lookup(name)
+ typepkgmu.Unlock()
+ return s
+}
+
+func TypeSymName(t *Type) string {
+ name := t.ShortString()
+ // Use a separate symbol name for Noalg types for #17752.
+ if TypeHasNoAlg(t) {
+ name = "noalg." + name
+ }
+ return name
+}
+
+// Fake package for runtime type info (headers)
+// Don't access directly, use typeLookup below.
+var (
+ typepkgmu sync.Mutex // protects typepkg lookups
+ typepkg = NewPkg("type", "type")
+)
+
+var SimType [NTYPE]Kind
diff --git a/src/cmd/compile/internal/types/utils.go b/src/cmd/compile/internal/types/utils.go
index e8b1073818054..f9f629ca3ea6c 100644
--- a/src/cmd/compile/internal/types/utils.go
+++ b/src/cmd/compile/internal/types/utils.go
@@ -4,64 +4,8 @@
package types
-import (
- "cmd/internal/obj"
- "fmt"
-)
-
const BADWIDTH = -1000000000
-// The following variables must be initialized early by the frontend.
-// They are here to break import cycles.
-// TODO(gri) eliminate these dependencies.
-var (
- Widthptr int
- Dowidth func(*Type)
- Fatalf func(string, ...interface{})
- Sconv func(*Sym, int, int) string // orig: func sconv(s *Sym, flag FmtFlag, mode fmtMode) string
- Tconv func(*Type, int, int) string // orig: func tconv(t *Type, flag FmtFlag, mode fmtMode) string
- FormatSym func(*Sym, fmt.State, rune, int) // orig: func symFormat(sym *Sym, s fmt.State, verb rune, mode fmtMode)
- FormatType func(*Type, fmt.State, rune, int) // orig: func typeFormat(t *Type, s fmt.State, verb rune, mode fmtMode)
- TypeLinkSym func(*Type) *obj.LSym
- Ctxt *obj.Link
-
- FmtLeft int
- FmtUnsigned int
- FErr int
-)
-
-func (s *Sym) String() string {
- return Sconv(s, 0, FErr)
-}
-
-func (sym *Sym) Format(s fmt.State, verb rune) {
- FormatSym(sym, s, verb, FErr)
-}
-
-func (t *Type) String() string {
- // The implementation of tconv (including typefmt and fldconv)
- // must handle recursive types correctly.
- return Tconv(t, 0, FErr)
-}
-
-// ShortString generates a short description of t.
-// It is used in autogenerated method names, reflection,
-// and itab names.
-func (t *Type) ShortString() string {
- return Tconv(t, FmtLeft, FErr)
-}
-
-// LongString generates a complete description of t.
-// It is useful for reflection,
-// or when a unique fingerprint or hash of a type is required.
-func (t *Type) LongString() string {
- return Tconv(t, FmtLeft|FmtUnsigned, FErr)
-}
-
-func (t *Type) Format(s fmt.State, verb rune) {
- FormatType(t, s, verb, FErr)
-}
-
type bitset8 uint8
func (f *bitset8) set(mask uint8, b bool) {
diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go
new file mode 100644
index 0000000000000..230b544148ee0
--- /dev/null
+++ b/src/cmd/compile/internal/walk/assign.go
@@ -0,0 +1,719 @@
+// Copyright 2009 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 walk
+
+import (
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// walkAssign walks an OAS (AssignExpr) or OASOP (AssignOpExpr) node.
+func walkAssign(init *ir.Nodes, n ir.Node) ir.Node {
+ init.Append(ir.TakeInit(n)...)
+
+ var left, right ir.Node
+ switch n.Op() {
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ left, right = n.X, n.Y
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ left, right = n.X, n.Y
+ }
+
+ // Recognize m[k] = append(m[k], ...) so we can reuse
+ // the mapassign call.
+ var mapAppend *ir.CallExpr
+ if left.Op() == ir.OINDEXMAP && right.Op() == ir.OAPPEND {
+ left := left.(*ir.IndexExpr)
+ mapAppend = right.(*ir.CallExpr)
+ if !ir.SameSafeExpr(left, mapAppend.Args[0]) {
+ base.Fatalf("not same expressions: %v != %v", left, mapAppend.Args[0])
+ }
+ }
+
+ left = walkExpr(left, init)
+ left = safeExpr(left, init)
+ if mapAppend != nil {
+ mapAppend.Args[0] = left
+ }
+
+ if n.Op() == ir.OASOP {
+ // Rewrite x op= y into x = x op y.
+ n = ir.NewAssignStmt(base.Pos, left, typecheck.Expr(ir.NewBinaryExpr(base.Pos, n.(*ir.AssignOpStmt).AsOp, left, right)))
+ } else {
+ n.(*ir.AssignStmt).X = left
+ }
+ as := n.(*ir.AssignStmt)
+
+ if oaslit(as, init) {
+ return ir.NewBlockStmt(as.Pos(), nil)
+ }
+
+ if as.Y == nil {
+ // TODO(austin): Check all "implicit zeroing"
+ return as
+ }
+
+ if !base.Flag.Cfg.Instrumenting && ir.IsZero(as.Y) {
+ return as
+ }
+
+ switch as.Y.Op() {
+ default:
+ as.Y = walkExpr(as.Y, init)
+
+ case ir.ORECV:
+ // x = <-c; as.Left is x, as.Right.Left is c.
+ // order.stmt made sure x is addressable.
+ recv := as.Y.(*ir.UnaryExpr)
+ recv.X = walkExpr(recv.X, init)
+
+ n1 := typecheck.NodAddr(as.X)
+ r := recv.X // the channel
+ return mkcall1(chanfn("chanrecv1", 2, r.Type()), nil, init, r, n1)
+
+ case ir.OAPPEND:
+ // x = append(...)
+ call := as.Y.(*ir.CallExpr)
+ if call.Type().Elem().NotInHeap() {
+ base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", call.Type().Elem())
+ }
+ var r ir.Node
+ switch {
+ case isAppendOfMake(call):
+ // x = append(y, make([]T, y)...)
+ r = extendSlice(call, init)
+ case call.IsDDD:
+ r = appendSlice(call, init) // also works for append(slice, string).
+ default:
+ r = walkAppend(call, init, as)
+ }
+ as.Y = r
+ if r.Op() == ir.OAPPEND {
+ // Left in place for back end.
+ // Do not add a new write barrier.
+ // Set up address of type for back end.
+ r.(*ir.CallExpr).X = reflectdata.TypePtr(r.Type().Elem())
+ return as
+ }
+ // Otherwise, lowered for race detector.
+ // Treat as ordinary assignment.
+ }
+
+ if as.X != nil && as.Y != nil {
+ return convas(as, init)
+ }
+ return as
+}
+
+// walkAssignDotType walks an OAS2DOTTYPE node.
+func walkAssignDotType(n *ir.AssignListStmt, init *ir.Nodes) ir.Node {
+ walkExprListSafe(n.Lhs, init)
+ n.Rhs[0] = walkExpr(n.Rhs[0], init)
+ return n
+}
+
+// walkAssignFunc walks an OAS2FUNC node.
+func walkAssignFunc(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
+ init.Append(ir.TakeInit(n)...)
+
+ r := n.Rhs[0]
+ walkExprListSafe(n.Lhs, init)
+ r = walkExpr(r, init)
+
+ if ir.IsIntrinsicCall(r.(*ir.CallExpr)) {
+ n.Rhs = []ir.Node{r}
+ return n
+ }
+ init.Append(r)
+
+ ll := ascompatet(n.Lhs, r.Type())
+ return ir.NewBlockStmt(src.NoXPos, ll)
+}
+
+// walkAssignList walks an OAS2 node.
+func walkAssignList(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
+ init.Append(ir.TakeInit(n)...)
+ return ir.NewBlockStmt(src.NoXPos, ascompatee(ir.OAS, n.Lhs, n.Rhs))
+}
+
+// walkAssignMapRead walks an OAS2MAPR node.
+func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
+ init.Append(ir.TakeInit(n)...)
+
+ r := n.Rhs[0].(*ir.IndexExpr)
+ walkExprListSafe(n.Lhs, init)
+ r.X = walkExpr(r.X, init)
+ r.Index = walkExpr(r.Index, init)
+ t := r.X.Type()
+
+ fast := mapfast(t)
+ var key ir.Node
+ if fast != mapslow {
+ // fast versions take key by value
+ key = r.Index
+ } else {
+ // standard version takes key by reference
+ // order.expr made sure key is addressable.
+ key = typecheck.NodAddr(r.Index)
+ }
+
+ // from:
+ // a,b = m[i]
+ // to:
+ // var,b = mapaccess2*(t, m, i)
+ // a = *var
+ a := n.Lhs[0]
+
+ var call *ir.CallExpr
+ if w := t.Elem().Width; w <= zeroValSize {
+ fn := mapfn(mapaccess2[fast], t)
+ call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key)
+ } else {
+ fn := mapfn("mapaccess2_fat", t)
+ z := reflectdata.ZeroAddr(w)
+ call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key, z)
+ }
+
+ // mapaccess2* returns a typed bool, but due to spec changes,
+ // the boolean result of i.(T) is now untyped so we make it the
+ // same type as the variable on the lhs.
+ if ok := n.Lhs[1]; !ir.IsBlank(ok) && ok.Type().IsBoolean() {
+ call.Type().Field(1).Type = ok.Type()
+ }
+ n.Rhs = []ir.Node{call}
+ n.SetOp(ir.OAS2FUNC)
+
+ // don't generate a = *var if a is _
+ if ir.IsBlank(a) {
+ return walkExpr(typecheck.Stmt(n), init)
+ }
+
+ var_ := typecheck.Temp(types.NewPtr(t.Elem()))
+ var_.SetTypecheck(1)
+ var_.MarkNonNil() // mapaccess always returns a non-nil pointer
+
+ n.Lhs[0] = var_
+ init.Append(walkExpr(n, init))
+
+ as := ir.NewAssignStmt(base.Pos, a, ir.NewStarExpr(base.Pos, var_))
+ return walkExpr(typecheck.Stmt(as), init)
+}
+
+// walkAssignRecv walks an OAS2RECV node.
+func walkAssignRecv(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
+ init.Append(ir.TakeInit(n)...)
+
+ r := n.Rhs[0].(*ir.UnaryExpr) // recv
+ walkExprListSafe(n.Lhs, init)
+ r.X = walkExpr(r.X, init)
+ var n1 ir.Node
+ if ir.IsBlank(n.Lhs[0]) {
+ n1 = typecheck.NodNil()
+ } else {
+ n1 = typecheck.NodAddr(n.Lhs[0])
+ }
+ fn := chanfn("chanrecv2", 2, r.X.Type())
+ ok := n.Lhs[1]
+ call := mkcall1(fn, types.Types[types.TBOOL], init, r.X, n1)
+ return typecheck.Stmt(ir.NewAssignStmt(base.Pos, ok, call))
+}
+
+// walkReturn walks an ORETURN node.
+func walkReturn(n *ir.ReturnStmt) ir.Node {
+ fn := ir.CurFunc
+
+ fn.NumReturns++
+ if len(n.Results) == 0 {
+ return n
+ }
+
+ results := fn.Type().Results().FieldSlice()
+ dsts := make([]ir.Node, len(results))
+ for i, v := range results {
+ // TODO(mdempsky): typecheck should have already checked the result variables.
+ dsts[i] = typecheck.AssignExpr(v.Nname.(*ir.Name))
+ }
+
+ n.Results = ascompatee(n.Op(), dsts, n.Results)
+ return n
+}
+
+// check assign type list to
+// an expression list. called in
+// expr-list = func()
+func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node {
+ if len(nl) != nr.NumFields() {
+ base.Fatalf("ascompatet: assignment count mismatch: %d = %d", len(nl), nr.NumFields())
+ }
+
+ var nn ir.Nodes
+ for i, l := range nl {
+ if ir.IsBlank(l) {
+ continue
+ }
+ r := nr.Field(i)
+
+ // Order should have created autotemps of the appropriate type for
+ // us to store results into.
+ if tmp, ok := l.(*ir.Name); !ok || !tmp.AutoTemp() || !types.Identical(tmp.Type(), r.Type) {
+ base.FatalfAt(l.Pos(), "assigning %v to %+v", r.Type, l)
+ }
+
+ res := ir.NewResultExpr(base.Pos, nil, types.BADWIDTH)
+ res.Offset = base.Ctxt.FixedFrameSize() + r.Offset
+ res.SetType(r.Type)
+ res.SetTypecheck(1)
+
+ nn.Append(ir.NewAssignStmt(base.Pos, l, res))
+ }
+ return nn
+}
+
+// check assign expression list to
+// an expression list. called in
+// expr-list = expr-list
+func ascompatee(op ir.Op, nl, nr []ir.Node) []ir.Node {
+ // cannot happen: should have been rejected during type checking
+ if len(nl) != len(nr) {
+ base.Fatalf("assignment operands mismatch: %+v / %+v", ir.Nodes(nl), ir.Nodes(nr))
+ }
+
+ var assigned ir.NameSet
+ var memWrite, deferResultWrite bool
+
+ // affected reports whether expression n could be affected by
+ // the assignments applied so far.
+ affected := func(n ir.Node) bool {
+ if deferResultWrite {
+ return true
+ }
+ return ir.Any(n, func(n ir.Node) bool {
+ if n.Op() == ir.ONAME && assigned.Has(n.(*ir.Name)) {
+ return true
+ }
+ if memWrite && readsMemory(n) {
+ return true
+ }
+ return false
+ })
+ }
+
+ // If a needed expression may be affected by an
+ // earlier assignment, make an early copy of that
+ // expression and use the copy instead.
+ var early ir.Nodes
+ save := func(np *ir.Node) {
+ if n := *np; affected(n) {
+ *np = copyExpr(n, n.Type(), &early)
+ }
+ }
+
+ var late ir.Nodes
+ for i, lorig := range nl {
+ l, r := lorig, nr[i]
+
+ // Do not generate 'x = x' during return. See issue 4014.
+ if op == ir.ORETURN && ir.SameSafeExpr(l, r) {
+ continue
+ }
+
+ // Save subexpressions needed on left side.
+ // Drill through non-dereferences.
+ for {
+ switch ll := l.(type) {
+ case *ir.IndexExpr:
+ if ll.X.Type().IsArray() {
+ save(&ll.Index)
+ l = ll.X
+ continue
+ }
+ case *ir.ParenExpr:
+ l = ll.X
+ continue
+ case *ir.SelectorExpr:
+ if ll.Op() == ir.ODOT {
+ l = ll.X
+ continue
+ }
+ }
+ break
+ }
+
+ var name *ir.Name
+ switch l.Op() {
+ default:
+ base.Fatalf("unexpected lvalue %v", l.Op())
+ case ir.ONAME:
+ name = l.(*ir.Name)
+ case ir.OINDEX, ir.OINDEXMAP:
+ l := l.(*ir.IndexExpr)
+ save(&l.X)
+ save(&l.Index)
+ case ir.ODEREF:
+ l := l.(*ir.StarExpr)
+ save(&l.X)
+ case ir.ODOTPTR:
+ l := l.(*ir.SelectorExpr)
+ save(&l.X)
+ }
+
+ // Save expression on right side.
+ save(&r)
+
+ appendWalkStmt(&late, convas(ir.NewAssignStmt(base.Pos, lorig, r), &late))
+
+ // Check for reasons why we may need to compute later expressions
+ // before this assignment happens.
+
+ if name == nil {
+ // Not a direct assignment to a declared variable.
+ // Conservatively assume any memory access might alias.
+ memWrite = true
+ continue
+ }
+
+ if name.Class == ir.PPARAMOUT && ir.CurFunc.HasDefer() {
+ // Assignments to a result parameter in a function with defers
+ // becomes visible early if evaluation of any later expression
+ // panics (#43835).
+ deferResultWrite = true
+ continue
+ }
+
+ if sym := types.OrigSym(name.Sym()); sym == nil || sym.IsBlank() {
+ // We can ignore assignments to blank or anonymous result parameters.
+ // These can't appear in expressions anyway.
+ continue
+ }
+
+ if name.Addrtaken() || !name.OnStack() {
+ // Global variable, heap escaped, or just addrtaken.
+ // Conservatively assume any memory access might alias.
+ memWrite = true
+ continue
+ }
+
+ // Local, non-addrtaken variable.
+ // Assignments can only alias with direct uses of this variable.
+ assigned.Add(name)
+ }
+
+ early.Append(late.Take()...)
+ return early
+}
+
+// readsMemory reports whether the evaluation n directly reads from
+// memory that might be written to indirectly.
+func readsMemory(n ir.Node) bool {
+ switch n.Op() {
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ if n.Class == ir.PFUNC {
+ return false
+ }
+ return n.Addrtaken() || !n.OnStack()
+
+ case ir.OADD,
+ ir.OAND,
+ ir.OANDAND,
+ ir.OANDNOT,
+ ir.OBITNOT,
+ ir.OCONV,
+ ir.OCONVIFACE,
+ ir.OCONVNOP,
+ ir.ODIV,
+ ir.ODOT,
+ ir.ODOTTYPE,
+ ir.OLITERAL,
+ ir.OLSH,
+ ir.OMOD,
+ ir.OMUL,
+ ir.ONEG,
+ ir.ONIL,
+ ir.OOR,
+ ir.OOROR,
+ ir.OPAREN,
+ ir.OPLUS,
+ ir.ORSH,
+ ir.OSUB,
+ ir.OXOR:
+ return false
+ }
+
+ // Be conservative.
+ return true
+}
+
+// expand append(l1, l2...) to
+// init {
+// s := l1
+// n := len(s) + len(l2)
+// // Compare as uint so growslice can panic on overflow.
+// if uint(n) > uint(cap(s)) {
+// s = growslice(s, n)
+// }
+// s = s[:n]
+// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+// }
+// s
+//
+// l2 is allowed to be a string.
+func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
+ walkAppendArgs(n, init)
+
+ l1 := n.Args[0]
+ l2 := n.Args[1]
+ l2 = cheapExpr(l2, init)
+ n.Args[1] = l2
+
+ var nodes ir.Nodes
+
+ // var s []T
+ s := typecheck.Temp(l1.Type())
+ nodes.Append(ir.NewAssignStmt(base.Pos, s, l1)) // s = l1
+
+ elemtype := s.Type().Elem()
+
+ // n := len(s) + len(l2)
+ nn := typecheck.Temp(types.Types[types.TINT])
+ nodes.Append(ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), ir.NewUnaryExpr(base.Pos, ir.OLEN, l2))))
+
+ // if uint(n) > uint(cap(s))
+ nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ nuint := typecheck.Conv(nn, types.Types[types.TUINT])
+ scapuint := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT])
+ nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, nuint, scapuint)
+
+ // instantiate growslice(typ *type, []any, int) []any
+ fn := typecheck.LookupRuntime("growslice")
+ fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)
+
+ // s = growslice(T, s, n)
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))}
+ nodes.Append(nif)
+
+ // s = s[:n]
+ nt := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, nn, nil)
+ nt.SetBounded(true)
+ nodes.Append(ir.NewAssignStmt(base.Pos, s, nt))
+
+ var ncopy ir.Node
+ if elemtype.HasPointers() {
+ // copy(s[len(l1):], l2)
+ slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1), nil, nil)
+ slice.SetType(s.Type())
+
+ ir.CurFunc.SetWBPos(n.Pos())
+
+ // instantiate typedslicecopy(typ *type, dstPtr *any, dstLen int, srcPtr *any, srcLen int) int
+ fn := typecheck.LookupRuntime("typedslicecopy")
+ fn = typecheck.SubstArgTypes(fn, l1.Type().Elem(), l2.Type().Elem())
+ ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes))
+ ptr2, len2 := backingArrayPtrLen(l2)
+ ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.TypePtr(elemtype), ptr1, len1, ptr2, len2)
+ } else if base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime {
+ // rely on runtime to instrument:
+ // copy(s[len(l1):], l2)
+ // l2 can be a slice or string.
+ slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1), nil, nil)
+ slice.SetType(s.Type())
+
+ ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes))
+ ptr2, len2 := backingArrayPtrLen(l2)
+
+ fn := typecheck.LookupRuntime("slicecopy")
+ fn = typecheck.SubstArgTypes(fn, ptr1.Type().Elem(), ptr2.Type().Elem())
+ ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, ptr1, len1, ptr2, len2, ir.NewInt(elemtype.Width))
+ } else {
+ // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+ ix := ir.NewIndexExpr(base.Pos, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1))
+ ix.SetBounded(true)
+ addr := typecheck.NodAddr(ix)
+
+ sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l2)
+
+ nwid := cheapExpr(typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, l2), types.Types[types.TUINTPTR]), &nodes)
+ nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(elemtype.Width))
+
+ // instantiate func memmove(to *any, frm *any, length uintptr)
+ fn := typecheck.LookupRuntime("memmove")
+ fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)
+ ncopy = mkcall1(fn, nil, &nodes, addr, sptr, nwid)
+ }
+ ln := append(nodes, ncopy)
+
+ typecheck.Stmts(ln)
+ walkStmtList(ln)
+ init.Append(ln...)
+ return s
+}
+
+// isAppendOfMake reports whether n is of the form append(x , make([]T, y)...).
+// isAppendOfMake assumes n has already been typechecked.
+func isAppendOfMake(n ir.Node) bool {
+ if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
+ return false
+ }
+
+ if n.Typecheck() == 0 {
+ base.Fatalf("missing typecheck: %+v", n)
+ }
+
+ if n.Op() != ir.OAPPEND {
+ return false
+ }
+ call := n.(*ir.CallExpr)
+ if !call.IsDDD || len(call.Args) != 2 || call.Args[1].Op() != ir.OMAKESLICE {
+ return false
+ }
+
+ mk := call.Args[1].(*ir.MakeExpr)
+ if mk.Cap != nil {
+ return false
+ }
+
+ // y must be either an integer constant or the largest possible positive value
+ // of variable y needs to fit into an uint.
+
+ // typecheck made sure that constant arguments to make are not negative and fit into an int.
+
+ // The care of overflow of the len argument to make will be handled by an explicit check of int(len) < 0 during runtime.
+ y := mk.Len
+ if !ir.IsConst(y, constant.Int) && y.Type().Size() > types.Types[types.TUINT].Size() {
+ return false
+ }
+
+ return true
+}
+
+// extendSlice rewrites append(l1, make([]T, l2)...) to
+// init {
+// if l2 >= 0 { // Empty if block here for more meaningful node.SetLikely(true)
+// } else {
+// panicmakeslicelen()
+// }
+// s := l1
+// n := len(s) + l2
+// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2.
+// // cap is a positive int and n can become negative when len(s) + l2
+// // overflows int. Interpreting n when negative as uint makes it larger
+// // than cap(s). growslice will check the int n arg and panic if n is
+// // negative. This prevents the overflow from being undetected.
+// if uint(n) > uint(cap(s)) {
+// s = growslice(T, s, n)
+// }
+// s = s[:n]
+// lptr := &l1[0]
+// sptr := &s[0]
+// if lptr == sptr || !T.HasPointers() {
+// // growslice did not clear the whole underlying array (or did not get called)
+// hp := &s[len(l1)]
+// hn := l2 * sizeof(T)
+// memclr(hp, hn)
+// }
+// }
+// s
+func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
+ // isAppendOfMake made sure all possible positive values of l2 fit into an uint.
+ // The case of l2 overflow when converting from e.g. uint to int is handled by an explicit
+ // check of l2 < 0 at runtime which is generated below.
+ l2 := typecheck.Conv(n.Args[1].(*ir.MakeExpr).Len, types.Types[types.TINT])
+ l2 = typecheck.Expr(l2)
+ n.Args[1] = l2 // walkAppendArgs expects l2 in n.List.Second().
+
+ walkAppendArgs(n, init)
+
+ l1 := n.Args[0]
+ l2 = n.Args[1] // re-read l2, as it may have been updated by walkAppendArgs
+
+ var nodes []ir.Node
+
+ // if l2 >= 0 (likely happens), do nothing
+ nifneg := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGE, l2, ir.NewInt(0)), nil, nil)
+ nifneg.Likely = true
+
+ // else panicmakeslicelen()
+ nifneg.Else = []ir.Node{mkcall("panicmakeslicelen", nil, init)}
+ nodes = append(nodes, nifneg)
+
+ // s := l1
+ s := typecheck.Temp(l1.Type())
+ nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, l1))
+
+ elemtype := s.Type().Elem()
+
+ // n := len(s) + l2
+ nn := typecheck.Temp(types.Types[types.TINT])
+ nodes = append(nodes, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2)))
+
+ // if uint(n) > uint(cap(s))
+ nuint := typecheck.Conv(nn, types.Types[types.TUINT])
+ capuint := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT])
+ nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGT, nuint, capuint), nil, nil)
+
+ // instantiate growslice(typ *type, old []any, newcap int) []any
+ fn := typecheck.LookupRuntime("growslice")
+ fn = typecheck.SubstArgTypes(fn, elemtype, elemtype)
+
+ // s = growslice(T, s, n)
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))}
+ nodes = append(nodes, nif)
+
+ // s = s[:n]
+ nt := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, nn, nil)
+ nt.SetBounded(true)
+ nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, nt))
+
+ // lptr := &l1[0]
+ l1ptr := typecheck.Temp(l1.Type().Elem().PtrTo())
+ tmp := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l1)
+ nodes = append(nodes, ir.NewAssignStmt(base.Pos, l1ptr, tmp))
+
+ // sptr := &s[0]
+ sptr := typecheck.Temp(elemtype.PtrTo())
+ tmp = ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
+ nodes = append(nodes, ir.NewAssignStmt(base.Pos, sptr, tmp))
+
+ // hp := &s[len(l1)]
+ ix := ir.NewIndexExpr(base.Pos, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1))
+ ix.SetBounded(true)
+ hp := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])
+
+ // hn := l2 * sizeof(elem(s))
+ hn := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, l2, ir.NewInt(elemtype.Width)), types.Types[types.TUINTPTR])
+
+ clrname := "memclrNoHeapPointers"
+ hasPointers := elemtype.HasPointers()
+ if hasPointers {
+ clrname = "memclrHasPointers"
+ ir.CurFunc.SetWBPos(n.Pos())
+ }
+
+ var clr ir.Nodes
+ clrfn := mkcall(clrname, nil, &clr, hp, hn)
+ clr.Append(clrfn)
+
+ if hasPointers {
+ // if l1ptr == sptr
+ nifclr := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OEQ, l1ptr, sptr), nil, nil)
+ nifclr.Body = clr
+ nodes = append(nodes, nifclr)
+ } else {
+ nodes = append(nodes, clr...)
+ }
+
+ typecheck.Stmts(nodes)
+ walkStmtList(nodes)
+ init.Append(nodes...)
+ return s
+}
diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go
new file mode 100644
index 0000000000000..97f9de9c1df33
--- /dev/null
+++ b/src/cmd/compile/internal/walk/builtin.go
@@ -0,0 +1,687 @@
+// Copyright 2009 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 walk
+
+import (
+ "fmt"
+ "go/constant"
+ "go/token"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/escape"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+)
+
+// Rewrite append(src, x, y, z) so that any side effects in
+// x, y, z (including runtime panics) are evaluated in
+// initialization statements before the append.
+// For normal code generation, stop there and leave the
+// rest to cgen_append.
+//
+// For race detector, expand append(src, a [, b]* ) to
+//
+// init {
+// s := src
+// const argc = len(args) - 1
+// if cap(s) - len(s) < argc {
+// s = growslice(s, len(s)+argc)
+// }
+// n := len(s)
+// s = s[:n+argc]
+// s[n] = a
+// s[n+1] = b
+// ...
+// }
+// s
+func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node {
+ if !ir.SameSafeExpr(dst, n.Args[0]) {
+ n.Args[0] = safeExpr(n.Args[0], init)
+ n.Args[0] = walkExpr(n.Args[0], init)
+ }
+ walkExprListSafe(n.Args[1:], init)
+
+ nsrc := n.Args[0]
+
+ // walkExprListSafe will leave OINDEX (s[n]) alone if both s
+ // and n are name or literal, but those may index the slice we're
+ // modifying here. Fix explicitly.
+ // Using cheapExpr also makes sure that the evaluation
+ // of all arguments (and especially any panics) happen
+ // before we begin to modify the slice in a visible way.
+ ls := n.Args[1:]
+ for i, n := range ls {
+ n = cheapExpr(n, init)
+ if !types.Identical(n.Type(), nsrc.Type().Elem()) {
+ n = typecheck.AssignConv(n, nsrc.Type().Elem(), "append")
+ n = walkExpr(n, init)
+ }
+ ls[i] = n
+ }
+
+ argc := len(n.Args) - 1
+ if argc < 1 {
+ return nsrc
+ }
+
+ // General case, with no function calls left as arguments.
+ // Leave for gen, except that instrumentation requires old form.
+ if !base.Flag.Cfg.Instrumenting || base.Flag.CompilingRuntime {
+ return n
+ }
+
+ var l []ir.Node
+
+ ns := typecheck.Temp(nsrc.Type())
+ l = append(l, ir.NewAssignStmt(base.Pos, ns, nsrc)) // s = src
+
+ na := ir.NewInt(int64(argc)) // const argc
+ nif := ir.NewIfStmt(base.Pos, nil, nil, nil) // if cap(s) - len(s) < argc
+ nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OCAP, ns), ir.NewUnaryExpr(base.Pos, ir.OLEN, ns)), na)
+
+ fn := typecheck.LookupRuntime("growslice") // growslice(, old []T, mincap int) (ret []T)
+ fn = typecheck.SubstArgTypes(fn, ns.Type().Elem(), ns.Type().Elem())
+
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.TypePtr(ns.Type().Elem()), ns,
+ ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns), na)))}
+
+ l = append(l, nif)
+
+ nn := typecheck.Temp(types.Types[types.TINT])
+ l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns))) // n = len(s)
+
+ slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, ns, nil, ir.NewBinaryExpr(base.Pos, ir.OADD, nn, na), nil) // ...s[:n+argc]
+ slice.SetBounded(true)
+ l = append(l, ir.NewAssignStmt(base.Pos, ns, slice)) // s = s[:n+argc]
+
+ ls = n.Args[1:]
+ for i, n := range ls {
+ ix := ir.NewIndexExpr(base.Pos, ns, nn) // s[n] ...
+ ix.SetBounded(true)
+ l = append(l, ir.NewAssignStmt(base.Pos, ix, n)) // s[n] = arg
+ if i+1 < len(ls) {
+ l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, nn, ir.NewInt(1)))) // n = n + 1
+ }
+ }
+
+ typecheck.Stmts(l)
+ walkStmtList(l)
+ init.Append(l...)
+ return ns
+}
+
+// walkClose walks an OCLOSE node.
+func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
+ // cannot use chanfn - closechan takes any, not chan any
+ fn := typecheck.LookupRuntime("closechan")
+ fn = typecheck.SubstArgTypes(fn, n.X.Type())
+ return mkcall1(fn, nil, init, n.X)
+}
+
+// Lower copy(a, b) to a memmove call or a runtime call.
+//
+// init {
+// n := len(a)
+// if n > len(b) { n = len(b) }
+// if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) }
+// }
+// n;
+//
+// Also works if b is a string.
+//
+func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node {
+ if n.X.Type().Elem().HasPointers() {
+ ir.CurFunc.SetWBPos(n.Pos())
+ fn := writebarrierfn("typedslicecopy", n.X.Type().Elem(), n.Y.Type().Elem())
+ n.X = cheapExpr(n.X, init)
+ ptrL, lenL := backingArrayPtrLen(n.X)
+ n.Y = cheapExpr(n.Y, init)
+ ptrR, lenR := backingArrayPtrLen(n.Y)
+ return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.X.Type().Elem()), ptrL, lenL, ptrR, lenR)
+ }
+
+ if runtimecall {
+ // rely on runtime to instrument:
+ // copy(n.Left, n.Right)
+ // n.Right can be a slice or string.
+
+ n.X = cheapExpr(n.X, init)
+ ptrL, lenL := backingArrayPtrLen(n.X)
+ n.Y = cheapExpr(n.Y, init)
+ ptrR, lenR := backingArrayPtrLen(n.Y)
+
+ fn := typecheck.LookupRuntime("slicecopy")
+ fn = typecheck.SubstArgTypes(fn, ptrL.Type().Elem(), ptrR.Type().Elem())
+
+ return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(n.X.Type().Elem().Width))
+ }
+
+ n.X = walkExpr(n.X, init)
+ n.Y = walkExpr(n.Y, init)
+ nl := typecheck.Temp(n.X.Type())
+ nr := typecheck.Temp(n.Y.Type())
+ var l []ir.Node
+ l = append(l, ir.NewAssignStmt(base.Pos, nl, n.X))
+ l = append(l, ir.NewAssignStmt(base.Pos, nr, n.Y))
+
+ nfrm := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nr)
+ nto := ir.NewUnaryExpr(base.Pos, ir.OSPTR, nl)
+
+ nlen := typecheck.Temp(types.Types[types.TINT])
+
+ // n = len(to)
+ l = append(l, ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nl)))
+
+ // if n > len(frm) { n = len(frm) }
+ nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
+
+ nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr))
+ nif.Body.Append(ir.NewAssignStmt(base.Pos, nlen, ir.NewUnaryExpr(base.Pos, ir.OLEN, nr)))
+ l = append(l, nif)
+
+ // if to.ptr != frm.ptr { memmove( ... ) }
+ ne := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.ONE, nto, nfrm), nil, nil)
+ ne.Likely = true
+ l = append(l, ne)
+
+ fn := typecheck.LookupRuntime("memmove")
+ fn = typecheck.SubstArgTypes(fn, nl.Type().Elem(), nl.Type().Elem())
+ nwid := ir.Node(typecheck.Temp(types.Types[types.TUINTPTR]))
+ setwid := ir.NewAssignStmt(base.Pos, nwid, typecheck.Conv(nlen, types.Types[types.TUINTPTR]))
+ ne.Body.Append(setwid)
+ nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(nl.Type().Elem().Width))
+ call := mkcall1(fn, nil, init, nto, nfrm, nwid)
+ ne.Body.Append(call)
+
+ typecheck.Stmts(l)
+ walkStmtList(l)
+ init.Append(l...)
+ return nlen
+}
+
+// walkDelete walks an ODELETE node.
+func walkDelete(init *ir.Nodes, n *ir.CallExpr) ir.Node {
+ init.Append(ir.TakeInit(n)...)
+ map_ := n.Args[0]
+ key := n.Args[1]
+ map_ = walkExpr(map_, init)
+ key = walkExpr(key, init)
+
+ t := map_.Type()
+ fast := mapfast(t)
+ if fast == mapslow {
+ // order.stmt made sure key is addressable.
+ key = typecheck.NodAddr(key)
+ }
+ return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.TypePtr(t), map_, key)
+}
+
+// walkLenCap walks an OLEN or OCAP node.
+func walkLenCap(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
+ if isRuneCount(n) {
+ // Replace len([]rune(string)) with runtime.countrunes(string).
+ return mkcall("countrunes", n.Type(), init, typecheck.Conv(n.X.(*ir.ConvExpr).X, types.Types[types.TSTRING]))
+ }
+
+ n.X = walkExpr(n.X, init)
+
+ // replace len(*[10]int) with 10.
+ // delayed until now to preserve side effects.
+ t := n.X.Type()
+
+ if t.IsPtr() {
+ t = t.Elem()
+ }
+ if t.IsArray() {
+ safeExpr(n.X, init)
+ con := typecheck.OrigInt(n, t.NumElem())
+ con.SetTypecheck(1)
+ return con
+ }
+ return n
+}
+
+// walkMakeChan walks an OMAKECHAN node.
+func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
+ // When size fits into int, use makechan instead of
+ // makechan64, which is faster and shorter on 32 bit platforms.
+ size := n.Len
+ fnname := "makechan64"
+ argtype := types.Types[types.TINT64]
+
+ // Type checking guarantees that TIDEAL size is positive and fits in an int.
+ // The case of size overflow when converting TUINT or TUINTPTR to TINT
+ // will be handled by the negative range checks in makechan during runtime.
+ if size.Type().IsKind(types.TIDEAL) || size.Type().Size() <= types.Types[types.TUINT].Size() {
+ fnname = "makechan"
+ argtype = types.Types[types.TINT]
+ }
+
+ return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(size, argtype))
+}
+
+// walkMakeMap walks an OMAKEMAP node.
+func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
+ t := n.Type()
+ hmapType := reflectdata.MapType(t)
+ hint := n.Len
+
+ // var h *hmap
+ var h ir.Node
+ if n.Esc() == ir.EscNone {
+ // Allocate hmap on stack.
+
+ // var hv hmap
+ // h = &hv
+ h = stackTempAddr(init, hmapType)
+
+ // Allocate one bucket pointed to by hmap.buckets on stack if hint
+ // is not larger than BUCKETSIZE. In case hint is larger than
+ // BUCKETSIZE runtime.makemap will allocate the buckets on the heap.
+ // Maximum key and elem size is 128 bytes, larger objects
+ // are stored with an indirection. So max bucket size is 2048+eps.
+ if !ir.IsConst(hint, constant.Int) ||
+ constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(reflectdata.BUCKETSIZE)) {
+
+ // In case hint is larger than BUCKETSIZE runtime.makemap
+ // will allocate the buckets on the heap, see #20184
+ //
+ // if hint <= BUCKETSIZE {
+ // var bv bmap
+ // b = &bv
+ // h.buckets = b
+ // }
+
+ nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLE, hint, ir.NewInt(reflectdata.BUCKETSIZE)), nil, nil)
+ nif.Likely = true
+
+ // var bv bmap
+ // b = &bv
+ b := stackTempAddr(&nif.Body, reflectdata.MapBucketType(t))
+
+ // h.buckets = b
+ bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap
+ na := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, bsym), b)
+ nif.Body.Append(na)
+ appendWalkStmt(init, nif)
+ }
+ }
+
+ if ir.IsConst(hint, constant.Int) && constant.Compare(hint.Val(), token.LEQ, constant.MakeInt64(reflectdata.BUCKETSIZE)) {
+ // Handling make(map[any]any) and
+ // make(map[any]any, hint) where hint <= BUCKETSIZE
+ // special allows for faster map initialization and
+ // improves binary size by using calls with fewer arguments.
+ // For hint <= BUCKETSIZE overLoadFactor(hint, 0) is false
+ // and no buckets will be allocated by makemap. Therefore,
+ // no buckets need to be allocated in this code path.
+ if n.Esc() == ir.EscNone {
+ // Only need to initialize h.hash0 since
+ // hmap h has been allocated on the stack already.
+ // h.hash0 = fastrand()
+ rand := mkcall("fastrand", types.Types[types.TUINT32], init)
+ hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, hashsym), rand))
+ return typecheck.ConvNop(h, t)
+ }
+ // Call runtime.makehmap to allocate an
+ // hmap on the heap and initialize hmap's hash0 field.
+ fn := typecheck.LookupRuntime("makemap_small")
+ fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem())
+ return mkcall1(fn, n.Type(), init)
+ }
+
+ if n.Esc() != ir.EscNone {
+ h = typecheck.NodNil()
+ }
+ // Map initialization with a variable or large hint is
+ // more complicated. We therefore generate a call to
+ // runtime.makemap to initialize hmap and allocate the
+ // map buckets.
+
+ // When hint fits into int, use makemap instead of
+ // makemap64, which is faster and shorter on 32 bit platforms.
+ fnname := "makemap64"
+ argtype := types.Types[types.TINT64]
+
+ // Type checking guarantees that TIDEAL hint is positive and fits in an int.
+ // See checkmake call in TMAP case of OMAKE case in OpSwitch in typecheck1 function.
+ // The case of hint overflow when converting TUINT or TUINTPTR to TINT
+ // will be handled by the negative range checks in makemap during runtime.
+ if hint.Type().IsKind(types.TIDEAL) || hint.Type().Size() <= types.Types[types.TUINT].Size() {
+ fnname = "makemap"
+ argtype = types.Types[types.TINT]
+ }
+
+ fn := typecheck.LookupRuntime(fnname)
+ fn = typecheck.SubstArgTypes(fn, hmapType, t.Key(), t.Elem())
+ return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(hint, argtype), h)
+}
+
+// walkMakeSlice walks an OMAKESLICE node.
+func walkMakeSlice(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
+ l := n.Len
+ r := n.Cap
+ if r == nil {
+ r = safeExpr(l, init)
+ l = r
+ }
+ t := n.Type()
+ if t.Elem().NotInHeap() {
+ base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
+ }
+ if n.Esc() == ir.EscNone {
+ if why := escape.HeapAllocReason(n); why != "" {
+ base.Fatalf("%v has EscNone, but %v", n, why)
+ }
+ // var arr [r]T
+ // n = arr[:l]
+ i := typecheck.IndexConst(r)
+ if i < 0 {
+ base.Fatalf("walkExpr: invalid index %v", r)
+ }
+
+ // cap is constrained to [0,2^31) or [0,2^63) depending on whether
+ // we're in 32-bit or 64-bit systems. So it's safe to do:
+ //
+ // if uint64(len) > cap {
+ // if len < 0 { panicmakeslicelen() }
+ // panicmakeslicecap()
+ // }
+ nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(l, types.Types[types.TUINT64]), ir.NewInt(i)), nil, nil)
+ niflen := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLT, l, ir.NewInt(0)), nil, nil)
+ niflen.Body = []ir.Node{mkcall("panicmakeslicelen", nil, init)}
+ nif.Body.Append(niflen, mkcall("panicmakeslicecap", nil, init))
+ init.Append(typecheck.Stmt(nif))
+
+ t = types.NewArray(t.Elem(), i) // [r]T
+ var_ := typecheck.Temp(t)
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, nil)) // zero temp
+ r := ir.NewSliceExpr(base.Pos, ir.OSLICE, var_, nil, l, nil) // arr[:l]
+ // The conv is necessary in case n.Type is named.
+ return walkExpr(typecheck.Expr(typecheck.Conv(r, n.Type())), init)
+ }
+
+ // n escapes; set up a call to makeslice.
+ // When len and cap can fit into int, use makeslice instead of
+ // makeslice64, which is faster and shorter on 32 bit platforms.
+
+ len, cap := l, r
+
+ fnname := "makeslice64"
+ argtype := types.Types[types.TINT64]
+
+ // Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
+ // The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
+ // will be handled by the negative range checks in makeslice during runtime.
+ if (len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size()) &&
+ (cap.Type().IsKind(types.TIDEAL) || cap.Type().Size() <= types.Types[types.TUINT].Size()) {
+ fnname = "makeslice"
+ argtype = types.Types[types.TINT]
+ }
+
+ m := ir.NewSliceHeaderExpr(base.Pos, nil, nil, nil, nil)
+ m.SetType(t)
+
+ fn := typecheck.LookupRuntime(fnname)
+ m.Ptr = mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype))
+ m.Ptr.MarkNonNil()
+ m.Len = typecheck.Conv(len, types.Types[types.TINT])
+ m.Cap = typecheck.Conv(cap, types.Types[types.TINT])
+ return walkExpr(typecheck.Expr(m), init)
+}
+
+// walkMakeSliceCopy walks an OMAKESLICECOPY node.
+func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
+ if n.Esc() == ir.EscNone {
+ base.Fatalf("OMAKESLICECOPY with EscNone: %v", n)
+ }
+
+ t := n.Type()
+ if t.Elem().NotInHeap() {
+ base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", t.Elem())
+ }
+
+ length := typecheck.Conv(n.Len, types.Types[types.TINT])
+ copylen := ir.NewUnaryExpr(base.Pos, ir.OLEN, n.Cap)
+ copyptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, n.Cap)
+
+ if !t.Elem().HasPointers() && n.Bounded() {
+ // When len(to)==len(from) and elements have no pointers:
+ // replace make+copy with runtime.mallocgc+runtime.memmove.
+
+ // We do not check for overflow of len(to)*elem.Width here
+ // since len(from) is an existing checked slice capacity
+ // with same elem.Width for the from slice.
+ size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(t.Elem().Width), types.Types[types.TUINTPTR]))
+
+ // instantiate mallocgc(size uintptr, typ *byte, needszero bool) unsafe.Pointer
+ fn := typecheck.LookupRuntime("mallocgc")
+ sh := ir.NewSliceHeaderExpr(base.Pos, nil, nil, nil, nil)
+ sh.Ptr = mkcall1(fn, types.Types[types.TUNSAFEPTR], init, size, typecheck.NodNil(), ir.NewBool(false))
+ sh.Ptr.MarkNonNil()
+ sh.Len = length
+ sh.Cap = length
+ sh.SetType(t)
+
+ s := typecheck.Temp(t)
+ r := typecheck.Stmt(ir.NewAssignStmt(base.Pos, s, sh))
+ r = walkExpr(r, init)
+ init.Append(r)
+
+ // instantiate memmove(to *any, frm *any, size uintptr)
+ fn = typecheck.LookupRuntime("memmove")
+ fn = typecheck.SubstArgTypes(fn, t.Elem(), t.Elem())
+ ncopy := mkcall1(fn, nil, init, ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), copyptr, size)
+ init.Append(walkExpr(typecheck.Stmt(ncopy), init))
+
+ return s
+ }
+ // Replace make+copy with runtime.makeslicecopy.
+ // instantiate makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
+ fn := typecheck.LookupRuntime("makeslicecopy")
+ s := ir.NewSliceHeaderExpr(base.Pos, nil, nil, nil, nil)
+ s.Ptr = mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR]))
+ s.Ptr.MarkNonNil()
+ s.Len = length
+ s.Cap = length
+ s.SetType(t)
+ return walkExpr(typecheck.Expr(s), init)
+}
+
+// walkNew walks an ONEW node.
+func walkNew(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
+ t := n.Type().Elem()
+ if t.NotInHeap() {
+ base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type().Elem())
+ }
+ if n.Esc() == ir.EscNone {
+ if t.Size() >= ir.MaxImplicitStackVarSize {
+ base.Fatalf("large ONEW with EscNone: %v", n)
+ }
+ return stackTempAddr(init, t)
+ }
+ types.CalcSize(t)
+ n.MarkNonNil()
+ return n
+}
+
+// generate code for print
+func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
+ // Hoist all the argument evaluation up before the lock.
+ walkExprListCheap(nn.Args, init)
+
+ // For println, add " " between elements and "\n" at the end.
+ if nn.Op() == ir.OPRINTN {
+ s := nn.Args
+ t := make([]ir.Node, 0, len(s)*2)
+ for i, n := range s {
+ if i != 0 {
+ t = append(t, ir.NewString(" "))
+ }
+ t = append(t, n)
+ }
+ t = append(t, ir.NewString("\n"))
+ nn.Args = t
+ }
+
+ // Collapse runs of constant strings.
+ s := nn.Args
+ t := make([]ir.Node, 0, len(s))
+ for i := 0; i < len(s); {
+ var strs []string
+ for i < len(s) && ir.IsConst(s[i], constant.String) {
+ strs = append(strs, ir.StringVal(s[i]))
+ i++
+ }
+ if len(strs) > 0 {
+ t = append(t, ir.NewString(strings.Join(strs, "")))
+ }
+ if i < len(s) {
+ t = append(t, s[i])
+ i++
+ }
+ }
+ nn.Args = t
+
+ calls := []ir.Node{mkcall("printlock", nil, init)}
+ for i, n := range nn.Args {
+ if n.Op() == ir.OLITERAL {
+ if n.Type() == types.UntypedRune {
+ n = typecheck.DefaultLit(n, types.RuneType)
+ }
+
+ switch n.Val().Kind() {
+ case constant.Int:
+ n = typecheck.DefaultLit(n, types.Types[types.TINT64])
+
+ case constant.Float:
+ n = typecheck.DefaultLit(n, types.Types[types.TFLOAT64])
+ }
+ }
+
+ if n.Op() != ir.OLITERAL && n.Type() != nil && n.Type().Kind() == types.TIDEAL {
+ n = typecheck.DefaultLit(n, types.Types[types.TINT64])
+ }
+ n = typecheck.DefaultLit(n, nil)
+ nn.Args[i] = n
+ if n.Type() == nil || n.Type().Kind() == types.TFORW {
+ continue
+ }
+
+ var on *ir.Name
+ switch n.Type().Kind() {
+ case types.TINTER:
+ if n.Type().IsEmptyInterface() {
+ on = typecheck.LookupRuntime("printeface")
+ } else {
+ on = typecheck.LookupRuntime("printiface")
+ }
+ on = typecheck.SubstArgTypes(on, n.Type()) // any-1
+ case types.TPTR:
+ if n.Type().Elem().NotInHeap() {
+ on = typecheck.LookupRuntime("printuintptr")
+ n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
+ n.SetType(types.Types[types.TUNSAFEPTR])
+ n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
+ n.SetType(types.Types[types.TUINTPTR])
+ break
+ }
+ fallthrough
+ case types.TCHAN, types.TMAP, types.TFUNC, types.TUNSAFEPTR:
+ on = typecheck.LookupRuntime("printpointer")
+ on = typecheck.SubstArgTypes(on, n.Type()) // any-1
+ case types.TSLICE:
+ on = typecheck.LookupRuntime("printslice")
+ on = typecheck.SubstArgTypes(on, n.Type()) // any-1
+ case types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, types.TUINTPTR:
+ if types.IsRuntimePkg(n.Type().Sym().Pkg) && n.Type().Sym().Name == "hex" {
+ on = typecheck.LookupRuntime("printhex")
+ } else {
+ on = typecheck.LookupRuntime("printuint")
+ }
+ case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64:
+ on = typecheck.LookupRuntime("printint")
+ case types.TFLOAT32, types.TFLOAT64:
+ on = typecheck.LookupRuntime("printfloat")
+ case types.TCOMPLEX64, types.TCOMPLEX128:
+ on = typecheck.LookupRuntime("printcomplex")
+ case types.TBOOL:
+ on = typecheck.LookupRuntime("printbool")
+ case types.TSTRING:
+ cs := ""
+ if ir.IsConst(n, constant.String) {
+ cs = ir.StringVal(n)
+ }
+ switch cs {
+ case " ":
+ on = typecheck.LookupRuntime("printsp")
+ case "\n":
+ on = typecheck.LookupRuntime("printnl")
+ default:
+ on = typecheck.LookupRuntime("printstring")
+ }
+ default:
+ badtype(ir.OPRINT, n.Type(), nil)
+ continue
+ }
+
+ r := ir.NewCallExpr(base.Pos, ir.OCALL, on, nil)
+ if params := on.Type().Params().FieldSlice(); len(params) > 0 {
+ t := params[0].Type
+ if !types.Identical(t, n.Type()) {
+ n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
+ n.SetType(t)
+ }
+ r.Args.Append(n)
+ }
+ calls = append(calls, r)
+ }
+
+ calls = append(calls, mkcall("printunlock", nil, init))
+
+ typecheck.Stmts(calls)
+ walkExprList(calls, init)
+
+ r := ir.NewBlockStmt(base.Pos, nil)
+ r.List = calls
+ return walkStmt(typecheck.Stmt(r))
+}
+
+func badtype(op ir.Op, tl, tr *types.Type) {
+ var s string
+ if tl != nil {
+ s += fmt.Sprintf("\n\t%v", tl)
+ }
+ if tr != nil {
+ s += fmt.Sprintf("\n\t%v", tr)
+ }
+
+ // common mistake: *struct and *interface.
+ if tl != nil && tr != nil && tl.IsPtr() && tr.IsPtr() {
+ if tl.Elem().IsStruct() && tr.Elem().IsInterface() {
+ s += "\n\t(*struct vs *interface)"
+ } else if tl.Elem().IsInterface() && tr.Elem().IsStruct() {
+ s += "\n\t(*interface vs *struct)"
+ }
+ }
+
+ base.Errorf("illegal types for operand: %v%s", op, s)
+}
+
+func writebarrierfn(name string, l *types.Type, r *types.Type) ir.Node {
+ fn := typecheck.LookupRuntime(name)
+ fn = typecheck.SubstArgTypes(fn, l, r)
+ return fn
+}
+
+// isRuneCount reports whether n is of the form len([]rune(string)).
+// These are optimized into a call to runtime.countrunes.
+func isRuneCount(n ir.Node) bool {
+ return base.Flag.N == 0 && !base.Flag.Cfg.Instrumenting && n.Op() == ir.OLEN && n.(*ir.UnaryExpr).X.Op() == ir.OSTR2RUNES
+}
diff --git a/src/cmd/compile/internal/walk/closure.go b/src/cmd/compile/internal/walk/closure.go
new file mode 100644
index 0000000000000..1d1cbc20540e7
--- /dev/null
+++ b/src/cmd/compile/internal/walk/closure.go
@@ -0,0 +1,199 @@
+// Copyright 2009 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 walk
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// 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 ir.IsTrivialClosure(clo) {
+ return // leave for walkClosure to handle
+ }
+
+ // 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
+ }
+
+ 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())
+ }
+
+ // 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 {
+ 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 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, clofn.Nname)}, closureArgs(clo)...)
+
+ addr := typecheck.NodAddr(clos)
+ addr.SetEsc(clo.Esc())
+
+ // Force type conversion from *struct to the func type.
+ cfn := typecheck.ConvNop(addr, clo.Type())
+
+ // non-escaping temp to use, if any.
+ if x := clo.Prealloc; x != nil {
+ if !types.Identical(typ, x.Type()) {
+ panic("closure type does not match order's assigned type")
+ }
+ addr.Prealloc = x
+ clo.Prealloc = nil
+ }
+
+ return walkExpr(cfn, init)
+}
+
+// closureArgs returns a slice of expressions that an be used to
+// initialize the given closure's free variables. These correspond
+// one-to-one with the variables in clo.Func.ClosureVars, and will be
+// either an ONAME node (if the variable is captured by value) or an
+// OADDR-of-ONAME node (if not).
+func closureArgs(clo *ir.ClosureExpr) []ir.Node {
+ fn := clo.Func
+
+ args := make([]ir.Node, len(fn.ClosureVars))
+ for i, v := range fn.ClosureVars {
+ var outer ir.Node
+ outer = v.Outer
+ if !v.Byval() {
+ outer = typecheck.NodAddrAt(fn.Pos(), outer)
+ }
+ args[i] = typecheck.Expr(outer)
+ }
+ return args
+}
+
+func walkCallPart(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
+ // Create closure in the form of a composite literal.
+ // For x.M with receiver (x) type T, the generated code looks like:
+ //
+ // clos = &struct{F uintptr; R T}{T.M·f, x}
+ //
+ // Like walkClosure above.
+
+ if n.X.Type().IsInterface() {
+ // Trigger panic for method on nil interface now.
+ // Otherwise it happens in the wrapper and is confusing.
+ n.X = cheapExpr(n.X, init)
+ n.X = walkExpr(n.X, nil)
+
+ tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X))
+
+ c := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab)
+ c.SetTypecheck(1)
+ init.Append(c)
+ }
+
+ typ := typecheck.PartialCallType(n)
+
+ clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
+ clos.SetEsc(n.Esc())
+ clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, typecheck.MethodValueWrapper(n).Nname), n.X}
+
+ addr := typecheck.NodAddr(clos)
+ addr.SetEsc(n.Esc())
+
+ // Force type conversion from *struct to the func type.
+ cfn := typecheck.ConvNop(addr, n.Type())
+
+ // non-escaping temp to use, if any.
+ if x := n.Prealloc; x != nil {
+ if !types.Identical(typ, x.Type()) {
+ panic("partial call type does not match order's assigned type")
+ }
+ addr.Prealloc = x
+ n.Prealloc = nil
+ }
+
+ return walkExpr(cfn, init)
+}
diff --git a/src/cmd/compile/internal/walk/compare.go b/src/cmd/compile/internal/walk/compare.go
new file mode 100644
index 0000000000000..7c385c0e0d14a
--- /dev/null
+++ b/src/cmd/compile/internal/walk/compare.go
@@ -0,0 +1,507 @@
+// Copyright 2009 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 walk
+
+import (
+ "encoding/binary"
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/sys"
+)
+
+// The result of walkCompare MUST be assigned back to n, e.g.
+// n.Left = walkCompare(n.Left, init)
+func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
+ if n.X.Type().IsInterface() && n.Y.Type().IsInterface() && n.X.Op() != ir.ONIL && n.Y.Op() != ir.ONIL {
+ return walkCompareInterface(n, init)
+ }
+
+ if n.X.Type().IsString() && n.Y.Type().IsString() {
+ return walkCompareString(n, init)
+ }
+
+ n.X = walkExpr(n.X, init)
+ n.Y = walkExpr(n.Y, init)
+
+ // Given mixed interface/concrete comparison,
+ // rewrite into types-equal && data-equal.
+ // This is efficient, avoids allocations, and avoids runtime calls.
+ if n.X.Type().IsInterface() != n.Y.Type().IsInterface() {
+ // Preserve side-effects in case of short-circuiting; see #32187.
+ l := cheapExpr(n.X, init)
+ r := cheapExpr(n.Y, init)
+ // Swap so that l is the interface value and r is the concrete value.
+ if n.Y.Type().IsInterface() {
+ l, r = r, l
+ }
+
+ // Handle both == and !=.
+ eq := n.Op()
+ andor := ir.OOROR
+ if eq == ir.OEQ {
+ andor = ir.OANDAND
+ }
+ // Check for types equal.
+ // For empty interface, this is:
+ // l.tab == type(r)
+ // For non-empty interface, this is:
+ // l.tab != nil && l.tab._type == type(r)
+ var eqtype ir.Node
+ tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, l)
+ rtyp := reflectdata.TypePtr(r.Type())
+ if l.Type().IsEmptyInterface() {
+ tab.SetType(types.NewPtr(types.Types[types.TUINT8]))
+ tab.SetTypecheck(1)
+ eqtype = ir.NewBinaryExpr(base.Pos, eq, tab, rtyp)
+ } else {
+ nonnil := ir.NewBinaryExpr(base.Pos, brcom(eq), typecheck.NodNil(), tab)
+ match := ir.NewBinaryExpr(base.Pos, eq, itabType(tab), rtyp)
+ eqtype = ir.NewLogicalExpr(base.Pos, andor, nonnil, match)
+ }
+ // Check for data equal.
+ eqdata := ir.NewBinaryExpr(base.Pos, eq, ifaceData(n.Pos(), l, r.Type()), r)
+ // Put it all together.
+ expr := ir.NewLogicalExpr(base.Pos, andor, eqtype, eqdata)
+ return finishCompare(n, expr, init)
+ }
+
+ // Must be comparison of array or struct.
+ // Otherwise back end handles it.
+ // While we're here, decide whether to
+ // inline or call an eq alg.
+ t := n.X.Type()
+ var inline bool
+
+ maxcmpsize := int64(4)
+ unalignedLoad := canMergeLoads()
+ if unalignedLoad {
+ // Keep this low enough to generate less code than a function call.
+ maxcmpsize = 2 * int64(ssagen.Arch.LinkArch.RegSize)
+ }
+
+ switch t.Kind() {
+ default:
+ if base.Debug.Libfuzzer != 0 && t.IsInteger() {
+ n.X = cheapExpr(n.X, init)
+ n.Y = cheapExpr(n.Y, init)
+
+ // If exactly one comparison operand is
+ // constant, invoke the constcmp functions
+ // instead, and arrange for the constant
+ // operand to be the first argument.
+ l, r := n.X, n.Y
+ if r.Op() == ir.OLITERAL {
+ l, r = r, l
+ }
+ constcmp := l.Op() == ir.OLITERAL && r.Op() != ir.OLITERAL
+
+ var fn string
+ var paramType *types.Type
+ switch t.Size() {
+ case 1:
+ fn = "libfuzzerTraceCmp1"
+ if constcmp {
+ fn = "libfuzzerTraceConstCmp1"
+ }
+ paramType = types.Types[types.TUINT8]
+ case 2:
+ fn = "libfuzzerTraceCmp2"
+ if constcmp {
+ fn = "libfuzzerTraceConstCmp2"
+ }
+ paramType = types.Types[types.TUINT16]
+ case 4:
+ fn = "libfuzzerTraceCmp4"
+ if constcmp {
+ fn = "libfuzzerTraceConstCmp4"
+ }
+ paramType = types.Types[types.TUINT32]
+ case 8:
+ fn = "libfuzzerTraceCmp8"
+ if constcmp {
+ fn = "libfuzzerTraceConstCmp8"
+ }
+ paramType = types.Types[types.TUINT64]
+ default:
+ base.Fatalf("unexpected integer size %d for %v", t.Size(), t)
+ }
+ init.Append(mkcall(fn, nil, init, tracecmpArg(l, paramType, init), tracecmpArg(r, paramType, init)))
+ }
+ return n
+ case types.TARRAY:
+ // We can compare several elements at once with 2/4/8 byte integer compares
+ inline = t.NumElem() <= 1 || (types.IsSimple[t.Elem().Kind()] && (t.NumElem() <= 4 || t.Elem().Width*t.NumElem() <= maxcmpsize))
+ case types.TSTRUCT:
+ inline = t.NumComponents(types.IgnoreBlankFields) <= 4
+ }
+
+ cmpl := n.X
+ for cmpl != nil && cmpl.Op() == ir.OCONVNOP {
+ cmpl = cmpl.(*ir.ConvExpr).X
+ }
+ cmpr := n.Y
+ for cmpr != nil && cmpr.Op() == ir.OCONVNOP {
+ cmpr = cmpr.(*ir.ConvExpr).X
+ }
+
+ // Chose not to inline. Call equality function directly.
+ if !inline {
+ // eq algs take pointers; cmpl and cmpr must be addressable
+ if !ir.IsAddressable(cmpl) || !ir.IsAddressable(cmpr) {
+ base.Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
+ }
+
+ fn, needsize := eqFor(t)
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
+ call.Args.Append(typecheck.NodAddr(cmpl))
+ call.Args.Append(typecheck.NodAddr(cmpr))
+ if needsize {
+ call.Args.Append(ir.NewInt(t.Width))
+ }
+ res := ir.Node(call)
+ if n.Op() != ir.OEQ {
+ res = ir.NewUnaryExpr(base.Pos, ir.ONOT, res)
+ }
+ return finishCompare(n, res, init)
+ }
+
+ // inline: build boolean expression comparing element by element
+ andor := ir.OANDAND
+ if n.Op() == ir.ONE {
+ andor = ir.OOROR
+ }
+ var expr ir.Node
+ compare := func(el, er ir.Node) {
+ a := ir.NewBinaryExpr(base.Pos, n.Op(), el, er)
+ if expr == nil {
+ expr = a
+ } else {
+ expr = ir.NewLogicalExpr(base.Pos, andor, expr, a)
+ }
+ }
+ cmpl = safeExpr(cmpl, init)
+ cmpr = safeExpr(cmpr, init)
+ if t.IsStruct() {
+ for _, f := range t.Fields().Slice() {
+ sym := f.Sym
+ if sym.IsBlank() {
+ continue
+ }
+ compare(
+ ir.NewSelectorExpr(base.Pos, ir.OXDOT, cmpl, sym),
+ ir.NewSelectorExpr(base.Pos, ir.OXDOT, cmpr, sym),
+ )
+ }
+ } else {
+ step := int64(1)
+ remains := t.NumElem() * t.Elem().Width
+ combine64bit := unalignedLoad && types.RegSize == 8 && t.Elem().Width <= 4 && t.Elem().IsInteger()
+ combine32bit := unalignedLoad && t.Elem().Width <= 2 && t.Elem().IsInteger()
+ combine16bit := unalignedLoad && t.Elem().Width == 1 && t.Elem().IsInteger()
+ for i := int64(0); remains > 0; {
+ var convType *types.Type
+ switch {
+ case remains >= 8 && combine64bit:
+ convType = types.Types[types.TINT64]
+ step = 8 / t.Elem().Width
+ case remains >= 4 && combine32bit:
+ convType = types.Types[types.TUINT32]
+ step = 4 / t.Elem().Width
+ case remains >= 2 && combine16bit:
+ convType = types.Types[types.TUINT16]
+ step = 2 / t.Elem().Width
+ default:
+ step = 1
+ }
+ if step == 1 {
+ compare(
+ ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i)),
+ ir.NewIndexExpr(base.Pos, cmpr, ir.NewInt(i)),
+ )
+ i++
+ remains -= t.Elem().Width
+ } else {
+ elemType := t.Elem().ToUnsigned()
+ cmplw := ir.Node(ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i)))
+ cmplw = typecheck.Conv(cmplw, elemType) // convert to unsigned
+ cmplw = typecheck.Conv(cmplw, convType) // widen
+ cmprw := ir.Node(ir.NewIndexExpr(base.Pos, cmpr, ir.NewInt(i)))
+ cmprw = typecheck.Conv(cmprw, elemType)
+ cmprw = typecheck.Conv(cmprw, convType)
+ // For code like this: uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ...
+ // ssa will generate a single large load.
+ for offset := int64(1); offset < step; offset++ {
+ lb := ir.Node(ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i+offset)))
+ lb = typecheck.Conv(lb, elemType)
+ lb = typecheck.Conv(lb, convType)
+ lb = ir.NewBinaryExpr(base.Pos, ir.OLSH, lb, ir.NewInt(8*t.Elem().Width*offset))
+ cmplw = ir.NewBinaryExpr(base.Pos, ir.OOR, cmplw, lb)
+ rb := ir.Node(ir.NewIndexExpr(base.Pos, cmpr, ir.NewInt(i+offset)))
+ rb = typecheck.Conv(rb, elemType)
+ rb = typecheck.Conv(rb, convType)
+ rb = ir.NewBinaryExpr(base.Pos, ir.OLSH, rb, ir.NewInt(8*t.Elem().Width*offset))
+ cmprw = ir.NewBinaryExpr(base.Pos, ir.OOR, cmprw, rb)
+ }
+ compare(cmplw, cmprw)
+ i += step
+ remains -= step * t.Elem().Width
+ }
+ }
+ }
+ if expr == nil {
+ expr = ir.NewBool(n.Op() == ir.OEQ)
+ // We still need to use cmpl and cmpr, in case they contain
+ // an expression which might panic. See issue 23837.
+ t := typecheck.Temp(cmpl.Type())
+ a1 := typecheck.Stmt(ir.NewAssignStmt(base.Pos, t, cmpl))
+ a2 := typecheck.Stmt(ir.NewAssignStmt(base.Pos, t, cmpr))
+ init.Append(a1, a2)
+ }
+ return finishCompare(n, expr, init)
+}
+
+func walkCompareInterface(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
+ n.Y = cheapExpr(n.Y, init)
+ n.X = cheapExpr(n.X, init)
+ eqtab, eqdata := reflectdata.EqInterface(n.X, n.Y)
+ var cmp ir.Node
+ if n.Op() == ir.OEQ {
+ cmp = ir.NewLogicalExpr(base.Pos, ir.OANDAND, eqtab, eqdata)
+ } else {
+ eqtab.SetOp(ir.ONE)
+ cmp = ir.NewLogicalExpr(base.Pos, ir.OOROR, eqtab, ir.NewUnaryExpr(base.Pos, ir.ONOT, eqdata))
+ }
+ return finishCompare(n, cmp, init)
+}
+
+func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
+ // Rewrite comparisons to short constant strings as length+byte-wise comparisons.
+ var cs, ncs ir.Node // const string, non-const string
+ switch {
+ case ir.IsConst(n.X, constant.String) && ir.IsConst(n.Y, constant.String):
+ // ignore; will be constant evaluated
+ case ir.IsConst(n.X, constant.String):
+ cs = n.X
+ ncs = n.Y
+ case ir.IsConst(n.Y, constant.String):
+ cs = n.Y
+ ncs = n.X
+ }
+ if cs != nil {
+ cmp := n.Op()
+ // Our comparison below assumes that the non-constant string
+ // is on the left hand side, so rewrite "" cmp x to x cmp "".
+ // See issue 24817.
+ if ir.IsConst(n.X, constant.String) {
+ cmp = brrev(cmp)
+ }
+
+ // maxRewriteLen was chosen empirically.
+ // It is the value that minimizes cmd/go file size
+ // across most architectures.
+ // See the commit description for CL 26758 for details.
+ maxRewriteLen := 6
+ // Some architectures can load unaligned byte sequence as 1 word.
+ // So we can cover longer strings with the same amount of code.
+ canCombineLoads := canMergeLoads()
+ combine64bit := false
+ if canCombineLoads {
+ // Keep this low enough to generate less code than a function call.
+ maxRewriteLen = 2 * ssagen.Arch.LinkArch.RegSize
+ combine64bit = ssagen.Arch.LinkArch.RegSize >= 8
+ }
+
+ var and ir.Op
+ switch cmp {
+ case ir.OEQ:
+ and = ir.OANDAND
+ case ir.ONE:
+ and = ir.OOROR
+ default:
+ // Don't do byte-wise comparisons for <, <=, etc.
+ // They're fairly complicated.
+ // Length-only checks are ok, though.
+ maxRewriteLen = 0
+ }
+ if s := ir.StringVal(cs); len(s) <= maxRewriteLen {
+ if len(s) > 0 {
+ ncs = safeExpr(ncs, init)
+ }
+ r := ir.Node(ir.NewBinaryExpr(base.Pos, cmp, ir.NewUnaryExpr(base.Pos, ir.OLEN, ncs), ir.NewInt(int64(len(s)))))
+ remains := len(s)
+ for i := 0; remains > 0; {
+ if remains == 1 || !canCombineLoads {
+ cb := ir.NewInt(int64(s[i]))
+ ncb := ir.NewIndexExpr(base.Pos, ncs, ir.NewInt(int64(i)))
+ r = ir.NewLogicalExpr(base.Pos, and, r, ir.NewBinaryExpr(base.Pos, cmp, ncb, cb))
+ remains--
+ i++
+ continue
+ }
+ var step int
+ var convType *types.Type
+ switch {
+ case remains >= 8 && combine64bit:
+ convType = types.Types[types.TINT64]
+ step = 8
+ case remains >= 4:
+ convType = types.Types[types.TUINT32]
+ step = 4
+ case remains >= 2:
+ convType = types.Types[types.TUINT16]
+ step = 2
+ }
+ ncsubstr := typecheck.Conv(ir.NewIndexExpr(base.Pos, ncs, ir.NewInt(int64(i))), convType)
+ csubstr := int64(s[i])
+ // Calculate large constant from bytes as sequence of shifts and ors.
+ // Like this: uint32(s[0]) | uint32(s[1])<<8 | uint32(s[2])<<16 ...
+ // ssa will combine this into a single large load.
+ for offset := 1; offset < step; offset++ {
+ b := typecheck.Conv(ir.NewIndexExpr(base.Pos, ncs, ir.NewInt(int64(i+offset))), convType)
+ b = ir.NewBinaryExpr(base.Pos, ir.OLSH, b, ir.NewInt(int64(8*offset)))
+ ncsubstr = ir.NewBinaryExpr(base.Pos, ir.OOR, ncsubstr, b)
+ csubstr |= int64(s[i+offset]) << uint8(8*offset)
+ }
+ csubstrPart := ir.NewInt(csubstr)
+ // Compare "step" bytes as once
+ r = ir.NewLogicalExpr(base.Pos, and, r, ir.NewBinaryExpr(base.Pos, cmp, csubstrPart, ncsubstr))
+ remains -= step
+ i += step
+ }
+ return finishCompare(n, r, init)
+ }
+ }
+
+ var r ir.Node
+ if n.Op() == ir.OEQ || n.Op() == ir.ONE {
+ // prepare for rewrite below
+ n.X = cheapExpr(n.X, init)
+ n.Y = cheapExpr(n.Y, init)
+ eqlen, eqmem := reflectdata.EqString(n.X, n.Y)
+ // quick check of len before full compare for == or !=.
+ // memequal then tests equality up to length len.
+ if n.Op() == ir.OEQ {
+ // len(left) == len(right) && memequal(left, right, len)
+ r = ir.NewLogicalExpr(base.Pos, ir.OANDAND, eqlen, eqmem)
+ } else {
+ // len(left) != len(right) || !memequal(left, right, len)
+ eqlen.SetOp(ir.ONE)
+ r = ir.NewLogicalExpr(base.Pos, ir.OOROR, eqlen, ir.NewUnaryExpr(base.Pos, ir.ONOT, eqmem))
+ }
+ } else {
+ // sys_cmpstring(s1, s2) :: 0
+ r = mkcall("cmpstring", types.Types[types.TINT], init, typecheck.Conv(n.X, types.Types[types.TSTRING]), typecheck.Conv(n.Y, types.Types[types.TSTRING]))
+ r = ir.NewBinaryExpr(base.Pos, n.Op(), r, ir.NewInt(0))
+ }
+
+ return finishCompare(n, r, init)
+}
+
+// The result of finishCompare MUST be assigned back to n, e.g.
+// n.Left = finishCompare(n.Left, x, r, init)
+func finishCompare(n *ir.BinaryExpr, r ir.Node, init *ir.Nodes) ir.Node {
+ r = typecheck.Expr(r)
+ r = typecheck.Conv(r, n.Type())
+ r = walkExpr(r, init)
+ return r
+}
+
+func eqFor(t *types.Type) (n ir.Node, needsize bool) {
+ // Should only arrive here with large memory or
+ // a struct/array containing a non-memory field/element.
+ // Small memory is handled inline, and single non-memory
+ // is handled by walkCompare.
+ switch a, _ := types.AlgType(t); a {
+ case types.AMEM:
+ n := typecheck.LookupRuntime("memequal")
+ n = typecheck.SubstArgTypes(n, t, t)
+ return n, true
+ case types.ASPECIAL:
+ sym := reflectdata.TypeSymPrefix(".eq", t)
+ n := typecheck.NewName(sym)
+ ir.MarkFunc(n)
+ n.SetType(types.NewSignature(types.NoPkg, nil, []*types.Field{
+ types.NewField(base.Pos, nil, types.NewPtr(t)),
+ types.NewField(base.Pos, nil, types.NewPtr(t)),
+ }, []*types.Field{
+ types.NewField(base.Pos, nil, types.Types[types.TBOOL]),
+ }))
+ return n, false
+ }
+ base.Fatalf("eqFor %v", t)
+ return nil, false
+}
+
+// brcom returns !(op).
+// For example, brcom(==) is !=.
+func brcom(op ir.Op) ir.Op {
+ switch op {
+ case ir.OEQ:
+ return ir.ONE
+ case ir.ONE:
+ return ir.OEQ
+ case ir.OLT:
+ return ir.OGE
+ case ir.OGT:
+ return ir.OLE
+ case ir.OLE:
+ return ir.OGT
+ case ir.OGE:
+ return ir.OLT
+ }
+ base.Fatalf("brcom: no com for %v\n", op)
+ return op
+}
+
+// brrev returns reverse(op).
+// For example, Brrev(<) is >.
+func brrev(op ir.Op) ir.Op {
+ switch op {
+ case ir.OEQ:
+ return ir.OEQ
+ case ir.ONE:
+ return ir.ONE
+ case ir.OLT:
+ return ir.OGT
+ case ir.OGT:
+ return ir.OLT
+ case ir.OLE:
+ return ir.OGE
+ case ir.OGE:
+ return ir.OLE
+ }
+ base.Fatalf("brrev: no rev for %v\n", op)
+ return op
+}
+
+func tracecmpArg(n ir.Node, t *types.Type, init *ir.Nodes) ir.Node {
+ // Ugly hack to avoid "constant -1 overflows uintptr" errors, etc.
+ if n.Op() == ir.OLITERAL && n.Type().IsSigned() && ir.Int64Val(n) < 0 {
+ n = copyExpr(n, n.Type(), init)
+ }
+
+ return typecheck.Conv(n, t)
+}
+
+// canMergeLoads reports whether the backend optimization passes for
+// the current architecture can combine adjacent loads into a single
+// larger, possibly unaligned, load. Note that currently the
+// optimizations must be able to handle little endian byte order.
+func canMergeLoads() bool {
+ switch ssagen.Arch.LinkArch.Family {
+ case sys.ARM64, sys.AMD64, sys.I386, sys.S390X:
+ return true
+ case sys.PPC64:
+ // Load combining only supported on ppc64le.
+ return ssagen.Arch.LinkArch.ByteOrder == binary.LittleEndian
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/walk/complit.go b/src/cmd/compile/internal/walk/complit.go
new file mode 100644
index 0000000000000..73442dc404cf6
--- /dev/null
+++ b/src/cmd/compile/internal/walk/complit.go
@@ -0,0 +1,663 @@
+// Copyright 2009 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 walk
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/staticdata"
+ "cmd/compile/internal/staticinit"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+)
+
+// walkCompLit walks a composite literal node:
+// OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT (all CompLitExpr), or OPTRLIT (AddrExpr).
+func walkCompLit(n ir.Node, init *ir.Nodes) ir.Node {
+ if isStaticCompositeLiteral(n) && !ssagen.TypeOK(n.Type()) {
+ n := n.(*ir.CompLitExpr) // not OPTRLIT
+ // n can be directly represented in the read-only data section.
+ // Make direct reference to the static data. See issue 12841.
+ vstat := readonlystaticname(n.Type())
+ fixedlit(inInitFunction, initKindStatic, n, vstat, init)
+ return typecheck.Expr(vstat)
+ }
+ var_ := typecheck.Temp(n.Type())
+ anylit(n, var_, init)
+ return var_
+}
+
+// initContext is the context in which static data is populated.
+// It is either in an init function or in any other function.
+// Static data populated in an init function will be written either
+// zero times (as a readonly, static data symbol) or
+// one time (during init function execution).
+// Either way, there is no opportunity for races or further modification,
+// so the data can be written to a (possibly readonly) data symbol.
+// Static data populated in any other function needs to be local to
+// that function to allow multiple instances of that function
+// to execute concurrently without clobbering each others' data.
+type initContext uint8
+
+const (
+ inInitFunction initContext = iota
+ inNonInitFunction
+)
+
+func (c initContext) String() string {
+ if c == inInitFunction {
+ return "inInitFunction"
+ }
+ return "inNonInitFunction"
+}
+
+// readonlystaticname returns a name backed by a (writable) static data symbol.
+func readonlystaticname(t *types.Type) *ir.Name {
+ n := staticinit.StaticName(t)
+ n.MarkReadonly()
+ n.Linksym().Set(obj.AttrContentAddressable, true)
+ return n
+}
+
+func isSimpleName(nn ir.Node) bool {
+ if nn.Op() != ir.ONAME || ir.IsBlank(nn) {
+ return false
+ }
+ n := nn.(*ir.Name)
+ return n.OnStack()
+}
+
+func litas(l ir.Node, r ir.Node, init *ir.Nodes) {
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, l, r))
+}
+
+// initGenType is a bitmap indicating the types of generation that will occur for a static value.
+type initGenType uint8
+
+const (
+ initDynamic initGenType = 1 << iota // contains some dynamic values, for which init code will be generated
+ initConst // contains some constant values, which may be written into data symbols
+)
+
+// getdyn calculates the initGenType for n.
+// If top is false, getdyn is recursing.
+func getdyn(n ir.Node, top bool) initGenType {
+ switch n.Op() {
+ default:
+ if ir.IsConstNode(n) {
+ return initConst
+ }
+ return initDynamic
+
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ if !top {
+ return initDynamic
+ }
+ if n.Len/4 > int64(len(n.List)) {
+ // <25% of entries have explicit values.
+ // Very rough estimation, it takes 4 bytes of instructions
+ // to initialize 1 byte of result. So don't use a static
+ // initializer if the dynamic initialization code would be
+ // smaller than the static value.
+ // See issue 23780.
+ return initDynamic
+ }
+
+ case ir.OARRAYLIT, ir.OSTRUCTLIT:
+ }
+ lit := n.(*ir.CompLitExpr)
+
+ var mode initGenType
+ for _, n1 := range lit.List {
+ switch n1.Op() {
+ case ir.OKEY:
+ n1 = n1.(*ir.KeyExpr).Value
+ case ir.OSTRUCTKEY:
+ n1 = n1.(*ir.StructKeyExpr).Value
+ }
+ mode |= getdyn(n1, false)
+ if mode == initDynamic|initConst {
+ break
+ }
+ }
+ return mode
+}
+
+// isStaticCompositeLiteral reports whether n is a compile-time constant.
+func isStaticCompositeLiteral(n ir.Node) bool {
+ switch n.Op() {
+ case ir.OSLICELIT:
+ return false
+ case ir.OARRAYLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, r := range n.List {
+ if r.Op() == ir.OKEY {
+ r = r.(*ir.KeyExpr).Value
+ }
+ if !isStaticCompositeLiteral(r) {
+ return false
+ }
+ }
+ return true
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, r := range n.List {
+ r := r.(*ir.StructKeyExpr)
+ if !isStaticCompositeLiteral(r.Value) {
+ return false
+ }
+ }
+ return true
+ case ir.OLITERAL, ir.ONIL:
+ return true
+ case ir.OCONVIFACE:
+ // See staticassign's OCONVIFACE case for comments.
+ n := n.(*ir.ConvExpr)
+ val := ir.Node(n)
+ for val.Op() == ir.OCONVIFACE {
+ val = val.(*ir.ConvExpr).X
+ }
+ if val.Type().IsInterface() {
+ return val.Op() == ir.ONIL
+ }
+ if types.IsDirectIface(val.Type()) && val.Op() == ir.ONIL {
+ return true
+ }
+ return isStaticCompositeLiteral(val)
+ }
+ return false
+}
+
+// initKind is a kind of static initialization: static, dynamic, or local.
+// Static initialization represents literals and
+// literal components of composite literals.
+// Dynamic initialization represents non-literals and
+// non-literal components of composite literals.
+// LocalCode initialization represents initialization
+// that occurs purely in generated code local to the function of use.
+// Initialization code is sometimes generated in passes,
+// first static then dynamic.
+type initKind uint8
+
+const (
+ initKindStatic initKind = iota + 1
+ initKindDynamic
+ initKindLocalCode
+)
+
+// fixedlit handles struct, array, and slice literals.
+// TODO: expand documentation.
+func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) {
+ isBlank := var_ == ir.BlankNode
+ var splitnode func(ir.Node) (a ir.Node, value ir.Node)
+ switch n.Op() {
+ case ir.OARRAYLIT, ir.OSLICELIT:
+ var k int64
+ splitnode = func(r ir.Node) (ir.Node, ir.Node) {
+ if r.Op() == ir.OKEY {
+ kv := r.(*ir.KeyExpr)
+ k = typecheck.IndexConst(kv.Key)
+ if k < 0 {
+ base.Fatalf("fixedlit: invalid index %v", kv.Key)
+ }
+ r = kv.Value
+ }
+ a := ir.NewIndexExpr(base.Pos, var_, ir.NewInt(k))
+ k++
+ if isBlank {
+ return ir.BlankNode, r
+ }
+ return a, r
+ }
+ case ir.OSTRUCTLIT:
+ splitnode = func(rn ir.Node) (ir.Node, ir.Node) {
+ r := rn.(*ir.StructKeyExpr)
+ if r.Field.IsBlank() || isBlank {
+ return ir.BlankNode, r.Value
+ }
+ ir.SetPos(r)
+ return ir.NewSelectorExpr(base.Pos, ir.ODOT, var_, r.Field), r.Value
+ }
+ default:
+ base.Fatalf("fixedlit bad op: %v", n.Op())
+ }
+
+ for _, r := range n.List {
+ a, value := splitnode(r)
+ if a == ir.BlankNode && !staticinit.AnySideEffects(value) {
+ // Discard.
+ continue
+ }
+
+ switch value.Op() {
+ case ir.OSLICELIT:
+ value := value.(*ir.CompLitExpr)
+ if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) {
+ slicelit(ctxt, value, a, init)
+ continue
+ }
+
+ case ir.OARRAYLIT, ir.OSTRUCTLIT:
+ value := value.(*ir.CompLitExpr)
+ fixedlit(ctxt, kind, value, a, init)
+ continue
+ }
+
+ islit := ir.IsConstNode(value)
+ if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
+ continue
+ }
+
+ // build list of assignments: var[index] = expr
+ ir.SetPos(a)
+ as := ir.NewAssignStmt(base.Pos, a, value)
+ as = typecheck.Stmt(as).(*ir.AssignStmt)
+ switch kind {
+ case initKindStatic:
+ genAsStatic(as)
+ case initKindDynamic, initKindLocalCode:
+ a = orderStmtInPlace(as, map[string][]*ir.Name{})
+ a = walkStmt(a)
+ init.Append(a)
+ default:
+ base.Fatalf("fixedlit: bad kind %d", kind)
+ }
+
+ }
+}
+
+func isSmallSliceLit(n *ir.CompLitExpr) bool {
+ if n.Op() != ir.OSLICELIT {
+ return false
+ }
+
+ return n.Type().Elem().Width == 0 || n.Len <= ir.MaxSmallArraySize/n.Type().Elem().Width
+}
+
+func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) {
+ // make an array type corresponding the number of elements we have
+ t := types.NewArray(n.Type().Elem(), n.Len)
+ types.CalcSize(t)
+
+ if ctxt == inNonInitFunction {
+ // put everything into static array
+ vstat := staticinit.StaticName(t)
+
+ fixedlit(ctxt, initKindStatic, n, vstat, init)
+ fixedlit(ctxt, initKindDynamic, n, vstat, init)
+
+ // copy static to slice
+ var_ = typecheck.AssignExpr(var_)
+ name, offset, ok := staticinit.StaticLoc(var_)
+ if !ok || name.Class != ir.PEXTERN {
+ base.Fatalf("slicelit: %v", var_)
+ }
+ staticdata.InitSlice(name, offset, vstat.Linksym(), t.NumElem())
+ return
+ }
+
+ // recipe for var = []t{...}
+ // 1. make a static array
+ // var vstat [...]t
+ // 2. assign (data statements) the constant part
+ // vstat = constpart{}
+ // 3. make an auto pointer to array and allocate heap to it
+ // var vauto *[...]t = new([...]t)
+ // 4. copy the static array to the auto array
+ // *vauto = vstat
+ // 5. for each dynamic part assign to the array
+ // vauto[i] = dynamic part
+ // 6. assign slice of allocated heap to var
+ // var = vauto[:]
+ //
+ // an optimization is done if there is no constant part
+ // 3. var vauto *[...]t = new([...]t)
+ // 5. vauto[i] = dynamic part
+ // 6. var = vauto[:]
+
+ // if the literal contains constants,
+ // make static initialized array (1),(2)
+ var vstat ir.Node
+
+ mode := getdyn(n, true)
+ if mode&initConst != 0 && !isSmallSliceLit(n) {
+ if ctxt == inInitFunction {
+ vstat = readonlystaticname(t)
+ } else {
+ vstat = staticinit.StaticName(t)
+ }
+ fixedlit(ctxt, initKindStatic, n, vstat, init)
+ }
+
+ // make new auto *array (3 declare)
+ vauto := typecheck.Temp(types.NewPtr(t))
+
+ // set auto to point at new temp or heap (3 assign)
+ var a ir.Node
+ if x := n.Prealloc; x != nil {
+ // temp allocated during order.go for dddarg
+ if !types.Identical(t, x.Type()) {
+ panic("dotdotdot base type does not match order's assigned type")
+ }
+ a = initStackTemp(init, x, vstat)
+ } else if n.Esc() == ir.EscNone {
+ a = initStackTemp(init, typecheck.Temp(t), vstat)
+ } else {
+ a = ir.NewUnaryExpr(base.Pos, ir.ONEW, ir.TypeNode(t))
+ }
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, vauto, a))
+
+ if vstat != nil && n.Prealloc == nil && n.Esc() != ir.EscNone {
+ // If we allocated on the heap with ONEW, copy the static to the
+ // heap (4). We skip this for stack temporaries, because
+ // initStackTemp already handled the copy.
+ a = ir.NewStarExpr(base.Pos, vauto)
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, a, vstat))
+ }
+
+ // put dynamics into array (5)
+ var index int64
+ for _, value := range n.List {
+ if value.Op() == ir.OKEY {
+ kv := value.(*ir.KeyExpr)
+ index = typecheck.IndexConst(kv.Key)
+ if index < 0 {
+ base.Fatalf("slicelit: invalid index %v", kv.Key)
+ }
+ value = kv.Value
+ }
+ a := ir.NewIndexExpr(base.Pos, vauto, ir.NewInt(index))
+ a.SetBounded(true)
+ index++
+
+ // TODO need to check bounds?
+
+ switch value.Op() {
+ case ir.OSLICELIT:
+ break
+
+ case ir.OARRAYLIT, ir.OSTRUCTLIT:
+ value := value.(*ir.CompLitExpr)
+ k := initKindDynamic
+ if vstat == nil {
+ // Generate both static and dynamic initializations.
+ // See issue #31987.
+ k = initKindLocalCode
+ }
+ fixedlit(ctxt, k, value, a, init)
+ continue
+ }
+
+ if vstat != nil && ir.IsConstNode(value) { // already set by copy from static value
+ continue
+ }
+
+ // build list of vauto[c] = expr
+ ir.SetPos(value)
+ as := typecheck.Stmt(ir.NewAssignStmt(base.Pos, a, value))
+ as = orderStmtInPlace(as, map[string][]*ir.Name{})
+ as = walkStmt(as)
+ init.Append(as)
+ }
+
+ // make slice out of heap (6)
+ a = ir.NewAssignStmt(base.Pos, var_, ir.NewSliceExpr(base.Pos, ir.OSLICE, vauto, nil, nil, nil))
+
+ a = typecheck.Stmt(a)
+ a = orderStmtInPlace(a, map[string][]*ir.Name{})
+ a = walkStmt(a)
+ init.Append(a)
+}
+
+func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) {
+ // make the map var
+ a := ir.NewCallExpr(base.Pos, ir.OMAKE, nil, nil)
+ a.SetEsc(n.Esc())
+ a.Args = []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(int64(len(n.List)))}
+ litas(m, a, init)
+
+ entries := n.List
+
+ // The order pass already removed any dynamic (runtime-computed) entries.
+ // All remaining entries are static. Double-check that.
+ for _, r := range entries {
+ r := r.(*ir.KeyExpr)
+ if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
+ base.Fatalf("maplit: entry is not a literal: %v", r)
+ }
+ }
+
+ if len(entries) > 25 {
+ // For a large number of entries, put them in an array and loop.
+
+ // build types [count]Tindex and [count]Tvalue
+ tk := types.NewArray(n.Type().Key(), int64(len(entries)))
+ te := types.NewArray(n.Type().Elem(), int64(len(entries)))
+
+ tk.SetNoalg(true)
+ te.SetNoalg(true)
+
+ types.CalcSize(tk)
+ types.CalcSize(te)
+
+ // make and initialize static arrays
+ vstatk := readonlystaticname(tk)
+ vstate := readonlystaticname(te)
+
+ datak := ir.NewCompLitExpr(base.Pos, ir.OARRAYLIT, nil, nil)
+ datae := ir.NewCompLitExpr(base.Pos, ir.OARRAYLIT, nil, nil)
+ for _, r := range entries {
+ r := r.(*ir.KeyExpr)
+ datak.List.Append(r.Key)
+ datae.List.Append(r.Value)
+ }
+ fixedlit(inInitFunction, initKindStatic, datak, vstatk, init)
+ fixedlit(inInitFunction, initKindStatic, datae, vstate, init)
+
+ // loop adding structure elements to map
+ // for i = 0; i < len(vstatk); i++ {
+ // map[vstatk[i]] = vstate[i]
+ // }
+ i := typecheck.Temp(types.Types[types.TINT])
+ rhs := ir.NewIndexExpr(base.Pos, vstate, i)
+ rhs.SetBounded(true)
+
+ kidx := ir.NewIndexExpr(base.Pos, vstatk, i)
+ kidx.SetBounded(true)
+ lhs := ir.NewIndexExpr(base.Pos, m, kidx)
+
+ zero := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0))
+ cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(tk.NumElem()))
+ incr := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
+ body := ir.NewAssignStmt(base.Pos, lhs, rhs)
+
+ loop := ir.NewForStmt(base.Pos, nil, cond, incr, nil)
+ loop.Body = []ir.Node{body}
+ *loop.PtrInit() = []ir.Node{zero}
+
+ appendWalkStmt(init, loop)
+ return
+ }
+ // For a small number of entries, just add them directly.
+
+ // Build list of var[c] = expr.
+ // Use temporaries so that mapassign1 can have addressable key, elem.
+ // TODO(josharian): avoid map key temporaries for mapfast_* assignments with literal keys.
+ tmpkey := typecheck.Temp(m.Type().Key())
+ tmpelem := typecheck.Temp(m.Type().Elem())
+
+ for _, r := range entries {
+ r := r.(*ir.KeyExpr)
+ index, elem := r.Key, r.Value
+
+ ir.SetPos(index)
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmpkey, index))
+
+ ir.SetPos(elem)
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmpelem, elem))
+
+ ir.SetPos(tmpelem)
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, tmpkey), tmpelem))
+ }
+
+ appendWalkStmt(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, tmpkey))
+ appendWalkStmt(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, tmpelem))
+}
+
+func anylit(n ir.Node, var_ ir.Node, init *ir.Nodes) {
+ t := n.Type()
+ switch n.Op() {
+ default:
+ base.Fatalf("anylit: not lit, op=%v node=%v", n.Op(), n)
+
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, n))
+
+ case ir.OMETHEXPR:
+ n := n.(*ir.SelectorExpr)
+ anylit(n.FuncName(), var_, init)
+
+ case ir.OPTRLIT:
+ n := n.(*ir.AddrExpr)
+ if !t.IsPtr() {
+ base.Fatalf("anylit: not ptr")
+ }
+
+ var r ir.Node
+ if n.Prealloc != nil {
+ // n.Prealloc is stack temporary used as backing store.
+ r = initStackTemp(init, n.Prealloc, nil)
+ } else {
+ r = ir.NewUnaryExpr(base.Pos, ir.ONEW, ir.TypeNode(n.X.Type()))
+ r.SetEsc(n.Esc())
+ }
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, r))
+
+ var_ = ir.NewStarExpr(base.Pos, var_)
+ var_ = typecheck.AssignExpr(var_)
+ anylit(n.X, var_, init)
+
+ case ir.OSTRUCTLIT, ir.OARRAYLIT:
+ n := n.(*ir.CompLitExpr)
+ if !t.IsStruct() && !t.IsArray() {
+ base.Fatalf("anylit: not struct/array")
+ }
+
+ if isSimpleName(var_) && len(n.List) > 4 {
+ // lay out static data
+ vstat := readonlystaticname(t)
+
+ ctxt := inInitFunction
+ if n.Op() == ir.OARRAYLIT {
+ ctxt = inNonInitFunction
+ }
+ fixedlit(ctxt, initKindStatic, n, vstat, init)
+
+ // copy static to var
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, vstat))
+
+ // add expressions to automatic
+ fixedlit(inInitFunction, initKindDynamic, n, var_, init)
+ break
+ }
+
+ var components int64
+ if n.Op() == ir.OARRAYLIT {
+ components = t.NumElem()
+ } else {
+ components = int64(t.NumFields())
+ }
+ // initialization of an array or struct with unspecified components (missing fields or arrays)
+ if isSimpleName(var_) || int64(len(n.List)) < components {
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, var_, nil))
+ }
+
+ fixedlit(inInitFunction, initKindLocalCode, n, var_, init)
+
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ slicelit(inInitFunction, n, var_, init)
+
+ case ir.OMAPLIT:
+ n := n.(*ir.CompLitExpr)
+ if !t.IsMap() {
+ base.Fatalf("anylit: not map")
+ }
+ maplit(n, var_, init)
+ }
+}
+
+// oaslit handles special composite literal assignments.
+// It returns true if n's effects have been added to init,
+// in which case n should be dropped from the program by the caller.
+func oaslit(n *ir.AssignStmt, init *ir.Nodes) bool {
+ if n.X == nil || n.Y == nil {
+ // not a special composite literal assignment
+ return false
+ }
+ if n.X.Type() == nil || n.Y.Type() == nil {
+ // not a special composite literal assignment
+ return false
+ }
+ if !isSimpleName(n.X) {
+ // not a special composite literal assignment
+ return false
+ }
+ x := n.X.(*ir.Name)
+ if !types.Identical(n.X.Type(), n.Y.Type()) {
+ // not a special composite literal assignment
+ return false
+ }
+
+ switch n.Y.Op() {
+ default:
+ // not a special composite literal assignment
+ return false
+
+ case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
+ if ir.Any(n.Y, func(y ir.Node) bool { return ir.Uses(y, x) }) {
+ // not a special composite literal assignment
+ return false
+ }
+ anylit(n.Y, n.X, init)
+ }
+
+ return true
+}
+
+func genAsStatic(as *ir.AssignStmt) {
+ if as.X.Type() == nil {
+ base.Fatalf("genAsStatic as.Left not typechecked")
+ }
+
+ name, offset, ok := staticinit.StaticLoc(as.X)
+ if !ok || (name.Class != ir.PEXTERN && as.X != ir.BlankNode) {
+ base.Fatalf("genAsStatic: lhs %v", as.X)
+ }
+
+ switch r := as.Y; r.Op() {
+ case ir.OLITERAL:
+ staticdata.InitConst(name, offset, r, int(r.Type().Width))
+ return
+ case ir.OMETHEXPR:
+ r := r.(*ir.SelectorExpr)
+ staticdata.InitAddr(name, offset, staticdata.FuncLinksym(r.FuncName()))
+ return
+ case ir.ONAME:
+ r := r.(*ir.Name)
+ if r.Offset_ != 0 {
+ base.Fatalf("genAsStatic %+v", as)
+ }
+ if r.Class == ir.PFUNC {
+ staticdata.InitAddr(name, offset, staticdata.FuncLinksym(r))
+ return
+ }
+ }
+ base.Fatalf("genAsStatic: rhs %v", as.Y)
+}
diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go
new file mode 100644
index 0000000000000..fa8e2c0bb8db4
--- /dev/null
+++ b/src/cmd/compile/internal/walk/convert.go
@@ -0,0 +1,490 @@
+// Copyright 2009 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 walk
+
+import (
+ "encoding/binary"
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/sys"
+)
+
+// walkConv walks an OCONV or OCONVNOP (but not OCONVIFACE) node.
+func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+ if n.Op() == ir.OCONVNOP && n.Type() == n.X.Type() {
+ return n.X
+ }
+ if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) {
+ if n.Type().IsPtr() && n.X.Type().IsUnsafePtr() { // unsafe.Pointer to *T
+ return walkCheckPtrAlignment(n, init, nil)
+ }
+ if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { // uintptr to unsafe.Pointer
+ return walkCheckPtrArithmetic(n, init)
+ }
+ }
+ param, result := rtconvfn(n.X.Type(), n.Type())
+ if param == types.Txxx {
+ return n
+ }
+ fn := types.BasicTypeNames[param] + "to" + types.BasicTypeNames[result]
+ return typecheck.Conv(mkcall(fn, types.Types[result], init, typecheck.Conv(n.X, types.Types[param])), n.Type())
+}
+
+// walkConvInterface walks an OCONVIFACE node.
+func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+
+ fromType := n.X.Type()
+ toType := n.Type()
+
+ if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) { // skip unnamed functions (func _())
+ reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym)
+ }
+
+ // typeword generates the type word of the interface value.
+ typeword := func() ir.Node {
+ if toType.IsEmptyInterface() {
+ return reflectdata.TypePtr(fromType)
+ }
+ return reflectdata.ITabAddr(fromType, toType)
+ }
+
+ // Optimize convT2E or convT2I as a two-word copy when T is pointer-shaped.
+ if types.IsDirectIface(fromType) {
+ l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), n.X)
+ l.SetType(toType)
+ l.SetTypecheck(n.Typecheck())
+ return l
+ }
+
+ // Optimize convT2{E,I} for many cases in which T is not pointer-shaped,
+ // by using an existing addressable value identical to n.Left
+ // or creating one on the stack.
+ var value ir.Node
+ switch {
+ case fromType.Size() == 0:
+ // n.Left is zero-sized. Use zerobase.
+ cheapExpr(n.X, init) // Evaluate n.Left for side-effects. See issue 19246.
+ value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
+ case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()):
+ // n.Left is a bool/byte. Use staticuint64s[n.Left * 8] on little-endian
+ // and staticuint64s[n.Left * 8 + 7] on big-endian.
+ n.X = cheapExpr(n.X, init)
+ // byteindex widens n.Left so that the multiplication doesn't overflow.
+ index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n.X), ir.NewInt(3))
+ if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
+ index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(7))
+ }
+ // The actual type is [256]uint64, but we use [256*8]uint8 so we can address
+ // individual bytes.
+ staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8))
+ xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
+ xe.SetBounded(true)
+ value = xe
+ case n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class == ir.PEXTERN && n.X.(*ir.Name).Readonly():
+ // n.Left is a readonly global; use it directly.
+ value = n.X
+ case !fromType.IsInterface() && n.Esc() == ir.EscNone && fromType.Width <= 1024:
+ // n.Left does not escape. Use a stack temporary initialized to n.Left.
+ value = typecheck.Temp(fromType)
+ init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n.X)))
+ }
+
+ if value != nil {
+ // Value is identical to n.Left.
+ // Construct the interface directly: {type/itab, &value}.
+ l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), typecheck.Expr(typecheck.NodAddr(value)))
+ l.SetType(toType)
+ l.SetTypecheck(n.Typecheck())
+ return l
+ }
+
+ // Implement interface to empty interface conversion.
+ // tmp = i.itab
+ // if tmp != nil {
+ // tmp = tmp.type
+ // }
+ // e = iface{tmp, i.data}
+ if toType.IsEmptyInterface() && fromType.IsInterface() && !fromType.IsEmptyInterface() {
+ // Evaluate the input interface.
+ c := typecheck.Temp(fromType)
+ init.Append(ir.NewAssignStmt(base.Pos, c, n.X))
+
+ // Get the itab out of the interface.
+ tmp := typecheck.Temp(types.NewPtr(types.Types[types.TUINT8]))
+ init.Append(ir.NewAssignStmt(base.Pos, tmp, typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, c))))
+
+ // Get the type out of the itab.
+ nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, tmp, typecheck.NodNil())), nil, nil)
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, tmp, itabType(tmp))}
+ init.Append(nif)
+
+ // Build the result.
+ e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, tmp, ifaceData(n.Pos(), c, types.NewPtr(types.Types[types.TUINT8])))
+ e.SetType(toType) // assign type manually, typecheck doesn't understand OEFACE.
+ e.SetTypecheck(1)
+ return e
+ }
+
+ fnname, needsaddr := convFuncName(fromType, toType)
+
+ if !needsaddr && !fromType.IsInterface() {
+ // Use a specialized conversion routine that only returns a data pointer.
+ // ptr = convT2X(val)
+ // e = iface{typ/tab, ptr}
+ fn := typecheck.LookupRuntime(fnname)
+ types.CalcSize(fromType)
+ fn = typecheck.SubstArgTypes(fn, fromType)
+ types.CalcSize(fn.Type())
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
+ call.Args = []ir.Node{n.X}
+ e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), safeExpr(walkExpr(typecheck.Expr(call), init), init))
+ e.SetType(toType)
+ e.SetTypecheck(1)
+ return e
+ }
+
+ var tab ir.Node
+ if fromType.IsInterface() {
+ // convI2I
+ tab = reflectdata.TypePtr(toType)
+ } else {
+ // convT2x
+ tab = typeword()
+ }
+
+ v := n.X
+ if needsaddr {
+ // Types of large or unknown size are passed by reference.
+ // Orderexpr arranged for n.Left to be a temporary for all
+ // the conversions it could see. Comparison of an interface
+ // with a non-interface, especially in a switch on interface value
+ // with non-interface cases, is not visible to order.stmt, so we
+ // have to fall back on allocating a temp here.
+ if !ir.IsAddressable(v) {
+ v = copyExpr(v, v.Type(), init)
+ }
+ v = typecheck.NodAddr(v)
+ }
+
+ types.CalcSize(fromType)
+ fn := typecheck.LookupRuntime(fnname)
+ fn = typecheck.SubstArgTypes(fn, fromType, toType)
+ types.CalcSize(fn.Type())
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
+ call.Args = []ir.Node{tab, v}
+ return walkExpr(typecheck.Expr(call), init)
+}
+
+// walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
+func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ a := typecheck.NodNil()
+ if n.Esc() == ir.EscNone {
+ // Create temporary buffer for string on stack.
+ a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
+ }
+ if n.Op() == ir.ORUNES2STR {
+ // slicerunetostring(*[32]byte, []rune) string
+ return mkcall("slicerunetostring", n.Type(), init, a, n.X)
+ }
+ // slicebytetostring(*[32]byte, ptr *byte, n int) string
+ n.X = cheapExpr(n.X, init)
+ ptr, len := backingArrayPtrLen(n.X)
+ return mkcall("slicebytetostring", n.Type(), init, a, ptr, len)
+}
+
+// walkBytesToStringTemp walks an OBYTES2STRTMP node.
+func walkBytesToStringTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+ if !base.Flag.Cfg.Instrumenting {
+ // Let the backend handle OBYTES2STRTMP directly
+ // to avoid a function call to slicebytetostringtmp.
+ return n
+ }
+ // slicebytetostringtmp(ptr *byte, n int) string
+ n.X = cheapExpr(n.X, init)
+ ptr, len := backingArrayPtrLen(n.X)
+ return mkcall("slicebytetostringtmp", n.Type(), init, ptr, len)
+}
+
+// walkRuneToString walks an ORUNESTR node.
+func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ a := typecheck.NodNil()
+ if n.Esc() == ir.EscNone {
+ a = stackBufAddr(4, types.Types[types.TUINT8])
+ }
+ // intstring(*[4]byte, rune)
+ return mkcall("intstring", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TINT64]))
+}
+
+// walkStringToBytes walks an OSTR2BYTES node.
+func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ s := n.X
+ if ir.IsConst(s, constant.String) {
+ sc := ir.StringVal(s)
+
+ // Allocate a [n]byte of the right size.
+ t := types.NewArray(types.Types[types.TUINT8], int64(len(sc)))
+ var a ir.Node
+ if n.Esc() == ir.EscNone && len(sc) <= int(ir.MaxImplicitStackVarSize) {
+ a = stackBufAddr(t.NumElem(), t.Elem())
+ } else {
+ types.CalcSize(t)
+ a = ir.NewUnaryExpr(base.Pos, ir.ONEW, nil)
+ a.SetType(types.NewPtr(t))
+ a.SetTypecheck(1)
+ a.MarkNonNil()
+ }
+ p := typecheck.Temp(t.PtrTo()) // *[n]byte
+ init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, p, a)))
+
+ // Copy from the static string data to the [n]byte.
+ if len(sc) > 0 {
+ as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), t.PtrTo())))
+ appendWalkStmt(init, as)
+ }
+
+ // Slice the [n]byte to a []byte.
+ slice := ir.NewSliceExpr(n.Pos(), ir.OSLICEARR, p, nil, nil, nil)
+ slice.SetType(n.Type())
+ slice.SetTypecheck(1)
+ return walkExpr(slice, init)
+ }
+
+ a := typecheck.NodNil()
+ if n.Esc() == ir.EscNone {
+ // Create temporary buffer for slice on stack.
+ a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
+ }
+ // stringtoslicebyte(*32[byte], string) []byte
+ return mkcall("stringtoslicebyte", n.Type(), init, a, typecheck.Conv(s, types.Types[types.TSTRING]))
+}
+
+// walkStringToBytesTemp walks an OSTR2BYTESTMP node.
+func walkStringToBytesTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ // []byte(string) conversion that creates a slice
+ // referring to the actual string bytes.
+ // This conversion is handled later by the backend and
+ // is only for use by internal compiler optimizations
+ // that know that the slice won't be mutated.
+ // The only such case today is:
+ // for i, c := range []byte(string)
+ n.X = walkExpr(n.X, init)
+ return n
+}
+
+// walkStringToRunes walks an OSTR2RUNES node.
+func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ a := typecheck.NodNil()
+ if n.Esc() == ir.EscNone {
+ // Create temporary buffer for slice on stack.
+ a = stackBufAddr(tmpstringbufsize, types.Types[types.TINT32])
+ }
+ // stringtoslicerune(*[32]rune, string) []rune
+ return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING]))
+}
+
+// convFuncName builds the runtime function name for interface conversion.
+// It also reports whether the function expects the data by address.
+// Not all names are possible. For example, we never generate convE2E or convE2I.
+func convFuncName(from, to *types.Type) (fnname string, needsaddr bool) {
+ tkind := to.Tie()
+ switch from.Tie() {
+ case 'I':
+ if tkind == 'I' {
+ return "convI2I", false
+ }
+ case 'T':
+ switch {
+ case from.Size() == 2 && from.Align == 2:
+ return "convT16", false
+ case from.Size() == 4 && from.Align == 4 && !from.HasPointers():
+ return "convT32", false
+ case from.Size() == 8 && from.Align == types.Types[types.TUINT64].Align && !from.HasPointers():
+ return "convT64", false
+ }
+ if sc := from.SoleComponent(); sc != nil {
+ switch {
+ case sc.IsString():
+ return "convTstring", false
+ case sc.IsSlice():
+ return "convTslice", false
+ }
+ }
+
+ switch tkind {
+ case 'E':
+ if !from.HasPointers() {
+ return "convT2Enoptr", true
+ }
+ return "convT2E", true
+ case 'I':
+ if !from.HasPointers() {
+ return "convT2Inoptr", true
+ }
+ return "convT2I", true
+ }
+ }
+ base.Fatalf("unknown conv func %c2%c", from.Tie(), to.Tie())
+ panic("unreachable")
+}
+
+// rtconvfn returns the parameter and result types that will be used by a
+// runtime function to convert from type src to type dst. The runtime function
+// name can be derived from the names of the returned types.
+//
+// If no such function is necessary, it returns (Txxx, Txxx).
+func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
+ if ssagen.Arch.SoftFloat {
+ return types.Txxx, types.Txxx
+ }
+
+ switch ssagen.Arch.LinkArch.Family {
+ case sys.ARM, sys.MIPS:
+ if src.IsFloat() {
+ switch dst.Kind() {
+ case types.TINT64, types.TUINT64:
+ return types.TFLOAT64, dst.Kind()
+ }
+ }
+ if dst.IsFloat() {
+ switch src.Kind() {
+ case types.TINT64, types.TUINT64:
+ return src.Kind(), types.TFLOAT64
+ }
+ }
+
+ case sys.I386:
+ if src.IsFloat() {
+ switch dst.Kind() {
+ case types.TINT64, types.TUINT64:
+ return types.TFLOAT64, dst.Kind()
+ case types.TUINT32, types.TUINT, types.TUINTPTR:
+ return types.TFLOAT64, types.TUINT32
+ }
+ }
+ if dst.IsFloat() {
+ switch src.Kind() {
+ case types.TINT64, types.TUINT64:
+ return src.Kind(), types.TFLOAT64
+ case types.TUINT32, types.TUINT, types.TUINTPTR:
+ return types.TUINT32, types.TFLOAT64
+ }
+ }
+ }
+ return types.Txxx, types.Txxx
+}
+
+// byteindex converts n, which is byte-sized, to an int used to index into an array.
+// We cannot use conv, because we allow converting bool to int here,
+// which is forbidden in user code.
+func byteindex(n ir.Node) ir.Node {
+ // We cannot convert from bool to int directly.
+ // While converting from int8 to int is possible, it would yield
+ // the wrong result for negative values.
+ // Reinterpreting the value as an unsigned byte solves both cases.
+ if !types.Identical(n.Type(), types.Types[types.TUINT8]) {
+ n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
+ n.SetType(types.Types[types.TUINT8])
+ n.SetTypecheck(1)
+ }
+ n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
+ n.SetType(types.Types[types.TINT])
+ n.SetTypecheck(1)
+ return n
+}
+
+func walkCheckPtrAlignment(n *ir.ConvExpr, init *ir.Nodes, count ir.Node) ir.Node {
+ if !n.Type().IsPtr() {
+ base.Fatalf("expected pointer type: %v", n.Type())
+ }
+ elem := n.Type().Elem()
+ if count != nil {
+ if !elem.IsArray() {
+ base.Fatalf("expected array type: %v", elem)
+ }
+ elem = elem.Elem()
+ }
+
+ size := elem.Size()
+ if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) {
+ return n
+ }
+
+ if count == nil {
+ count = ir.NewInt(1)
+ }
+
+ n.X = cheapExpr(n.X, init)
+ init.Append(mkcall("checkptrAlignment", nil, init, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]), reflectdata.TypePtr(elem), typecheck.Conv(count, types.Types[types.TUINTPTR])))
+ return n
+}
+
+func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ // Calling cheapExpr(n, init) below leads to a recursive call to
+ // walkExpr, which leads us back here again. Use n.Checkptr to
+ // prevent infinite loops.
+ if n.CheckPtr() {
+ return n
+ }
+ n.SetCheckPtr(true)
+ defer n.SetCheckPtr(false)
+
+ // TODO(mdempsky): Make stricter. We only need to exempt
+ // reflect.Value.Pointer and reflect.Value.UnsafeAddr.
+ switch n.X.Op() {
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
+ return n
+ }
+
+ if n.X.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(n.X) {
+ return n
+ }
+
+ // Find original unsafe.Pointer operands involved in this
+ // arithmetic expression.
+ //
+ // "It is valid both to add and to subtract offsets from a
+ // pointer in this way. It is also valid to use &^ to round
+ // pointers, usually for alignment."
+ var originals []ir.Node
+ var walk func(n ir.Node)
+ walk = func(n ir.Node) {
+ switch n.Op() {
+ case ir.OADD:
+ n := n.(*ir.BinaryExpr)
+ walk(n.X)
+ walk(n.Y)
+ case ir.OSUB, ir.OANDNOT:
+ n := n.(*ir.BinaryExpr)
+ walk(n.X)
+ case ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ if n.X.Type().IsUnsafePtr() {
+ n.X = cheapExpr(n.X, init)
+ originals = append(originals, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]))
+ }
+ }
+ }
+ walk(n.X)
+
+ cheap := cheapExpr(n, init)
+
+ slice := typecheck.MakeDotArgs(types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
+ slice.SetEsc(ir.EscNone)
+
+ init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice))
+ // TODO(khr): Mark backing store of slice as dead. This will allow us to reuse
+ // the backing store for multiple calls to checkptrArithmetic.
+
+ return cheap
+}
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
new file mode 100644
index 0000000000000..7b65db510062f
--- /dev/null
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -0,0 +1,966 @@
+// Copyright 2009 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 walk
+
+import (
+ "fmt"
+ "go/constant"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/staticdata"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+)
+
+// The result of walkExpr MUST be assigned back to n, e.g.
+// n.Left = walkExpr(n.Left, init)
+func walkExpr(n ir.Node, init *ir.Nodes) ir.Node {
+ if n == nil {
+ return n
+ }
+
+ if n, ok := n.(ir.InitNode); ok && init == n.PtrInit() {
+ // not okay to use n->ninit when walking n,
+ // because we might replace n with some other node
+ // and would lose the init list.
+ base.Fatalf("walkExpr init == &n->ninit")
+ }
+
+ if len(n.Init()) != 0 {
+ walkStmtList(n.Init())
+ init.Append(ir.TakeInit(n)...)
+ }
+
+ lno := ir.SetPos(n)
+
+ if base.Flag.LowerW > 1 {
+ ir.Dump("before walk expr", n)
+ }
+
+ if n.Typecheck() != 1 {
+ base.Fatalf("missed typecheck: %+v", n)
+ }
+
+ if n.Type().IsUntyped() {
+ base.Fatalf("expression has untyped type: %+v", n)
+ }
+
+ 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.
+ _ = staticdata.StringSym(n.Pos(), constant.StringVal(n.Val()))
+ }
+
+ if base.Flag.LowerW != 0 && n != nil {
+ ir.Dump("after walk expr", n)
+ }
+
+ base.Pos = lno
+ return n
+}
+
+func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
+ switch n.Op() {
+ default:
+ ir.Dump("walk", n)
+ base.Fatalf("walkExpr: switch 1 unknown op %+v", n.Op())
+ panic("unreachable")
+
+ case ir.ONONAME, ir.OGETG:
+ return n
+
+ case ir.OTYPE, ir.ONAME, ir.OLITERAL, ir.ONIL, ir.OLINKSYMOFFSET:
+ // TODO(mdempsky): Just return n; see discussion on CL 38655.
+ // Perhaps refactor to use Node.mayBeShared for these instead.
+ // If these return early, make sure to still call
+ // StringSym for constant strings.
+ return n
+
+ case ir.OMETHEXPR:
+ // TODO(mdempsky): Do this right after type checking.
+ n := n.(*ir.SelectorExpr)
+ return n.FuncName()
+
+ case ir.ONOT, ir.ONEG, ir.OPLUS, ir.OBITNOT, ir.OREAL, ir.OIMAG, ir.OSPTR, ir.OITAB, ir.OIDATA:
+ n := n.(*ir.UnaryExpr)
+ n.X = walkExpr(n.X, init)
+ return n
+
+ case ir.ODOTMETH, ir.ODOTINTER:
+ n := n.(*ir.SelectorExpr)
+ n.X = walkExpr(n.X, init)
+ return n
+
+ case ir.OADDR:
+ n := n.(*ir.AddrExpr)
+ n.X = walkExpr(n.X, init)
+ return n
+
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ n.X = walkExpr(n.X, init)
+ return n
+
+ case ir.OEFACE, ir.OAND, ir.OANDNOT, ir.OSUB, ir.OMUL, ir.OADD, ir.OOR, ir.OXOR, ir.OLSH, ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ n.X = walkExpr(n.X, init)
+ n.Y = walkExpr(n.Y, init)
+ return n
+
+ case ir.ODOT, ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ return walkDot(n, init)
+
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ n := n.(*ir.TypeAssertExpr)
+ return walkDotType(n, init)
+
+ case ir.OLEN, ir.OCAP:
+ n := n.(*ir.UnaryExpr)
+ return walkLenCap(n, init)
+
+ case ir.OCOMPLEX:
+ n := n.(*ir.BinaryExpr)
+ n.X = walkExpr(n.X, init)
+ n.Y = walkExpr(n.Y, init)
+ return n
+
+ case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ n := n.(*ir.BinaryExpr)
+ return walkCompare(n, init)
+
+ case ir.OANDAND, ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ return walkLogical(n, init)
+
+ case ir.OPRINT, ir.OPRINTN:
+ return walkPrint(n.(*ir.CallExpr), init)
+
+ case ir.OPANIC:
+ n := n.(*ir.UnaryExpr)
+ return mkcall("gopanic", nil, init, n.X)
+
+ case ir.ORECOVER:
+ n := n.(*ir.CallExpr)
+ return mkcall("gorecover", n.Type(), init, typecheck.NodAddr(ir.RegFP))
+
+ case ir.OCFUNC:
+ return n
+
+ case ir.OCALLINTER, ir.OCALLFUNC, ir.OCALLMETH:
+ n := n.(*ir.CallExpr)
+ return walkCall(n, init)
+
+ case ir.OAS, ir.OASOP:
+ return walkAssign(init, n)
+
+ case ir.OAS2:
+ n := n.(*ir.AssignListStmt)
+ return walkAssignList(init, n)
+
+ // a,b,... = fn()
+ case ir.OAS2FUNC:
+ n := n.(*ir.AssignListStmt)
+ return walkAssignFunc(init, n)
+
+ // x, y = <-c
+ // order.stmt made sure x is addressable or blank.
+ case ir.OAS2RECV:
+ n := n.(*ir.AssignListStmt)
+ return walkAssignRecv(init, n)
+
+ // a,b = m[i]
+ case ir.OAS2MAPR:
+ n := n.(*ir.AssignListStmt)
+ return walkAssignMapRead(init, n)
+
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ return walkDelete(init, n)
+
+ case ir.OAS2DOTTYPE:
+ n := n.(*ir.AssignListStmt)
+ return walkAssignDotType(n, init)
+
+ case ir.OCONVIFACE:
+ n := n.(*ir.ConvExpr)
+ return walkConvInterface(n, init)
+
+ case ir.OCONV, ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ return walkConv(n, init)
+
+ case ir.ODIV, ir.OMOD:
+ n := n.(*ir.BinaryExpr)
+ return walkDivMod(n, init)
+
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ return walkIndex(n, init)
+
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ return walkIndexMap(n, init)
+
+ case ir.ORECV:
+ base.Fatalf("walkExpr ORECV") // should see inside OAS only
+ panic("unreachable")
+
+ case ir.OSLICEHEADER:
+ n := n.(*ir.SliceHeaderExpr)
+ return walkSliceHeader(n, init)
+
+ case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR:
+ n := n.(*ir.SliceExpr)
+ return walkSlice(n, init)
+
+ case ir.ONEW:
+ n := n.(*ir.UnaryExpr)
+ return walkNew(n, init)
+
+ case ir.OADDSTR:
+ return walkAddString(n.(*ir.AddStringExpr), init)
+
+ case ir.OAPPEND:
+ // order should make sure we only see OAS(node, OAPPEND), which we handle above.
+ base.Fatalf("append outside assignment")
+ panic("unreachable")
+
+ case ir.OCOPY:
+ return walkCopy(n.(*ir.BinaryExpr), init, base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime)
+
+ case ir.OCLOSE:
+ n := n.(*ir.UnaryExpr)
+ return walkClose(n, init)
+
+ case ir.OMAKECHAN:
+ n := n.(*ir.MakeExpr)
+ return walkMakeChan(n, init)
+
+ case ir.OMAKEMAP:
+ n := n.(*ir.MakeExpr)
+ return walkMakeMap(n, init)
+
+ case ir.OMAKESLICE:
+ n := n.(*ir.MakeExpr)
+ return walkMakeSlice(n, init)
+
+ case ir.OMAKESLICECOPY:
+ n := n.(*ir.MakeExpr)
+ return walkMakeSliceCopy(n, init)
+
+ case ir.ORUNESTR:
+ n := n.(*ir.ConvExpr)
+ return walkRuneToString(n, init)
+
+ case ir.OBYTES2STR, ir.ORUNES2STR:
+ n := n.(*ir.ConvExpr)
+ return walkBytesRunesToString(n, init)
+
+ case ir.OBYTES2STRTMP:
+ n := n.(*ir.ConvExpr)
+ return walkBytesToStringTemp(n, init)
+
+ case ir.OSTR2BYTES:
+ n := n.(*ir.ConvExpr)
+ return walkStringToBytes(n, init)
+
+ case ir.OSTR2BYTESTMP:
+ n := n.(*ir.ConvExpr)
+ return walkStringToBytesTemp(n, init)
+
+ case ir.OSTR2RUNES:
+ n := n.(*ir.ConvExpr)
+ return walkStringToRunes(n, init)
+
+ case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT, ir.OPTRLIT:
+ return walkCompLit(n, init)
+
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ return walkSend(n, init)
+
+ case ir.OCLOSURE:
+ return walkClosure(n.(*ir.ClosureExpr), init)
+
+ case ir.OCALLPART:
+ return walkCallPart(n.(*ir.SelectorExpr), init)
+ }
+
+ // No return! Each case must return (or panic),
+ // to avoid confusion about what gets returned
+ // in the presence of type assertions.
+}
+
+// walk the whole tree of the body of an
+// expression or simple statement.
+// the types expressions are calculated.
+// compile-time constants are evaluated.
+// complex side effects like statements are appended to init
+func walkExprList(s []ir.Node, init *ir.Nodes) {
+ for i := range s {
+ s[i] = walkExpr(s[i], init)
+ }
+}
+
+func walkExprListCheap(s []ir.Node, init *ir.Nodes) {
+ for i, n := range s {
+ s[i] = cheapExpr(n, init)
+ s[i] = walkExpr(s[i], init)
+ }
+}
+
+func walkExprListSafe(s []ir.Node, init *ir.Nodes) {
+ for i, n := range s {
+ s[i] = safeExpr(n, init)
+ s[i] = walkExpr(s[i], init)
+ }
+}
+
+// return side-effect free and cheap n, appending side effects to init.
+// result may not be assignable.
+func cheapExpr(n ir.Node, init *ir.Nodes) ir.Node {
+ switch n.Op() {
+ case ir.ONAME, ir.OLITERAL, ir.ONIL:
+ return n
+ }
+
+ return copyExpr(n, n.Type(), init)
+}
+
+// return side effect-free n, appending side effects to init.
+// result is assignable if n is.
+func safeExpr(n ir.Node, init *ir.Nodes) ir.Node {
+ if n == nil {
+ return nil
+ }
+
+ if len(n.Init()) != 0 {
+ walkStmtList(n.Init())
+ init.Append(ir.TakeInit(n)...)
+ }
+
+ switch n.Op() {
+ case ir.ONAME, ir.OLITERAL, ir.ONIL, ir.OLINKSYMOFFSET:
+ return n
+
+ case ir.OLEN, ir.OCAP:
+ n := n.(*ir.UnaryExpr)
+ l := safeExpr(n.X, init)
+ if l == n.X {
+ return n
+ }
+ a := ir.Copy(n).(*ir.UnaryExpr)
+ a.X = l
+ return walkExpr(typecheck.Expr(a), init)
+
+ case ir.ODOT, ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ l := safeExpr(n.X, init)
+ if l == n.X {
+ return n
+ }
+ a := ir.Copy(n).(*ir.SelectorExpr)
+ a.X = l
+ return walkExpr(typecheck.Expr(a), init)
+
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ l := safeExpr(n.X, init)
+ if l == n.X {
+ return n
+ }
+ a := ir.Copy(n).(*ir.StarExpr)
+ a.X = l
+ return walkExpr(typecheck.Expr(a), init)
+
+ case ir.OINDEX, ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ l := safeExpr(n.X, init)
+ r := safeExpr(n.Index, init)
+ if l == n.X && r == n.Index {
+ return n
+ }
+ a := ir.Copy(n).(*ir.IndexExpr)
+ a.X = l
+ a.Index = r
+ return walkExpr(typecheck.Expr(a), init)
+
+ case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ if isStaticCompositeLiteral(n) {
+ return n
+ }
+ }
+
+ // make a copy; must not be used as an lvalue
+ if ir.IsAddressable(n) {
+ base.Fatalf("missing lvalue case in safeExpr: %v", n)
+ }
+ return cheapExpr(n, init)
+}
+
+func copyExpr(n ir.Node, t *types.Type, init *ir.Nodes) ir.Node {
+ l := typecheck.Temp(t)
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, l, n))
+ return l
+}
+
+func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
+ c := len(n.List)
+
+ if c < 2 {
+ base.Fatalf("walkAddString count %d too small", c)
+ }
+
+ buf := typecheck.NodNil()
+ if n.Esc() == ir.EscNone {
+ sz := int64(0)
+ for _, n1 := range n.List {
+ if n1.Op() == ir.OLITERAL {
+ sz += int64(len(ir.StringVal(n1)))
+ }
+ }
+
+ // Don't allocate the buffer if the result won't fit.
+ if sz < tmpstringbufsize {
+ // Create temporary buffer for result string on stack.
+ buf = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
+ }
+ }
+
+ // build list of string arguments
+ args := []ir.Node{buf}
+ for _, n2 := range n.List {
+ args = append(args, typecheck.Conv(n2, types.Types[types.TSTRING]))
+ }
+
+ var fn string
+ if c <= 5 {
+ // small numbers of strings use direct runtime helpers.
+ // note: order.expr knows this cutoff too.
+ fn = fmt.Sprintf("concatstring%d", c)
+ } else {
+ // large numbers of strings are passed to the runtime as a slice.
+ fn = "concatstrings"
+
+ t := types.NewSlice(types.Types[types.TSTRING])
+ // args[1:] to skip buf arg
+ slice := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(t), args[1:])
+ slice.Prealloc = n.Prealloc
+ args = []ir.Node{buf, slice}
+ slice.SetEsc(ir.EscNone)
+ }
+
+ cat := typecheck.LookupRuntime(fn)
+ r := ir.NewCallExpr(base.Pos, ir.OCALL, cat, nil)
+ r.Args = args
+ r1 := typecheck.Expr(r)
+ r1 = walkExpr(r1, init)
+ r1.SetType(n.Type())
+
+ return r1
+}
+
+// walkCall walks an OCALLFUNC, OCALLINTER, or OCALLMETH node.
+func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
+ if n.Op() == ir.OCALLINTER || n.Op() == ir.OCALLMETH {
+ // We expect both interface call reflect.Type.Method and concrete
+ // call reflect.(*rtype).Method.
+ usemethod(n)
+ }
+ if n.Op() == ir.OCALLINTER {
+ reflectdata.MarkUsedIfaceMethod(n)
+ }
+
+ if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.OCLOSURE {
+ directClosureCall(n)
+ }
+
+ walkCall1(n, init)
+ return n
+}
+
+func walkCall1(n *ir.CallExpr, init *ir.Nodes) {
+ if n.Walked() {
+ return // already walked
+ }
+ n.SetWalked(true)
+
+ // If this is a method call t.M(...),
+ // rewrite into a function call T.M(t, ...).
+ // TODO(mdempsky): Do this right after type checking.
+ if n.Op() == ir.OCALLMETH {
+ withRecv := make([]ir.Node, len(n.Args)+1)
+ dot := n.X.(*ir.SelectorExpr)
+ withRecv[0] = dot.X
+ copy(withRecv[1:], n.Args)
+ n.Args = withRecv
+
+ dot = ir.NewSelectorExpr(dot.Pos(), ir.OXDOT, ir.TypeNode(dot.X.Type()), dot.Selection.Sym)
+
+ n.SetOp(ir.OCALLFUNC)
+ n.X = typecheck.Expr(dot)
+ }
+
+ args := n.Args
+ params := n.X.Type().Params()
+
+ n.X = walkExpr(n.X, init)
+ walkExprList(args, init)
+
+ for i, arg := range args {
+ // Validate argument and parameter types match.
+ param := params.Field(i)
+ if !types.Identical(arg.Type(), param.Type) {
+ base.FatalfAt(n.Pos(), "assigning %L to parameter %v (type %v)", arg, param.Sym, param.Type)
+ }
+
+ // For any argument whose evaluation might require a function call,
+ // store that argument into a temporary variable,
+ // to prevent that calls from clobbering arguments already on the stack.
+ if mayCall(arg) {
+ // assignment of arg to Temp
+ tmp := typecheck.Temp(param.Type)
+ init.Append(convas(typecheck.Stmt(ir.NewAssignStmt(base.Pos, tmp, arg)).(*ir.AssignStmt), init))
+ // replace arg with temp
+ args[i] = tmp
+ }
+ }
+
+ n.Args = args
+}
+
+// walkDivMod walks an ODIV or OMOD node.
+func walkDivMod(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+ n.Y = walkExpr(n.Y, init)
+
+ // rewrite complex div into function call.
+ et := n.X.Type().Kind()
+
+ if types.IsComplex[et] && n.Op() == ir.ODIV {
+ t := n.Type()
+ call := mkcall("complex128div", types.Types[types.TCOMPLEX128], init, typecheck.Conv(n.X, types.Types[types.TCOMPLEX128]), typecheck.Conv(n.Y, types.Types[types.TCOMPLEX128]))
+ return typecheck.Conv(call, t)
+ }
+
+ // Nothing to do for float divisions.
+ if types.IsFloat[et] {
+ return n
+ }
+
+ // rewrite 64-bit div and mod on 32-bit architectures.
+ // TODO: Remove this code once we can introduce
+ // runtime calls late in SSA processing.
+ if types.RegSize < 8 && (et == types.TINT64 || et == types.TUINT64) {
+ if n.Y.Op() == ir.OLITERAL {
+ // Leave div/mod by constant powers of 2 or small 16-bit constants.
+ // The SSA backend will handle those.
+ switch et {
+ case types.TINT64:
+ c := ir.Int64Val(n.Y)
+ if c < 0 {
+ c = -c
+ }
+ if c != 0 && c&(c-1) == 0 {
+ return n
+ }
+ case types.TUINT64:
+ c := ir.Uint64Val(n.Y)
+ if c < 1<<16 {
+ return n
+ }
+ if c != 0 && c&(c-1) == 0 {
+ return n
+ }
+ }
+ }
+ var fn string
+ if et == types.TINT64 {
+ fn = "int64"
+ } else {
+ fn = "uint64"
+ }
+ if n.Op() == ir.ODIV {
+ fn += "div"
+ } else {
+ fn += "mod"
+ }
+ return mkcall(fn, n.Type(), init, typecheck.Conv(n.X, types.Types[et]), typecheck.Conv(n.Y, types.Types[et]))
+ }
+ return n
+}
+
+// walkDot walks an ODOT or ODOTPTR node.
+func walkDot(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
+ usefield(n)
+ n.X = walkExpr(n.X, init)
+ return n
+}
+
+// walkDotType walks an ODOTTYPE or ODOTTYPE2 node.
+func walkDotType(n *ir.TypeAssertExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+ // Set up interface type addresses for back end.
+ if !n.Type().IsInterface() && !n.X.Type().IsEmptyInterface() {
+ n.Itab = reflectdata.ITabAddr(n.Type(), n.X.Type())
+ }
+ return n
+}
+
+// walkIndex walks an OINDEX node.
+func walkIndex(n *ir.IndexExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+
+ // save the original node for bounds checking elision.
+ // If it was a ODIV/OMOD walk might rewrite it.
+ r := n.Index
+
+ n.Index = walkExpr(n.Index, init)
+
+ // if range of type cannot exceed static array bound,
+ // disable bounds check.
+ if n.Bounded() {
+ return n
+ }
+ t := n.X.Type()
+ if t != nil && t.IsPtr() {
+ t = t.Elem()
+ }
+ if t.IsArray() {
+ n.SetBounded(bounded(r, t.NumElem()))
+ if base.Flag.LowerM != 0 && n.Bounded() && !ir.IsConst(n.Index, constant.Int) {
+ base.Warn("index bounds check elided")
+ }
+ if ir.IsSmallIntConst(n.Index) && !n.Bounded() {
+ base.Errorf("index out of bounds")
+ }
+ } else if ir.IsConst(n.X, constant.String) {
+ n.SetBounded(bounded(r, int64(len(ir.StringVal(n.X)))))
+ if base.Flag.LowerM != 0 && n.Bounded() && !ir.IsConst(n.Index, constant.Int) {
+ base.Warn("index bounds check elided")
+ }
+ if ir.IsSmallIntConst(n.Index) && !n.Bounded() {
+ base.Errorf("index out of bounds")
+ }
+ }
+
+ if ir.IsConst(n.Index, constant.Int) {
+ if v := n.Index.Val(); constant.Sign(v) < 0 || ir.ConstOverflow(v, types.Types[types.TINT]) {
+ base.Errorf("index out of bounds")
+ }
+ }
+ return n
+}
+
+// walkIndexMap walks an OINDEXMAP node.
+func walkIndexMap(n *ir.IndexExpr, init *ir.Nodes) ir.Node {
+ // Replace m[k] with *map{access1,assign}(maptype, m, &k)
+ n.X = walkExpr(n.X, init)
+ n.Index = walkExpr(n.Index, init)
+ map_ := n.X
+ key := n.Index
+ t := map_.Type()
+ var call *ir.CallExpr
+ if n.Assigned {
+ // This m[k] expression is on the left-hand side of an assignment.
+ fast := mapfast(t)
+ if fast == mapslow {
+ // standard version takes key by reference.
+ // order.expr made sure key is addressable.
+ key = typecheck.NodAddr(key)
+ }
+ call = mkcall1(mapfn(mapassign[fast], t), nil, init, reflectdata.TypePtr(t), map_, key)
+ } else {
+ // m[k] is not the target of an assignment.
+ fast := mapfast(t)
+ if fast == mapslow {
+ // standard version takes key by reference.
+ // order.expr made sure key is addressable.
+ key = typecheck.NodAddr(key)
+ }
+
+ if w := t.Elem().Width; w <= zeroValSize {
+ call = mkcall1(mapfn(mapaccess1[fast], t), types.NewPtr(t.Elem()), init, reflectdata.TypePtr(t), map_, key)
+ } else {
+ z := reflectdata.ZeroAddr(w)
+ call = mkcall1(mapfn("mapaccess1_fat", t), types.NewPtr(t.Elem()), init, reflectdata.TypePtr(t), map_, key, z)
+ }
+ }
+ call.SetType(types.NewPtr(t.Elem()))
+ call.MarkNonNil() // mapaccess1* and mapassign always return non-nil pointers.
+ star := ir.NewStarExpr(base.Pos, call)
+ star.SetType(t.Elem())
+ star.SetTypecheck(1)
+ return star
+}
+
+// walkLogical walks an OANDAND or OOROR node.
+func walkLogical(n *ir.LogicalExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+
+ // cannot put side effects from n.Right on init,
+ // because they cannot run before n.Left is checked.
+ // save elsewhere and store on the eventual n.Right.
+ var ll ir.Nodes
+
+ n.Y = walkExpr(n.Y, &ll)
+ n.Y = ir.InitExpr(ll, n.Y)
+ return n
+}
+
+// walkSend walks an OSEND node.
+func walkSend(n *ir.SendStmt, init *ir.Nodes) ir.Node {
+ n1 := n.Value
+ n1 = typecheck.AssignConv(n1, n.Chan.Type().Elem(), "chan send")
+ n1 = walkExpr(n1, init)
+ n1 = typecheck.NodAddr(n1)
+ return mkcall1(chanfn("chansend1", 2, n.Chan.Type()), nil, init, n.Chan, n1)
+}
+
+// walkSlice walks an OSLICE, OSLICEARR, OSLICESTR, OSLICE3, or OSLICE3ARR node.
+func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node {
+
+ checkSlice := ir.ShouldCheckPtr(ir.CurFunc, 1) && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr()
+ if checkSlice {
+ conv := n.X.(*ir.ConvExpr)
+ conv.X = walkExpr(conv.X, init)
+ } else {
+ n.X = walkExpr(n.X, init)
+ }
+
+ n.Low = walkExpr(n.Low, init)
+ if n.Low != nil && ir.IsZero(n.Low) {
+ // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k].
+ n.Low = nil
+ }
+ n.High = walkExpr(n.High, init)
+ n.Max = walkExpr(n.Max, init)
+ if checkSlice {
+ n.X = walkCheckPtrAlignment(n.X.(*ir.ConvExpr), init, n.Max)
+ }
+
+ if n.Op().IsSlice3() {
+ if n.Max != nil && n.Max.Op() == ir.OCAP && ir.SameSafeExpr(n.X, n.Max.(*ir.UnaryExpr).X) {
+ // Reduce x[i:j:cap(x)] to x[i:j].
+ if n.Op() == ir.OSLICE3 {
+ n.SetOp(ir.OSLICE)
+ } else {
+ n.SetOp(ir.OSLICEARR)
+ }
+ return reduceSlice(n)
+ }
+ return n
+ }
+ return reduceSlice(n)
+}
+
+// walkSliceHeader walks an OSLICEHEADER node.
+func walkSliceHeader(n *ir.SliceHeaderExpr, init *ir.Nodes) ir.Node {
+ n.Ptr = walkExpr(n.Ptr, init)
+ n.Len = walkExpr(n.Len, init)
+ n.Cap = walkExpr(n.Cap, init)
+ return n
+}
+
+// TODO(josharian): combine this with its caller and simplify
+func reduceSlice(n *ir.SliceExpr) ir.Node {
+ if n.High != nil && n.High.Op() == ir.OLEN && ir.SameSafeExpr(n.X, n.High.(*ir.UnaryExpr).X) {
+ // Reduce x[i:len(x)] to x[i:].
+ n.High = nil
+ }
+ if (n.Op() == ir.OSLICE || n.Op() == ir.OSLICESTR) && n.Low == nil && n.High == nil {
+ // Reduce x[:] to x.
+ if base.Debug.Slice > 0 {
+ base.Warn("slice: omit slice operation")
+ }
+ return n.X
+ }
+ return n
+}
+
+// return 1 if integer n must be in range [0, max), 0 otherwise
+func bounded(n ir.Node, max int64) bool {
+ if n.Type() == nil || !n.Type().IsInteger() {
+ return false
+ }
+
+ sign := n.Type().IsSigned()
+ bits := int32(8 * n.Type().Width)
+
+ if ir.IsSmallIntConst(n) {
+ v := ir.Int64Val(n)
+ return 0 <= v && v < max
+ }
+
+ switch n.Op() {
+ case ir.OAND, ir.OANDNOT:
+ n := n.(*ir.BinaryExpr)
+ v := int64(-1)
+ switch {
+ case ir.IsSmallIntConst(n.X):
+ v = ir.Int64Val(n.X)
+ case ir.IsSmallIntConst(n.Y):
+ v = ir.Int64Val(n.Y)
+ if n.Op() == ir.OANDNOT {
+ v = ^v
+ if !sign {
+ v &= 1< 0 && v >= 2 {
+ bits--
+ v >>= 1
+ }
+ }
+
+ case ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ if !sign && ir.IsSmallIntConst(n.Y) {
+ v := ir.Int64Val(n.Y)
+ if v > int64(bits) {
+ return true
+ }
+ bits -= int32(v)
+ }
+ }
+
+ if !sign && bits <= 62 && 1< 1 {
+ s := fmt.Sprintf("\nbefore order %v", fn.Sym())
+ ir.DumpList(s, fn.Body)
+ }
+
+ orderBlock(&fn.Body, map[string][]*ir.Name{})
+}
+
+// append typechecks stmt and appends it to out.
+func (o *orderState) append(stmt ir.Node) {
+ o.out = append(o.out, typecheck.Stmt(stmt))
+}
+
+// newTemp allocates a new temporary with the given type,
+// pushes it onto the temp stack, and returns it.
+// If clear is true, newTemp emits code to zero the temporary.
+func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
+ var v *ir.Name
+ // Note: LongString is close to the type equality we want,
+ // but not exactly. We still need to double-check with types.Identical.
+ key := t.LongString()
+ a := o.free[key]
+ for i, n := range a {
+ if types.Identical(t, n.Type()) {
+ v = a[i]
+ a[i] = a[len(a)-1]
+ a = a[:len(a)-1]
+ o.free[key] = a
+ break
+ }
+ }
+ if v == nil {
+ v = typecheck.Temp(t)
+ }
+ if clear {
+ o.append(ir.NewAssignStmt(base.Pos, v, nil))
+ }
+
+ o.temp = append(o.temp, v)
+ return v
+}
+
+// copyExpr behaves like newTemp but also emits
+// code to initialize the temporary to the value n.
+func (o *orderState) copyExpr(n ir.Node) *ir.Name {
+ return o.copyExpr1(n, false)
+}
+
+// copyExprClear is like copyExpr but clears the temp before assignment.
+// It is provided for use when the evaluation of tmp = n turns into
+// a function call that is passed a pointer to the temporary as the output space.
+// If the call blocks before tmp has been written,
+// the garbage collector will still treat the temporary as live,
+// so we must zero it before entering that call.
+// Today, this only happens for channel receive operations.
+// (The other candidate would be map access, but map access
+// returns a pointer to the result data instead of taking a pointer
+// to be filled in.)
+func (o *orderState) copyExprClear(n ir.Node) *ir.Name {
+ return o.copyExpr1(n, true)
+}
+
+func (o *orderState) copyExpr1(n ir.Node, clear bool) *ir.Name {
+ t := n.Type()
+ v := o.newTemp(t, clear)
+ o.append(ir.NewAssignStmt(base.Pos, v, n))
+ return v
+}
+
+// cheapExpr returns a cheap version of n.
+// The definition of cheap is that n is a variable or constant.
+// If not, cheapExpr allocates a new tmp, emits tmp = n,
+// and then returns tmp.
+func (o *orderState) cheapExpr(n ir.Node) ir.Node {
+ if n == nil {
+ return nil
+ }
+
+ switch n.Op() {
+ case ir.ONAME, ir.OLITERAL, ir.ONIL:
+ return n
+ case ir.OLEN, ir.OCAP:
+ n := n.(*ir.UnaryExpr)
+ l := o.cheapExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.UnaryExpr)
+ a.X = l
+ return typecheck.Expr(a)
+ }
+
+ return o.copyExpr(n)
+}
+
+// safeExpr returns a safe version of n.
+// The definition of safe is that n can appear multiple times
+// without violating the semantics of the original program,
+// and that assigning to the safe version has the same effect
+// as assigning to the original n.
+//
+// The intended use is to apply to x when rewriting x += y into x = x + y.
+func (o *orderState) safeExpr(n ir.Node) ir.Node {
+ switch n.Op() {
+ case ir.ONAME, ir.OLITERAL, ir.ONIL:
+ return n
+
+ case ir.OLEN, ir.OCAP:
+ n := n.(*ir.UnaryExpr)
+ l := o.safeExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.UnaryExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.ODOT:
+ n := n.(*ir.SelectorExpr)
+ l := o.safeExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.SelectorExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ l := o.cheapExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.SelectorExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ l := o.cheapExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.StarExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.OINDEX, ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ var l ir.Node
+ if n.X.Type().IsArray() {
+ l = o.safeExpr(n.X)
+ } else {
+ l = o.cheapExpr(n.X)
+ }
+ r := o.cheapExpr(n.Index)
+ if l == n.X && r == n.Index {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.IndexExpr)
+ a.X = l
+ a.Index = r
+ return typecheck.Expr(a)
+
+ default:
+ base.Fatalf("order.safeExpr %v", n.Op())
+ return nil // not reached
+ }
+}
+
+// isaddrokay reports whether it is okay to pass n's address to runtime routines.
+// Taking the address of a variable makes the liveness and optimization analyses
+// lose track of where the variable's lifetime ends. To avoid hurting the analyses
+// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
+// because we emit explicit VARKILL instructions marking the end of those
+// temporaries' lifetimes.
+func isaddrokay(n ir.Node) bool {
+ return ir.IsAddressable(n) && (n.Op() != ir.ONAME || n.(*ir.Name).Class == ir.PEXTERN || ir.IsAutoTmp(n))
+}
+
+// addrTemp ensures that n is okay to pass by address to runtime routines.
+// If the original argument n is not okay, addrTemp creates a tmp, emits
+// tmp = n, and then returns tmp.
+// The result of addrTemp MUST be assigned back to n, e.g.
+// n.Left = o.addrTemp(n.Left)
+func (o *orderState) addrTemp(n ir.Node) ir.Node {
+ if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL {
+ // TODO: expand this to all static composite literal nodes?
+ n = typecheck.DefaultLit(n, nil)
+ types.CalcSize(n.Type())
+ vstat := readonlystaticname(n.Type())
+ var s staticinit.Schedule
+ s.StaticAssign(vstat, 0, n, n.Type())
+ if s.Out != nil {
+ base.Fatalf("staticassign of const generated code: %+v", n)
+ }
+ vstat = typecheck.Expr(vstat).(*ir.Name)
+ return vstat
+ }
+ if isaddrokay(n) {
+ return n
+ }
+ return o.copyExpr(n)
+}
+
+// mapKeyTemp prepares n to be a key in a map runtime call and returns n.
+// It should only be used for map runtime calls which have *_fast* versions.
+func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
+ // Most map calls need to take the address of the key.
+ // Exception: map*_fast* calls. See golang.org/issue/19015.
+ if mapfast(t) == mapslow {
+ return o.addrTemp(n)
+ }
+ return n
+}
+
+// mapKeyReplaceStrConv replaces OBYTES2STR by OBYTES2STRTMP
+// in n to avoid string allocations for keys in map lookups.
+// Returns a bool that signals if a modification was made.
+//
+// For:
+// x = m[string(k)]
+// x = m[T1{... Tn{..., string(k), ...}]
+// where k is []byte, T1 to Tn is a nesting of struct and array literals,
+// the allocation of backing bytes for the string can be avoided
+// by reusing the []byte backing array. These are special cases
+// for avoiding allocations when converting byte slices to strings.
+// It would be nice to handle these generally, but because
+// []byte keys are not allowed in maps, the use of string(k)
+// comes up in important cases in practice. See issue 3512.
+func mapKeyReplaceStrConv(n ir.Node) bool {
+ var replaced bool
+ switch n.Op() {
+ case ir.OBYTES2STR:
+ n := n.(*ir.ConvExpr)
+ n.SetOp(ir.OBYTES2STRTMP)
+ replaced = true
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elem := range n.List {
+ elem := elem.(*ir.StructKeyExpr)
+ if mapKeyReplaceStrConv(elem.Value) {
+ replaced = true
+ }
+ }
+ case ir.OARRAYLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elem := range n.List {
+ if elem.Op() == ir.OKEY {
+ elem = elem.(*ir.KeyExpr).Value
+ }
+ if mapKeyReplaceStrConv(elem) {
+ replaced = true
+ }
+ }
+ }
+ return replaced
+}
+
+type ordermarker int
+
+// markTemp returns the top of the temporary variable stack.
+func (o *orderState) markTemp() ordermarker {
+ return ordermarker(len(o.temp))
+}
+
+// popTemp pops temporaries off the stack until reaching the mark,
+// which must have been returned by markTemp.
+func (o *orderState) popTemp(mark ordermarker) {
+ for _, n := range o.temp[mark:] {
+ key := n.Type().LongString()
+ o.free[key] = append(o.free[key], n)
+ }
+ o.temp = o.temp[:mark]
+}
+
+// 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 *orderState) cleanTempNoPop(mark ordermarker) []ir.Node {
+ var out []ir.Node
+ for i := len(o.temp) - 1; i >= int(mark); i-- {
+ n := o.temp[i]
+ out = append(out, typecheck.Stmt(ir.NewUnaryExpr(base.Pos, ir.OVARKILL, n)))
+ }
+ return out
+}
+
+// cleanTemp emits VARKILL instructions for each temporary above the
+// mark on the temporary stack and removes them from the stack.
+func (o *orderState) cleanTemp(top ordermarker) {
+ o.out = append(o.out, o.cleanTempNoPop(top)...)
+ o.popTemp(top)
+}
+
+// stmtList orders each of the statements in the list.
+func (o *orderState) stmtList(l ir.Nodes) {
+ s := l
+ for i := range s {
+ orderMakeSliceCopy(s[i:])
+ o.stmt(s[i])
+ }
+}
+
+// orderMakeSliceCopy matches the pattern:
+// m = OMAKESLICE([]T, x); OCOPY(m, s)
+// and rewrites it to:
+// m = OMAKESLICECOPY([]T, x, s); nil
+func orderMakeSliceCopy(s []ir.Node) {
+ if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
+ return
+ }
+ if len(s) < 2 || s[0] == nil || s[0].Op() != ir.OAS || s[1] == nil || s[1].Op() != ir.OCOPY {
+ return
+ }
+
+ as := s[0].(*ir.AssignStmt)
+ cp := s[1].(*ir.BinaryExpr)
+ if as.Y == nil || as.Y.Op() != ir.OMAKESLICE || ir.IsBlank(as.X) ||
+ as.X.Op() != ir.ONAME || cp.X.Op() != ir.ONAME || cp.Y.Op() != ir.ONAME ||
+ as.X.Name() != cp.X.Name() || cp.X.Name() == cp.Y.Name() {
+ // The line above this one is correct with the differing equality operators:
+ // we want as.X and cp.X to be the same name,
+ // but we want the initial data to be coming from a different name.
+ return
+ }
+
+ mk := as.Y.(*ir.MakeExpr)
+ if mk.Esc() == ir.EscNone || mk.Len == nil || mk.Cap != nil {
+ return
+ }
+ mk.SetOp(ir.OMAKESLICECOPY)
+ mk.Cap = cp.Y
+ // Set bounded when m = OMAKESLICE([]T, len(s)); OCOPY(m, s)
+ mk.SetBounded(mk.Len.Op() == ir.OLEN && ir.SameSafeExpr(mk.Len.(*ir.UnaryExpr).X, cp.Y))
+ as.Y = typecheck.Expr(mk)
+ s[1] = nil // remove separate copy call
+}
+
+// edge inserts coverage instrumentation for libfuzzer.
+func (o *orderState) edge() {
+ if base.Debug.Libfuzzer == 0 {
+ return
+ }
+
+ // Create a new uint8 counter to be allocated in section
+ // __libfuzzer_extra_counters.
+ counter := staticinit.StaticName(types.Types[types.TUINT8])
+ counter.SetLibfuzzerExtraCounter(true)
+
+ // counter += 1
+ incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
+ o.append(incr)
+}
+
+// orderBlock orders the block of statements in n into a new slice,
+// and then replaces the old slice in n with the new slice.
+// free is a map that can be used to obtain temporary variables by type.
+func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
+ var order orderState
+ order.free = free
+ mark := order.markTemp()
+ order.edge()
+ order.stmtList(*n)
+ order.cleanTemp(mark)
+ *n = order.out
+}
+
+// exprInPlace orders the side effects in *np and
+// leaves them as the init list of the final *np.
+// The result of exprInPlace MUST be assigned back to n, e.g.
+// n.Left = o.exprInPlace(n.Left)
+func (o *orderState) exprInPlace(n ir.Node) ir.Node {
+ var order orderState
+ order.free = o.free
+ n = order.expr(n, nil)
+ n = ir.InitExpr(order.out, n)
+
+ // insert new temporaries from order
+ // at head of outer list.
+ o.temp = append(o.temp, order.temp...)
+ return n
+}
+
+// orderStmtInPlace orders the side effects of the single statement *np
+// and replaces it with the resulting statement list.
+// The result of orderStmtInPlace MUST be assigned back to n, e.g.
+// n.Left = orderStmtInPlace(n.Left)
+// free is a map that can be used to obtain temporary variables by type.
+func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
+ var order orderState
+ order.free = free
+ mark := order.markTemp()
+ order.stmt(n)
+ order.cleanTemp(mark)
+ return ir.NewBlockStmt(src.NoXPos, order.out)
+}
+
+// init moves n's init list to o.out.
+func (o *orderState) init(n ir.Node) {
+ if ir.MayBeShared(n) {
+ // For concurrency safety, don't mutate potentially shared nodes.
+ // First, ensure that no work is required here.
+ if len(n.Init()) > 0 {
+ base.Fatalf("order.init shared node with ninit")
+ }
+ return
+ }
+ o.stmtList(ir.TakeInit(n))
+}
+
+// call orders the call expression n.
+// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
+func (o *orderState) call(nn ir.Node) {
+ if len(nn.Init()) > 0 {
+ // Caller should have already called o.init(nn).
+ base.Fatalf("%v with unexpected ninit", nn.Op())
+ }
+
+ // Builtin functions.
+ if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER {
+ switch n := nn.(type) {
+ default:
+ base.Fatalf("unexpected call: %+v", n)
+ case *ir.UnaryExpr:
+ n.X = o.expr(n.X, nil)
+ case *ir.ConvExpr:
+ n.X = o.expr(n.X, nil)
+ case *ir.BinaryExpr:
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+ case *ir.MakeExpr:
+ n.Len = o.expr(n.Len, nil)
+ n.Cap = o.expr(n.Cap, nil)
+ case *ir.CallExpr:
+ o.exprList(n.Args)
+ }
+ return
+ }
+
+ n := nn.(*ir.CallExpr)
+ typecheck.FixVariadicCall(n)
+ n.X = o.expr(n.X, nil)
+ o.exprList(n.Args)
+
+ if n.Op() == ir.OCALLINTER {
+ return
+ }
+ keepAlive := func(arg ir.Node) {
+ // If the argument is really a pointer being converted to uintptr,
+ // arrange for the pointer to be kept alive until the call returns,
+ // by copying it into a temp and marking that temp
+ // still alive when we pop the temp stack.
+ if arg.Op() == ir.OCONVNOP {
+ arg := arg.(*ir.ConvExpr)
+ if arg.X.Type().IsUnsafePtr() {
+ x := o.copyExpr(arg.X)
+ arg.X = x
+ x.SetAddrtaken(true) // ensure SSA keeps the x variable
+ n.KeepAlive = append(n.KeepAlive, x)
+ }
+ }
+ }
+
+ // Check for "unsafe-uintptr" tag provided by escape analysis.
+ for i, param := range n.X.Type().Params().FieldSlice() {
+ if param.Note == escape.UnsafeUintptrNote || param.Note == escape.UintptrEscapesNote {
+ if arg := n.Args[i]; arg.Op() == ir.OSLICELIT {
+ arg := arg.(*ir.CompLitExpr)
+ for _, elt := range arg.List {
+ keepAlive(elt)
+ }
+ } else {
+ keepAlive(arg)
+ }
+ }
+ }
+}
+
+// mapAssign appends n to o.out.
+func (o *orderState) mapAssign(n ir.Node) {
+ switch n.Op() {
+ default:
+ base.Fatalf("order.mapAssign %v", n.Op())
+
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ if n.X.Op() == ir.OINDEXMAP {
+ n.Y = o.safeMapRHS(n.Y)
+ }
+ o.out = append(o.out, n)
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ if n.X.Op() == ir.OINDEXMAP {
+ n.Y = o.safeMapRHS(n.Y)
+ }
+ o.out = append(o.out, n)
+ }
+}
+
+func (o *orderState) safeMapRHS(r ir.Node) ir.Node {
+ // Make sure we evaluate the RHS before starting the map insert.
+ // We need to make sure the RHS won't panic. See issue 22881.
+ if r.Op() == ir.OAPPEND {
+ r := r.(*ir.CallExpr)
+ s := r.Args[1:]
+ for i, n := range s {
+ s[i] = o.cheapExpr(n)
+ }
+ return r
+ }
+ return o.cheapExpr(r)
+}
+
+// stmt orders the statement n, appending to o.out.
+// Temporaries created during the statement are cleaned
+// up using VARKILL instructions as possible.
+func (o *orderState) stmt(n ir.Node) {
+ if n == nil {
+ return
+ }
+
+ lno := ir.SetPos(n)
+ o.init(n)
+
+ switch n.Op() {
+ default:
+ base.Fatalf("order.stmt %v", n.Op())
+
+ case ir.OVARKILL, ir.OVARLIVE, ir.OINLMARK:
+ o.out = append(o.out, n)
+
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, n.X)
+ o.mapAssign(n)
+ o.cleanTemp(t)
+
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+
+ if base.Flag.Cfg.Instrumenting || n.X.Op() == ir.OINDEXMAP && (n.AsOp == ir.ODIV || n.AsOp == ir.OMOD) {
+ // Rewrite m[k] op= r into m[k] = m[k] op r so
+ // that we can ensure that if op panics
+ // because r is zero, the panic happens before
+ // the map assignment.
+ // DeepCopy is a big hammer here, but safeExpr
+ // makes sure there is nothing too deep being copied.
+ l1 := o.safeExpr(n.X)
+ l2 := ir.DeepCopy(src.NoXPos, l1)
+ if l2.Op() == ir.OINDEXMAP {
+ l2 := l2.(*ir.IndexExpr)
+ l2.Assigned = false
+ }
+ l2 = o.copyExpr(l2)
+ r := o.expr(typecheck.Expr(ir.NewBinaryExpr(n.Pos(), n.AsOp, l2, n.Y)), nil)
+ as := typecheck.Stmt(ir.NewAssignStmt(n.Pos(), l1, r))
+ o.mapAssign(as)
+ o.cleanTemp(t)
+ return
+ }
+
+ o.mapAssign(n)
+ o.cleanTemp(t)
+
+ case ir.OAS2:
+ n := n.(*ir.AssignListStmt)
+ t := o.markTemp()
+ o.exprList(n.Lhs)
+ o.exprList(n.Rhs)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Special: avoid copy of func call n.Right
+ case ir.OAS2FUNC:
+ n := n.(*ir.AssignListStmt)
+ t := o.markTemp()
+ o.exprList(n.Lhs)
+ o.init(n.Rhs[0])
+ o.call(n.Rhs[0])
+ o.as2func(n)
+ o.cleanTemp(t)
+
+ // Special: use temporary variables to hold result,
+ // so that runtime can take address of temporary.
+ // No temporary for blank assignment.
+ //
+ // OAS2MAPR: make sure key is addressable if needed,
+ // and make sure OINDEXMAP is not copied out.
+ case ir.OAS2DOTTYPE, ir.OAS2RECV, ir.OAS2MAPR:
+ n := n.(*ir.AssignListStmt)
+ t := o.markTemp()
+ o.exprList(n.Lhs)
+
+ switch r := n.Rhs[0]; r.Op() {
+ case ir.ODOTTYPE2:
+ r := r.(*ir.TypeAssertExpr)
+ r.X = o.expr(r.X, nil)
+ case ir.ORECV:
+ r := r.(*ir.UnaryExpr)
+ r.X = o.expr(r.X, nil)
+ case ir.OINDEXMAP:
+ r := r.(*ir.IndexExpr)
+ r.X = o.expr(r.X, nil)
+ r.Index = o.expr(r.Index, nil)
+ // See similar conversion for OINDEXMAP below.
+ _ = mapKeyReplaceStrConv(r.Index)
+ r.Index = o.mapKeyTemp(r.X.Type(), r.Index)
+ default:
+ base.Fatalf("order.stmt: %v", r.Op())
+ }
+
+ o.as2ok(n)
+ o.cleanTemp(t)
+
+ // Special: does not save n onto out.
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
+ o.stmtList(n.List)
+
+ // Special: n->left is not an expression; save as is.
+ case ir.OBREAK,
+ ir.OCONTINUE,
+ ir.ODCL,
+ ir.ODCLCONST,
+ ir.ODCLTYPE,
+ ir.OFALL,
+ ir.OGOTO,
+ ir.OLABEL,
+ ir.OTAILCALL:
+ o.out = append(o.out, n)
+
+ // Special: handle call arguments.
+ case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
+ n := n.(*ir.CallExpr)
+ t := o.markTemp()
+ o.call(n)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.OCLOSE, ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.OCOPY:
+ n := n.(*ir.BinaryExpr)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ n := n.(*ir.CallExpr)
+ t := o.markTemp()
+ o.exprList(n.Args)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Special: order arguments to inner call but not call itself.
+ case ir.ODEFER, ir.OGO:
+ n := n.(*ir.GoDeferStmt)
+ t := o.markTemp()
+ o.init(n.Call)
+ o.call(n.Call)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ t := o.markTemp()
+ n.Args[0] = o.expr(n.Args[0], nil)
+ n.Args[1] = o.expr(n.Args[1], nil)
+ n.Args[1] = o.mapKeyTemp(n.Args[0].Type(), n.Args[1])
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Clean temporaries from condition evaluation at
+ // beginning of loop body and after for statement.
+ case ir.OFOR:
+ n := n.(*ir.ForStmt)
+ t := o.markTemp()
+ n.Cond = o.exprInPlace(n.Cond)
+ n.Body.Prepend(o.cleanTempNoPop(t)...)
+ orderBlock(&n.Body, o.free)
+ n.Post = orderStmtInPlace(n.Post, o.free)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Clean temporaries from condition at
+ // beginning of both branches.
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ t := o.markTemp()
+ n.Cond = o.exprInPlace(n.Cond)
+ n.Body.Prepend(o.cleanTempNoPop(t)...)
+ n.Else.Prepend(o.cleanTempNoPop(t)...)
+ o.popTemp(t)
+ orderBlock(&n.Body, o.free)
+ orderBlock(&n.Else, o.free)
+ o.out = append(o.out, n)
+
+ case ir.OPANIC:
+ n := n.(*ir.UnaryExpr)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ if !n.X.Type().IsEmptyInterface() {
+ base.FatalfAt(n.Pos(), "bad argument to panic: %L", n.X)
+ }
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.ORANGE:
+ // n.Right is the expression being ranged over.
+ // order it, and then make a copy if we need one.
+ // We almost always do, to ensure that we don't
+ // see any value changes made during the loop.
+ // Usually the copy is cheap (e.g., array pointer,
+ // chan, slice, string are all tiny).
+ // The exception is ranging over an array value
+ // (not a slice, not a pointer to array),
+ // which must make a copy to avoid seeing updates made during
+ // the range body. Ranging over an array value is uncommon though.
+
+ // Mark []byte(str) range expression to reuse string backing storage.
+ // It is safe because the storage cannot be mutated.
+ n := n.(*ir.RangeStmt)
+ if n.X.Op() == ir.OSTR2BYTES {
+ n.X.(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP)
+ }
+
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+
+ orderBody := true
+ xt := typecheck.RangeExprType(n.X.Type())
+ switch xt.Kind() {
+ default:
+ base.Fatalf("order.stmt range %v", n.Type())
+
+ case types.TARRAY, types.TSLICE:
+ if n.Value == nil || ir.IsBlank(n.Value) {
+ // for i := range x will only use x once, to compute len(x).
+ // No need to copy it.
+ break
+ }
+ fallthrough
+
+ case types.TCHAN, types.TSTRING:
+ // chan, string, slice, array ranges use value multiple times.
+ // make copy.
+ r := n.X
+
+ if r.Type().IsString() && r.Type() != types.Types[types.TSTRING] {
+ r = ir.NewConvExpr(base.Pos, ir.OCONV, nil, r)
+ r.SetType(types.Types[types.TSTRING])
+ r = typecheck.Expr(r)
+ }
+
+ n.X = o.copyExpr(r)
+
+ case types.TMAP:
+ if isMapClear(n) {
+ // Preserve the body of the map clear pattern so it can
+ // be detected during walk. The loop body will not be used
+ // when optimizing away the range loop to a runtime call.
+ orderBody = false
+ break
+ }
+
+ // copy the map value in case it is a map literal.
+ // TODO(rsc): Make tmp = literal expressions reuse tmp.
+ // For maps tmp is just one word so it hardly matters.
+ r := n.X
+ n.X = o.copyExpr(r)
+
+ // n.Prealloc is the temp for the iterator.
+ // MapIterType contains pointers and needs to be zeroed.
+ n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true)
+ }
+ n.Key = o.exprInPlace(n.Key)
+ n.Value = o.exprInPlace(n.Value)
+ if orderBody {
+ orderBlock(&n.Body, o.free)
+ }
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ o.exprList(n.Results)
+ o.out = append(o.out, n)
+
+ // Special: clean case temporaries in each block entry.
+ // Select must enter one of its blocks, so there is no
+ // need for a cleaning at the end.
+ // Doubly special: evaluation order for select is stricter
+ // than ordinary expressions. Even something like p.c
+ // has to be hoisted into a temporary, so that it cannot be
+ // reordered after the channel evaluation for a different
+ // case (if p were nil, then the timing of the fault would
+ // give this away).
+ case ir.OSELECT:
+ n := n.(*ir.SelectStmt)
+ t := o.markTemp()
+ for _, ncas := range n.Cases {
+ r := ncas.Comm
+ ir.SetPos(ncas)
+
+ // Append any new body prologue to ninit.
+ // The next loop will insert ninit into nbody.
+ if len(ncas.Init()) != 0 {
+ base.Fatalf("order select ninit")
+ }
+ if r == nil {
+ continue
+ }
+ switch r.Op() {
+ default:
+ ir.Dump("select case", r)
+ base.Fatalf("unknown op in select %v", r.Op())
+
+ case ir.OSELRECV2:
+ // case x, ok = <-c
+ r := r.(*ir.AssignListStmt)
+ recv := r.Rhs[0].(*ir.UnaryExpr)
+ recv.X = o.expr(recv.X, nil)
+ if !ir.IsAutoTmp(recv.X) {
+ recv.X = o.copyExpr(recv.X)
+ }
+ init := ir.TakeInit(r)
+
+ colas := r.Def
+ do := func(i int, t *types.Type) {
+ n := r.Lhs[i]
+ if ir.IsBlank(n) {
+ return
+ }
+ // If this is case x := <-ch or case x, y := <-ch, the case has
+ // the ODCL nodes to declare x and y. We want to delay that
+ // declaration (and possible allocation) until inside the case body.
+ // Delete the ODCL nodes here and recreate them inside the body below.
+ if colas {
+ if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n {
+ init = init[1:]
+ }
+ dcl := typecheck.Stmt(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
+ ncas.PtrInit().Append(dcl)
+ }
+ tmp := o.newTemp(t, t.HasPointers())
+ as := typecheck.Stmt(ir.NewAssignStmt(base.Pos, n, typecheck.Conv(tmp, n.Type())))
+ ncas.PtrInit().Append(as)
+ r.Lhs[i] = tmp
+ }
+ do(0, recv.X.Type().Elem())
+ do(1, types.Types[types.TBOOL])
+ if len(init) != 0 {
+ ir.DumpList("ninit", r.Init())
+ base.Fatalf("ninit on select recv")
+ }
+ orderBlock(ncas.PtrInit(), o.free)
+
+ case ir.OSEND:
+ r := r.(*ir.SendStmt)
+ if len(r.Init()) != 0 {
+ ir.DumpList("ninit", r.Init())
+ base.Fatalf("ninit on select send")
+ }
+
+ // case c <- x
+ // r->left is c, r->right is x, both are always evaluated.
+ r.Chan = o.expr(r.Chan, nil)
+
+ if !ir.IsAutoTmp(r.Chan) {
+ r.Chan = o.copyExpr(r.Chan)
+ }
+ r.Value = o.expr(r.Value, nil)
+ if !ir.IsAutoTmp(r.Value) {
+ r.Value = o.copyExpr(r.Value)
+ }
+ }
+ }
+ // Now that we have accumulated all the temporaries, clean them.
+ // Also insert any ninit queued during the previous loop.
+ // (The temporary cleaning must follow that ninit work.)
+ for _, cas := range n.Cases {
+ orderBlock(&cas.Body, o.free)
+ cas.Body.Prepend(o.cleanTempNoPop(t)...)
+
+ // TODO(mdempsky): Is this actually necessary?
+ // walkSelect appears to walk Ninit.
+ cas.Body.Prepend(ir.TakeInit(cas)...)
+ }
+
+ o.out = append(o.out, n)
+ o.popTemp(t)
+
+ // Special: value being sent is passed as a pointer; make it addressable.
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ t := o.markTemp()
+ n.Chan = o.expr(n.Chan, nil)
+ n.Value = o.expr(n.Value, nil)
+ if base.Flag.Cfg.Instrumenting {
+ // Force copying to the stack so that (chan T)(nil) <- x
+ // is still instrumented as a read of x.
+ n.Value = o.copyExpr(n.Value)
+ } else {
+ n.Value = o.addrTemp(n.Value)
+ }
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // TODO(rsc): Clean temporaries more aggressively.
+ // Note that because walkSwitch will rewrite some of the
+ // switch into a binary search, this is not as easy as it looks.
+ // (If we ran that code here we could invoke order.stmt on
+ // the if-else chain instead.)
+ // For now just clean all the temporaries at the end.
+ // In practice that's fine.
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+ if base.Debug.Libfuzzer != 0 && !hasDefaultCase(n) {
+ // Add empty "default:" case for instrumentation.
+ n.Cases = append(n.Cases, ir.NewCaseStmt(base.Pos, nil, nil))
+ }
+
+ t := o.markTemp()
+ n.Tag = o.expr(n.Tag, nil)
+ for _, ncas := range n.Cases {
+ o.exprListInPlace(ncas.List)
+ orderBlock(&ncas.Body, o.free)
+ }
+
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+ }
+
+ base.Pos = lno
+}
+
+func hasDefaultCase(n *ir.SwitchStmt) bool {
+ for _, ncas := range n.Cases {
+ if len(ncas.List) == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// exprList orders the expression list l into o.
+func (o *orderState) exprList(l ir.Nodes) {
+ s := l
+ for i := range s {
+ s[i] = o.expr(s[i], nil)
+ }
+}
+
+// exprListInPlace orders the expression list l but saves
+// the side effects on the individual expression ninit lists.
+func (o *orderState) exprListInPlace(l ir.Nodes) {
+ s := l
+ for i := range s {
+ s[i] = o.exprInPlace(s[i])
+ }
+}
+
+func (o *orderState) exprNoLHS(n ir.Node) ir.Node {
+ return o.expr(n, nil)
+}
+
+// expr orders a single expression, appending side
+// effects to o.out as needed.
+// If this is part of an assignment lhs = *np, lhs is given.
+// Otherwise lhs == nil. (When lhs != nil it may be possible
+// to avoid copying the result of the expression to a temporary.)
+// The result of expr MUST be assigned back to n, e.g.
+// n.Left = o.expr(n.Left, lhs)
+func (o *orderState) expr(n, lhs ir.Node) ir.Node {
+ if n == nil {
+ return n
+ }
+ lno := ir.SetPos(n)
+ n = o.expr1(n, lhs)
+ base.Pos = lno
+ return n
+}
+
+func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
+ o.init(n)
+
+ switch n.Op() {
+ default:
+ if o.edit == nil {
+ o.edit = o.exprNoLHS // create closure once
+ }
+ ir.EditChildren(n, o.edit)
+ return n
+
+ // Addition of strings turns into a function call.
+ // Allocate a temporary to hold the strings.
+ // Fewer than 5 strings use direct runtime helpers.
+ case ir.OADDSTR:
+ n := n.(*ir.AddStringExpr)
+ o.exprList(n.List)
+
+ if len(n.List) > 5 {
+ t := types.NewArray(types.Types[types.TSTRING], int64(len(n.List)))
+ n.Prealloc = o.newTemp(t, false)
+ }
+
+ // Mark string(byteSlice) arguments to reuse byteSlice backing
+ // buffer during conversion. String concatenation does not
+ // memorize the strings for later use, so it is safe.
+ // However, we can do it only if there is at least one non-empty string literal.
+ // Otherwise if all other arguments are empty strings,
+ // concatstrings will return the reference to the temp string
+ // to the caller.
+ hasbyte := false
+
+ haslit := false
+ for _, n1 := range n.List {
+ hasbyte = hasbyte || n1.Op() == ir.OBYTES2STR
+ haslit = haslit || n1.Op() == ir.OLITERAL && len(ir.StringVal(n1)) != 0
+ }
+
+ if haslit && hasbyte {
+ for _, n2 := range n.List {
+ if n2.Op() == ir.OBYTES2STR {
+ n2 := n2.(*ir.ConvExpr)
+ n2.SetOp(ir.OBYTES2STRTMP)
+ }
+ }
+ }
+ return n
+
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ n.X = o.expr(n.X, nil)
+ n.Index = o.expr(n.Index, nil)
+ needCopy := false
+
+ if !n.Assigned {
+ // Enforce that any []byte slices we are not copying
+ // can not be changed before the map index by forcing
+ // the map index to happen immediately following the
+ // conversions. See copyExpr a few lines below.
+ needCopy = mapKeyReplaceStrConv(n.Index)
+
+ if base.Flag.Cfg.Instrumenting {
+ // Race detector needs the copy.
+ needCopy = true
+ }
+ }
+
+ // key must be addressable
+ n.Index = o.mapKeyTemp(n.X.Type(), n.Index)
+ if needCopy {
+ return o.copyExpr(n)
+ }
+ return n
+
+ // concrete type (not interface) argument might need an addressable
+ // temporary to pass to the runtime conversion routine.
+ case ir.OCONVIFACE:
+ n := n.(*ir.ConvExpr)
+ n.X = o.expr(n.X, nil)
+ if n.X.Type().IsInterface() {
+ return n
+ }
+ if _, needsaddr := convFuncName(n.X.Type(), n.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
+ // Need a temp if we need to pass the address to the conversion function.
+ // We also process static composite literal node here, making a named static global
+ // whose address we can put directly in an interface (see OCONVIFACE case in walk).
+ n.X = o.addrTemp(n.X)
+ }
+ return n
+
+ case ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER || n.X.Op() == ir.OCALLMETH) {
+ call := n.X.(*ir.CallExpr)
+ // When reordering unsafe.Pointer(f()) into a separate
+ // statement, the conversion and function call must stay
+ // together. See golang.org/issue/15329.
+ o.init(call)
+ o.call(call)
+ if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
+ return o.copyExpr(n)
+ }
+ } else {
+ n.X = o.expr(n.X, nil)
+ }
+ return n
+
+ case ir.OANDAND, ir.OOROR:
+ // ... = LHS && RHS
+ //
+ // var r bool
+ // r = LHS
+ // if r { // or !r, for OROR
+ // r = RHS
+ // }
+ // ... = r
+
+ n := n.(*ir.LogicalExpr)
+ r := o.newTemp(n.Type(), false)
+
+ // Evaluate left-hand side.
+ lhs := o.expr(n.X, nil)
+ o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, lhs)))
+
+ // Evaluate right-hand side, save generated code.
+ saveout := o.out
+ o.out = nil
+ t := o.markTemp()
+ o.edge()
+ rhs := o.expr(n.Y, nil)
+ o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, rhs)))
+ o.cleanTemp(t)
+ gen := o.out
+ o.out = saveout
+
+ // If left-hand side doesn't cause a short-circuit, issue right-hand side.
+ nif := ir.NewIfStmt(base.Pos, r, nil, nil)
+ if n.Op() == ir.OANDAND {
+ nif.Body = gen
+ } else {
+ nif.Else = gen
+ }
+ o.out = append(o.out, nif)
+ return r
+
+ case ir.OCALLFUNC,
+ ir.OCALLINTER,
+ ir.OCALLMETH,
+ ir.OCAP,
+ ir.OCOMPLEX,
+ ir.OCOPY,
+ ir.OIMAG,
+ ir.OLEN,
+ ir.OMAKECHAN,
+ ir.OMAKEMAP,
+ ir.OMAKESLICE,
+ ir.OMAKESLICECOPY,
+ ir.ONEW,
+ ir.OREAL,
+ ir.ORECOVER,
+ ir.OSTR2BYTES,
+ ir.OSTR2BYTESTMP,
+ ir.OSTR2RUNES:
+
+ if isRuneCount(n) {
+ // len([]rune(s)) is rewritten to runtime.countrunes(s) later.
+ conv := n.(*ir.UnaryExpr).X.(*ir.ConvExpr)
+ conv.X = o.expr(conv.X, nil)
+ } else {
+ o.call(n)
+ }
+
+ if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
+ return o.copyExpr(n)
+ }
+ return n
+
+ case ir.OAPPEND:
+ // Check for append(x, make([]T, y)...) .
+ n := n.(*ir.CallExpr)
+ if isAppendOfMake(n) {
+ n.Args[0] = o.expr(n.Args[0], nil) // order x
+ mk := n.Args[1].(*ir.MakeExpr)
+ mk.Len = o.expr(mk.Len, nil) // order y
+ } else {
+ o.exprList(n.Args)
+ }
+
+ if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.Args[0]) {
+ return o.copyExpr(n)
+ }
+ return n
+
+ case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR:
+ n := n.(*ir.SliceExpr)
+ n.X = o.expr(n.X, nil)
+ n.Low = o.cheapExpr(o.expr(n.Low, nil))
+ n.High = o.cheapExpr(o.expr(n.High, nil))
+ n.Max = o.cheapExpr(o.expr(n.Max, nil))
+ if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.X) {
+ return o.copyExpr(n)
+ }
+ return n
+
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ if n.Transient() && len(n.Func.ClosureVars) > 0 {
+ n.Prealloc = o.newTemp(typecheck.ClosureType(n), false)
+ }
+ return n
+
+ case ir.OCALLPART:
+ n := n.(*ir.SelectorExpr)
+ n.X = o.expr(n.X, nil)
+ if n.Transient() {
+ t := typecheck.PartialCallType(n)
+ n.Prealloc = o.newTemp(t, false)
+ }
+ return n
+
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ o.exprList(n.List)
+ if n.Transient() {
+ t := types.NewArray(n.Type().Elem(), n.Len)
+ n.Prealloc = o.newTemp(t, false)
+ }
+ return n
+
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ n := n.(*ir.TypeAssertExpr)
+ n.X = o.expr(n.X, nil)
+ if !types.IsDirectIface(n.Type()) || base.Flag.Cfg.Instrumenting {
+ return o.copyExprClear(n)
+ }
+ return n
+
+ case ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ n.X = o.expr(n.X, nil)
+ return o.copyExprClear(n)
+
+ case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ n := n.(*ir.BinaryExpr)
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+
+ t := n.X.Type()
+ switch {
+ case t.IsString():
+ // Mark string(byteSlice) arguments to reuse byteSlice backing
+ // buffer during conversion. String comparison does not
+ // memorize the strings for later use, so it is safe.
+ if n.X.Op() == ir.OBYTES2STR {
+ n.X.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
+ }
+ if n.Y.Op() == ir.OBYTES2STR {
+ n.Y.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
+ }
+
+ case t.IsStruct() || t.IsArray():
+ // for complex comparisons, we need both args to be
+ // addressable so we can pass them to the runtime.
+ n.X = o.addrTemp(n.X)
+ n.Y = o.addrTemp(n.Y)
+ }
+ return n
+
+ case ir.OMAPLIT:
+ // Order map by converting:
+ // map[int]int{
+ // a(): b(),
+ // c(): d(),
+ // e(): f(),
+ // }
+ // to
+ // m := map[int]int{}
+ // m[a()] = b()
+ // m[c()] = d()
+ // m[e()] = f()
+ // Then order the result.
+ // Without this special case, order would otherwise compute all
+ // the keys and values before storing any of them to the map.
+ // See issue 26552.
+ n := n.(*ir.CompLitExpr)
+ entries := n.List
+ statics := entries[:0]
+ var dynamics []*ir.KeyExpr
+ for _, r := range entries {
+ r := r.(*ir.KeyExpr)
+
+ if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
+ dynamics = append(dynamics, r)
+ continue
+ }
+
+ // Recursively ordering some static entries can change them to dynamic;
+ // e.g., OCONVIFACE nodes. See #31777.
+ r = o.expr(r, nil).(*ir.KeyExpr)
+ if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
+ dynamics = append(dynamics, r)
+ continue
+ }
+
+ statics = append(statics, r)
+ }
+ n.List = statics
+
+ if len(dynamics) == 0 {
+ return n
+ }
+
+ // Emit the creation of the map (with all its static entries).
+ m := o.newTemp(n.Type(), false)
+ as := ir.NewAssignStmt(base.Pos, m, n)
+ typecheck.Stmt(as)
+ o.stmt(as)
+
+ // Emit eval+insert of dynamic entries, one at a time.
+ for _, r := range dynamics {
+ as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value)
+ typecheck.Stmt(as) // Note: this converts the OINDEX to an OINDEXMAP
+ o.stmt(as)
+ }
+ return m
+ }
+
+ // No return - type-assertions above. Each case must return for itself.
+}
+
+// as2func orders OAS2FUNC nodes. It creates temporaries to ensure left-to-right assignment.
+// The caller should order the right-hand side of the assignment before calling order.as2func.
+// It rewrites,
+// a, b, a = ...
+// as
+// tmp1, tmp2, tmp3 = ...
+// a, b, a = tmp1, tmp2, tmp3
+// This is necessary to ensure left to right assignment order.
+func (o *orderState) as2func(n *ir.AssignListStmt) {
+ results := n.Rhs[0].Type()
+ as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)
+ for i, nl := range n.Lhs {
+ if !ir.IsBlank(nl) {
+ typ := results.Field(i).Type
+ tmp := o.newTemp(typ, typ.HasPointers())
+ n.Lhs[i] = tmp
+ as.Lhs = append(as.Lhs, nl)
+ as.Rhs = append(as.Rhs, tmp)
+ }
+ }
+
+ o.out = append(o.out, n)
+ o.stmt(typecheck.Stmt(as))
+}
+
+// as2ok orders OAS2XXX with ok.
+// Just like as2func, this also adds temporaries to ensure left-to-right assignment.
+func (o *orderState) as2ok(n *ir.AssignListStmt) {
+ as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)
+
+ do := func(i int, typ *types.Type) {
+ if nl := n.Lhs[i]; !ir.IsBlank(nl) {
+ var tmp ir.Node = o.newTemp(typ, typ.HasPointers())
+ n.Lhs[i] = tmp
+ as.Lhs = append(as.Lhs, nl)
+ if i == 1 {
+ // The "ok" result is an untyped boolean according to the Go
+ // spec. We need to explicitly convert it to the LHS type in
+ // case the latter is a defined boolean type (#8475).
+ tmp = typecheck.Conv(tmp, nl.Type())
+ }
+ as.Rhs = append(as.Rhs, tmp)
+ }
+ }
+
+ do(0, n.Rhs[0].Type())
+ do(1, types.Types[types.TBOOL])
+
+ o.out = append(o.out, n)
+ o.stmt(typecheck.Stmt(as))
+}
diff --git a/src/cmd/compile/internal/walk/race.go b/src/cmd/compile/internal/walk/race.go
new file mode 100644
index 0000000000000..47cd2fdc222a6
--- /dev/null
+++ b/src/cmd/compile/internal/walk/race.go
@@ -0,0 +1,51 @@
+// 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 walk
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+ "cmd/internal/sys"
+)
+
+func instrument(fn *ir.Func) {
+ if fn.Pragma&ir.Norace != 0 || (fn.Linksym() != nil && fn.Linksym().ABIWrapper()) {
+ return
+ }
+
+ if !base.Flag.Race || !base.Compiling(base.NoRacePkgs) {
+ fn.SetInstrumentBody(true)
+ }
+
+ if base.Flag.Race {
+ lno := base.Pos
+ base.Pos = src.NoXPos
+ if ssagen.Arch.LinkArch.Arch.Family != sys.AMD64 {
+ fn.Enter.Prepend(mkcallstmt("racefuncenterfp"))
+ fn.Exit.Append(mkcallstmt("racefuncexit"))
+ } else {
+
+ // nodpc is the PC of the caller as extracted by
+ // getcallerpc. We use -widthptr(FP) for x86.
+ // This only works for amd64. This will not
+ // work on arm or others that might support
+ // race in the future.
+
+ nodpc := ir.NewNameAt(src.NoXPos, typecheck.Lookup(".fp"))
+ nodpc.Class = ir.PPARAM
+ nodpc.SetUsed(true)
+ nodpc.SetType(types.Types[types.TUINTPTR])
+ nodpc.SetFrameOffset(int64(-types.PtrSize))
+ fn.Dcl = append(fn.Dcl, nodpc)
+ fn.Enter.Prepend(mkcallstmt("racefuncenter", nodpc))
+ fn.Exit.Append(mkcallstmt("racefuncexit"))
+ }
+ base.Pos = lno
+ }
+}
diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go
new file mode 100644
index 0000000000000..5ab24b2188404
--- /dev/null
+++ b/src/cmd/compile/internal/walk/range.go
@@ -0,0 +1,486 @@
+// Copyright 2009 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 walk
+
+import (
+ "unicode/utf8"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/sys"
+)
+
+func cheapComputableIndex(width int64) bool {
+ switch ssagen.Arch.LinkArch.Family {
+ // MIPS does not have R+R addressing
+ // Arm64 may lack ability to generate this code in our assembler,
+ // but the architecture supports it.
+ case sys.PPC64, sys.S390X:
+ return width == 1
+ case sys.AMD64, sys.I386, sys.ARM64, sys.ARM:
+ switch width {
+ case 1, 2, 4, 8:
+ return true
+ }
+ }
+ return false
+}
+
+// walkRange transforms various forms of ORANGE into
+// simpler forms. The result must be assigned back to n.
+// Node n may also be modified in place, and may also be
+// the returned node.
+func walkRange(nrange *ir.RangeStmt) ir.Node {
+ if isMapClear(nrange) {
+ m := nrange.X
+ lno := ir.SetPos(m)
+ n := mapClear(m)
+ base.Pos = lno
+ return n
+ }
+
+ nfor := ir.NewForStmt(nrange.Pos(), nil, nil, nil, nil)
+ nfor.SetInit(nrange.Init())
+ nfor.Label = nrange.Label
+
+ // variable name conventions:
+ // ohv1, hv1, hv2: hidden (old) val 1, 2
+ // ha, hit: hidden aggregate, iterator
+ // hn, hp: hidden len, pointer
+ // hb: hidden bool
+ // a, v1, v2: not hidden aggregate, val 1, 2
+
+ a := nrange.X
+ t := typecheck.RangeExprType(a.Type())
+ lno := ir.SetPos(a)
+
+ v1, v2 := nrange.Key, nrange.Value
+
+ if ir.IsBlank(v2) {
+ v2 = nil
+ }
+
+ if ir.IsBlank(v1) && v2 == nil {
+ v1 = nil
+ }
+
+ if v1 == nil && v2 != nil {
+ base.Fatalf("walkRange: v2 != nil while v1 == nil")
+ }
+
+ var ifGuard *ir.IfStmt
+
+ var body []ir.Node
+ var init []ir.Node
+ switch t.Kind() {
+ default:
+ base.Fatalf("walkRange")
+
+ case types.TARRAY, types.TSLICE:
+ if nn := arrayClear(nrange, v1, v2, a); nn != nil {
+ base.Pos = lno
+ return nn
+ }
+
+ // order.stmt arranged for a copy of the array/slice variable if needed.
+ ha := a
+
+ hv1 := typecheck.Temp(types.Types[types.TINT])
+ hn := typecheck.Temp(types.Types[types.TINT])
+
+ init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
+ init = append(init, ir.NewAssignStmt(base.Pos, hn, ir.NewUnaryExpr(base.Pos, ir.OLEN, ha)))
+
+ nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, hn)
+ nfor.Post = ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(1)))
+
+ // for range ha { body }
+ if v1 == nil {
+ break
+ }
+
+ // for v1 := range ha { body }
+ if v2 == nil {
+ body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)}
+ break
+ }
+
+ // for v1, v2 := range ha { body }
+ if cheapComputableIndex(t.Elem().Width) {
+ // v1, v2 = hv1, ha[hv1]
+ tmp := ir.NewIndexExpr(base.Pos, ha, hv1)
+ tmp.SetBounded(true)
+ // Use OAS2 to correctly handle assignments
+ // of the form "v1, a[v1] := range".
+ a := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+ a.Lhs = []ir.Node{v1, v2}
+ a.Rhs = []ir.Node{hv1, tmp}
+ body = []ir.Node{a}
+ break
+ }
+
+ // TODO(austin): OFORUNTIL is a strange beast, but is
+ // necessary for expressing the control flow we need
+ // while also making "break" and "continue" work. It
+ // would be nice to just lower ORANGE during SSA, but
+ // racewalk needs to see many of the operations
+ // involved in ORANGE's implementation. If racewalk
+ // moves into SSA, consider moving ORANGE into SSA and
+ // eliminating OFORUNTIL.
+
+ // TODO(austin): OFORUNTIL inhibits bounds-check
+ // elimination on the index variable (see #20711).
+ // Enhance the prove pass to understand this.
+ ifGuard = ir.NewIfStmt(base.Pos, nil, nil, nil)
+ ifGuard.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, hn)
+ nfor.SetOp(ir.OFORUNTIL)
+
+ hp := typecheck.Temp(types.NewPtr(t.Elem()))
+ tmp := ir.NewIndexExpr(base.Pos, ha, ir.NewInt(0))
+ tmp.SetBounded(true)
+ init = append(init, ir.NewAssignStmt(base.Pos, hp, typecheck.NodAddr(tmp)))
+
+ // Use OAS2 to correctly handle assignments
+ // of the form "v1, a[v1] := range".
+ a := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+ a.Lhs = []ir.Node{v1, v2}
+ a.Rhs = []ir.Node{hv1, ir.NewStarExpr(base.Pos, hp)}
+ body = append(body, a)
+
+ // Advance pointer as part of the late increment.
+ //
+ // This runs *after* the condition check, so we know
+ // advancing the pointer is safe and won't go past the
+ // end of the allocation.
+ as := ir.NewAssignStmt(base.Pos, hp, addptr(hp, t.Elem().Width))
+ nfor.Late = []ir.Node{typecheck.Stmt(as)}
+
+ case types.TMAP:
+ // order.stmt allocated the iterator for us.
+ // we only use a once, so no copy needed.
+ ha := a
+
+ hit := nrange.Prealloc
+ th := hit.Type()
+ keysym := th.Field(0).Sym // depends on layout of iterator struct. See reflect.go:MapIterType
+ elemsym := th.Field(1).Sym // ditto
+
+ fn := typecheck.LookupRuntime("mapiterinit")
+
+ fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem(), th)
+ init = append(init, mkcallstmt1(fn, reflectdata.TypePtr(t), ha, typecheck.NodAddr(hit)))
+ nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil())
+
+ fn = typecheck.LookupRuntime("mapiternext")
+ fn = typecheck.SubstArgTypes(fn, th)
+ nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit))
+
+ key := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym))
+ if v1 == nil {
+ body = nil
+ } else if v2 == nil {
+ body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, key)}
+ } else {
+ elem := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym))
+ a := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+ a.Lhs = []ir.Node{v1, v2}
+ a.Rhs = []ir.Node{key, elem}
+ body = []ir.Node{a}
+ }
+
+ case types.TCHAN:
+ // order.stmt arranged for a copy of the channel variable.
+ ha := a
+
+ hv1 := typecheck.Temp(t.Elem())
+ hv1.SetTypecheck(1)
+ if t.Elem().HasPointers() {
+ init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
+ }
+ hb := typecheck.Temp(types.Types[types.TBOOL])
+
+ nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, hb, ir.NewBool(false))
+ a := ir.NewAssignListStmt(base.Pos, ir.OAS2RECV, nil, nil)
+ a.SetTypecheck(1)
+ a.Lhs = []ir.Node{hv1, hb}
+ a.Rhs = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.ORECV, ha)}
+ nfor.Cond = ir.InitExpr([]ir.Node{a}, nfor.Cond)
+ if v1 == nil {
+ body = nil
+ } else {
+ body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)}
+ }
+ // Zero hv1. This prevents hv1 from being the sole, inaccessible
+ // reference to an otherwise GC-able value during the next channel receive.
+ // See issue 15281.
+ body = append(body, ir.NewAssignStmt(base.Pos, hv1, nil))
+
+ case types.TSTRING:
+ // Transform string range statements like "for v1, v2 = range a" into
+ //
+ // ha := a
+ // for hv1 := 0; hv1 < len(ha); {
+ // hv1t := hv1
+ // hv2 := rune(ha[hv1])
+ // if hv2 < utf8.RuneSelf {
+ // hv1++
+ // } else {
+ // hv2, hv1 = decoderune(ha, hv1)
+ // }
+ // v1, v2 = hv1t, hv2
+ // // original body
+ // }
+
+ // order.stmt arranged for a copy of the string variable.
+ ha := a
+
+ hv1 := typecheck.Temp(types.Types[types.TINT])
+ hv1t := typecheck.Temp(types.Types[types.TINT])
+ hv2 := typecheck.Temp(types.RuneType)
+
+ // hv1 := 0
+ init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
+
+ // hv1 < len(ha)
+ nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, ir.NewUnaryExpr(base.Pos, ir.OLEN, ha))
+
+ if v1 != nil {
+ // hv1t = hv1
+ body = append(body, ir.NewAssignStmt(base.Pos, hv1t, hv1))
+ }
+
+ // hv2 := rune(ha[hv1])
+ nind := ir.NewIndexExpr(base.Pos, ha, hv1)
+ nind.SetBounded(true)
+ body = append(body, ir.NewAssignStmt(base.Pos, hv2, typecheck.Conv(nind, types.RuneType)))
+
+ // if hv2 < utf8.RuneSelf
+ nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv2, ir.NewInt(utf8.RuneSelf))
+
+ // hv1++
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(1)))}
+
+ // } else {
+ eif := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+
+ // hv2, hv1 = decoderune(ha, hv1)
+ eif.Lhs = []ir.Node{hv2, hv1}
+ fn := typecheck.LookupRuntime("decoderune")
+ var fnInit ir.Nodes
+ eif.Rhs = []ir.Node{mkcall1(fn, fn.Type().Results(), &fnInit, ha, hv1)}
+ fnInit.Append(eif)
+ nif.Else = fnInit
+
+ body = append(body, nif)
+
+ if v1 != nil {
+ if v2 != nil {
+ // v1, v2 = hv1t, hv2
+ a := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+ a.Lhs = []ir.Node{v1, v2}
+ a.Rhs = []ir.Node{hv1t, hv2}
+ body = append(body, a)
+ } else {
+ // v1 = hv1t
+ body = append(body, ir.NewAssignStmt(base.Pos, v1, hv1t))
+ }
+ }
+ }
+
+ typecheck.Stmts(init)
+
+ if ifGuard != nil {
+ ifGuard.PtrInit().Append(init...)
+ ifGuard = typecheck.Stmt(ifGuard).(*ir.IfStmt)
+ } else {
+ nfor.PtrInit().Append(init...)
+ }
+
+ typecheck.Stmts(nfor.Cond.Init())
+
+ nfor.Cond = typecheck.Expr(nfor.Cond)
+ nfor.Cond = typecheck.DefaultLit(nfor.Cond, nil)
+ nfor.Post = typecheck.Stmt(nfor.Post)
+ typecheck.Stmts(body)
+ nfor.Body.Append(body...)
+ nfor.Body.Append(nrange.Body...)
+
+ var n ir.Node = nfor
+ if ifGuard != nil {
+ ifGuard.Body = []ir.Node{n}
+ n = ifGuard
+ }
+
+ n = walkStmt(n)
+
+ base.Pos = lno
+ return n
+}
+
+// isMapClear checks if n is of the form:
+//
+// for k := range m {
+// delete(m, k)
+// }
+//
+// where == for keys of map m is reflexive.
+func isMapClear(n *ir.RangeStmt) bool {
+ if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
+ return false
+ }
+
+ t := n.X.Type()
+ if n.Op() != ir.ORANGE || t.Kind() != types.TMAP || n.Key == nil || n.Value != nil {
+ return false
+ }
+
+ k := n.Key
+ // Require k to be a new variable name.
+ if !ir.DeclaredBy(k, n) {
+ return false
+ }
+
+ if len(n.Body) != 1 {
+ return false
+ }
+
+ stmt := n.Body[0] // only stmt in body
+ if stmt == nil || stmt.Op() != ir.ODELETE {
+ return false
+ }
+
+ m := n.X
+ if delete := stmt.(*ir.CallExpr); !ir.SameSafeExpr(delete.Args[0], m) || !ir.SameSafeExpr(delete.Args[1], k) {
+ return false
+ }
+
+ // Keys where equality is not reflexive can not be deleted from maps.
+ if !types.IsReflexive(t.Key()) {
+ return false
+ }
+
+ return true
+}
+
+// mapClear constructs a call to runtime.mapclear for the map m.
+func mapClear(m ir.Node) ir.Node {
+ t := m.Type()
+
+ // instantiate mapclear(typ *type, hmap map[any]any)
+ fn := typecheck.LookupRuntime("mapclear")
+ fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem())
+ n := mkcallstmt1(fn, reflectdata.TypePtr(t), m)
+ return walkStmt(typecheck.Stmt(n))
+}
+
+// Lower n into runtime·memclr if possible, for
+// fast zeroing of slices and arrays (issue 5373).
+// Look for instances of
+//
+// for i := range a {
+// a[i] = zero
+// }
+//
+// in which the evaluation of a is side-effect-free.
+//
+// Parameters are as in walkRange: "for v1, v2 = range a".
+func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node {
+ if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
+ return nil
+ }
+
+ if v1 == nil || v2 != nil {
+ return nil
+ }
+
+ if len(loop.Body) != 1 || loop.Body[0] == nil {
+ return nil
+ }
+
+ stmt1 := loop.Body[0] // only stmt in body
+ if stmt1.Op() != ir.OAS {
+ return nil
+ }
+ stmt := stmt1.(*ir.AssignStmt)
+ if stmt.X.Op() != ir.OINDEX {
+ return nil
+ }
+ lhs := stmt.X.(*ir.IndexExpr)
+
+ if !ir.SameSafeExpr(lhs.X, a) || !ir.SameSafeExpr(lhs.Index, v1) {
+ return nil
+ }
+
+ elemsize := typecheck.RangeExprType(loop.X.Type()).Elem().Width
+ if elemsize <= 0 || !ir.IsZero(stmt.Y) {
+ return nil
+ }
+
+ // Convert to
+ // if len(a) != 0 {
+ // hp = &a[0]
+ // hn = len(a)*sizeof(elem(a))
+ // memclr{NoHeap,Has}Pointers(hp, hn)
+ // i = len(a) - 1
+ // }
+ n := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ n.Body = nil
+ n.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(0))
+
+ // hp = &a[0]
+ hp := typecheck.Temp(types.Types[types.TUNSAFEPTR])
+
+ ix := ir.NewIndexExpr(base.Pos, a, ir.NewInt(0))
+ ix.SetBounded(true)
+ addr := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])
+ n.Body.Append(ir.NewAssignStmt(base.Pos, hp, addr))
+
+ // hn = len(a) * sizeof(elem(a))
+ hn := typecheck.Temp(types.Types[types.TUINTPTR])
+ mul := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(elemsize)), types.Types[types.TUINTPTR])
+ n.Body.Append(ir.NewAssignStmt(base.Pos, hn, mul))
+
+ var fn ir.Node
+ if a.Type().Elem().HasPointers() {
+ // memclrHasPointers(hp, hn)
+ ir.CurFunc.SetWBPos(stmt.Pos())
+ fn = mkcallstmt("memclrHasPointers", hp, hn)
+ } else {
+ // memclrNoHeapPointers(hp, hn)
+ fn = mkcallstmt("memclrNoHeapPointers", hp, hn)
+ }
+
+ n.Body.Append(fn)
+
+ // i = len(a) - 1
+ v1 = ir.NewAssignStmt(base.Pos, v1, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OLEN, a), ir.NewInt(1)))
+
+ n.Body.Append(v1)
+
+ n.Cond = typecheck.Expr(n.Cond)
+ n.Cond = typecheck.DefaultLit(n.Cond, nil)
+ typecheck.Stmts(n.Body)
+ return walkStmt(n)
+}
+
+// addptr returns (*T)(uintptr(p) + n).
+func addptr(p ir.Node, n int64) ir.Node {
+ t := p.Type()
+
+ p = ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, p)
+ p.SetType(types.Types[types.TUINTPTR])
+
+ p = ir.NewBinaryExpr(base.Pos, ir.OADD, p, ir.NewInt(n))
+
+ p = ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, p)
+ p.SetType(t)
+
+ return p
+}
diff --git a/src/cmd/compile/internal/walk/select.go b/src/cmd/compile/internal/walk/select.go
new file mode 100644
index 0000000000000..873be289dcc6a
--- /dev/null
+++ b/src/cmd/compile/internal/walk/select.go
@@ -0,0 +1,295 @@
+// Copyright 2009 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 walk
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+)
+
+func walkSelect(sel *ir.SelectStmt) {
+ lno := ir.SetPos(sel)
+ if sel.Walked() {
+ base.Fatalf("double walkSelect")
+ }
+ sel.SetWalked(true)
+
+ init := ir.TakeInit(sel)
+
+ init = append(init, walkSelectCases(sel.Cases)...)
+ sel.Cases = nil
+
+ sel.Compiled = init
+ walkStmtList(sel.Compiled)
+
+ base.Pos = lno
+}
+
+func walkSelectCases(cases []*ir.CommClause) []ir.Node {
+ ncas := len(cases)
+ sellineno := base.Pos
+
+ // optimization: zero-case select
+ if ncas == 0 {
+ return []ir.Node{mkcallstmt("block")}
+ }
+
+ // optimization: one-case select: single op.
+ if ncas == 1 {
+ cas := cases[0]
+ ir.SetPos(cas)
+ l := cas.Init()
+ if cas.Comm != nil { // not default:
+ n := cas.Comm
+ l = append(l, ir.TakeInit(n)...)
+ switch n.Op() {
+ default:
+ base.Fatalf("select %v", n.Op())
+
+ case ir.OSEND:
+ // already ok
+
+ case ir.OSELRECV2:
+ r := n.(*ir.AssignListStmt)
+ if ir.IsBlank(r.Lhs[0]) && ir.IsBlank(r.Lhs[1]) {
+ n = r.Rhs[0]
+ break
+ }
+ r.SetOp(ir.OAS2RECV)
+ }
+
+ l = append(l, n)
+ }
+
+ l = append(l, cas.Body...)
+ l = append(l, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
+ return l
+ }
+
+ // convert case value arguments to addresses.
+ // this rewrite is used by both the general code and the next optimization.
+ var dflt *ir.CommClause
+ for _, cas := range cases {
+ ir.SetPos(cas)
+ n := cas.Comm
+ if n == nil {
+ dflt = cas
+ continue
+ }
+ switch n.Op() {
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ n.Value = typecheck.NodAddr(n.Value)
+ n.Value = typecheck.Expr(n.Value)
+
+ case ir.OSELRECV2:
+ n := n.(*ir.AssignListStmt)
+ if !ir.IsBlank(n.Lhs[0]) {
+ n.Lhs[0] = typecheck.NodAddr(n.Lhs[0])
+ n.Lhs[0] = typecheck.Expr(n.Lhs[0])
+ }
+ }
+ }
+
+ // optimization: two-case select but one is default: single non-blocking op.
+ if ncas == 2 && dflt != nil {
+ cas := cases[0]
+ if cas == dflt {
+ cas = cases[1]
+ }
+
+ n := cas.Comm
+ ir.SetPos(n)
+ r := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ *r.PtrInit() = cas.Init()
+ var call ir.Node
+ switch n.Op() {
+ default:
+ base.Fatalf("select %v", n.Op())
+
+ case ir.OSEND:
+ // if selectnbsend(c, v) { body } else { default body }
+ n := n.(*ir.SendStmt)
+ ch := n.Chan
+ call = mkcall1(chanfn("selectnbsend", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), ch, n.Value)
+
+ case ir.OSELRECV2:
+ n := n.(*ir.AssignListStmt)
+ recv := n.Rhs[0].(*ir.UnaryExpr)
+ ch := recv.X
+ elem := n.Lhs[0]
+ if ir.IsBlank(elem) {
+ elem = typecheck.NodNil()
+ }
+ if ir.IsBlank(n.Lhs[1]) {
+ // if selectnbrecv(&v, c) { body } else { default body }
+ call = mkcall1(chanfn("selectnbrecv", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, ch)
+ } else {
+ // TODO(cuonglm): make this use selectnbrecv()
+ // if selectnbrecv2(&v, &received, c) { body } else { default body }
+ receivedp := typecheck.Expr(typecheck.NodAddr(n.Lhs[1]))
+ call = mkcall1(chanfn("selectnbrecv2", 2, ch.Type()), types.Types[types.TBOOL], r.PtrInit(), elem, receivedp, ch)
+ }
+ }
+
+ r.Cond = typecheck.Expr(call)
+ r.Body = cas.Body
+ r.Else = append(dflt.Init(), dflt.Body...)
+ return []ir.Node{r, ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)}
+ }
+
+ if dflt != nil {
+ ncas--
+ }
+ casorder := make([]*ir.CommClause, ncas)
+ nsends, nrecvs := 0, 0
+
+ var init []ir.Node
+
+ // generate sel-struct
+ base.Pos = sellineno
+ selv := typecheck.Temp(types.NewArray(scasetype(), int64(ncas)))
+ init = append(init, typecheck.Stmt(ir.NewAssignStmt(base.Pos, selv, nil)))
+
+ // No initialization for order; runtime.selectgo is responsible for that.
+ order := typecheck.Temp(types.NewArray(types.Types[types.TUINT16], 2*int64(ncas)))
+
+ var pc0, pcs ir.Node
+ if base.Flag.Race {
+ pcs = typecheck.Temp(types.NewArray(types.Types[types.TUINTPTR], int64(ncas)))
+ pc0 = typecheck.Expr(typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(0))))
+ } else {
+ pc0 = typecheck.NodNil()
+ }
+
+ // register cases
+ for _, cas := range cases {
+ ir.SetPos(cas)
+
+ init = append(init, ir.TakeInit(cas)...)
+
+ n := cas.Comm
+ if n == nil { // default:
+ continue
+ }
+
+ var i int
+ var c, elem ir.Node
+ switch n.Op() {
+ default:
+ base.Fatalf("select %v", n.Op())
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ i = nsends
+ nsends++
+ c = n.Chan
+ elem = n.Value
+ case ir.OSELRECV2:
+ n := n.(*ir.AssignListStmt)
+ nrecvs++
+ i = ncas - nrecvs
+ recv := n.Rhs[0].(*ir.UnaryExpr)
+ c = recv.X
+ elem = n.Lhs[0]
+ }
+
+ casorder[i] = cas
+
+ setField := func(f string, val ir.Node) {
+ r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, ir.NewIndexExpr(base.Pos, selv, ir.NewInt(int64(i))), typecheck.Lookup(f)), val)
+ init = append(init, typecheck.Stmt(r))
+ }
+
+ c = typecheck.ConvNop(c, types.Types[types.TUNSAFEPTR])
+ setField("c", c)
+ if !ir.IsBlank(elem) {
+ elem = typecheck.ConvNop(elem, types.Types[types.TUNSAFEPTR])
+ setField("elem", elem)
+ }
+
+ // TODO(mdempsky): There should be a cleaner way to
+ // handle this.
+ if base.Flag.Race {
+ r := mkcallstmt("selectsetpc", typecheck.NodAddr(ir.NewIndexExpr(base.Pos, pcs, ir.NewInt(int64(i)))))
+ init = append(init, r)
+ }
+ }
+ if nsends+nrecvs != ncas {
+ base.Fatalf("walkSelectCases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
+ }
+
+ // run the select
+ base.Pos = sellineno
+ chosen := typecheck.Temp(types.Types[types.TINT])
+ recvOK := typecheck.Temp(types.Types[types.TBOOL])
+ r := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
+ r.Lhs = []ir.Node{chosen, recvOK}
+ fn := typecheck.LookupRuntime("selectgo")
+ var fnInit ir.Nodes
+ r.Rhs = []ir.Node{mkcall1(fn, fn.Type().Results(), &fnInit, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, ir.NewInt(int64(nsends)), ir.NewInt(int64(nrecvs)), ir.NewBool(dflt == nil))}
+ init = append(init, fnInit...)
+ init = append(init, typecheck.Stmt(r))
+
+ // selv and order are no longer alive after selectgo.
+ init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, selv))
+ init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, order))
+ if base.Flag.Race {
+ init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, pcs))
+ }
+
+ // dispatch cases
+ dispatch := func(cond ir.Node, cas *ir.CommClause) {
+ cond = typecheck.Expr(cond)
+ cond = typecheck.DefaultLit(cond, nil)
+
+ r := ir.NewIfStmt(base.Pos, cond, nil, nil)
+
+ if n := cas.Comm; n != nil && n.Op() == ir.OSELRECV2 {
+ n := n.(*ir.AssignListStmt)
+ if !ir.IsBlank(n.Lhs[1]) {
+ x := ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK)
+ r.Body.Append(typecheck.Stmt(x))
+ }
+ }
+
+ r.Body.Append(cas.Body.Take()...)
+ r.Body.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil))
+ init = append(init, r)
+ }
+
+ if dflt != nil {
+ ir.SetPos(dflt)
+ dispatch(ir.NewBinaryExpr(base.Pos, ir.OLT, chosen, ir.NewInt(0)), dflt)
+ }
+ for i, cas := range casorder {
+ ir.SetPos(cas)
+ dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(int64(i))), cas)
+ }
+
+ return init
+}
+
+// bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
+func bytePtrToIndex(n ir.Node, i int64) ir.Node {
+ s := typecheck.NodAddr(ir.NewIndexExpr(base.Pos, n, ir.NewInt(i)))
+ t := types.NewPtr(types.Types[types.TUINT8])
+ return typecheck.ConvNop(s, t)
+}
+
+var scase *types.Type
+
+// Keep in sync with src/runtime/select.go.
+func scasetype() *types.Type {
+ if scase == nil {
+ scase = types.NewStruct(types.NoPkg, []*types.Field{
+ types.NewField(base.Pos, typecheck.Lookup("c"), types.Types[types.TUNSAFEPTR]),
+ types.NewField(base.Pos, typecheck.Lookup("elem"), types.Types[types.TUNSAFEPTR]),
+ })
+ scase.SetNoalg(true)
+ }
+ return scase
+}
diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go
new file mode 100644
index 0000000000000..46a621c2ba74d
--- /dev/null
+++ b/src/cmd/compile/internal/walk/stmt.go
@@ -0,0 +1,296 @@
+// Copyright 2009 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 walk
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+)
+
+// The result of walkStmt MUST be assigned back to n, e.g.
+// n.Left = walkStmt(n.Left)
+func walkStmt(n ir.Node) ir.Node {
+ if n == nil {
+ return n
+ }
+
+ ir.SetPos(n)
+
+ walkStmtList(n.Init())
+
+ switch n.Op() {
+ default:
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ base.Errorf("%v is not a top level statement", n.Sym())
+ } else {
+ base.Errorf("%v is not a top level statement", n.Op())
+ }
+ ir.Dump("nottop", n)
+ return n
+
+ case ir.OAS,
+ ir.OASOP,
+ ir.OAS2,
+ ir.OAS2DOTTYPE,
+ ir.OAS2RECV,
+ ir.OAS2FUNC,
+ ir.OAS2MAPR,
+ ir.OCLOSE,
+ ir.OCOPY,
+ ir.OCALLMETH,
+ ir.OCALLINTER,
+ ir.OCALL,
+ ir.OCALLFUNC,
+ ir.ODELETE,
+ ir.OSEND,
+ ir.OPRINT,
+ ir.OPRINTN,
+ ir.OPANIC,
+ ir.ORECOVER,
+ ir.OGETG:
+ if n.Typecheck() == 0 {
+ base.Fatalf("missing typecheck: %+v", n)
+ }
+ init := ir.TakeInit(n)
+ n = walkExpr(n, &init)
+ if n.Op() == ir.ONAME {
+ // copy rewrote to a statement list and a temp for the length.
+ // Throw away the temp to avoid plain values as statements.
+ n = ir.NewBlockStmt(n.Pos(), init)
+ init = nil
+ }
+ if len(init) > 0 {
+ switch n.Op() {
+ case ir.OAS, ir.OAS2, ir.OBLOCK:
+ n.(ir.InitNode).PtrInit().Prepend(init...)
+
+ default:
+ init.Append(n)
+ n = ir.NewBlockStmt(n.Pos(), init)
+ }
+ }
+ return n
+
+ // special case for a receive where we throw away
+ // the value received.
+ case ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ return walkRecv(n)
+
+ case ir.OBREAK,
+ ir.OCONTINUE,
+ ir.OFALL,
+ ir.OGOTO,
+ ir.OLABEL,
+ ir.ODCL,
+ ir.ODCLCONST,
+ ir.ODCLTYPE,
+ ir.OCHECKNIL,
+ ir.OVARDEF,
+ ir.OVARKILL,
+ ir.OVARLIVE:
+ return n
+
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
+ walkStmtList(n.List)
+ return n
+
+ case ir.OCASE:
+ base.Errorf("case statement out of place")
+ panic("unreachable")
+
+ case ir.ODEFER:
+ n := n.(*ir.GoDeferStmt)
+ ir.CurFunc.SetHasDefer(true)
+ ir.CurFunc.NumDefers++
+ if ir.CurFunc.NumDefers > maxOpenDefers {
+ // Don't allow open-coded defers if there are more than
+ // 8 defers in the function, since we use a single
+ // byte to record active defers.
+ ir.CurFunc.SetOpenCodedDeferDisallowed(true)
+ }
+ if n.Esc() != ir.EscNever {
+ // If n.Esc is not EscNever, then this defer occurs in a loop,
+ // so open-coded defers cannot be used in this function.
+ ir.CurFunc.SetOpenCodedDeferDisallowed(true)
+ }
+ fallthrough
+ case ir.OGO:
+ n := n.(*ir.GoDeferStmt)
+ return walkGoDefer(n)
+
+ case ir.OFOR, ir.OFORUNTIL:
+ n := n.(*ir.ForStmt)
+ return walkFor(n)
+
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ return walkIf(n)
+
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ return walkReturn(n)
+
+ case ir.OTAILCALL:
+ n := n.(*ir.TailCallStmt)
+ return n
+
+ case ir.OINLMARK:
+ n := n.(*ir.InlineMarkStmt)
+ return n
+
+ case ir.OSELECT:
+ n := n.(*ir.SelectStmt)
+ walkSelect(n)
+ return n
+
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+ walkSwitch(n)
+ return n
+
+ case ir.ORANGE:
+ n := n.(*ir.RangeStmt)
+ return walkRange(n)
+ }
+
+ // No return! Each case must return (or panic),
+ // to avoid confusion about what gets returned
+ // in the presence of type assertions.
+}
+
+func walkStmtList(s []ir.Node) {
+ for i := range s {
+ s[i] = walkStmt(s[i])
+ }
+}
+
+// walkFor walks an OFOR or OFORUNTIL node.
+func walkFor(n *ir.ForStmt) ir.Node {
+ if n.Cond != nil {
+ init := ir.TakeInit(n.Cond)
+ walkStmtList(init)
+ n.Cond = walkExpr(n.Cond, &init)
+ n.Cond = ir.InitExpr(init, n.Cond)
+ }
+
+ n.Post = walkStmt(n.Post)
+ if n.Op() == ir.OFORUNTIL {
+ walkStmtList(n.Late)
+ }
+ walkStmtList(n.Body)
+ return n
+}
+
+// walkGoDefer walks an OGO or ODEFER node.
+func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
+ var init ir.Nodes
+ switch call := n.Call; call.Op() {
+ case ir.OPRINT, ir.OPRINTN:
+ call := call.(*ir.CallExpr)
+ n.Call = wrapCall(call, &init)
+
+ case ir.ODELETE:
+ call := call.(*ir.CallExpr)
+ if mapfast(call.Args[0].Type()) == mapslow {
+ n.Call = wrapCall(call, &init)
+ } else {
+ n.Call = walkExpr(call, &init)
+ }
+
+ case ir.OCOPY:
+ call := call.(*ir.BinaryExpr)
+ n.Call = walkCopy(call, &init, true)
+
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
+ call := call.(*ir.CallExpr)
+ if len(call.KeepAlive) > 0 {
+ n.Call = wrapCall(call, &init)
+ } else {
+ n.Call = walkExpr(call, &init)
+ }
+
+ default:
+ n.Call = walkExpr(call, &init)
+ }
+ if len(init) > 0 {
+ init.Append(n)
+ return ir.NewBlockStmt(n.Pos(), init)
+ }
+ return n
+}
+
+// walkIf walks an OIF node.
+func walkIf(n *ir.IfStmt) ir.Node {
+ n.Cond = walkExpr(n.Cond, n.PtrInit())
+ walkStmtList(n.Body)
+ walkStmtList(n.Else)
+ return n
+}
+
+// The result of wrapCall MUST be assigned back to n, e.g.
+// n.Left = wrapCall(n.Left, init)
+func wrapCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
+ if len(n.Init()) != 0 {
+ walkStmtList(n.Init())
+ init.Append(ir.TakeInit(n)...)
+ }
+
+ isBuiltinCall := n.Op() != ir.OCALLFUNC && n.Op() != ir.OCALLMETH && n.Op() != ir.OCALLINTER
+
+ // Turn f(a, b, []T{c, d, e}...) back into f(a, b, c, d, e).
+ if !isBuiltinCall && n.IsDDD {
+ last := len(n.Args) - 1
+ if va := n.Args[last]; va.Op() == ir.OSLICELIT {
+ va := va.(*ir.CompLitExpr)
+ n.Args = append(n.Args[:last], va.List...)
+ n.IsDDD = false
+ }
+ }
+
+ // origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
+ origArgs := make([]ir.Node, len(n.Args))
+ var funcArgs []*ir.Field
+ for i, arg := range n.Args {
+ s := typecheck.LookupNum("a", i)
+ if !isBuiltinCall && arg.Op() == ir.OCONVNOP && arg.Type().IsUintptr() && arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
+ origArgs[i] = arg
+ arg = arg.(*ir.ConvExpr).X
+ n.Args[i] = arg
+ }
+ funcArgs = append(funcArgs, ir.NewField(base.Pos, s, nil, arg.Type()))
+ }
+ t := ir.NewFuncType(base.Pos, nil, funcArgs, nil)
+
+ wrapCall_prgen++
+ sym := typecheck.LookupNum("wrap·", wrapCall_prgen)
+ fn := typecheck.DeclFunc(sym, t)
+
+ args := ir.ParamNames(t.Type())
+ for i, origArg := range origArgs {
+ if origArg == nil {
+ continue
+ }
+ args[i] = ir.NewConvExpr(base.Pos, origArg.Op(), origArg.Type(), args[i])
+ }
+ call := ir.NewCallExpr(base.Pos, n.Op(), n.X, args)
+ if !isBuiltinCall {
+ call.SetOp(ir.OCALL)
+ call.IsDDD = n.IsDDD
+ }
+ fn.Body = []ir.Node{call}
+
+ typecheck.FinishFuncBody()
+
+ typecheck.Func(fn)
+ typecheck.Stmts(fn.Body)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+ call = ir.NewCallExpr(base.Pos, ir.OCALL, fn.Nname, n.Args)
+ return walkExpr(typecheck.Stmt(call), init)
+}
diff --git a/src/cmd/compile/internal/walk/switch.go b/src/cmd/compile/internal/walk/switch.go
new file mode 100644
index 0000000000000..162de018f637e
--- /dev/null
+++ b/src/cmd/compile/internal/walk/switch.go
@@ -0,0 +1,568 @@
+// Copyright 2009 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 walk
+
+import (
+ "go/constant"
+ "go/token"
+ "sort"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// walkSwitch walks a switch statement.
+func walkSwitch(sw *ir.SwitchStmt) {
+ // Guard against double walk, see #25776.
+ if sw.Walked() {
+ return // Was fatal, but eliminating every possible source of double-walking is hard
+ }
+ sw.SetWalked(true)
+
+ if sw.Tag != nil && sw.Tag.Op() == ir.OTYPESW {
+ walkSwitchType(sw)
+ } else {
+ walkSwitchExpr(sw)
+ }
+}
+
+// walkSwitchExpr generates an AST implementing sw. sw is an
+// expression switch.
+func walkSwitchExpr(sw *ir.SwitchStmt) {
+ lno := ir.SetPos(sw)
+
+ cond := sw.Tag
+ sw.Tag = nil
+
+ // convert switch {...} to switch true {...}
+ if cond == nil {
+ cond = ir.NewBool(true)
+ cond = typecheck.Expr(cond)
+ cond = typecheck.DefaultLit(cond, nil)
+ }
+
+ // Given "switch string(byteslice)",
+ // with all cases being side-effect free,
+ // use a zero-cost alias of the byte slice.
+ // Do this before calling walkExpr on cond,
+ // because walkExpr will lower the string
+ // conversion into a runtime call.
+ // See issue 24937 for more discussion.
+ if cond.Op() == ir.OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
+ cond := cond.(*ir.ConvExpr)
+ cond.SetOp(ir.OBYTES2STRTMP)
+ }
+
+ cond = walkExpr(cond, sw.PtrInit())
+ if cond.Op() != ir.OLITERAL && cond.Op() != ir.ONIL {
+ cond = copyExpr(cond, cond.Type(), &sw.Compiled)
+ }
+
+ base.Pos = lno
+
+ s := exprSwitch{
+ exprname: cond,
+ }
+
+ var defaultGoto ir.Node
+ var body ir.Nodes
+ for _, ncase := range sw.Cases {
+ label := typecheck.AutoLabel(".s")
+ jmp := ir.NewBranchStmt(ncase.Pos(), ir.OGOTO, label)
+
+ // Process case dispatch.
+ if len(ncase.List) == 0 {
+ if defaultGoto != nil {
+ base.Fatalf("duplicate default case not detected during typechecking")
+ }
+ defaultGoto = jmp
+ }
+
+ for _, n1 := range ncase.List {
+ s.Add(ncase.Pos(), n1, jmp)
+ }
+
+ // Process body.
+ body.Append(ir.NewLabelStmt(ncase.Pos(), label))
+ body.Append(ncase.Body...)
+ if fall, pos := endsInFallthrough(ncase.Body); !fall {
+ br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
+ br.SetPos(pos)
+ body.Append(br)
+ }
+ }
+ sw.Cases = nil
+
+ if defaultGoto == nil {
+ br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
+ br.SetPos(br.Pos().WithNotStmt())
+ defaultGoto = br
+ }
+
+ s.Emit(&sw.Compiled)
+ sw.Compiled.Append(defaultGoto)
+ sw.Compiled.Append(body.Take()...)
+ walkStmtList(sw.Compiled)
+}
+
+// An exprSwitch walks an expression switch.
+type exprSwitch struct {
+ exprname ir.Node // value being switched on
+
+ done ir.Nodes
+ clauses []exprClause
+}
+
+type exprClause struct {
+ pos src.XPos
+ lo, hi ir.Node
+ jmp ir.Node
+}
+
+func (s *exprSwitch) Add(pos src.XPos, expr, jmp ir.Node) {
+ c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
+ if types.IsOrdered[s.exprname.Type().Kind()] && expr.Op() == ir.OLITERAL {
+ s.clauses = append(s.clauses, c)
+ return
+ }
+
+ s.flush()
+ s.clauses = append(s.clauses, c)
+ s.flush()
+}
+
+func (s *exprSwitch) Emit(out *ir.Nodes) {
+ s.flush()
+ out.Append(s.done.Take()...)
+}
+
+func (s *exprSwitch) flush() {
+ cc := s.clauses
+ s.clauses = nil
+ if len(cc) == 0 {
+ return
+ }
+
+ // Caution: If len(cc) == 1, then cc[0] might not an OLITERAL.
+ // The code below is structured to implicitly handle this case
+ // (e.g., sort.Slice doesn't need to invoke the less function
+ // when there's only a single slice element).
+
+ if s.exprname.Type().IsString() && len(cc) >= 2 {
+ // Sort strings by length and then by value. It is
+ // much cheaper to compare lengths than values, and
+ // all we need here is consistency. We respect this
+ // sorting below.
+ sort.Slice(cc, func(i, j int) bool {
+ si := ir.StringVal(cc[i].lo)
+ sj := ir.StringVal(cc[j].lo)
+ if len(si) != len(sj) {
+ return len(si) < len(sj)
+ }
+ return si < sj
+ })
+
+ // runLen returns the string length associated with a
+ // particular run of exprClauses.
+ runLen := func(run []exprClause) int64 { return int64(len(ir.StringVal(run[0].lo))) }
+
+ // Collapse runs of consecutive strings with the same length.
+ var runs [][]exprClause
+ start := 0
+ for i := 1; i < len(cc); i++ {
+ if runLen(cc[start:]) != runLen(cc[i:]) {
+ runs = append(runs, cc[start:i])
+ start = i
+ }
+ }
+ runs = append(runs, cc[start:])
+
+ // Perform two-level binary search.
+ binarySearch(len(runs), &s.done,
+ func(i int) ir.Node {
+ return ir.NewBinaryExpr(base.Pos, ir.OLE, ir.NewUnaryExpr(base.Pos, ir.OLEN, s.exprname), ir.NewInt(runLen(runs[i-1])))
+ },
+ func(i int, nif *ir.IfStmt) {
+ run := runs[i]
+ nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, ir.NewUnaryExpr(base.Pos, ir.OLEN, s.exprname), ir.NewInt(runLen(run)))
+ s.search(run, &nif.Body)
+ },
+ )
+ return
+ }
+
+ sort.Slice(cc, func(i, j int) bool {
+ return constant.Compare(cc[i].lo.Val(), token.LSS, cc[j].lo.Val())
+ })
+
+ // Merge consecutive integer cases.
+ if s.exprname.Type().IsInteger() {
+ consecutive := func(last, next constant.Value) bool {
+ delta := constant.BinaryOp(next, token.SUB, last)
+ return constant.Compare(delta, token.EQL, constant.MakeInt64(1))
+ }
+
+ merged := cc[:1]
+ for _, c := range cc[1:] {
+ last := &merged[len(merged)-1]
+ if last.jmp == c.jmp && consecutive(last.hi.Val(), c.lo.Val()) {
+ last.hi = c.lo
+ } else {
+ merged = append(merged, c)
+ }
+ }
+ cc = merged
+ }
+
+ s.search(cc, &s.done)
+}
+
+func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) {
+ binarySearch(len(cc), out,
+ func(i int) ir.Node {
+ return ir.NewBinaryExpr(base.Pos, ir.OLE, s.exprname, cc[i-1].hi)
+ },
+ func(i int, nif *ir.IfStmt) {
+ c := &cc[i]
+ nif.Cond = c.test(s.exprname)
+ nif.Body = []ir.Node{c.jmp}
+ },
+ )
+}
+
+func (c *exprClause) test(exprname ir.Node) ir.Node {
+ // Integer range.
+ if c.hi != c.lo {
+ low := ir.NewBinaryExpr(c.pos, ir.OGE, exprname, c.lo)
+ high := ir.NewBinaryExpr(c.pos, ir.OLE, exprname, c.hi)
+ return ir.NewLogicalExpr(c.pos, ir.OANDAND, low, high)
+ }
+
+ // Optimize "switch true { ...}" and "switch false { ... }".
+ if ir.IsConst(exprname, constant.Bool) && !c.lo.Type().IsInterface() {
+ if ir.BoolVal(exprname) {
+ return c.lo
+ } else {
+ return ir.NewUnaryExpr(c.pos, ir.ONOT, c.lo)
+ }
+ }
+
+ return ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo)
+}
+
+func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool {
+ // In theory, we could be more aggressive, allowing any
+ // side-effect-free expressions in cases, but it's a bit
+ // tricky because some of that information is unavailable due
+ // to the introduction of temporaries during order.
+ // Restricting to constants is simple and probably powerful
+ // enough.
+
+ for _, ncase := range sw.Cases {
+ for _, v := range ncase.List {
+ if v.Op() != ir.OLITERAL {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// endsInFallthrough reports whether stmts ends with a "fallthrough" statement.
+func endsInFallthrough(stmts []ir.Node) (bool, src.XPos) {
+ // Search backwards for the index of the fallthrough
+ // statement. Do not assume it'll be in the last
+ // position, since in some cases (e.g. when the statement
+ // list contains autotmp_ variables), one or more OVARKILL
+ // nodes will be at the end of the list.
+
+ i := len(stmts) - 1
+ for i >= 0 && stmts[i].Op() == ir.OVARKILL {
+ i--
+ }
+ if i < 0 {
+ return false, src.NoXPos
+ }
+ return stmts[i].Op() == ir.OFALL, stmts[i].Pos()
+}
+
+// walkSwitchType generates an AST that implements sw, where sw is a
+// type switch.
+func walkSwitchType(sw *ir.SwitchStmt) {
+ var s typeSwitch
+ s.facename = sw.Tag.(*ir.TypeSwitchGuard).X
+ sw.Tag = nil
+
+ s.facename = walkExpr(s.facename, sw.PtrInit())
+ s.facename = copyExpr(s.facename, s.facename.Type(), &sw.Compiled)
+ s.okname = typecheck.Temp(types.Types[types.TBOOL])
+
+ // Get interface descriptor word.
+ // For empty interfaces this will be the type.
+ // For non-empty interfaces this will be the itab.
+ itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s.facename)
+
+ // For empty interfaces, do:
+ // if e._type == nil {
+ // do nil case if it exists, otherwise default
+ // }
+ // h := e._type.hash
+ // Use a similar strategy for non-empty interfaces.
+ ifNil := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ ifNil.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, itab, typecheck.NodNil())
+ base.Pos = base.Pos.WithNotStmt() // disable statement marks after the first check.
+ ifNil.Cond = typecheck.Expr(ifNil.Cond)
+ ifNil.Cond = typecheck.DefaultLit(ifNil.Cond, nil)
+ // ifNil.Nbody assigned at end.
+ sw.Compiled.Append(ifNil)
+
+ // Load hash from type or itab.
+ dotHash := typeHashFieldOf(base.Pos, itab)
+ s.hashname = copyExpr(dotHash, dotHash.Type(), &sw.Compiled)
+
+ br := ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)
+ var defaultGoto, nilGoto ir.Node
+ var body ir.Nodes
+ for _, ncase := range sw.Cases {
+ caseVar := ncase.Var
+
+ // For single-type cases with an interface type,
+ // we initialize the case variable as part of the type assertion.
+ // In other cases, we initialize it in the body.
+ var singleType *types.Type
+ if len(ncase.List) == 1 && ncase.List[0].Op() == ir.OTYPE {
+ singleType = ncase.List[0].Type()
+ }
+ caseVarInitialized := false
+
+ label := typecheck.AutoLabel(".s")
+ jmp := ir.NewBranchStmt(ncase.Pos(), ir.OGOTO, label)
+
+ if len(ncase.List) == 0 { // default:
+ if defaultGoto != nil {
+ base.Fatalf("duplicate default case not detected during typechecking")
+ }
+ defaultGoto = jmp
+ }
+
+ for _, n1 := range ncase.List {
+ if ir.IsNil(n1) { // case nil:
+ if nilGoto != nil {
+ base.Fatalf("duplicate nil case not detected during typechecking")
+ }
+ nilGoto = jmp
+ continue
+ }
+
+ if singleType != nil && singleType.IsInterface() {
+ s.Add(ncase.Pos(), n1.Type(), caseVar, jmp)
+ caseVarInitialized = true
+ } else {
+ s.Add(ncase.Pos(), n1.Type(), nil, jmp)
+ }
+ }
+
+ body.Append(ir.NewLabelStmt(ncase.Pos(), label))
+ if caseVar != nil && !caseVarInitialized {
+ val := s.facename
+ if singleType != nil {
+ // We have a single concrete type. Extract the data.
+ if singleType.IsInterface() {
+ base.Fatalf("singleType interface should have been handled in Add")
+ }
+ val = ifaceData(ncase.Pos(), s.facename, singleType)
+ }
+ l := []ir.Node{
+ ir.NewDecl(ncase.Pos(), ir.ODCL, caseVar),
+ ir.NewAssignStmt(ncase.Pos(), caseVar, val),
+ }
+ typecheck.Stmts(l)
+ body.Append(l...)
+ }
+ body.Append(ncase.Body...)
+ body.Append(br)
+ }
+ sw.Cases = nil
+
+ if defaultGoto == nil {
+ defaultGoto = br
+ }
+ if nilGoto == nil {
+ nilGoto = defaultGoto
+ }
+ ifNil.Body = []ir.Node{nilGoto}
+
+ s.Emit(&sw.Compiled)
+ sw.Compiled.Append(defaultGoto)
+ sw.Compiled.Append(body.Take()...)
+
+ walkStmtList(sw.Compiled)
+}
+
+// typeHashFieldOf returns an expression to select the type hash field
+// from an interface's descriptor word (whether a *runtime._type or
+// *runtime.itab pointer).
+func typeHashFieldOf(pos src.XPos, itab *ir.UnaryExpr) *ir.SelectorExpr {
+ if itab.Op() != ir.OITAB {
+ base.Fatalf("expected OITAB, got %v", itab.Op())
+ }
+ var hashField *types.Field
+ if itab.X.Type().IsEmptyInterface() {
+ // runtime._type's hash field
+ if rtypeHashField == nil {
+ rtypeHashField = runtimeField("hash", int64(2*types.PtrSize), types.Types[types.TUINT32])
+ }
+ hashField = rtypeHashField
+ } else {
+ // runtime.itab's hash field
+ if itabHashField == nil {
+ itabHashField = runtimeField("hash", int64(2*types.PtrSize), types.Types[types.TUINT32])
+ }
+ hashField = itabHashField
+ }
+ return boundedDotPtr(pos, itab, hashField)
+}
+
+var rtypeHashField, itabHashField *types.Field
+
+// A typeSwitch walks a type switch.
+type typeSwitch struct {
+ // Temporary variables (i.e., ONAMEs) used by type switch dispatch logic:
+ facename ir.Node // value being type-switched on
+ hashname ir.Node // type hash of the value being type-switched on
+ okname ir.Node // boolean used for comma-ok type assertions
+
+ done ir.Nodes
+ clauses []typeClause
+}
+
+type typeClause struct {
+ hash uint32
+ body ir.Nodes
+}
+
+func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir.Node) {
+ var body ir.Nodes
+ if caseVar != nil {
+ l := []ir.Node{
+ ir.NewDecl(pos, ir.ODCL, caseVar),
+ ir.NewAssignStmt(pos, caseVar, nil),
+ }
+ typecheck.Stmts(l)
+ body.Append(l...)
+ } else {
+ caseVar = ir.BlankNode.(*ir.Name)
+ }
+
+ // cv, ok = iface.(type)
+ as := ir.NewAssignListStmt(pos, ir.OAS2, nil, nil)
+ as.Lhs = []ir.Node{caseVar, s.okname} // cv, ok =
+ dot := ir.NewTypeAssertExpr(pos, s.facename, nil)
+ dot.SetType(typ) // iface.(type)
+ as.Rhs = []ir.Node{dot}
+ appendWalkStmt(&body, as)
+
+ // if ok { goto label }
+ nif := ir.NewIfStmt(pos, nil, nil, nil)
+ nif.Cond = s.okname
+ nif.Body = []ir.Node{jmp}
+ body.Append(nif)
+
+ if !typ.IsInterface() {
+ s.clauses = append(s.clauses, typeClause{
+ hash: types.TypeHash(typ),
+ body: body,
+ })
+ return
+ }
+
+ s.flush()
+ s.done.Append(body.Take()...)
+}
+
+func (s *typeSwitch) Emit(out *ir.Nodes) {
+ s.flush()
+ out.Append(s.done.Take()...)
+}
+
+func (s *typeSwitch) flush() {
+ cc := s.clauses
+ s.clauses = nil
+ if len(cc) == 0 {
+ return
+ }
+
+ sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
+
+ // Combine adjacent cases with the same hash.
+ merged := cc[:1]
+ for _, c := range cc[1:] {
+ last := &merged[len(merged)-1]
+ if last.hash == c.hash {
+ last.body.Append(c.body.Take()...)
+ } else {
+ merged = append(merged, c)
+ }
+ }
+ cc = merged
+
+ binarySearch(len(cc), &s.done,
+ func(i int) ir.Node {
+ return ir.NewBinaryExpr(base.Pos, ir.OLE, s.hashname, ir.NewInt(int64(cc[i-1].hash)))
+ },
+ func(i int, nif *ir.IfStmt) {
+ // TODO(mdempsky): Omit hash equality check if
+ // there's only one type.
+ c := cc[i]
+ nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, s.hashname, ir.NewInt(int64(c.hash)))
+ nif.Body.Append(c.body.Take()...)
+ },
+ )
+}
+
+// binarySearch constructs a binary search tree for handling n cases,
+// and appends it to out. It's used for efficiently implementing
+// switch statements.
+//
+// less(i) should return a boolean expression. If it evaluates true,
+// then cases before i will be tested; otherwise, cases i and later.
+//
+// leaf(i, nif) should setup nif (an OIF node) to test case i. In
+// particular, it should set nif.Left and nif.Nbody.
+func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i int, nif *ir.IfStmt)) {
+ const binarySearchMin = 4 // minimum number of cases for binary search
+
+ var do func(lo, hi int, out *ir.Nodes)
+ do = func(lo, hi int, out *ir.Nodes) {
+ n := hi - lo
+ if n < binarySearchMin {
+ for i := lo; i < hi; i++ {
+ nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ leaf(i, nif)
+ base.Pos = base.Pos.WithNotStmt()
+ nif.Cond = typecheck.Expr(nif.Cond)
+ nif.Cond = typecheck.DefaultLit(nif.Cond, nil)
+ out.Append(nif)
+ out = &nif.Else
+ }
+ return
+ }
+
+ half := lo + n/2
+ nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
+ nif.Cond = less(half)
+ base.Pos = base.Pos.WithNotStmt()
+ nif.Cond = typecheck.Expr(nif.Cond)
+ nif.Cond = typecheck.DefaultLit(nif.Cond, nil)
+ do(lo, half, &nif.Body)
+ do(half, hi, &nif.Else)
+ out.Append(nif)
+ }
+
+ do(0, n, out)
+}
diff --git a/src/cmd/compile/internal/walk/temp.go b/src/cmd/compile/internal/walk/temp.go
new file mode 100644
index 0000000000000..9879a6c69d7c6
--- /dev/null
+++ b/src/cmd/compile/internal/walk/temp.go
@@ -0,0 +1,40 @@
+// 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 walk
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+)
+
+// initStackTemp appends statements to init to initialize the given
+// temporary variable to val, and then returns the expression &tmp.
+func initStackTemp(init *ir.Nodes, tmp *ir.Name, val ir.Node) *ir.AddrExpr {
+ if val != nil && !types.Identical(tmp.Type(), val.Type()) {
+ base.Fatalf("bad initial value for %L: %L", tmp, val)
+ }
+ appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmp, val))
+ return typecheck.Expr(typecheck.NodAddr(tmp)).(*ir.AddrExpr)
+}
+
+// stackTempAddr returns the expression &tmp, where tmp is a newly
+// allocated temporary variable of the given type. Statements to
+// zero-initialize tmp are appended to init.
+func stackTempAddr(init *ir.Nodes, typ *types.Type) *ir.AddrExpr {
+ return initStackTemp(init, typecheck.Temp(typ), nil)
+}
+
+// stackBufAddr returns thte expression &tmp, where tmp is a newly
+// allocated temporary variable of type [len]elem. This variable is
+// initialized, and elem must not contain pointers.
+func stackBufAddr(len int64, elem *types.Type) *ir.AddrExpr {
+ if elem.HasPointers() {
+ base.FatalfAt(base.Pos, "%v has pointers", elem)
+ }
+ tmp := typecheck.Temp(types.NewArray(elem, len))
+ return typecheck.Expr(typecheck.NodAddr(tmp)).(*ir.AddrExpr)
+}
diff --git a/src/cmd/compile/internal/walk/walk.go b/src/cmd/compile/internal/walk/walk.go
new file mode 100644
index 0000000000000..b47d96dc4c933
--- /dev/null
+++ b/src/cmd/compile/internal/walk/walk.go
@@ -0,0 +1,412 @@
+// Copyright 2009 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 walk
+
+import (
+ "errors"
+ "fmt"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/ssagen"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+)
+
+// The constant is known to runtime.
+const tmpstringbufsize = 32
+const zeroValSize = 1024 // must match value of runtime/map.go:maxZero
+
+func Walk(fn *ir.Func) {
+ ir.CurFunc = fn
+ errorsBefore := base.Errors()
+ order(fn)
+ if base.Errors() > errorsBefore {
+ return
+ }
+
+ if base.Flag.W != 0 {
+ s := fmt.Sprintf("\nbefore walk %v", ir.CurFunc.Sym())
+ ir.DumpList(s, ir.CurFunc.Body)
+ }
+
+ lno := base.Pos
+
+ base.Pos = lno
+ if base.Errors() > errorsBefore {
+ return
+ }
+ walkStmtList(ir.CurFunc.Body)
+ if base.Flag.W != 0 {
+ s := fmt.Sprintf("after walk %v", ir.CurFunc.Sym())
+ ir.DumpList(s, ir.CurFunc.Body)
+ }
+
+ if base.Flag.Cfg.Instrumenting {
+ instrument(fn)
+ }
+
+ // Eagerly compute sizes of all variables for SSA.
+ for _, n := range fn.Dcl {
+ types.CalcSize(n.Type())
+ }
+}
+
+// walkRecv walks an ORECV node.
+func walkRecv(n *ir.UnaryExpr) ir.Node {
+ if n.Typecheck() == 0 {
+ base.Fatalf("missing typecheck: %+v", n)
+ }
+ init := ir.TakeInit(n)
+
+ n.X = walkExpr(n.X, &init)
+ call := walkExpr(mkcall1(chanfn("chanrecv1", 2, n.X.Type()), nil, &init, n.X, typecheck.NodNil()), &init)
+ return ir.InitExpr(init, call)
+}
+
+func convas(n *ir.AssignStmt, init *ir.Nodes) *ir.AssignStmt {
+ if n.Op() != ir.OAS {
+ base.Fatalf("convas: not OAS %v", n.Op())
+ }
+ n.SetTypecheck(1)
+
+ if n.X == nil || n.Y == nil {
+ return n
+ }
+
+ lt := n.X.Type()
+ rt := n.Y.Type()
+ if lt == nil || rt == nil {
+ return n
+ }
+
+ if ir.IsBlank(n.X) {
+ n.Y = typecheck.DefaultLit(n.Y, nil)
+ return n
+ }
+
+ if !types.Identical(lt, rt) {
+ n.Y = typecheck.AssignConv(n.Y, lt, "assignment")
+ n.Y = walkExpr(n.Y, init)
+ }
+ types.CalcSize(n.Y.Type())
+
+ return n
+}
+
+var stop = errors.New("stop")
+
+func vmkcall(fn ir.Node, t *types.Type, init *ir.Nodes, va []ir.Node) *ir.CallExpr {
+ if init == nil {
+ base.Fatalf("mkcall with nil init: %v", fn)
+ }
+ if fn.Type() == nil || fn.Type().Kind() != types.TFUNC {
+ base.Fatalf("mkcall %v %v", fn, fn.Type())
+ }
+
+ n := fn.Type().NumParams()
+ if n != len(va) {
+ base.Fatalf("vmkcall %v needs %v args got %v", fn, n, len(va))
+ }
+
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, va)
+ typecheck.Call(call)
+ call.SetType(t)
+ return walkExpr(call, init).(*ir.CallExpr)
+}
+
+func mkcall(name string, t *types.Type, init *ir.Nodes, args ...ir.Node) *ir.CallExpr {
+ return vmkcall(typecheck.LookupRuntime(name), t, init, args)
+}
+
+func mkcallstmt(name string, args ...ir.Node) ir.Node {
+ return mkcallstmt1(typecheck.LookupRuntime(name), args...)
+}
+
+func mkcall1(fn ir.Node, t *types.Type, init *ir.Nodes, args ...ir.Node) *ir.CallExpr {
+ return vmkcall(fn, t, init, args)
+}
+
+func mkcallstmt1(fn ir.Node, args ...ir.Node) ir.Node {
+ var init ir.Nodes
+ n := vmkcall(fn, nil, &init, args)
+ if len(init) == 0 {
+ return n
+ }
+ init.Append(n)
+ return ir.NewBlockStmt(n.Pos(), init)
+}
+
+func chanfn(name string, n int, t *types.Type) ir.Node {
+ if !t.IsChan() {
+ base.Fatalf("chanfn %v", t)
+ }
+ fn := typecheck.LookupRuntime(name)
+ switch n {
+ default:
+ base.Fatalf("chanfn %d", n)
+ case 1:
+ fn = typecheck.SubstArgTypes(fn, t.Elem())
+ case 2:
+ fn = typecheck.SubstArgTypes(fn, t.Elem(), t.Elem())
+ }
+ return fn
+}
+
+func mapfn(name string, t *types.Type) ir.Node {
+ if !t.IsMap() {
+ base.Fatalf("mapfn %v", t)
+ }
+ fn := typecheck.LookupRuntime(name)
+ fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem(), t.Key(), t.Elem())
+ return fn
+}
+
+func mapfndel(name string, t *types.Type) ir.Node {
+ if !t.IsMap() {
+ base.Fatalf("mapfn %v", t)
+ }
+ fn := typecheck.LookupRuntime(name)
+ fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem(), t.Key())
+ return fn
+}
+
+const (
+ mapslow = iota
+ mapfast32
+ mapfast32ptr
+ mapfast64
+ mapfast64ptr
+ mapfaststr
+ nmapfast
+)
+
+type mapnames [nmapfast]string
+
+func mkmapnames(base string, ptr string) mapnames {
+ return mapnames{base, base + "_fast32", base + "_fast32" + ptr, base + "_fast64", base + "_fast64" + ptr, base + "_faststr"}
+}
+
+var mapaccess1 = mkmapnames("mapaccess1", "")
+var mapaccess2 = mkmapnames("mapaccess2", "")
+var mapassign = mkmapnames("mapassign", "ptr")
+var mapdelete = mkmapnames("mapdelete", "")
+
+func mapfast(t *types.Type) int {
+ // Check runtime/map.go:maxElemSize before changing.
+ if t.Elem().Width > 128 {
+ return mapslow
+ }
+ switch reflectdata.AlgType(t.Key()) {
+ case types.AMEM32:
+ if !t.Key().HasPointers() {
+ return mapfast32
+ }
+ if types.PtrSize == 4 {
+ return mapfast32ptr
+ }
+ base.Fatalf("small pointer %v", t.Key())
+ case types.AMEM64:
+ if !t.Key().HasPointers() {
+ return mapfast64
+ }
+ if types.PtrSize == 8 {
+ return mapfast64ptr
+ }
+ // Two-word object, at least one of which is a pointer.
+ // Use the slow path.
+ case types.ASTRING:
+ return mapfaststr
+ }
+ return mapslow
+}
+
+func walkAppendArgs(n *ir.CallExpr, init *ir.Nodes) {
+ walkExprListSafe(n.Args, init)
+
+ // walkExprListSafe will leave OINDEX (s[n]) alone if both s
+ // and n are name or literal, but those may index the slice we're
+ // modifying here. Fix explicitly.
+ ls := n.Args
+ for i1, n1 := range ls {
+ ls[i1] = cheapExpr(n1, init)
+ }
+}
+
+// Rewrite
+// go builtin(x, y, z)
+// into
+// go func(a1, a2, a3) {
+// builtin(a1, a2, a3)
+// }(x, y, z)
+// for print, println, and delete.
+//
+// Rewrite
+// go f(x, y, uintptr(unsafe.Pointer(z)))
+// into
+// go func(a1, a2, a3) {
+// builtin(a1, a2, uintptr(a3))
+// }(x, y, unsafe.Pointer(z))
+// for function contains unsafe-uintptr arguments.
+
+var wrapCall_prgen int
+
+// appendWalkStmt typechecks and walks stmt and then appends it to init.
+func appendWalkStmt(init *ir.Nodes, stmt ir.Node) {
+ op := stmt.Op()
+ n := typecheck.Stmt(stmt)
+ if op == ir.OAS || op == ir.OAS2 {
+ // If the assignment has side effects, walkExpr will append them
+ // directly to init for us, while walkStmt will wrap it in an OBLOCK.
+ // We need to append them directly.
+ // TODO(rsc): Clean this up.
+ n = walkExpr(n, init)
+ } else {
+ n = walkStmt(n)
+ }
+ init.Append(n)
+}
+
+// The max number of defers in a function using open-coded defers. We enforce this
+// limit because the deferBits bitmask is currently a single byte (to minimize code size)
+const maxOpenDefers = 8
+
+// 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 backingArrayPtrLen(n ir.Node) (ptr, length ir.Node) {
+ var init ir.Nodes
+ c := cheapExpr(n, &init)
+ if c != n || len(init) != 0 {
+ base.Fatalf("backingArrayPtrLen not cheap: %v", n)
+ }
+ ptr = ir.NewUnaryExpr(base.Pos, ir.OSPTR, n)
+ if n.Type().IsString() {
+ ptr.SetType(types.Types[types.TUINT8].PtrTo())
+ } else {
+ ptr.SetType(n.Type().Elem().PtrTo())
+ }
+ length = ir.NewUnaryExpr(base.Pos, ir.OLEN, n)
+ length.SetType(types.Types[types.TINT])
+ return ptr, length
+}
+
+// mayCall reports whether evaluating expression n may require
+// function calls, which could clobber function call arguments/results
+// currently on the stack.
+func mayCall(n ir.Node) bool {
+ // When instrumenting, any expression might require function calls.
+ if base.Flag.Cfg.Instrumenting {
+ return true
+ }
+
+ isSoftFloat := func(typ *types.Type) bool {
+ return types.IsFloat[typ.Kind()] || types.IsComplex[typ.Kind()]
+ }
+
+ return ir.Any(n, func(n ir.Node) bool {
+ // walk should have already moved any Init blocks off of
+ // expressions.
+ if len(n.Init()) != 0 {
+ base.FatalfAt(n.Pos(), "mayCall %+v", n)
+ }
+
+ switch n.Op() {
+ default:
+ base.FatalfAt(n.Pos(), "mayCall %+v", n)
+
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
+ return true
+
+ case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR,
+ ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD:
+ // These ops might panic, make sure they are done
+ // before we start marshaling args for a call. See issue 16760.
+ return true
+
+ case ir.OANDAND, ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ // The RHS expression may have init statements that
+ // should only execute conditionally, and so cannot be
+ // pulled out to the top-level init list. We could try
+ // to be more precise here.
+ return len(n.Y.Init()) != 0
+
+ // When using soft-float, these ops might be rewritten to function calls
+ // so we ensure they are evaluated first.
+ case ir.OADD, ir.OSUB, ir.OMUL, ir.ONEG:
+ return ssagen.Arch.SoftFloat && isSoftFloat(n.Type())
+ case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT:
+ n := n.(*ir.BinaryExpr)
+ return ssagen.Arch.SoftFloat && isSoftFloat(n.X.Type())
+ case ir.OCONV:
+ n := n.(*ir.ConvExpr)
+ return ssagen.Arch.SoftFloat && (isSoftFloat(n.Type()) || isSoftFloat(n.X.Type()))
+
+ case ir.OLITERAL, ir.ONIL, ir.ONAME, ir.OLINKSYMOFFSET, ir.OMETHEXPR,
+ ir.OAND, ir.OANDNOT, ir.OLSH, ir.OOR, ir.ORSH, ir.OXOR, ir.OCOMPLEX, ir.OEFACE,
+ ir.OADDR, ir.OBITNOT, ir.ONOT, ir.OPLUS,
+ ir.OCAP, ir.OIMAG, ir.OLEN, ir.OREAL,
+ ir.OCONVNOP, ir.ODOT,
+ ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.OSPTR,
+ ir.OBYTES2STRTMP, ir.OGETG, ir.OSLICEHEADER:
+ // ok: operations that don't require function calls.
+ // Expand as needed.
+ }
+
+ return false
+ })
+}
+
+// itabType loads the _type field from a runtime.itab struct.
+func itabType(itab ir.Node) ir.Node {
+ if itabTypeField == nil {
+ // runtime.itab's _type field
+ itabTypeField = runtimeField("_type", int64(types.PtrSize), types.NewPtr(types.Types[types.TUINT8]))
+ }
+ return boundedDotPtr(base.Pos, itab, itabTypeField)
+}
+
+var itabTypeField *types.Field
+
+// boundedDotPtr returns a selector expression representing ptr.field
+// and omits nil-pointer checks for ptr.
+func boundedDotPtr(pos src.XPos, ptr ir.Node, field *types.Field) *ir.SelectorExpr {
+ sel := ir.NewSelectorExpr(pos, ir.ODOTPTR, ptr, field.Sym)
+ sel.Selection = field
+ sel.SetType(field.Type)
+ sel.SetTypecheck(1)
+ sel.SetBounded(true) // guaranteed not to fault
+ return sel
+}
+
+func runtimeField(name string, offset int64, typ *types.Type) *types.Field {
+ f := types.NewField(src.NoXPos, ir.Pkgs.Runtime.Lookup(name), typ)
+ f.Offset = offset
+ return f
+}
+
+// ifaceData loads the data field from an interface.
+// The concrete type must be known to have type t.
+// It follows the pointer if !IsDirectIface(t).
+func ifaceData(pos src.XPos, n ir.Node, t *types.Type) ir.Node {
+ if t.IsInterface() {
+ base.Fatalf("ifaceData interface: %v", t)
+ }
+ ptr := ir.NewUnaryExpr(pos, ir.OIDATA, n)
+ if types.IsDirectIface(t) {
+ ptr.SetType(t)
+ ptr.SetTypecheck(1)
+ return ptr
+ }
+ ptr.SetType(types.NewPtr(t))
+ ptr.SetTypecheck(1)
+ ind := ir.NewStarExpr(pos, ptr)
+ ind.SetType(t)
+ ind.SetTypecheck(1)
+ ind.SetBounded(true)
+ return ind
+}
diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go
index 9c9f6edc5f491..e4ef9d7c6a876 100644
--- a/src/cmd/compile/internal/wasm/ssa.go
+++ b/src/cmd/compile/internal/wasm/ssa.go
@@ -5,16 +5,19 @@
package wasm
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
+ "cmd/compile/internal/objw"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/wasm"
"cmd/internal/objabi"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &wasm.Linkwasm
arch.REGSP = wasm.REG_SP
arch.MAXWIDTH = 1 << 50
@@ -28,31 +31,31 @@ func Init(arch *gc.Arch) {
arch.SSAGenBlock = ssaGenBlock
}
-func zeroRange(pp *gc.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
+func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
if cnt == 0 {
return p
}
if cnt%8 != 0 {
- gc.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
+ base.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
}
for i := int64(0); i < cnt; i += 8 {
- p = pp.Appendpp(p, wasm.AGet, obj.TYPE_REG, wasm.REG_SP, 0, 0, 0, 0)
- p = pp.Appendpp(p, wasm.AI64Const, obj.TYPE_CONST, 0, 0, 0, 0, 0)
- p = pp.Appendpp(p, wasm.AI64Store, 0, 0, 0, obj.TYPE_CONST, 0, off+i)
+ p = pp.Append(p, wasm.AGet, obj.TYPE_REG, wasm.REG_SP, 0, 0, 0, 0)
+ p = pp.Append(p, wasm.AI64Const, obj.TYPE_CONST, 0, 0, 0, 0, 0)
+ p = pp.Append(p, wasm.AI64Store, 0, 0, 0, obj.TYPE_CONST, 0, off+i)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
return pp.Prog(wasm.ANop)
}
-func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if next != b.Succs[0].Block() {
@@ -118,11 +121,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
}
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall:
s.PrepareCall(v)
- if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == gc.Deferreturn {
+ if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == ir.Syms.Deferreturn {
// add a resume point before call to deferreturn so it can be called again via jmpdefer
s.Prog(wasm.ARESUMEPOINT)
}
@@ -147,26 +150,26 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
getValue32(s, v.Args[1])
i32Const(s, int32(v.AuxInt))
p := s.Prog(wasm.ACall)
- p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmMove}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmMove}
case ssa.OpWasmLoweredZero:
getValue32(s, v.Args[0])
i32Const(s, int32(v.AuxInt))
p := s.Prog(wasm.ACall)
- p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmZero}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmZero}
case ssa.OpWasmLoweredNilCheck:
getValue64(s, v.Args[0])
s.Prog(wasm.AI64Eqz)
s.Prog(wasm.AIf)
p := s.Prog(wasm.ACALLNORESUME)
- p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.SigPanic}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.SigPanic}
s.Prog(wasm.AEnd)
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpWasmLoweredWB:
@@ -185,7 +188,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
getReg(s, wasm.REG_SP)
getValue64(s, v.Args[0])
p := s.Prog(storeOp(v.Type))
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
default:
if v.Type.IsMemory() {
@@ -205,7 +208,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
}
-func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value, extend bool) {
+func ssaGenValueOnStack(s *ssagen.State, v *ssa.Value, extend bool) {
switch v.Op {
case ssa.OpWasmLoweredGetClosurePtr:
getReg(s, wasm.REG_CTXT)
@@ -240,10 +243,10 @@ func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value, extend bool) {
p.From.Type = obj.TYPE_ADDR
switch v.Aux.(type) {
case *obj.LSym:
- gc.AddAux(&p.From, v)
- case *gc.Node:
+ ssagen.AddAux(&p.From, v)
+ case *ir.Name:
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
default:
panic("wasm: bad LoweredAddr")
}
@@ -312,7 +315,7 @@ func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value, extend bool) {
if v.Type.Size() == 8 {
// Division of int64 needs helper function wasmDiv to handle the MinInt64 / -1 case.
p := s.Prog(wasm.ACall)
- p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmDiv}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmDiv}
break
}
s.Prog(wasm.AI64DivS)
@@ -326,7 +329,7 @@ func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value, extend bool) {
s.Prog(wasm.AF64PromoteF32)
}
p := s.Prog(wasm.ACall)
- p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncS}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncS}
}
case ssa.OpWasmI64TruncSatF32U, ssa.OpWasmI64TruncSatF64U:
@@ -338,7 +341,7 @@ func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value, extend bool) {
s.Prog(wasm.AF64PromoteF32)
}
p := s.Prog(wasm.ACall)
- p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncU}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: ir.Syms.WasmTruncU}
}
case ssa.OpWasmF32DemoteF64:
@@ -360,7 +363,7 @@ func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value, extend bool) {
case ssa.OpLoadReg:
p := s.Prog(loadOp(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
case ssa.OpCopy:
getValue64(s, v.Args[0])
@@ -382,7 +385,7 @@ func isCmp(v *ssa.Value) bool {
}
}
-func getValue32(s *gc.SSAGenState, v *ssa.Value) {
+func getValue32(s *ssagen.State, v *ssa.Value) {
if v.OnWasmStack {
s.OnWasmStackSkipped--
ssaGenValueOnStack(s, v, false)
@@ -399,7 +402,7 @@ func getValue32(s *gc.SSAGenState, v *ssa.Value) {
}
}
-func getValue64(s *gc.SSAGenState, v *ssa.Value) {
+func getValue64(s *ssagen.State, v *ssa.Value) {
if v.OnWasmStack {
s.OnWasmStackSkipped--
ssaGenValueOnStack(s, v, true)
@@ -413,32 +416,32 @@ func getValue64(s *gc.SSAGenState, v *ssa.Value) {
}
}
-func i32Const(s *gc.SSAGenState, val int32) {
+func i32Const(s *ssagen.State, val int32) {
p := s.Prog(wasm.AI32Const)
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(val)}
}
-func i64Const(s *gc.SSAGenState, val int64) {
+func i64Const(s *ssagen.State, val int64) {
p := s.Prog(wasm.AI64Const)
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: val}
}
-func f32Const(s *gc.SSAGenState, val float64) {
+func f32Const(s *ssagen.State, val float64) {
p := s.Prog(wasm.AF32Const)
p.From = obj.Addr{Type: obj.TYPE_FCONST, Val: val}
}
-func f64Const(s *gc.SSAGenState, val float64) {
+func f64Const(s *ssagen.State, val float64) {
p := s.Prog(wasm.AF64Const)
p.From = obj.Addr{Type: obj.TYPE_FCONST, Val: val}
}
-func getReg(s *gc.SSAGenState, reg int16) {
+func getReg(s *ssagen.State, reg int16) {
p := s.Prog(wasm.AGet)
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
}
-func setReg(s *gc.SSAGenState, reg int16) {
+func setReg(s *ssagen.State, reg int16) {
p := s.Prog(wasm.ASet)
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: reg}
}
diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go
index e137daa3fc590..fc806f91196df 100644
--- a/src/cmd/compile/internal/x86/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -5,14 +5,15 @@
package x86
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ssagen"
"cmd/internal/obj/x86"
"cmd/internal/objabi"
"fmt"
"os"
)
-func Init(arch *gc.Arch) {
+func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &x86.Link386
arch.REGSP = x86.REGSP
arch.SSAGenValue = ssaGenValue
@@ -24,10 +25,10 @@ func Init(arch *gc.Arch) {
arch.SoftFloat = true
case "387":
fmt.Fprintf(os.Stderr, "unsupported setting GO386=387. Consider using GO386=softfloat instead.\n")
- gc.Exit(1)
+ base.Exit(1)
default:
fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v)
- gc.Exit(1)
+ base.Exit(1)
}
diff --git a/src/cmd/compile/internal/x86/ggen.go b/src/cmd/compile/internal/x86/ggen.go
index a33ddc81e3dc0..3ca479763e63a 100644
--- a/src/cmd/compile/internal/x86/ggen.go
+++ b/src/cmd/compile/internal/x86/ggen.go
@@ -5,39 +5,41 @@
package x86
import (
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
-func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, ax *uint32) *obj.Prog {
+func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, ax *uint32) *obj.Prog {
if cnt == 0 {
return p
}
if *ax == 0 {
- p = pp.Appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+ p = pp.Append(p, x86.AMOVL, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
*ax = 1
}
- if cnt <= int64(4*gc.Widthreg) {
- for i := int64(0); i < cnt; i += int64(gc.Widthreg) {
- p = pp.Appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off+i)
+ if cnt <= int64(4*types.RegSize) {
+ for i := int64(0); i < cnt; i += int64(types.RegSize) {
+ p = pp.Append(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off+i)
}
- } else if cnt <= int64(128*gc.Widthreg) {
- p = pp.Appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
- p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, 1*(128-cnt/int64(gc.Widthreg)))
- p.To.Sym = gc.Duffzero
+ } else if cnt <= int64(128*types.RegSize) {
+ p = pp.Append(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
+ p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, 1*(128-cnt/int64(types.RegSize)))
+ p.To.Sym = ir.Syms.Duffzero
} else {
- p = pp.Appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0)
- p = pp.Appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
- p = pp.Appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
- p = pp.Appendpp(p, x86.ASTOSL, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+ p = pp.Append(p, x86.AMOVL, obj.TYPE_CONST, 0, cnt/int64(types.RegSize), obj.TYPE_REG, x86.REG_CX, 0)
+ p = pp.Append(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
+ p = pp.Append(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+ p = pp.Append(p, x86.ASTOSL, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
}
return p
}
-func ginsnop(pp *gc.Progs) *obj.Prog {
+func ginsnop(pp *objw.Progs) *obj.Prog {
// See comment in ../amd64/ggen.go.
p := pp.Prog(x86.AXCHGL)
p.From.Type = obj.TYPE_REG
diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go
index fbf76d0c5efb9..00dfa07bf78be 100644
--- a/src/cmd/compile/internal/x86/ssa.go
+++ b/src/cmd/compile/internal/x86/ssa.go
@@ -8,16 +8,18 @@ import (
"fmt"
"math"
- "cmd/compile/internal/gc"
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
-func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
flive := b.FlagsLiveAtEnd
for _, c := range b.ControlValues() {
flive = c.Type.IsFlags() || flive
@@ -107,7 +109,7 @@ func moveByType(t *types.Type) obj.As {
// dest := dest(To) op src(From)
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
-func opregreg(s *gc.SSAGenState, op obj.As, dest, src int16) *obj.Prog {
+func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
p := s.Prog(op)
p.From.Type = obj.TYPE_REG
p.To.Type = obj.TYPE_REG
@@ -116,7 +118,7 @@ func opregreg(s *gc.SSAGenState, op obj.As, dest, src int16) *obj.Prog {
return p
}
-func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.Op386ADDL:
r := v.Reg()
@@ -404,14 +406,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Type = obj.TYPE_MEM
p.From.Reg = r
p.From.Index = i
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.Op386LEAL:
p := s.Prog(x86.ALEAL)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.Op386CMPL, ssa.Op386CMPW, ssa.Op386CMPB,
@@ -437,7 +439,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Args[1].Reg()
case ssa.Op386CMPLconstload, ssa.Op386CMPWconstload, ssa.Op386CMPBconstload:
@@ -445,7 +447,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.From, v, sc.Off())
+ ssagen.AddAux2(&p.From, v, sc.Off())
p.To.Type = obj.TYPE_CONST
p.To.Offset = sc.Val()
case ssa.Op386MOVLconst:
@@ -480,9 +482,9 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Name = obj.NAME_EXTERN
f := math.Float64frombits(uint64(v.AuxInt))
if v.Op == ssa.Op386MOVSDconst1 {
- p.From.Sym = gc.Ctxt.Float64Sym(f)
+ p.From.Sym = base.Ctxt.Float64Sym(f)
} else {
- p.From.Sym = gc.Ctxt.Float32Sym(float32(f))
+ p.From.Sym = base.Ctxt.Float32Sym(float32(f))
}
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -497,7 +499,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.Op386MOVBloadidx1, ssa.Op386MOVWloadidx1, ssa.Op386MOVLloadidx1, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1,
@@ -521,7 +523,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
p.From.Reg = r
p.From.Index = i
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.Op386ADDLloadidx4, ssa.Op386SUBLloadidx4, ssa.Op386MULLloadidx4,
@@ -531,7 +533,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.From.Index = v.Args[2].Reg()
p.From.Scale = 4
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
if v.Reg() != v.Args[0].Reg() {
@@ -544,7 +546,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[1].Reg()
- gc.AddAux(&p.From, v)
+ ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
if v.Reg() != v.Args[0].Reg() {
@@ -557,7 +559,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.Op386ADDLconstmodify:
sc := v.AuxValAndOff()
val := sc.Val()
@@ -571,7 +573,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
off := sc.Off()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, off)
+ ssagen.AddAux2(&p.To, v, off)
break
}
fallthrough
@@ -584,7 +586,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = val
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, off)
+ ssagen.AddAux2(&p.To, v, off)
case ssa.Op386MOVBstoreidx1, ssa.Op386MOVWstoreidx1, ssa.Op386MOVLstoreidx1, ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1,
ssa.Op386MOVSDstoreidx8, ssa.Op386MOVSSstoreidx4, ssa.Op386MOVLstoreidx4, ssa.Op386MOVWstoreidx2,
ssa.Op386ADDLmodifyidx4, ssa.Op386SUBLmodifyidx4, ssa.Op386ANDLmodifyidx4, ssa.Op386ORLmodifyidx4, ssa.Op386XORLmodifyidx4:
@@ -610,7 +612,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
p.To.Reg = r
p.To.Index = i
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
case ssa.Op386MOVLstoreconst, ssa.Op386MOVWstoreconst, ssa.Op386MOVBstoreconst:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
@@ -618,7 +620,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = sc.Val()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux2(&p.To, v, sc.Off())
+ ssagen.AddAux2(&p.To, v, sc.Off())
case ssa.Op386ADDLconstmodifyidx4:
sc := v.AuxValAndOff()
val := sc.Val()
@@ -634,7 +636,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Args[0].Reg()
p.To.Scale = 4
p.To.Index = v.Args[1].Reg()
- gc.AddAux2(&p.To, v, off)
+ ssagen.AddAux2(&p.To, v, off)
break
}
fallthrough
@@ -661,7 +663,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = r
p.To.Index = i
- gc.AddAux2(&p.To, v, sc.Off())
+ ssagen.AddAux2(&p.To, v, sc.Off())
case ssa.Op386MOVWLSX, ssa.Op386MOVBLSX, ssa.Op386MOVWLZX, ssa.Op386MOVBLZX,
ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD,
ssa.Op386CVTTSS2SL, ssa.Op386CVTTSD2SL,
@@ -670,12 +672,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.Op386DUFFZERO:
p := s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_ADDR
- p.To.Sym = gc.Duffzero
+ p.To.Sym = ir.Syms.Duffzero
p.To.Offset = v.AuxInt
case ssa.Op386DUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_ADDR
- p.To.Sym = gc.Duffcopy
+ p.To.Sym = ir.Syms.Duffcopy
p.To.Offset = v.AuxInt
case ssa.OpCopy: // TODO: use MOVLreg for reg->reg copies instead of OpCopy?
@@ -693,7 +695,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
+ ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -705,15 +707,15 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
- gc.AddrAuto(&p.To, v)
+ ssagen.AddrAuto(&p.To, v)
case ssa.Op386LoweredGetClosurePtr:
// Closure pointer is DX.
- gc.CheckLoweredGetClosurePtr(v)
+ ssagen.CheckLoweredGetClosurePtr(v)
case ssa.Op386LoweredGetG:
r := v.Reg()
// See the comments in cmd/internal/obj/x86/obj6.go
// near CanUse1InsnTLS for a detailed explanation of these instructions.
- if x86.CanUse1InsnTLS(gc.Ctxt) {
+ if x86.CanUse1InsnTLS(base.Ctxt) {
// MOVL (TLS), r
p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_MEM
@@ -749,7 +751,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// caller's SP is the address of the first arg
p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -gc.Ctxt.FixedFrameSize() // 0 on 386, just to be consistent with other architectures
+ p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on 386, just to be consistent with other architectures
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -764,14 +766,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(8) // space used in callee args area by assembly stubs
case ssa.Op386LoweredPanicExtendA, ssa.Op386LoweredPanicExtendB, ssa.Op386LoweredPanicExtendC:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = gc.ExtendCheckFunc[v.AuxInt]
+ p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
s.UseArgs(12) // space used in callee args area by assembly stubs
case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter:
@@ -846,12 +848,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = x86.REG_AX
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
- if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
- gc.Warnl(v.Pos, "generated nil check")
+ if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
+ base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpClobber:
p := s.Prog(x86.AMOVL)
@@ -859,7 +861,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = 0xdeaddead
p.To.Type = obj.TYPE_MEM
p.To.Reg = x86.REG_SP
- gc.AddAux(&p.To, v)
+ ssagen.AddAux(&p.To, v)
default:
v.Fatalf("genValue not implemented: %s", v.LongString())
}
@@ -884,22 +886,22 @@ var blockJump = [...]struct {
ssa.Block386NAN: {x86.AJPS, x86.AJPC},
}
-var eqfJumps = [2][2]gc.IndexJump{
+var eqfJumps = [2][2]ssagen.IndexJump{
{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPS, Index: 1}}, // next == b.Succs[0]
{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPC, Index: 0}}, // next == b.Succs[1]
}
-var nefJumps = [2][2]gc.IndexJump{
+var nefJumps = [2][2]ssagen.IndexJump{
{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPC, Index: 1}}, // next == b.Succs[0]
{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPS, Index: 0}}, // next == b.Succs[1]
}
-func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
// defer returns in rax:
@@ -912,11 +914,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.To.Reg = x86.REG_AX
p = s.Prog(x86.AJNE)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+ s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
diff --git a/src/cmd/compile/main.go b/src/cmd/compile/main.go
index 3aa64a5ce251b..cb2f4e8cf475a 100644
--- a/src/cmd/compile/main.go
+++ b/src/cmd/compile/main.go
@@ -8,12 +8,14 @@ import (
"cmd/compile/internal/amd64"
"cmd/compile/internal/arm"
"cmd/compile/internal/arm64"
+ "cmd/compile/internal/base"
"cmd/compile/internal/gc"
"cmd/compile/internal/mips"
"cmd/compile/internal/mips64"
"cmd/compile/internal/ppc64"
"cmd/compile/internal/riscv64"
"cmd/compile/internal/s390x"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/wasm"
"cmd/compile/internal/x86"
"cmd/internal/objabi"
@@ -22,7 +24,7 @@ import (
"os"
)
-var archInits = map[string]func(*gc.Arch){
+var archInits = map[string]func(*ssagen.ArchInfo){
"386": x86.Init,
"amd64": amd64.Init,
"arm": arm.Init,
@@ -50,5 +52,5 @@ func main() {
}
gc.Main(archInit)
- gc.Exit(0)
+ base.Exit(0)
}
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index c8c3212d1661f..332f2fab5816f 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -1765,6 +1765,8 @@ func IsRuntimePackagePath(pkgpath string) bool {
rval = true
case "syscall":
rval = true
+ case "crypto/x509/internal/macos": // libc function wrappers need to be ABIInternal
+ rval = true
default:
rval = strings.HasPrefix(pkgpath, "runtime/internal")
}
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index cf85f2ac8ea5f..7520b0ef18648 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -23,80 +23,41 @@ import (
// compiled with a Go 1.4 toolchain to produce the bootstrapTargets.
// All directories in this list are relative to and must be below $GOROOT/src.
//
-// The list has have two kinds of entries: names beginning with cmd/ with
+// The list has two kinds of entries: names beginning with cmd/ with
// no other slashes, which are commands, and other paths, which are packages
// supporting the commands. Packages in the standard library can be listed
// if a newer copy needs to be substituted for the Go 1.4 copy when used
-// by the command packages.
+// by the command packages. Paths ending with /... automatically
+// include all packages within subdirectories as well.
// These will be imported during bootstrap as bootstrap/name, like bootstrap/math/big.
var bootstrapDirs = []string{
"cmd/asm",
- "cmd/asm/internal/arch",
- "cmd/asm/internal/asm",
- "cmd/asm/internal/flags",
- "cmd/asm/internal/lex",
+ "cmd/asm/internal/...",
"cmd/cgo",
"cmd/compile",
- "cmd/compile/internal/amd64",
- "cmd/compile/internal/arm",
- "cmd/compile/internal/arm64",
- "cmd/compile/internal/gc",
- "cmd/compile/internal/logopt",
- "cmd/compile/internal/mips",
- "cmd/compile/internal/mips64",
- "cmd/compile/internal/ppc64",
- "cmd/compile/internal/riscv64",
- "cmd/compile/internal/s390x",
- "cmd/compile/internal/ssa",
- "cmd/compile/internal/syntax",
- "cmd/compile/internal/types",
- "cmd/compile/internal/x86",
- "cmd/compile/internal/wasm",
+ "cmd/compile/internal/...",
+ "cmd/internal/archive",
"cmd/internal/bio",
"cmd/internal/codesign",
- "cmd/internal/gcprog",
"cmd/internal/dwarf",
"cmd/internal/edit",
+ "cmd/internal/gcprog",
"cmd/internal/goobj",
+ "cmd/internal/obj/...",
"cmd/internal/objabi",
- "cmd/internal/obj",
- "cmd/internal/obj/arm",
- "cmd/internal/obj/arm64",
- "cmd/internal/obj/mips",
- "cmd/internal/obj/ppc64",
- "cmd/internal/obj/riscv",
- "cmd/internal/obj/s390x",
- "cmd/internal/obj/x86",
- "cmd/internal/obj/wasm",
"cmd/internal/pkgpath",
"cmd/internal/src",
"cmd/internal/sys",
"cmd/link",
- "cmd/link/internal/amd64",
- "cmd/link/internal/arm",
- "cmd/link/internal/arm64",
- "cmd/link/internal/benchmark",
- "cmd/link/internal/ld",
- "cmd/link/internal/loadelf",
- "cmd/link/internal/loader",
- "cmd/link/internal/loadmacho",
- "cmd/link/internal/loadpe",
- "cmd/link/internal/loadxcoff",
- "cmd/link/internal/mips",
- "cmd/link/internal/mips64",
- "cmd/link/internal/ppc64",
- "cmd/link/internal/riscv64",
- "cmd/link/internal/s390x",
- "cmd/link/internal/sym",
- "cmd/link/internal/x86",
+ "cmd/link/internal/...",
"compress/flate",
"compress/zlib",
- "cmd/link/internal/wasm",
"container/heap",
"debug/dwarf",
"debug/elf",
"debug/macho",
"debug/pe",
+ "go/constant",
"internal/goversion",
"internal/race",
"internal/unsafeheader",
@@ -104,6 +65,7 @@ var bootstrapDirs = []string{
"math/big",
"math/bits",
"sort",
+ "strconv",
}
// File prefixes that are ignored by go/build anyway, and cause
@@ -111,6 +73,7 @@ var bootstrapDirs = []string{
var ignorePrefixes = []string{
".",
"_",
+ "#",
}
// File suffixes that use build tags introduced since Go 1.4.
@@ -124,6 +87,7 @@ var ignoreSuffixes = []string{
"_wasm.s",
"_wasm.go",
"_test.s",
+ "_test.go",
}
func bootstrapBuildTools() {
@@ -149,31 +113,47 @@ func bootstrapBuildTools() {
// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
writefile("module bootstrap\n", pathf("%s/%s", base, "go.mod"), 0)
for _, dir := range bootstrapDirs {
- src := pathf("%s/src/%s", goroot, dir)
- dst := pathf("%s/%s", base, dir)
- xmkdirall(dst)
- if dir == "cmd/cgo" {
- // Write to src because we need the file both for bootstrap
- // and for later in the main build.
- mkzdefaultcc("", pathf("%s/zdefaultcc.go", src))
- }
- Dir:
- for _, name := range xreaddirfiles(src) {
+ recurse := strings.HasSuffix(dir, "/...")
+ dir = strings.TrimSuffix(dir, "/...")
+ filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ fatalf("walking bootstrap dirs failed: %v: %v", path, err)
+ }
+
+ name := filepath.Base(path)
+ src := pathf("%s/src/%s", goroot, path)
+ dst := pathf("%s/%s", base, path)
+
+ if info.IsDir() {
+ if !recurse && path != dir || name == "testdata" {
+ return filepath.SkipDir
+ }
+
+ xmkdirall(dst)
+ if path == "cmd/cgo" {
+ // Write to src because we need the file both for bootstrap
+ // and for later in the main build.
+ mkzdefaultcc("", pathf("%s/zdefaultcc.go", src))
+ mkzdefaultcc("", pathf("%s/zdefaultcc.go", dst))
+ }
+ return nil
+ }
+
for _, pre := range ignorePrefixes {
if strings.HasPrefix(name, pre) {
- continue Dir
+ return nil
}
}
for _, suf := range ignoreSuffixes {
if strings.HasSuffix(name, suf) {
- continue Dir
+ return nil
}
}
- srcFile := pathf("%s/%s", src, name)
- dstFile := pathf("%s/%s", dst, name)
- text := bootstrapRewriteFile(srcFile)
- writefile(text, dstFile, 0)
- }
+
+ text := bootstrapRewriteFile(src)
+ writefile(text, dst, 0)
+ return nil
+ })
}
// Set up environment for invoking Go 1.4 go command.
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index e7c63f0749d69..db3f281ef35a3 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -111,7 +111,7 @@
// -p n
// the number of programs, such as build commands or
// test binaries, that can be run in parallel.
-// The default is the number of CPUs available.
+// The default is GOMAXPROCS, normally the number of CPUs available.
// -race
// enable data race detection.
// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64,
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 3ce32388d05dd..d14b2328bfaa0 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -811,6 +811,7 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
// so that we can change files.
for _, copydir := range []string{
"src/runtime",
+ "src/internal/abi",
"src/internal/bytealg",
"src/internal/cpu",
"src/math/bits",
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index c48904eacceab..322247962f895 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -28,18 +28,18 @@ var (
BuildA bool // -a flag
BuildBuildmode string // -buildmode flag
BuildContext = defaultContext()
- BuildMod string // -mod flag
- BuildModExplicit bool // whether -mod was set explicitly
- BuildModReason string // reason -mod was set, if set by default
- BuildI bool // -i flag
- BuildLinkshared bool // -linkshared flag
- BuildMSan bool // -msan flag
- BuildN bool // -n flag
- BuildO string // -o flag
- BuildP = runtime.NumCPU() // -p flag
- BuildPkgdir string // -pkgdir flag
- BuildRace bool // -race flag
- BuildToolexec []string // -toolexec flag
+ BuildMod string // -mod flag
+ BuildModExplicit bool // whether -mod was set explicitly
+ BuildModReason string // reason -mod was set, if set by default
+ BuildI bool // -i flag
+ BuildLinkshared bool // -linkshared flag
+ BuildMSan bool // -msan flag
+ BuildN bool // -n flag
+ BuildO string // -o flag
+ BuildP = runtime.GOMAXPROCS(0) // -p flag
+ BuildPkgdir string // -pkgdir flag
+ BuildRace bool // -race flag
+ BuildToolexec []string // -toolexec flag
BuildToolchainName string
BuildToolchainCompiler func() string
BuildToolchainLinker func() string
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 780d639c5d9f3..0e7af6d33f570 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -71,7 +71,7 @@ and test commands:
-p n
the number of programs, such as build commands or
test binaries, that can be run in parallel.
- The default is the number of CPUs available.
+ The default is GOMAXPROCS, normally the number of CPUs available.
-race
enable data race detection.
Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64,
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index cc4e2b2b2b965..2087855b3c04a 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -129,7 +129,11 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
}
}
- args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), gcflags, gcargs, "-D", p.Internal.LocalPrefix}
+ args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), gcflags, gcargs}
+ if p.Internal.LocalPrefix != "" {
+ // Workaround #43883.
+ args = append(args, "-D", p.Internal.LocalPrefix)
+ }
if importcfg != nil {
if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
return "", nil, err
@@ -235,16 +239,19 @@ CheckFlags:
// - it has no successor packages to compile (usually package main)
// - all paths through the build graph pass through it
// - critical path scheduling says it is high priority
- // and in such a case, set c to runtime.NumCPU.
+ // and in such a case, set c to runtime.GOMAXPROCS(0).
+ // By default this is the same as runtime.NumCPU.
// We do this now when p==1.
+ // To limit parallelism, set GOMAXPROCS below numCPU; this may be useful
+ // on a low-memory builder, or if a deterministic build order is required.
+ c := runtime.GOMAXPROCS(0)
if cfg.BuildP == 1 {
- // No process parallelism. Max out c.
- return runtime.NumCPU()
+ // No process parallelism, do not cap compiler parallelism.
+ return c
}
- // Some process parallelism. Set c to min(4, numcpu).
- c := 4
- if ncpu := runtime.NumCPU(); ncpu < c {
- c = ncpu
+ // Some process parallelism. Set c to min(4, maxprocs).
+ if c > 4 {
+ c = 4
}
return c
}
diff --git a/src/cmd/internal/archive/archive.go b/src/cmd/internal/archive/archive.go
index c1661d7711ac9..e9b25fe240a34 100644
--- a/src/cmd/internal/archive/archive.go
+++ b/src/cmd/internal/archive/archive.go
@@ -118,9 +118,9 @@ type objReader struct {
func (r *objReader) init(f *os.File) {
r.a = &Archive{f, nil}
- r.offset, _ = f.Seek(0, io.SeekCurrent)
- r.limit, _ = f.Seek(0, io.SeekEnd)
- f.Seek(r.offset, io.SeekStart)
+ r.offset, _ = f.Seek(0, os.SEEK_CUR)
+ r.limit, _ = f.Seek(0, os.SEEK_END)
+ f.Seek(r.offset, os.SEEK_SET)
r.b = bio.NewReader(f)
}
@@ -221,7 +221,7 @@ func (r *objReader) skip(n int64) {
r.readFull(r.tmp[:n])
} else {
// Seek, giving up buffered data.
- r.b.MustSeek(r.offset+n, io.SeekStart)
+ r.b.MustSeek(r.offset+n, os.SEEK_SET)
r.offset += n
}
}
@@ -426,7 +426,7 @@ func (r *objReader) parseObject(o *GoObj, size int64) error {
// AddEntry adds an entry to the end of a, with the content from r.
func (a *Archive) AddEntry(typ EntryType, name string, mtime int64, uid, gid int, mode os.FileMode, size int64, r io.Reader) {
- off, err := a.f.Seek(0, io.SeekEnd)
+ off, err := a.f.Seek(0, os.SEEK_END)
if err != nil {
log.Fatal(err)
}
@@ -464,3 +464,24 @@ func exactly16Bytes(s string) string {
s += sixteenSpaces[:16-len(s)]
return s
}
+
+// architecture-independent object file output
+const HeaderSize = 60
+
+func ReadHeader(b *bufio.Reader, name string) int {
+ var buf [HeaderSize]byte
+ if _, err := io.ReadFull(b, buf[:]); err != nil {
+ return -1
+ }
+ aname := strings.Trim(string(buf[0:16]), " ")
+ if !strings.HasPrefix(aname, name) {
+ return -1
+ }
+ asize := strings.Trim(string(buf[48:58]), " ")
+ i, _ := strconv.Atoi(asize)
+ return i
+}
+
+func FormatHeader(arhdr []byte, name string, size int64) {
+ copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
+}
diff --git a/src/cmd/internal/goobj/mkbuiltin.go b/src/cmd/internal/goobj/mkbuiltin.go
index 07c340668138d..22608e7e6995e 100644
--- a/src/cmd/internal/goobj/mkbuiltin.go
+++ b/src/cmd/internal/goobj/mkbuiltin.go
@@ -118,8 +118,8 @@ func mkbuiltin(w io.Writer) {
// addBasicTypes returns the symbol names for basic types that are
// defined in the runtime and referenced in other packages.
-// Needs to be kept in sync with reflect.go:dumpbasictypes() and
-// reflect.go:dtypesym() in the compiler.
+// Needs to be kept in sync with reflect.go:WriteBasicTypes() and
+// reflect.go:writeType() in the compiler.
func enumerateBasicTypes() []extra {
names := [...]string{
"int8", "uint8", "int16", "uint16",
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 8c8ff587ffce7..8206902328077 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -39,6 +39,7 @@ import (
"cmd/internal/sys"
"fmt"
"sync"
+ "sync/atomic"
)
// An Addr is an argument to an instruction.
@@ -250,6 +251,12 @@ func (a *Addr) SetTarget(t *Prog) {
a.Val = t
}
+func (a *Addr) SetConst(v int64) {
+ a.Sym = nil
+ a.Type = TYPE_CONST
+ a.Offset = v
+}
+
// Prog describes a single machine instruction.
//
// The general instruction form is:
@@ -629,6 +636,10 @@ const (
// ContentAddressable indicates this is a content-addressable symbol.
AttrContentAddressable
+ // ABI wrapper is set for compiler-generated text symbols that
+ // convert between ABI0 and ABIInternal calling conventions.
+ AttrABIWrapper
+
// attrABIBase is the value at which the ABI is encoded in
// Attribute. This must be last; all bits after this are
// assumed to be an ABI value.
@@ -637,36 +648,52 @@ const (
attrABIBase
)
-func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
-func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 }
-func (a Attribute) CFunc() bool { return a&AttrCFunc != 0 }
-func (a Attribute) NoSplit() bool { return a&AttrNoSplit != 0 }
-func (a Attribute) Leaf() bool { return a&AttrLeaf != 0 }
-func (a Attribute) OnList() bool { return a&AttrOnList != 0 }
-func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 }
-func (a Attribute) Local() bool { return a&AttrLocal != 0 }
-func (a Attribute) Wrapper() bool { return a&AttrWrapper != 0 }
-func (a Attribute) NeedCtxt() bool { return a&AttrNeedCtxt != 0 }
-func (a Attribute) NoFrame() bool { return a&AttrNoFrame != 0 }
-func (a Attribute) Static() bool { return a&AttrStatic != 0 }
-func (a Attribute) WasInlined() bool { return a&AttrWasInlined != 0 }
-func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 }
-func (a Attribute) Indexed() bool { return a&AttrIndexed != 0 }
-func (a Attribute) UsedInIface() bool { return a&AttrUsedInIface != 0 }
-func (a Attribute) ContentAddressable() bool { return a&AttrContentAddressable != 0 }
+func (a *Attribute) load() Attribute { return Attribute(atomic.LoadUint32((*uint32)(a))) }
+
+func (a *Attribute) DuplicateOK() bool { return a.load()&AttrDuplicateOK != 0 }
+func (a *Attribute) MakeTypelink() bool { return a.load()&AttrMakeTypelink != 0 }
+func (a *Attribute) CFunc() bool { return a.load()&AttrCFunc != 0 }
+func (a *Attribute) NoSplit() bool { return a.load()&AttrNoSplit != 0 }
+func (a *Attribute) Leaf() bool { return a.load()&AttrLeaf != 0 }
+func (a *Attribute) OnList() bool { return a.load()&AttrOnList != 0 }
+func (a *Attribute) ReflectMethod() bool { return a.load()&AttrReflectMethod != 0 }
+func (a *Attribute) Local() bool { return a.load()&AttrLocal != 0 }
+func (a *Attribute) Wrapper() bool { return a.load()&AttrWrapper != 0 }
+func (a *Attribute) NeedCtxt() bool { return a.load()&AttrNeedCtxt != 0 }
+func (a *Attribute) NoFrame() bool { return a.load()&AttrNoFrame != 0 }
+func (a *Attribute) Static() bool { return a.load()&AttrStatic != 0 }
+func (a *Attribute) WasInlined() bool { return a.load()&AttrWasInlined != 0 }
+func (a *Attribute) TopFrame() bool { return a.load()&AttrTopFrame != 0 }
+func (a *Attribute) Indexed() bool { return a.load()&AttrIndexed != 0 }
+func (a *Attribute) UsedInIface() bool { return a.load()&AttrUsedInIface != 0 }
+func (a *Attribute) ContentAddressable() bool { return a.load()&AttrContentAddressable != 0 }
+func (a *Attribute) ABIWrapper() bool { return a.load()&AttrABIWrapper != 0 }
func (a *Attribute) Set(flag Attribute, value bool) {
- if value {
- *a |= flag
- } else {
- *a &^= flag
+ for {
+ v0 := a.load()
+ v := v0
+ if value {
+ v |= flag
+ } else {
+ v &^= flag
+ }
+ if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
+ break
+ }
}
}
-func (a Attribute) ABI() ABI { return ABI(a / attrABIBase) }
+func (a *Attribute) ABI() ABI { return ABI(a.load() / attrABIBase) }
func (a *Attribute) SetABI(abi ABI) {
const mask = 1 // Only one ABI bit for now.
- *a = (*a &^ (mask * attrABIBase)) | Attribute(abi)*attrABIBase
+ for {
+ v0 := a.load()
+ v := (v0 &^ (mask * attrABIBase)) | Attribute(abi)*attrABIBase
+ if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
+ break
+ }
+ }
}
var textAttrStrings = [...]struct {
@@ -689,6 +716,7 @@ var textAttrStrings = [...]struct {
{bit: AttrTopFrame, s: "TOPFRAME"},
{bit: AttrIndexed, s: ""},
{bit: AttrContentAddressable, s: ""},
+ {bit: AttrABIWrapper, s: "ABIWRAPPER"},
}
// TextAttrString formats a for printing in as part of a TEXT prog.
@@ -723,8 +751,8 @@ func (s *LSym) String() string {
}
// The compiler needs *LSym to be assignable to cmd/compile/internal/ssa.Sym.
-func (s *LSym) CanBeAnSSASym() {
-}
+func (*LSym) CanBeAnSSASym() {}
+func (*LSym) CanBeAnSSAAux() {}
type Pcln struct {
// Aux symbols for pcln
@@ -754,6 +782,17 @@ type Auto struct {
Gotype *LSym
}
+// RegArg provides spill/fill information for a register-resident argument
+// to a function. These need spilling/filling in the safepoint/stackgrowth case.
+// At the time of fill/spill, the offset must be adjusted by the architecture-dependent
+// adjustment to hardware SP that occurs in a call instruction. E.g., for AMD64,
+// at Offset+8 because the return address was pushed.
+type RegArg struct {
+ Addr Addr
+ Reg int16
+ Spill, Unspill As
+}
+
// Link holds the context for writing object code from a compiler
// to be linker input or for reading that input into the linker.
type Link struct {
@@ -784,6 +823,7 @@ type Link struct {
DebugInfo func(fn *LSym, info *LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) // if non-nil, curfn is a *gc.Node
GenAbstractFunc func(fn *LSym)
Errors int
+ RegArgs []RegArg
InParallel bool // parallel backend phase in effect
UseBASEntries bool // use Base Address Selection Entries in location lists and PC ranges
@@ -832,6 +872,32 @@ func (ctxt *Link) Logf(format string, args ...interface{}) {
ctxt.Bso.Flush()
}
+func (ctxt *Link) SpillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
+ // Spill register args.
+ for _, ra := range ctxt.RegArgs {
+ spill := Appendp(last, pa)
+ spill.As = ra.Spill
+ spill.From.Type = TYPE_REG
+ spill.From.Reg = ra.Reg
+ spill.To = ra.Addr
+ last = spill
+ }
+ return last
+}
+
+func (ctxt *Link) UnspillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
+ // Unspill any spilled register args
+ for _, ra := range ctxt.RegArgs {
+ unspill := Appendp(last, pa)
+ unspill.As = ra.Unspill
+ unspill.From = ra.Addr
+ unspill.To.Type = TYPE_REG
+ unspill.To.Reg = ra.Reg
+ last = unspill
+ }
+ return last
+}
+
// The smallest possible offset from the hardware stack pointer to a local
// variable on the stack. Architectures that use a link register save its value
// on the stack in the function prologue and so always have a pointer between
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
index 2b096996f7a1b..679ce7eb8f61d 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -80,6 +80,11 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string
if !strings.HasPrefix(s.Name, "\"\".") {
continue
}
+ if s.ABIWrapper() {
+ // Don't create an args_stackmap symbol reference for an ABI
+ // wrapper function
+ continue
+ }
found := false
for p := s.Func().Text; p != nil; p = p.Link {
if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == objabi.FUNCDATA_ArgsPointerMaps {
@@ -134,6 +139,7 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
s.Set(AttrNoSplit, flag&NOSPLIT != 0)
s.Set(AttrReflectMethod, flag&REFLECTMETHOD != 0)
s.Set(AttrWrapper, flag&WRAPPER != 0)
+ s.Set(AttrABIWrapper, flag&ABIWRAPPER != 0)
s.Set(AttrNeedCtxt, flag&NEEDCTXT != 0)
s.Set(AttrNoFrame, flag&NOFRAME != 0)
s.Set(AttrTopFrame, flag&TOPFRAME != 0)
diff --git a/src/cmd/internal/obj/s390x/condition_code.go b/src/cmd/internal/obj/s390x/condition_code.go
index 764fc5bc6a855..f498fd6f77442 100644
--- a/src/cmd/internal/obj/s390x/condition_code.go
+++ b/src/cmd/internal/obj/s390x/condition_code.go
@@ -124,3 +124,5 @@ func (c CCMask) String() string {
// invalid
return fmt.Sprintf("Invalid (%#x)", c)
}
+
+func (CCMask) CanBeAnSSAAux() {}
diff --git a/src/cmd/internal/obj/s390x/rotate.go b/src/cmd/internal/obj/s390x/rotate.go
index 388bd40f41b2d..5407c8df11099 100644
--- a/src/cmd/internal/obj/s390x/rotate.go
+++ b/src/cmd/internal/obj/s390x/rotate.go
@@ -113,3 +113,5 @@ func (r RotateParams) OutMerge(mask uint64) *RotateParams {
func (r RotateParams) InMerge(mask uint64) *RotateParams {
return r.OutMerge(bits.RotateLeft64(mask, int(r.Amount)))
}
+
+func (RotateParams) CanBeAnSSAAux() {}
diff --git a/src/cmd/internal/obj/textflag.go b/src/cmd/internal/obj/textflag.go
index d2cec734b1c1b..2f55793285e2e 100644
--- a/src/cmd/internal/obj/textflag.go
+++ b/src/cmd/internal/obj/textflag.go
@@ -33,7 +33,7 @@ const (
// This function uses its incoming context register.
NEEDCTXT = 64
- // When passed to ggloblsym, causes Local to be set to true on the LSym it creates.
+ // When passed to objw.Global, causes Local to be set to true on the LSym it creates.
LOCAL = 128
// Allocate a word of thread local storage and store the offset from the
@@ -51,4 +51,7 @@ const (
// Function is the top of the call stack. Call stack unwinders should stop
// at this function.
TOPFRAME = 2048
+
+ // Function is an ABI wrapper.
+ ABIWRAPPER = 4096
)
diff --git a/src/cmd/internal/obj/x86/a.out.go b/src/cmd/internal/obj/x86/a.out.go
index 30c1a6a44547d..3be4b59da461f 100644
--- a/src/cmd/internal/obj/x86/a.out.go
+++ b/src/cmd/internal/obj/x86/a.out.go
@@ -263,6 +263,7 @@ const (
FREGRET = REG_X0
REGSP = REG_SP
REGCTXT = REG_DX
+ REGG = REG_R14 // g register in ABIInternal
REGEXT = REG_R15 // compiler allocates external registers R15 down
FREGMIN = REG_X0 + 5 // first register variable
FREGEXT = REG_X0 + 15 // first external register
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 184fb4308bd01..84de58a4c4b87 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -637,13 +637,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
- if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() {
- p = obj.Appendp(p, newprog)
- p = load_g_cx(ctxt, p, newprog) // load g into CX
+ var regg int16
+ if !p.From.Sym.NoSplit() || (p.From.Sym.Wrapper() && !p.From.Sym.ABIWrapper()) {
+ if ctxt.Arch.Family == sys.AMD64 && objabi.Regabi_enabled != 0 && cursym.ABI() == obj.ABIInternal {
+ regg = REGG // use the g register directly in ABIInternal
+ } else {
+ p = obj.Appendp(p, newprog)
+ p = load_g_cx(ctxt, p, newprog) // load g into CX
+ regg = REG_CX
+ }
}
if !cursym.Func().Text.From.Sym.NoSplit() {
- p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg)) // emit split check
+ p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg), regg) // emit split check
}
// Delve debugger would like the next instruction to be noted as the end of the function prologue.
@@ -690,12 +696,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.To.Reg = REG_BP
}
- if cursym.Func().Text.From.Sym.Wrapper() {
+ if cursym.Func().Text.From.Sym.Wrapper() && !cursym.Func().Text.From.Sym.ABIWrapper() {
// if g._panic != nil && g._panic.argp == FP {
// g._panic.argp = bottom-of-frame
// }
//
- // MOVQ g_panic(CX), BX
+ // MOVQ g_panic(g), BX
// TESTQ BX, BX
// JNE checkargp
// end:
@@ -718,7 +724,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p = obj.Appendp(p, newprog)
p.As = AMOVQ
p.From.Type = obj.TYPE_MEM
- p.From.Reg = REG_CX
+ p.From.Reg = regg
p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // g_panic
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_BX
@@ -969,9 +975,9 @@ func load_g_cx(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) *obj.Prog {
// Append code to p to check for stack split.
// Appends to (does not overwrite) p.
-// Assumes g is in CX.
+// Assumes g is in rg.
// Returns last new instruction.
-func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32) *obj.Prog {
+func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32, rg int16) *obj.Prog {
cmp := ACMPQ
lea := ALEAQ
mov := AMOVQ
@@ -993,7 +999,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
p.As = cmp
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_SP
- indir_cx(ctxt, &p.To)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = rg
p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if cursym.CFunc() {
p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
@@ -1021,7 +1028,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
p.As = cmp
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_AX
- indir_cx(ctxt, &p.To)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = rg
p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if cursym.CFunc() {
p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
@@ -1047,7 +1055,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
p = obj.Appendp(p, newprog)
p.As = mov
- indir_cx(ctxt, &p.From)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = rg
p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if cursym.CFunc() {
p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
@@ -1114,7 +1123,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
spfix.Spadj = -framesize
pcdata := ctxt.EmitEntryStackMap(cursym, spfix, newprog)
- pcdata = ctxt.StartUnsafePoint(pcdata, newprog)
+ spill := ctxt.StartUnsafePoint(pcdata, newprog)
+ pcdata = ctxt.SpillRegisterArgs(spill, newprog)
call := obj.Appendp(pcdata, newprog)
call.Pos = cursym.Func().Text.Pos
@@ -1139,7 +1149,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
progedit(ctxt, callend.Link, newprog)
}
- pcdata = ctxt.EndUnsafePoint(callend, newprog, -1)
+ pcdata = ctxt.UnspillRegisterArgs(callend, newprog)
+ pcdata = ctxt.EndUnsafePoint(pcdata, newprog, -1)
jmp := obj.Appendp(pcdata, newprog)
jmp.As = obj.AJMP
@@ -1147,9 +1158,9 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
jmp.To.SetTarget(cursym.Func().Text.Link)
jmp.Spadj = +framesize
- jls.To.SetTarget(call)
+ jls.To.SetTarget(spill)
if q1 != nil {
- q1.To.SetTarget(call)
+ q1.To.SetTarget(spill)
}
return end
diff --git a/src/cmd/internal/objabi/path.go b/src/cmd/internal/objabi/path.go
index fd1c9981c69d0..1a0784cf7f0d2 100644
--- a/src/cmd/internal/objabi/path.go
+++ b/src/cmd/internal/objabi/path.go
@@ -56,6 +56,8 @@ func IsRuntimePackagePath(pkgpath string) bool {
rval = true
case "syscall":
rval = true
+ case "crypto/x509/internal/macos": // libc function wrappers need to be ABIInternal
+ rval = true
default:
rval = strings.HasPrefix(pkgpath, "runtime/internal")
}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 52035e96301cf..92d38bb63e54a 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -55,6 +55,7 @@ func isRuntimeDepPkg(pkg string) bool {
switch pkg {
case "runtime",
"sync/atomic", // runtime may call to sync/atomic, due to go:linkname
+ "internal/abi", // used by reflectcall (and maybe more)
"internal/bytealg", // for IndexByte
"internal/cpu": // for cpu features
return true
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 17d5040827c0e..314896824a03f 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -489,10 +489,16 @@ func (ctxt *Link) loadlib() {
case 0:
// nothing to do
case 1, 2:
- flags = loader.FlagStrictDups
+ flags |= loader.FlagStrictDups
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
+ if !*flagAbiWrap || ctxt.linkShared {
+ // Use ABI aliases if ABI wrappers are not used.
+ // TODO: for now we still use ABI aliases in shared linkage, even if
+ // the wrapper is enabled.
+ flags |= loader.FlagUseABIAlias
+ }
elfsetstring1 := func(str string, off int) { elfsetstring(ctxt, 0, str, off) }
ctxt.loader = loader.NewLoader(flags, elfsetstring1, &ctxt.ErrorReporter.ErrorReporter)
ctxt.ErrorReporter.SymName = func(s loader.Sym) string {
@@ -2091,6 +2097,26 @@ func ldshlibsyms(ctxt *Link, shlib string) {
Errorf(nil, "cannot read symbols from shared library: %s", libpath)
return
}
+
+ // collect text symbol ABI versions.
+ symabi := make(map[string]int) // map (unmangled) symbol name to version
+ if *flagAbiWrap {
+ for _, elfsym := range syms {
+ if elf.ST_TYPE(elfsym.Info) != elf.STT_FUNC {
+ continue
+ }
+ // Demangle the name. Keep in sync with symtab.go:putelfsym.
+ if strings.HasSuffix(elfsym.Name, ".abiinternal") {
+ // ABIInternal symbol has mangled name, so the primary symbol is ABI0.
+ symabi[strings.TrimSuffix(elfsym.Name, ".abiinternal")] = 0
+ }
+ if strings.HasSuffix(elfsym.Name, ".abi0") {
+ // ABI0 symbol has mangled name, so the primary symbol is ABIInternal.
+ symabi[strings.TrimSuffix(elfsym.Name, ".abi0")] = sym.SymVerABIInternal
+ }
+ }
+ }
+
for _, elfsym := range syms {
if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
continue
@@ -2099,12 +2125,23 @@ func ldshlibsyms(ctxt *Link, shlib string) {
// Symbols whose names start with "type." are compiler
// generated, so make functions with that prefix internal.
ver := 0
+ symname := elfsym.Name // (unmangled) symbol name
if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type.") {
ver = sym.SymVerABIInternal
+ } else if *flagAbiWrap && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC {
+ if strings.HasSuffix(elfsym.Name, ".abiinternal") {
+ ver = sym.SymVerABIInternal
+ symname = strings.TrimSuffix(elfsym.Name, ".abiinternal")
+ } else if strings.HasSuffix(elfsym.Name, ".abi0") {
+ ver = 0
+ symname = strings.TrimSuffix(elfsym.Name, ".abi0")
+ } else if abi, ok := symabi[elfsym.Name]; ok {
+ ver = abi
+ }
}
l := ctxt.loader
- s := l.LookupOrCreateSym(elfsym.Name, ver)
+ s := l.LookupOrCreateSym(symname, ver)
// Because loadlib above loads all .a files before loading
// any shared libraries, any non-dynimport symbols we find
@@ -2129,6 +2166,10 @@ func ldshlibsyms(ctxt *Link, shlib string) {
}
}
+ if symname != elfsym.Name {
+ l.SetSymExtname(s, elfsym.Name)
+ }
+
// For function symbols, we don't know what ABI is
// available, so alias it under both ABIs.
//
@@ -2137,7 +2178,12 @@ func ldshlibsyms(ctxt *Link, shlib string) {
// mangle Go function names in the .so to include the
// ABI.
if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
- alias := ctxt.loader.LookupOrCreateSym(elfsym.Name, sym.SymVerABIInternal)
+ if *flagAbiWrap {
+ if _, ok := symabi[symname]; ok {
+ continue // only use alias for functions w/o ABI wrappers
+ }
+ }
+ alias := ctxt.loader.LookupOrCreateSym(symname, sym.SymVerABIInternal)
if l.SymType(alias) != 0 {
continue
}
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index 5a096f1b3b9fd..cbd811846b6ff 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -92,11 +92,10 @@ var (
FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")
flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
-
- cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
- memprofile = flag.String("memprofile", "", "write memory profile to `file`")
- memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
-
+ cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
+ memprofile = flag.String("memprofile", "", "write memory profile to `file`")
+ memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
+ flagAbiWrap = flag.Bool("abiwrap", objabi.Regabi_enabled != 0, "support ABI wrapper functions")
benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
)
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index c98e4de03f8bf..85a8ff42ad0d0 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -102,6 +102,41 @@ func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
elfshnum = xosect.Elfsect.(*ElfShdr).shnum
}
+ sname := ldr.SymExtname(x)
+
+ // For functions with ABI wrappers, we have to make sure that we
+ // don't wind up with two elf symbol table entries with the same
+ // name (since this will generated an error from the external
+ // linker). In the CgoExportStatic case, we want the ABI0 symbol
+ // to have the primary symbol table entry (since it's going to be
+ // called from C), so we rename the ABIInternal symbol. In all
+ // other cases, we rename the ABI0 symbol, since we want
+ // cross-load-module calls to target ABIInternal.
+ //
+ // TODO: generalize this for non-ELF (put the rename code in the
+ // loader, and store the rename result in SymExtname).
+ //
+ // TODO: avoid the ldr.Lookup calls below by instead using an aux
+ // sym or marker relocation to associate the wrapper with the
+ // wrapped function.
+ //
+ if *flagAbiWrap {
+ if !ldr.IsExternal(x) && ldr.SymType(x) == sym.STEXT {
+ // First case
+ if ldr.SymVersion(x) == sym.SymVerABIInternal {
+ if s2 := ldr.Lookup(sname, sym.SymVerABI0); s2 != 0 && ldr.AttrCgoExportStatic(s2) && ldr.SymType(s2) == sym.STEXT {
+ sname = sname + ".abiinternal"
+ }
+ }
+ // Second case
+ if ldr.SymVersion(x) == sym.SymVerABI0 && !ldr.AttrCgoExportStatic(x) {
+ if s2 := ldr.Lookup(sname, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT {
+ sname = sname + ".abi0"
+ }
+ }
+ }
+ }
+
// One pass for each binding: elf.STB_LOCAL, elf.STB_GLOBAL,
// maybe one day elf.STB_WEAK.
bind := elf.STB_GLOBAL
@@ -140,8 +175,6 @@ func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
other |= 3 << 5
}
- sname := ldr.SymExtname(x)
-
// When dynamically linking, we create Symbols by reading the names from
// the symbol tables of the shared libraries and so the names need to
// match exactly. Tools like DTrace will have to wait for now.
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 971cc432ff54b..98c2131c2b43f 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -322,6 +322,7 @@ type extSymPayload struct {
const (
// Loader.flags
FlagStrictDups = 1 << iota
+ FlagUseABIAlias
)
func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader {
@@ -2270,6 +2271,9 @@ func abiToVer(abi uint16, localSymVersion int) int {
// symbol. If the sym in question is not an alias, the sym itself is
// returned.
func (l *Loader) ResolveABIAlias(s Sym) Sym {
+ if l.flags&FlagUseABIAlias == 0 {
+ return s
+ }
if s == 0 {
return 0
}
diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go
index 9b776d4b8571e..0572c6ccd8108 100644
--- a/src/crypto/x509/internal/macos/corefoundation.go
+++ b/src/crypto/x509/internal/macos/corefoundation.go
@@ -39,7 +39,6 @@ type CFString CFRef
const kCFAllocatorDefault = 0
const kCFStringEncodingUTF8 = 0x08000100
-//go:linkname x509_CFStringCreateWithBytes x509_CFStringCreateWithBytes
//go:cgo_import_dynamic x509_CFStringCreateWithBytes CFStringCreateWithBytes "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
// StringToCFString returns a copy of the UTF-8 contents of s as a new CFString.
@@ -52,7 +51,6 @@ func StringToCFString(s string) CFString {
}
func x509_CFStringCreateWithBytes_trampoline()
-//go:linkname x509_CFDictionaryGetValueIfPresent x509_CFDictionaryGetValueIfPresent
//go:cgo_import_dynamic x509_CFDictionaryGetValueIfPresent CFDictionaryGetValueIfPresent "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFDictionaryGetValueIfPresent(dict CFRef, key CFString) (value CFRef, ok bool) {
@@ -67,7 +65,6 @@ func x509_CFDictionaryGetValueIfPresent_trampoline()
const kCFNumberSInt32Type = 3
-//go:linkname x509_CFNumberGetValue x509_CFNumberGetValue
//go:cgo_import_dynamic x509_CFNumberGetValue CFNumberGetValue "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFNumberGetValue(num CFRef) (int32, error) {
@@ -81,7 +78,6 @@ func CFNumberGetValue(num CFRef) (int32, error) {
}
func x509_CFNumberGetValue_trampoline()
-//go:linkname x509_CFDataGetLength x509_CFDataGetLength
//go:cgo_import_dynamic x509_CFDataGetLength CFDataGetLength "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFDataGetLength(data CFRef) int {
@@ -90,7 +86,6 @@ func CFDataGetLength(data CFRef) int {
}
func x509_CFDataGetLength_trampoline()
-//go:linkname x509_CFDataGetBytePtr x509_CFDataGetBytePtr
//go:cgo_import_dynamic x509_CFDataGetBytePtr CFDataGetBytePtr "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFDataGetBytePtr(data CFRef) uintptr {
@@ -99,7 +94,6 @@ func CFDataGetBytePtr(data CFRef) uintptr {
}
func x509_CFDataGetBytePtr_trampoline()
-//go:linkname x509_CFArrayGetCount x509_CFArrayGetCount
//go:cgo_import_dynamic x509_CFArrayGetCount CFArrayGetCount "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFArrayGetCount(array CFRef) int {
@@ -108,7 +102,6 @@ func CFArrayGetCount(array CFRef) int {
}
func x509_CFArrayGetCount_trampoline()
-//go:linkname x509_CFArrayGetValueAtIndex x509_CFArrayGetValueAtIndex
//go:cgo_import_dynamic x509_CFArrayGetValueAtIndex CFArrayGetValueAtIndex "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFArrayGetValueAtIndex(array CFRef, index int) CFRef {
@@ -117,7 +110,6 @@ func CFArrayGetValueAtIndex(array CFRef, index int) CFRef {
}
func x509_CFArrayGetValueAtIndex_trampoline()
-//go:linkname x509_CFEqual x509_CFEqual
//go:cgo_import_dynamic x509_CFEqual CFEqual "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFEqual(a, b CFRef) bool {
@@ -126,7 +118,6 @@ func CFEqual(a, b CFRef) bool {
}
func x509_CFEqual_trampoline()
-//go:linkname x509_CFRelease x509_CFRelease
//go:cgo_import_dynamic x509_CFRelease CFRelease "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFRelease(ref CFRef) {
diff --git a/src/crypto/x509/internal/macos/corefoundation.s b/src/crypto/x509/internal/macos/corefoundation.s
index a4495d68dd357..1ce39fac9d752 100644
--- a/src/crypto/x509/internal/macos/corefoundation.s
+++ b/src/crypto/x509/internal/macos/corefoundation.s
@@ -6,21 +6,24 @@
#include "textflag.h"
-TEXT ·x509_CFArrayGetCount_trampoline(SB),NOSPLIT,$0-0
+// The trampolines are ABIInternal as they are address-taken in
+// Go code.
+
+TEXT ·x509_CFArrayGetCount_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFArrayGetCount(SB)
-TEXT ·x509_CFArrayGetValueAtIndex_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFArrayGetValueAtIndex_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFArrayGetValueAtIndex(SB)
-TEXT ·x509_CFDataGetBytePtr_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFDataGetBytePtr_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFDataGetBytePtr(SB)
-TEXT ·x509_CFDataGetLength_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFDataGetLength_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFDataGetLength(SB)
-TEXT ·x509_CFStringCreateWithBytes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFStringCreateWithBytes_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFStringCreateWithBytes(SB)
-TEXT ·x509_CFRelease_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFRelease_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFRelease(SB)
-TEXT ·x509_CFDictionaryGetValueIfPresent_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFDictionaryGetValueIfPresent_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFDictionaryGetValueIfPresent(SB)
-TEXT ·x509_CFNumberGetValue_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFNumberGetValue_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFNumberGetValue(SB)
-TEXT ·x509_CFEqual_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFEqual_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFEqual(SB)
diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go
index 5e39e93666629..3163e3a4f727f 100644
--- a/src/crypto/x509/internal/macos/security.go
+++ b/src/crypto/x509/internal/macos/security.go
@@ -63,7 +63,6 @@ var ErrNoTrustSettings = errors.New("no trust settings found")
const errSecNoTrustSettings = -25263
-//go:linkname x509_SecTrustSettingsCopyCertificates x509_SecTrustSettingsCopyCertificates
//go:cgo_import_dynamic x509_SecTrustSettingsCopyCertificates SecTrustSettingsCopyCertificates "/System/Library/Frameworks/Security.framework/Versions/A/Security"
func SecTrustSettingsCopyCertificates(domain SecTrustSettingsDomain) (certArray CFRef, err error) {
@@ -80,7 +79,6 @@ func x509_SecTrustSettingsCopyCertificates_trampoline()
const kSecFormatX509Cert int32 = 9
-//go:linkname x509_SecItemExport x509_SecItemExport
//go:cgo_import_dynamic x509_SecItemExport SecItemExport "/System/Library/Frameworks/Security.framework/Versions/A/Security"
func SecItemExport(cert CFRef) (data CFRef, err error) {
@@ -95,7 +93,6 @@ func x509_SecItemExport_trampoline()
const errSecItemNotFound = -25300
-//go:linkname x509_SecTrustSettingsCopyTrustSettings x509_SecTrustSettingsCopyTrustSettings
//go:cgo_import_dynamic x509_SecTrustSettingsCopyTrustSettings SecTrustSettingsCopyTrustSettings "/System/Library/Frameworks/Security.framework/Versions/A/Security"
func SecTrustSettingsCopyTrustSettings(cert CFRef, domain SecTrustSettingsDomain) (trustSettings CFRef, err error) {
@@ -110,7 +107,6 @@ func SecTrustSettingsCopyTrustSettings(cert CFRef, domain SecTrustSettingsDomain
}
func x509_SecTrustSettingsCopyTrustSettings_trampoline()
-//go:linkname x509_SecPolicyCopyProperties x509_SecPolicyCopyProperties
//go:cgo_import_dynamic x509_SecPolicyCopyProperties SecPolicyCopyProperties "/System/Library/Frameworks/Security.framework/Versions/A/Security"
func SecPolicyCopyProperties(policy CFRef) CFRef {
diff --git a/src/crypto/x509/internal/macos/security.s b/src/crypto/x509/internal/macos/security.s
index bd446dbcbe260..bea265a5ef916 100644
--- a/src/crypto/x509/internal/macos/security.s
+++ b/src/crypto/x509/internal/macos/security.s
@@ -6,11 +6,14 @@
#include "textflag.h"
-TEXT ·x509_SecTrustSettingsCopyCertificates_trampoline(SB),NOSPLIT,$0-0
+// The trampolines are ABIInternal as they are address-taken in
+// Go code.
+
+TEXT ·x509_SecTrustSettingsCopyCertificates_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustSettingsCopyCertificates(SB)
-TEXT ·x509_SecItemExport_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_SecItemExport_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecItemExport(SB)
-TEXT ·x509_SecTrustSettingsCopyTrustSettings_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_SecTrustSettingsCopyTrustSettings_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustSettingsCopyTrustSettings(SB)
-TEXT ·x509_SecPolicyCopyProperties_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_SecPolicyCopyProperties_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecPolicyCopyProperties(SB)
diff --git a/src/embed/embed.go b/src/embed/embed.go
index 98da870ac6b00..7f2719d2a7806 100644
--- a/src/embed/embed.go
+++ b/src/embed/embed.go
@@ -143,7 +143,7 @@ import (
// See the package documentation for more details about initializing an FS.
type FS struct {
// The compiler knows the layout of this struct.
- // See cmd/compile/internal/gc's initEmbed.
+ // See cmd/compile/internal/staticdata's WriteEmbed.
//
// The files list is sorted by name but not by simple string comparison.
// Instead, each file's name takes the form "dir/elem" or "dir/elem/".
@@ -213,7 +213,7 @@ var (
// It implements fs.FileInfo and fs.DirEntry.
type file struct {
// The compiler knows the layout of this struct.
- // See cmd/compile/internal/gc's initEmbed.
+ // See cmd/compile/internal/staticdata's WriteEmbed.
name string
data string
hash [16]byte // truncated SHA256 hash
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index c97c668cc4922..e5c849e8f53ea 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -76,8 +76,12 @@ var depsRules = `
unicode/utf8, unicode/utf16, unicode,
unsafe;
+ # These packages depend only on unsafe.
+ unsafe
+ < internal/abi;
+
# RUNTIME is the core runtime group of packages, all of them very light-weight.
- internal/cpu, unsafe
+ internal/abi, internal/cpu, unsafe
< internal/bytealg
< internal/unsafeheader
< runtime/internal/sys
@@ -285,7 +289,7 @@ var depsRules = `
math/big, go/token
< go/constant;
- container/heap, go/constant, go/parser
+ container/heap, go/constant, go/parser, regexp
< go/types;
FMT
diff --git a/src/go/constant/value.go b/src/go/constant/value.go
index 46414423f2d12..223c363d9b9ee 100644
--- a/src/go/constant/value.go
+++ b/src/go/constant/value.go
@@ -17,6 +17,7 @@ import (
"go/token"
"math"
"math/big"
+ "math/bits"
"strconv"
"strings"
"sync"
@@ -607,7 +608,11 @@ func Make(x interface{}) Value {
func BitLen(x Value) int {
switch x := x.(type) {
case int64Val:
- return i64toi(x).val.BitLen()
+ u := uint64(x)
+ if x < 0 {
+ u = uint64(-x)
+ }
+ return 64 - bits.LeadingZeros64(u)
case intVal:
return x.val.BitLen()
case unknownVal:
@@ -1015,52 +1020,55 @@ func match(x, y Value) (_, _ Value) {
}
// ord(x) <= ord(y)
- switch x := x.(type) {
+ // Prefer to return the original x and y arguments when possible,
+ // to avoid unnecessary heap allocations.
+
+ switch x1 := x.(type) {
case boolVal, *stringVal, complexVal:
return x, y
case int64Val:
- switch y := y.(type) {
+ switch y.(type) {
case int64Val:
return x, y
case intVal:
- return i64toi(x), y
+ return i64toi(x1), y
case ratVal:
- return i64tor(x), y
+ return i64tor(x1), y
case floatVal:
- return i64tof(x), y
+ return i64tof(x1), y
case complexVal:
- return vtoc(x), y
+ return vtoc(x1), y
}
case intVal:
- switch y := y.(type) {
+ switch y.(type) {
case intVal:
return x, y
case ratVal:
- return itor(x), y
+ return itor(x1), y
case floatVal:
- return itof(x), y
+ return itof(x1), y
case complexVal:
- return vtoc(x), y
+ return vtoc(x1), y
}
case ratVal:
- switch y := y.(type) {
+ switch y.(type) {
case ratVal:
return x, y
case floatVal:
- return rtof(x), y
+ return rtof(x1), y
case complexVal:
- return vtoc(x), y
+ return vtoc(x1), y
}
case floatVal:
- switch y := y.(type) {
+ switch y.(type) {
case floatVal:
return x, y
case complexVal:
- return vtoc(x), y
+ return vtoc(x1), y
}
}
diff --git a/src/go/constant/value_test.go b/src/go/constant/value_test.go
index 286677407d98d..91ad0b0c2b9d9 100644
--- a/src/go/constant/value_test.go
+++ b/src/go/constant/value_test.go
@@ -706,3 +706,24 @@ func BenchmarkStringAdd(b *testing.B) {
})
}
}
+
+var bitLenTests = []struct {
+ val int64
+ want int
+}{
+ {0, 0},
+ {1, 1},
+ {-16, 5},
+ {1 << 61, 62},
+ {1 << 62, 63},
+ {-1 << 62, 63},
+ {-1 << 63, 64},
+}
+
+func TestBitLen(t *testing.T) {
+ for _, test := range bitLenTests {
+ if got := BitLen(MakeInt64(test.val)); got != test.want {
+ t.Errorf("%v: got %v, want %v", test.val, got, test.want)
+ }
+ }
+}
diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go
index c59dd16533173..a3184e7641aa0 100644
--- a/src/go/internal/gcimporter/iimport.go
+++ b/src/go/internal/gcimporter/iimport.go
@@ -15,6 +15,7 @@ import (
"go/token"
"go/types"
"io"
+ "math/big"
"sort"
)
@@ -320,7 +321,9 @@ func (r *importReader) value() (typ types.Type, val constant.Value) {
val = constant.MakeString(r.string())
case types.IsInteger:
- val = r.mpint(b)
+ var x big.Int
+ r.mpint(&x, b)
+ val = constant.Make(&x)
case types.IsFloat:
val = r.mpfloat(b)
@@ -365,8 +368,8 @@ func intSize(b *types.Basic) (signed bool, maxBytes uint) {
return
}
-func (r *importReader) mpint(b *types.Basic) constant.Value {
- signed, maxBytes := intSize(b)
+func (r *importReader) mpint(x *big.Int, typ *types.Basic) {
+ signed, maxBytes := intSize(typ)
maxSmall := 256 - maxBytes
if signed {
@@ -385,7 +388,8 @@ func (r *importReader) mpint(b *types.Basic) constant.Value {
v = ^v
}
}
- return constant.MakeInt64(v)
+ x.SetInt64(v)
+ return
}
v := -n
@@ -395,39 +399,23 @@ func (r *importReader) mpint(b *types.Basic) constant.Value {
if v < 1 || uint(v) > maxBytes {
errorf("weird decoding: %v, %v => %v", n, signed, v)
}
-
- buf := make([]byte, v)
- io.ReadFull(&r.declReader, buf)
-
- // convert to little endian
- // TODO(gri) go/constant should have a more direct conversion function
- // (e.g., once it supports a big.Float based implementation)
- for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
- buf[i], buf[j] = buf[j], buf[i]
- }
-
- x := constant.MakeFromBytes(buf)
+ b := make([]byte, v)
+ io.ReadFull(&r.declReader, b)
+ x.SetBytes(b)
if signed && n&1 != 0 {
- x = constant.UnaryOp(token.SUB, x, 0)
+ x.Neg(x)
}
- return x
}
-func (r *importReader) mpfloat(b *types.Basic) constant.Value {
- x := r.mpint(b)
- if constant.Sign(x) == 0 {
- return x
- }
-
- exp := r.int64()
- switch {
- case exp > 0:
- x = constant.Shift(x, token.SHL, uint(exp))
- case exp < 0:
- d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
- x = constant.BinaryOp(x, token.QUO, d)
+func (r *importReader) mpfloat(typ *types.Basic) constant.Value {
+ var mant big.Int
+ r.mpint(&mant, typ)
+ var f big.Float
+ f.SetInt(&mant)
+ if f.Sign() != 0 {
+ f.SetMantExp(&f, int(r.int64()))
}
- return x
+ return constant.Make(&f)
}
func (r *importReader) ident() string {
diff --git a/src/go/types/api.go b/src/go/types/api.go
index d625959817203..b5bbb2d97dc7e 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -101,6 +101,13 @@ type ImporterFrom interface {
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
+ // GoVersion describes the accepted Go language version. The string
+ // must follow the format "go%d.%d" (e.g. "go1.12") or it must be
+ // empty; an empty string indicates the latest language version.
+ // If the format is invalid, invoking the type checker will cause a
+ // panic.
+ GoVersion string
+
// If IgnoreFuncBodies is set, function bodies are not
// type-checked.
IgnoreFuncBodies bool
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 75cebc9826112..dde451ee3cf76 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -42,7 +42,7 @@ func mustTypecheck(t *testing.T, path, source string, info *Info) string {
return pkg.Name()
}
-func mayTypecheck(t *testing.T, path, source string, info *Info) string {
+func mayTypecheck(t *testing.T, path, source string, info *Info) (string, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, source, 0)
if f == nil { // ignore errors unless f is nil
@@ -52,8 +52,8 @@ func mayTypecheck(t *testing.T, path, source string, info *Info) string {
Error: func(err error) {},
Importer: importer.Default(),
}
- pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
- return pkg.Name()
+ pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
+ return pkg.Name(), err
}
func TestValuesInfo(t *testing.T) {
@@ -175,6 +175,9 @@ func TestValuesInfo(t *testing.T) {
}
func TestTypesInfo(t *testing.T) {
+ // Test sources that are not expected to typecheck must start with the broken prefix.
+ const broken = "package broken_"
+
var tests = []struct {
src string
expr string // expression
@@ -187,6 +190,39 @@ func TestTypesInfo(t *testing.T) {
{`package b3; var x interface{} = 0i`, `0i`, `complex128`},
{`package b4; var x interface{} = "foo"`, `"foo"`, `string`},
+ // uses of nil
+ {`package n0; var _ *int = nil`, `nil`, `untyped nil`},
+ {`package n1; var _ func() = nil`, `nil`, `untyped nil`},
+ {`package n2; var _ []byte = nil`, `nil`, `untyped nil`},
+ {`package n3; var _ map[int]int = nil`, `nil`, `untyped nil`},
+ {`package n4; var _ chan int = nil`, `nil`, `untyped nil`},
+ {`package n5; var _ interface{} = nil`, `nil`, `untyped nil`},
+ {`package n6; import "unsafe"; var _ unsafe.Pointer = nil`, `nil`, `untyped nil`},
+
+ {`package n10; var (x *int; _ = x == nil)`, `nil`, `untyped nil`},
+ {`package n11; var (x func(); _ = x == nil)`, `nil`, `untyped nil`},
+ {`package n12; var (x []byte; _ = x == nil)`, `nil`, `untyped nil`},
+ {`package n13; var (x map[int]int; _ = x == nil)`, `nil`, `untyped nil`},
+ {`package n14; var (x chan int; _ = x == nil)`, `nil`, `untyped nil`},
+ {`package n15; var (x interface{}; _ = x == nil)`, `nil`, `untyped nil`},
+ {`package n15; import "unsafe"; var (x unsafe.Pointer; _ = x == nil)`, `nil`, `untyped nil`},
+
+ {`package n20; var _ = (*int)(nil)`, `nil`, `untyped nil`},
+ {`package n21; var _ = (func())(nil)`, `nil`, `untyped nil`},
+ {`package n22; var _ = ([]byte)(nil)`, `nil`, `untyped nil`},
+ {`package n23; var _ = (map[int]int)(nil)`, `nil`, `untyped nil`},
+ {`package n24; var _ = (chan int)(nil)`, `nil`, `untyped nil`},
+ {`package n25; var _ = (interface{})(nil)`, `nil`, `untyped nil`},
+ {`package n26; import "unsafe"; var _ = unsafe.Pointer(nil)`, `nil`, `untyped nil`},
+
+ {`package n30; func f(*int) { f(nil) }`, `nil`, `untyped nil`},
+ {`package n31; func f(func()) { f(nil) }`, `nil`, `untyped nil`},
+ {`package n32; func f([]byte) { f(nil) }`, `nil`, `untyped nil`},
+ {`package n33; func f(map[int]int) { f(nil) }`, `nil`, `untyped nil`},
+ {`package n34; func f(chan int) { f(nil) }`, `nil`, `untyped nil`},
+ {`package n35; func f(interface{}) { f(nil) }`, `nil`, `untyped nil`},
+ {`package n35; import "unsafe"; func f(unsafe.Pointer) { f(nil) }`, `nil`, `untyped nil`},
+
// comma-ok expressions
{`package p0; var x interface{}; var _, _ = x.(int)`,
`x.(int)`,
@@ -268,17 +304,27 @@ func TestTypesInfo(t *testing.T) {
},
// tests for broken code that doesn't parse or type-check
- {`package x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`},
- {`package x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`},
- {`package x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`},
- {`package x3; var x = panic("");`, `panic`, `func(interface{})`},
+ {broken + `x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`},
+ {broken + `x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`},
+ {broken + `x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`},
+ {broken + `x3; var x = panic("");`, `panic`, `func(interface{})`},
{`package x4; func _() { panic("") }`, `panic`, `func(interface{})`},
- {`package x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`},
+ {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`},
}
for _, test := range tests {
info := Info{Types: make(map[ast.Expr]TypeAndValue)}
- name := mayTypecheck(t, "TypesInfo", test.src, &info)
+ var name string
+ if strings.HasPrefix(test.src, broken) {
+ var err error
+ name, err = mayTypecheck(t, "TypesInfo", test.src, &info)
+ if err == nil {
+ t.Errorf("package %s: expected to fail but passed", name)
+ continue
+ }
+ } else {
+ name = mustTypecheck(t, "TypesInfo", test.src, &info)
+ }
// look for expression type
var typ Type
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
index 616564b567d73..d6f18c9bee1e9 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -120,6 +120,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
+ lhs.used = true
return nil
}
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index fd35f7867696d..078ed4488d11b 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -353,8 +353,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return
}
- if ok, code := x.assignableTo(check, m.key, nil); !ok {
- check.invalidArg(x, code, "%s is not assignable to %s", x, m.key)
+ check.assignment(x, m.key, "argument to delete")
+ if x.mode == invalid {
return
}
diff --git a/src/go/types/check.go b/src/go/types/check.go
index 280792e838ef2..3bc8ee067c2b1 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -8,6 +8,7 @@ package types
import (
"errors"
+ "fmt"
"go/ast"
"go/constant"
"go/token"
@@ -69,6 +70,12 @@ type importKey struct {
path, dir string
}
+// A dotImportKey describes a dot-imported object in the given scope.
+type dotImportKey struct {
+ scope *Scope
+ obj Object
+}
+
// A Checker maintains the state of the type checker.
// It must be created with NewChecker.
type Checker struct {
@@ -78,16 +85,18 @@ type Checker struct {
fset *token.FileSet
pkg *Package
*Info
- objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
- impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
- posMap map[*Interface][]token.Pos // maps interface types to lists of embedded interface positions
- pkgCnt map[string]int // counts number of imported packages with a given name (for better error messages)
+ version version // accepted language version
+ objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
+ impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
+ posMap map[*Interface][]token.Pos // maps interface types to lists of embedded interface positions
+ pkgCnt map[string]int // counts number of imported packages with a given name (for better error messages)
// information collected during type-checking of a set of package files
// (initialized by Files, valid only for the duration of check.Files;
// maps and lists are allocated on demand)
- files []*ast.File // package files
- unusedDotImports map[*Scope]map[*Package]*ast.ImportSpec // unused dot-imported packages
+ files []*ast.File // package files
+ imports []*PkgName // list of imported packages
+ dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
firstErr error // first error encountered
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
@@ -104,22 +113,6 @@ type Checker struct {
indent int // indentation for tracing
}
-// addUnusedImport adds the position of a dot-imported package
-// pkg to the map of dot imports for the given file scope.
-func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, spec *ast.ImportSpec) {
- mm := check.unusedDotImports
- if mm == nil {
- mm = make(map[*Scope]map[*Package]*ast.ImportSpec)
- check.unusedDotImports = mm
- }
- m := mm[scope]
- if m == nil {
- m = make(map[*Package]*ast.ImportSpec)
- mm[scope] = m
- }
- m[pkg] = spec
-}
-
// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
func (check *Checker) addDeclDep(to Object) {
from := check.decl
@@ -185,15 +178,21 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
info = new(Info)
}
+ version, err := parseGoVersion(conf.GoVersion)
+ if err != nil {
+ panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
+ }
+
return &Checker{
- conf: conf,
- fset: fset,
- pkg: pkg,
- Info: info,
- objMap: make(map[Object]*declInfo),
- impMap: make(map[importKey]*Package),
- posMap: make(map[*Interface][]token.Pos),
- pkgCnt: make(map[string]int),
+ conf: conf,
+ fset: fset,
+ pkg: pkg,
+ Info: info,
+ version: version,
+ objMap: make(map[Object]*declInfo),
+ impMap: make(map[importKey]*Package),
+ posMap: make(map[*Interface][]token.Pos),
+ pkgCnt: make(map[string]int),
}
}
@@ -202,7 +201,8 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
func (check *Checker) initFiles(files []*ast.File) {
// start with a clean slate (check.Files may be called multiple times)
check.files = nil
- check.unusedDotImports = nil
+ check.imports = nil
+ check.dotImportMap = nil
check.firstErr = nil
check.methods = nil
@@ -272,10 +272,16 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
if !check.conf.DisableUnusedImportCheck {
check.unusedImports()
}
+ // no longer needed - release memory
+ check.imports = nil
+ check.dotImportMap = nil
check.recordUntyped()
check.pkg.complete = true
+
+ // TODO(rFindley) There's more memory we should release at this point.
+
return
}
diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go
index ce31dab68bb18..ca7d926ca9039 100644
--- a/src/go/types/check_test.go
+++ b/src/go/types/check_test.go
@@ -27,12 +27,14 @@ package types_test
import (
"flag"
+ "fmt"
"go/ast"
"go/importer"
"go/parser"
"go/scanner"
"go/token"
"internal/testenv"
+ "io/ioutil"
"os"
"path/filepath"
"regexp"
@@ -45,57 +47,10 @@ import (
var (
haltOnError = flag.Bool("halt", false, "halt on error")
listErrors = flag.Bool("errlist", false, "list errors")
- testFiles = flag.String("files", "", "space-separated list of test files")
+ testFiles = flag.String("files", "", "comma-separated list of test files")
+ goVersion = flag.String("lang", "", "Go language version (e.g. \"go1.12\"")
)
-// The test filenames do not end in .go so that they are invisible
-// to gofmt since they contain comments that must not change their
-// positions relative to surrounding tokens.
-
-// Each tests entry is list of files belonging to the same package.
-var tests = [][]string{
- {"testdata/errors.src"},
- {"testdata/importdecl0a.src", "testdata/importdecl0b.src"},
- {"testdata/importdecl1a.src", "testdata/importdecl1b.src"},
- {"testdata/importC.src"}, // special handling in checkFiles
- {"testdata/cycles.src"},
- {"testdata/cycles1.src"},
- {"testdata/cycles2.src"},
- {"testdata/cycles3.src"},
- {"testdata/cycles4.src"},
- {"testdata/cycles5.src"},
- {"testdata/init0.src"},
- {"testdata/init1.src"},
- {"testdata/init2.src"},
- {"testdata/decls0.src"},
- {"testdata/decls1.src"},
- {"testdata/decls2a.src", "testdata/decls2b.src"},
- {"testdata/decls3.src"},
- {"testdata/decls4.src"},
- {"testdata/decls5.src"},
- {"testdata/const0.src"},
- {"testdata/const1.src"},
- {"testdata/constdecl.src"},
- {"testdata/vardecl.src"},
- {"testdata/expr0.src"},
- {"testdata/expr1.src"},
- {"testdata/expr2.src"},
- {"testdata/expr3.src"},
- {"testdata/methodsets.src"},
- {"testdata/shifts.src"},
- {"testdata/builtins.src"},
- {"testdata/conversions.src"},
- {"testdata/conversions2.src"},
- {"testdata/stmt0.src"},
- {"testdata/stmt1.src"},
- {"testdata/gotos.src"},
- {"testdata/labels.src"},
- {"testdata/literals.src"},
- {"testdata/issues.src"},
- {"testdata/blank.src"},
- {"testdata/issue25008b.src", "testdata/issue25008a.src"}, // order (b before a) is crucial!
-}
-
var fset = token.NewFileSet()
// Positioned errors are of the form filename:line:column: message .
@@ -114,11 +69,11 @@ func splitError(err error) (pos, msg string) {
return
}
-func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
+func parseFiles(t *testing.T, filenames []string, srcs [][]byte) ([]*ast.File, []error) {
var files []*ast.File
var errlist []error
- for _, filename := range filenames {
- file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
+ for i, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, srcs[i], parser.AllErrors)
if file == nil {
t.Fatalf("%s: %s", filename, err)
}
@@ -147,19 +102,17 @@ var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`)
// errMap collects the regular expressions of ERROR comments found
// in files and returns them as a map of error positions to error messages.
//
-func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
+// srcs must be a slice of the same length as files, containing the original
+// source for the parsed AST.
+func errMap(t *testing.T, files []*ast.File, srcs [][]byte) map[string][]string {
// map of position strings to lists of error message patterns
errmap := make(map[string][]string)
- for _, file := range files {
- filename := fset.Position(file.Package).Filename
- src, err := os.ReadFile(filename)
- if err != nil {
- t.Fatalf("%s: could not read %s", testname, filename)
- }
-
+ for i, file := range files {
+ tok := fset.File(file.Package)
+ src := srcs[i]
var s scanner.Scanner
- s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
+ s.Init(tok, src, nil, scanner.ScanComments)
var prev token.Pos // position of last non-comment, non-semicolon token
var here token.Pos // position immediately after the token at position prev
@@ -236,15 +189,38 @@ func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
}
}
-func checkFiles(t *testing.T, testfiles []string) {
+// goVersionRx matches a Go version string using '_', e.g. "go1_12".
+var goVersionRx = regexp.MustCompile(`^go[1-9][0-9]*_(0|[1-9][0-9]*)$`)
+
+// asGoVersion returns a regular Go language version string
+// if s is a Go version string using '_' rather than '.' to
+// separate the major and minor version numbers (e.g. "go1_12").
+// Otherwise it returns the empty string.
+func asGoVersion(s string) string {
+ if goVersionRx.MatchString(s) {
+ return strings.Replace(s, "_", ".", 1)
+ }
+ return ""
+}
+
+func checkFiles(t *testing.T, goVersion string, filenames []string, srcs [][]byte) {
+ if len(filenames) == 0 {
+ t.Fatal("no source files")
+ }
+
// parse files and collect parser errors
- files, errlist := parseFiles(t, testfiles)
+ files, errlist := parseFiles(t, filenames, srcs)
pkgName := ""
if len(files) > 0 {
pkgName = files[0].Name.Name
}
+ // if no Go version is given, consider the package name
+ if goVersion == "" {
+ goVersion = asGoVersion(pkgName)
+ }
+
if *listErrors && len(errlist) > 0 {
t.Errorf("--- %s:", pkgName)
for _, err := range errlist {
@@ -254,10 +230,15 @@ func checkFiles(t *testing.T, testfiles []string) {
// typecheck and collect typechecker errors
var conf Config
+ conf.GoVersion = goVersion
+
// special case for importC.src
- if len(testfiles) == 1 && strings.HasSuffix(testfiles[0], "importC.src") {
- conf.FakeImportC = true
+ if len(filenames) == 1 {
+ if strings.HasSuffix(filenames[0], "importC.src") {
+ conf.FakeImportC = true
+ }
}
+
conf.Importer = importer.Default()
conf.Error = func(err error) {
if *haltOnError {
@@ -292,7 +273,7 @@ func checkFiles(t *testing.T, testfiles []string) {
// match and eliminate errors;
// we are expecting the following errors
- errmap := errMap(t, pkgName, files)
+ errmap := errMap(t, files, srcs)
eliminate(t, errmap, errlist)
// there should be no expected errors left
@@ -306,44 +287,66 @@ func checkFiles(t *testing.T, testfiles []string) {
}
}
+// TestCheck is for manual testing of selected input files, provided with -files.
+// The accepted Go language version can be controlled with the -lang flag.
func TestCheck(t *testing.T) {
- testenv.MustHaveGoBuild(t)
-
- // Declare builtins for testing.
- DefPredeclaredTestFuncs()
-
- // If explicit test files are specified, only check those.
- if files := *testFiles; files != "" {
- checkFiles(t, strings.Split(files, " "))
+ if *testFiles == "" {
return
}
+ testenv.MustHaveGoBuild(t)
+ DefPredeclaredTestFuncs()
+ testPkg(t, strings.Split(*testFiles, ","), *goVersion)
+}
- // Otherwise, run all the tests.
- for _, files := range tests {
- checkFiles(t, files)
- }
+func TestLongConstants(t *testing.T) {
+ format := "package longconst\n\nconst _ = %s\nconst _ = %s // ERROR excessively long constant"
+ src := fmt.Sprintf(format, strings.Repeat("1", 9999), strings.Repeat("1", 10001))
+ checkFiles(t, "", []string{"longconst.go"}, [][]byte{[]byte(src)})
}
-func TestFixedBugs(t *testing.T) { testDir(t, "fixedbugs") }
+func TestTestdata(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, "testdata") }
+func TestFixedbugs(t *testing.T) { testDir(t, "fixedbugs") }
func testDir(t *testing.T, dir string) {
testenv.MustHaveGoBuild(t)
- dirs, err := os.ReadDir(dir)
+ fis, err := os.ReadDir(dir)
if err != nil {
- t.Fatal(err)
+ t.Error(err)
+ return
}
- for _, d := range dirs {
- testname := filepath.Base(d.Name())
- testname = strings.TrimSuffix(testname, filepath.Ext(testname))
- t.Run(testname, func(t *testing.T) {
- filename := filepath.Join(dir, d.Name())
- if d.IsDir() {
- t.Errorf("skipped directory %q", filename)
- return
+ for _, fi := range fis {
+ path := filepath.Join(dir, fi.Name())
+
+ // if fi is a directory, its files make up a single package
+ var filenames []string
+ if fi.IsDir() {
+ fis, err := ioutil.ReadDir(path)
+ if err != nil {
+ t.Error(err)
+ continue
}
- checkFiles(t, []string{filename})
+ for _, fi := range fis {
+ filenames = append(filenames, filepath.Join(path, fi.Name()))
+ }
+ } else {
+ filenames = []string{path}
+ }
+ t.Run(filepath.Base(path), func(t *testing.T) {
+ testPkg(t, filenames, "")
})
}
}
+
+func testPkg(t *testing.T, filenames []string, goVersion string) {
+ srcs := make([][]byte, len(filenames))
+ for i, filename := range filenames {
+ src, err := os.ReadFile(filename)
+ if err != nil {
+ t.Fatalf("could not read %s: %v", filename, err)
+ }
+ srcs[i] = src
+ }
+ checkFiles(t, goVersion, filenames, srcs)
+}
diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go
index 1cab1cc70f0e8..c634d27aa9499 100644
--- a/src/go/types/conversions.go
+++ b/src/go/types/conversions.go
@@ -55,8 +55,8 @@ func (check *Checker) conversion(x *operand, T Type) {
// - Keep untyped nil for untyped nil arguments.
// - For integer to string conversions, keep the argument type.
// (See also the TODO below.)
- if IsInterface(T) || constArg && !isConstType(T) {
- final = Default(x.typ)
+ if IsInterface(T) || constArg && !isConstType(T) || x.isNil() {
+ final = Default(x.typ) // default type of untyped nil is untyped nil
} else if isInteger(x.typ) && isString(T) {
final = x.typ
}
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 1f0bc358a26e9..b861cde496490 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -189,7 +189,7 @@ func (check *Checker) objDecl(obj Object, def *Named) {
check.varDecl(obj, d.lhs, d.typ, d.init)
case *TypeName:
// invalid recursive types are detected via path
- check.typeDecl(obj, d.typ, def, d.alias)
+ check.typeDecl(obj, d.typ, def, d.aliasPos)
case *Func:
// functions may be recursive - no need to track dependencies
check.funcDecl(obj, d)
@@ -234,7 +234,7 @@ func (check *Checker) cycle(obj Object) (isCycle bool) {
// this information explicitly in the object.
var alias bool
if d := check.objMap[obj]; d != nil {
- alias = d.alias // package-level object
+ alias = d.aliasPos.IsValid() // package-level object
} else {
alias = obj.IsAlias() // function local object
}
@@ -504,6 +504,20 @@ func (check *Checker) constDecl(obj *Const, typ, init ast.Expr, inherited bool)
func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
assert(obj.typ == nil)
+ // If we have undefined variable types due to errors,
+ // mark variables as used to avoid follow-on errors.
+ // Matches compiler behavior.
+ defer func() {
+ if obj.typ == Typ[Invalid] {
+ obj.used = true
+ }
+ for _, lhs := range lhs {
+ if lhs.typ == Typ[Invalid] {
+ lhs.used = true
+ }
+ }
+ }()
+
// determine type, if any
if typ != nil {
obj.typ = check.typ(typ)
@@ -626,14 +640,17 @@ func (n *Named) setUnderlying(typ Type) {
}
}
-func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, alias bool) {
+func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, aliasPos token.Pos) {
assert(obj.typ == nil)
check.later(func() {
check.validType(obj.typ, nil)
})
- if alias {
+ if aliasPos.IsValid() {
+ if !check.allowVersion(obj.pkg, 1, 9) {
+ check.errorf(atPos(aliasPos), _BadDecl, "type aliases requires go1.9 or later")
+ }
obj.typ = Typ[Invalid]
obj.typ = check.typ(typ)
@@ -664,9 +681,12 @@ func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, alias bo
}
+ // TODO(rFindley): move to the callsite, as this is only needed for top-level
+ // decls.
check.addMethodDecls(obj)
}
+// TODO(rFindley): rename to collectMethods, to be consistent with types2.
func (check *Checker) addMethodDecls(obj *TypeName) {
// get associated methods
// (Checker.collectObjects only collects methods with non-blank names;
@@ -677,7 +697,7 @@ func (check *Checker) addMethodDecls(obj *TypeName) {
return
}
delete(check.methods, obj)
- assert(!check.objMap[obj].alias) // don't use TypeName.IsAlias (requires fully set up object)
+ assert(!check.objMap[obj].aliasPos.IsValid()) // don't use TypeName.IsAlias (requires fully set up object)
// use an objset to check for name conflicts
var mset objset
@@ -737,8 +757,12 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
obj.typ = sig // guard against cycles
fdecl := decl.fdecl
check.funcType(sig, fdecl.Recv, fdecl.Type)
- if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
- check.errorf(fdecl, _InvalidInitSig, "func init must have no arguments and no return values")
+ if sig.recv == nil {
+ if obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
+ check.errorf(fdecl, _InvalidInitDecl, "func init must have no arguments and no return values")
+ } else if obj.name == "main" && check.pkg.name == "main" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
+ check.errorf(fdecl, _InvalidMainDecl, "func main must have no arguments and no return values")
+ }
// ok to continue
}
@@ -846,7 +870,7 @@ func (check *Checker) declStmt(d ast.Decl) {
check.declare(check.scope, d.spec.Name, obj, scopePos)
// mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
obj.setColor(grey + color(check.push(obj)))
- check.typeDecl(obj, d.spec.Type, nil, d.spec.Assign.IsValid())
+ check.typeDecl(obj, d.spec.Type, nil, d.spec.Assign)
check.pop().setColor(black)
default:
check.invalidAST(d.node(), "unknown ast.Decl node %T", d.node())
diff --git a/src/go/types/errorcodes.go b/src/go/types/errorcodes.go
index c01a12c346d3e..ac28c3bd13443 100644
--- a/src/go/types/errorcodes.go
+++ b/src/go/types/errorcodes.go
@@ -386,8 +386,8 @@ const (
// _InvalidInitSig occurs when an init function declares parameters or
// results.
//
- // Example:
- // func init() int { return 1 }
+ // Deprecated: no longer emitted by the type checker. _InvalidInitDecl is
+ // used instead.
_InvalidInitSig
// _InvalidInitDecl occurs when init is declared as anything other than a
@@ -395,6 +395,9 @@ const (
//
// Example:
// var init = 1
+ //
+ // Example:
+ // func init() int { return 1 }
_InvalidInitDecl
// _InvalidMainDecl occurs when main is declared as anything other than a
@@ -1363,4 +1366,7 @@ const (
// return i
// }
_InvalidGo
+
+ // _BadDecl occurs when a declaration has invalid syntax.
+ _BadDecl
)
diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go
index d940bf0e80a9c..3a97ac0471620 100644
--- a/src/go/types/eval_test.go
+++ b/src/go/types/eval_test.go
@@ -76,7 +76,7 @@ func TestEvalArith(t *testing.T) {
`false == false`,
`12345678 + 87654321 == 99999999`,
`10 * 20 == 200`,
- `(1<<1000)*2 >> 100 == 2<<900`,
+ `(1<<500)*2 >> 100 == 2<<400`,
`"foo" + "bar" == "foobar"`,
`"abc" <= "bcd"`,
`len([10]struct{}{}) == 2*5`,
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index eb2056125a7b7..aec3172327877 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -78,13 +78,77 @@ func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
return true
}
+// overflow checks that the constant x is representable by its type.
+// For untyped constants, it checks that the value doesn't become
+// arbitrarily large.
+func (check *Checker) overflow(x *operand, op token.Token, opPos token.Pos) {
+ assert(x.mode == constant_)
+
+ if x.val.Kind() == constant.Unknown {
+ // TODO(gri) We should report exactly what went wrong. At the
+ // moment we don't have the (go/constant) API for that.
+ // See also TODO in go/constant/value.go.
+ check.errorf(atPos(opPos), _InvalidConstVal, "constant result is not representable")
+ return
+ }
+
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if typ, ok := x.typ.Underlying().(*Basic); ok && isTyped(typ) {
+ check.representable(x, typ)
+ return
+ }
+
+ // Untyped integer values must not grow arbitrarily.
+ const prec = 512 // 512 is the constant precision
+ if x.val.Kind() == constant.Int && constant.BitLen(x.val) > prec {
+ check.errorf(atPos(opPos), _InvalidConstVal, "constant %s overflow", opName(x.expr))
+ x.val = constant.MakeUnknown()
+ }
+}
+
+// opName returns the name of an operation, or the empty string.
+// For now, only operations that might overflow are handled.
+// TODO(gri) Expand this to a general mechanism giving names to
+// nodes?
+func opName(e ast.Expr) string {
+ switch e := e.(type) {
+ case *ast.BinaryExpr:
+ if int(e.Op) < len(op2str2) {
+ return op2str2[e.Op]
+ }
+ case *ast.UnaryExpr:
+ if int(e.Op) < len(op2str1) {
+ return op2str1[e.Op]
+ }
+ }
+ return ""
+}
+
+var op2str1 = [...]string{
+ token.XOR: "bitwise complement",
+}
+
+// This is only used for operations that may cause overflow.
+var op2str2 = [...]string{
+ token.ADD: "addition",
+ token.SUB: "subtraction",
+ token.XOR: "bitwise XOR",
+ token.MUL: "multiplication",
+ token.SHL: "shift",
+}
+
// The unary expression e may be nil. It's passed in for better error messages only.
-func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
- switch op {
+func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ return
+ }
+ switch e.Op {
case token.AND:
// spec: "As an exception to the addressability
// requirement x may also be a composite literal."
- if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable {
+ if _, ok := unparen(e.X).(*ast.CompositeLit); !ok && x.mode != variable {
check.invalidOp(x, _UnaddressableOperand, "cannot take address of %s", x)
x.mode = invalid
return
@@ -111,26 +175,23 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
return
}
- if !check.op(unaryOpPredicates, x, op) {
+ if !check.op(unaryOpPredicates, x, e.Op) {
x.mode = invalid
return
}
if x.mode == constant_ {
- typ := x.typ.Underlying().(*Basic)
- var prec uint
- if isUnsigned(typ) {
- prec = uint(check.conf.sizeof(typ) * 8)
+ if x.val.Kind() == constant.Unknown {
+ // nothing to do (and don't cause an error below in the overflow check)
+ return
}
- x.val = constant.UnaryOp(op, x.val, prec)
- // Typed constants must be representable in
- // their type after each constant operation.
- if isTyped(typ) {
- if e != nil {
- x.expr = e // for better error message
- }
- check.representable(x, typ)
+ var prec uint
+ if isUnsigned(x.typ) {
+ prec = uint(check.conf.sizeof(x.typ) * 8)
}
+ x.val = constant.UnaryOp(e.Op, x.val, prec)
+ x.expr = e
+ check.overflow(x, e.Op, x.Pos())
return
}
@@ -579,6 +640,8 @@ func (check *Checker) implicitType(x *operand, target Type) Type {
if !hasNil(target) {
return nil
}
+ // Preserve the type of nil as UntypedNil: see #13061.
+ return Typ[UntypedNil]
default:
return nil
}
@@ -665,15 +728,16 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
x.typ = Typ[UntypedBool]
}
-func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
- untypedx := isUntyped(x.typ)
+// If e != nil, it must be the shift expression; it may be nil for non-constant shifts.
+func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) {
+ // TODO(gri) This function seems overly complex. Revisit.
var xval constant.Value
if x.mode == constant_ {
xval = constant.ToInt(x.val)
}
- if isInteger(x.typ) || untypedx && xval != nil && xval.Kind() == constant.Int {
+ if isInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int {
// The lhs is of integer type or an untyped constant representable
// as an integer. Nothing to do.
} else {
@@ -685,19 +749,33 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
// spec: "The right operand in a shift expression must have integer type
// or be an untyped constant representable by a value of type uint."
- switch {
- case isInteger(y.typ):
- // nothing to do
- case isUntyped(y.typ):
+
+ // Provide a good error message for negative shift counts.
+ if y.mode == constant_ {
+ yval := constant.ToInt(y.val) // consider -1, 1.0, but not -1.1
+ if yval.Kind() == constant.Int && constant.Sign(yval) < 0 {
+ check.invalidOp(y, _InvalidShiftCount, "negative shift count %s", y)
+ x.mode = invalid
+ return
+ }
+ }
+
+ // Caution: Check for isUntyped first because isInteger includes untyped
+ // integers (was bug #43697).
+ if isUntyped(y.typ) {
check.convertUntyped(y, Typ[Uint])
if y.mode == invalid {
x.mode = invalid
return
}
- default:
+ } else if !isInteger(y.typ) {
check.invalidOp(y, _InvalidShiftCount, "shift count %s must be integer", y)
x.mode = invalid
return
+ } else if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
+ check.invalidOp(y, _InvalidShiftCount, "signed shift count %s requires go1.13 or later", y)
+ x.mode = invalid
+ return
}
var yval constant.Value
@@ -716,8 +794,17 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
if x.mode == constant_ {
if y.mode == constant_ {
+ // if either x or y has an unknown value, the result is unknown
+ if x.val.Kind() == constant.Unknown || y.val.Kind() == constant.Unknown {
+ x.val = constant.MakeUnknown()
+ // ensure the correct type - see comment below
+ if !isInteger(x.typ) {
+ x.typ = Typ[UntypedInt]
+ }
+ return
+ }
// rhs must be within reasonable bounds in constant shifts
- const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64
+ const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64 (see issue #44057)
s, ok := constant.Uint64Val(yval)
if !ok || s > shiftBound {
check.invalidOp(y, _InvalidShiftCount, "invalid shift count %s", y)
@@ -733,19 +820,17 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
}
// x is a constant so xval != nil and it must be of Int kind.
x.val = constant.Shift(xval, op, uint(s))
- // Typed constants must be representable in
- // their type after each constant operation.
- if isTyped(x.typ) {
- if e != nil {
- x.expr = e // for better error message
- }
- check.representable(x, x.typ.Underlying().(*Basic))
+ x.expr = e
+ opPos := x.Pos()
+ if b, _ := e.(*ast.BinaryExpr); b != nil {
+ opPos = b.OpPos
}
+ check.overflow(x, op, opPos)
return
}
// non-constant shift with constant lhs
- if untypedx {
+ if isUntyped(x.typ) {
// spec: "If the left operand of a non-constant shift
// expression is an untyped constant, the type of the
// constant is what it would be if the shift expression
@@ -801,8 +886,9 @@ var binaryOpPredicates = opPredicates{
token.LOR: isBoolean,
}
-// The binary expression e may be nil. It's passed in for better error messages only.
-func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, op token.Token, opPos token.Pos) {
+// If e != nil, it must be the binary expression; it may be nil for non-constant expressions
+// (when invoked for an assignment operation where the binary expression is implicit).
+func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token.Token, opPos token.Pos) {
var y operand
check.expr(x, lhs)
@@ -877,30 +963,19 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o
}
if x.mode == constant_ && y.mode == constant_ {
- xval := x.val
- yval := y.val
- typ := x.typ.Underlying().(*Basic)
+ // if either x or y has an unknown value, the result is unknown
+ if x.val.Kind() == constant.Unknown || y.val.Kind() == constant.Unknown {
+ x.val = constant.MakeUnknown()
+ // x.typ is unchanged
+ return
+ }
// force integer division of integer operands
- if op == token.QUO && isInteger(typ) {
+ if op == token.QUO && isInteger(x.typ) {
op = token.QUO_ASSIGN
}
- x.val = constant.BinaryOp(xval, op, yval)
- // report error if valid operands lead to an invalid result
- if xval.Kind() != constant.Unknown && yval.Kind() != constant.Unknown && x.val.Kind() == constant.Unknown {
- // TODO(gri) We should report exactly what went wrong. At the
- // moment we don't have the (go/constant) API for that.
- // See also TODO in go/constant/value.go.
- check.errorf(atPos(opPos), _InvalidConstVal, "constant result is not representable")
- // TODO(gri) Should we mark operands with unknown values as invalid?
- }
- // Typed constants must be representable in
- // their type after each constant operation.
- if isTyped(typ) {
- if e != nil {
- x.expr = e // for better error message
- }
- check.representable(x, typ)
- }
+ x.val = constant.BinaryOp(x.val, op, y.val)
+ x.expr = e
+ check.overflow(x, op, opPos)
return
}
@@ -1079,6 +1154,24 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
goto Error
case *ast.BasicLit:
+ switch e.Kind {
+ case token.INT, token.FLOAT, token.IMAG:
+ check.langCompat(e)
+ // The max. mantissa precision for untyped numeric values
+ // is 512 bits, or 4048 bits for each of the two integer
+ // parts of a fraction for floating-point numbers that are
+ // represented accurately in the go/constant package.
+ // Constant literals that are longer than this many bits
+ // are not meaningful; and excessively long constants may
+ // consume a lot of space and time for a useless conversion.
+ // Cap constant length with a generous upper limit that also
+ // allows for separators between all digits.
+ const limit = 10000
+ if len(e.Value) > limit {
+ check.errorf(e, _InvalidConstVal, "excessively long constant: %s... (%d chars)", e.Value[:10], len(e.Value))
+ goto Error
+ }
+ }
x.setConst(e.Kind, e.Value)
if x.mode == invalid {
// The parser already establishes syntactic correctness.
@@ -1536,11 +1629,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
}
case *ast.UnaryExpr:
- check.expr(x, e.X)
- if x.mode == invalid {
- goto Error
- }
- check.unary(x, e, e.Op)
+ check.unary(x, e)
if x.mode == invalid {
goto Error
}
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 148edbfb767a4..954a7ca987a8f 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -6,8 +6,6 @@
package types
-import "sort"
-
func isNamed(typ Type) bool {
if _, ok := typ.(*Basic); ok {
return ok
@@ -273,8 +271,8 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
p = p.prev
}
if debug {
- assert(sort.IsSorted(byUniqueMethodName(a)))
- assert(sort.IsSorted(byUniqueMethodName(b)))
+ assertSortedMethods(a)
+ assertSortedMethods(b)
}
for i, f := range a {
g := b[i]
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index b637f8b8cacf7..e4411592e82a0 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -23,7 +23,7 @@ type declInfo struct {
init ast.Expr // init/orig expression, or nil
inherited bool // if set, the init expression is inherited from a previous constant declaration
fdecl *ast.FuncDecl // func declaration, or nil
- alias bool // type alias declaration
+ aliasPos token.Pos // If valid, the decl is a type alias and aliasPos is the position of '='.
// The deps field tracks initialization expression dependencies.
deps map[Object]bool // lazily initialized
@@ -252,14 +252,6 @@ func (check *Checker) collectObjects() {
return
}
- // add package to list of explicit imports
- // (this functionality is provided as a convenience
- // for clients; it is not needed for type-checking)
- if !pkgImports[imp] {
- pkgImports[imp] = true
- pkg.imports = append(pkg.imports, imp)
- }
-
// local name overrides imported package name
name := imp.name
if d.spec.Name != nil {
@@ -269,27 +261,41 @@ func (check *Checker) collectObjects() {
check.errorf(d.spec.Name, _ImportCRenamed, `cannot rename import "C"`)
return
}
- if name == "init" {
- check.errorf(d.spec.Name, _InvalidInitDecl, "cannot declare init - must be func")
- return
- }
}
- obj := NewPkgName(d.spec.Pos(), pkg, name, imp)
+ if name == "init" {
+ check.errorf(d.spec.Name, _InvalidInitDecl, "cannot import package as init - init must be a func")
+ return
+ }
+
+ // add package to list of explicit imports
+ // (this functionality is provided as a convenience
+ // for clients; it is not needed for type-checking)
+ if !pkgImports[imp] {
+ pkgImports[imp] = true
+ pkg.imports = append(pkg.imports, imp)
+ }
+
+ pkgName := NewPkgName(d.spec.Pos(), pkg, name, imp)
if d.spec.Name != nil {
// in a dot-import, the dot represents the package
- check.recordDef(d.spec.Name, obj)
+ check.recordDef(d.spec.Name, pkgName)
} else {
- check.recordImplicit(d.spec, obj)
+ check.recordImplicit(d.spec, pkgName)
}
if path == "C" {
// match cmd/compile (not prescribed by spec)
- obj.used = true
+ pkgName.used = true
}
// add import to file scope
+ check.imports = append(check.imports, pkgName)
if name == "." {
+ // dot-import
+ if check.dotImportMap == nil {
+ check.dotImportMap = make(map[dotImportKey]*PkgName)
+ }
// merge imported scope with file scope
for _, obj := range imp.scope.elems {
// A package scope may contain non-exported objects,
@@ -303,16 +309,15 @@ func (check *Checker) collectObjects() {
if alt := fileScope.Insert(obj); alt != nil {
check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt)
+ } else {
+ check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName
}
}
}
- // add position to set of dot-import positions for this file
- // (this is only needed for "imported but not used" errors)
- check.addUnusedDotImport(fileScope, imp, d.spec)
} else {
// declare imported package object in file scope
// (no need to provide s.Name since we called check.recordDef earlier)
- check.declare(fileScope, nil, obj, token.NoPos)
+ check.declare(fileScope, nil, pkgName, token.NoPos)
}
case constDecl:
// declare all constants
@@ -361,7 +366,7 @@ func (check *Checker) collectObjects() {
}
case typeDecl:
obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
- check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, typ: d.spec.Type, alias: d.spec.Assign.IsValid()})
+ check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, typ: d.spec.Type, aliasPos: d.spec.Assign})
case funcDecl:
info := &declInfo{file: fileScope, fdecl: d.decl}
name := d.decl.Name.Name
@@ -488,7 +493,7 @@ func (check *Checker) resolveBaseTypeName(typ ast.Expr) (ptr bool, base *TypeNam
// we're done if tdecl defined tname as a new type
// (rather than an alias)
tdecl := check.objMap[tname] // must exist for objects in package scope
- if !tdecl.alias {
+ if !tdecl.aliasPos.IsValid() {
return ptr, tname
}
@@ -529,7 +534,7 @@ func (check *Checker) packageObjects() {
// phase 1
for _, obj := range objList {
// If we have a type alias, collect it for the 2nd phase.
- if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].alias {
+ if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].aliasPos.IsValid() {
aliasList = append(aliasList, tname)
continue
}
@@ -566,39 +571,30 @@ func (check *Checker) unusedImports() {
// any of its exported identifiers. To import a package solely for its side-effects
// (initialization), use the blank identifier as explicit package name."
- // check use of regular imported packages
- for _, scope := range check.pkg.scope.children /* file scopes */ {
- for _, obj := range scope.elems {
- if obj, ok := obj.(*PkgName); ok {
- // Unused "blank imports" are automatically ignored
- // since _ identifiers are not entered into scopes.
- if !obj.used {
- path := obj.imported.path
- base := pkgName(path)
- if obj.name == base {
- check.softErrorf(obj, _UnusedImport, "%q imported but not used", path)
- } else {
- check.softErrorf(obj, _UnusedImport, "%q imported but not used as %s", path, obj.name)
- }
- }
- }
- }
- }
-
- // check use of dot-imported packages
- for _, unusedDotImports := range check.unusedDotImports {
- for pkg, pos := range unusedDotImports {
- check.softErrorf(pos, _UnusedImport, "%q imported but not used", pkg.path)
+ for _, obj := range check.imports {
+ if !obj.used && obj.name != "_" {
+ check.errorUnusedPkg(obj)
}
}
}
-// pkgName returns the package name (last element) of an import path.
-func pkgName(path string) string {
- if i := strings.LastIndex(path, "/"); i >= 0 {
- path = path[i+1:]
+func (check *Checker) errorUnusedPkg(obj *PkgName) {
+ // If the package was imported with a name other than the final
+ // import path element, show it explicitly in the error message.
+ // Note that this handles both renamed imports and imports of
+ // packages containing unconventional package declarations.
+ // Note that this uses / always, even on Windows, because Go import
+ // paths always use forward slashes.
+ path := obj.imported.path
+ elem := path
+ if i := strings.LastIndex(elem, "/"); i >= 0 {
+ elem = elem[i+1:]
+ }
+ if obj.name == "" || obj.name == "." || obj.name == elem {
+ check.softErrorf(obj, _UnusedImport, "%q imported but not used", path)
+ } else {
+ check.softErrorf(obj, _UnusedImport, "%q imported but not used as %s", path, obj.name)
}
- return path
}
// dir makes a good-faith attempt to return the directory
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index 5ca44936eaf46..29f71137df839 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -106,6 +106,7 @@ func testTestDir(t *testing.T, path string, ignore ...string) {
// get per-file instructions
expectErrors := false
filename := filepath.Join(path, f.Name())
+ goVersion := ""
if comment := firstComment(filename); comment != "" {
fields := strings.Fields(comment)
switch fields[0] {
@@ -115,13 +116,17 @@ func testTestDir(t *testing.T, path string, ignore ...string) {
expectErrors = true
for _, arg := range fields[1:] {
if arg == "-0" || arg == "-+" || arg == "-std" {
- // Marked explicitly as not expected errors (-0),
+ // Marked explicitly as not expecting errors (-0),
// or marked as compiling runtime/stdlib, which is only done
// to trigger runtime/stdlib-only error output.
// In both cases, the code should typecheck.
expectErrors = false
break
}
+ const prefix = "-lang="
+ if strings.HasPrefix(arg, prefix) {
+ goVersion = arg[len(prefix):]
+ }
}
}
}
@@ -129,7 +134,7 @@ func testTestDir(t *testing.T, path string, ignore ...string) {
// parse and type-check file
file, err := parser.ParseFile(fset, filename, nil, 0)
if err == nil {
- conf := Config{Importer: stdLibImporter}
+ conf := Config{GoVersion: goVersion, Importer: stdLibImporter}
_, err = conf.Check(filename, fset, []*ast.File{file}, nil)
}
@@ -157,6 +162,7 @@ func TestStdTest(t *testing.T) {
"directive.go", // tests compiler rejection of bad directive placement - ignore
"embedfunc.go", // tests //go:embed
"embedvers.go", // tests //go:embed
+ "linkname2.go", // go/types doesn't check validity of //go:xxx directives
)
}
@@ -170,19 +176,15 @@ func TestStdFixed(t *testing.T) {
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
"issue6889.go", // gc-specific test
- "issue7746.go", // large constants - consumes too much memory
"issue11362.go", // canonical import path check
"issue16369.go", // go/types handles this correctly - not an issue
"issue18459.go", // go/types doesn't check validity of //go:xxx directives
"issue18882.go", // go/types doesn't check validity of //go:xxx directives
- "issue20232.go", // go/types handles larger constants than gc
"issue20529.go", // go/types does not have constraints on stack size
"issue22200.go", // go/types does not have constraints on stack size
"issue22200b.go", // go/types does not have constraints on stack size
"issue25507.go", // go/types does not have constraints on stack size
"issue20780.go", // go/types does not have constraints on stack size
- "issue31747.go", // go/types does not have constraints on language level (-lang=go1.12) (see #31793)
- "issue34329.go", // go/types does not have constraints on language level (-lang=go1.13) (see #31793)
"bug251.go", // issue #34333 which was exposed with fix for #34151
"issue42058a.go", // go/types does not have constraints on channel element size
"issue42058b.go", // go/types does not have constraints on channel element size
diff --git a/src/go/types/testdata/builtins.src b/src/go/types/testdata/builtins.src
index 98830eb08c601..6ee28f13b4291 100644
--- a/src/go/types/testdata/builtins.src
+++ b/src/go/types/testdata/builtins.src
@@ -283,7 +283,7 @@ func delete1() {
delete() // ERROR not enough arguments
delete(1) // ERROR not enough arguments
delete(1, 2, 3) // ERROR too many arguments
- delete(m, 0 /* ERROR not assignable */)
+ delete(m, 0 /* ERROR cannot use */)
delete(m, s)
_ = delete /* ERROR used as value */ (m, s)
@@ -514,7 +514,7 @@ func panic1() {
panic("foo")
panic(false)
panic(1<<10)
- panic(1 /* ERROR overflows */ <<1000)
+ panic(1 << /* ERROR constant shift overflow */ 1000)
_ = panic /* ERROR used as value */ (0)
var s []byte
@@ -538,7 +538,7 @@ func print1() {
print(2.718281828)
print(false)
print(1<<10)
- print(1 /* ERROR overflows */ <<1000)
+ print(1 << /* ERROR constant shift overflow */ 1000)
println(nil /* ERROR untyped nil */ )
var s []int
@@ -564,7 +564,7 @@ func println1() {
println(2.718281828)
println(false)
println(1<<10)
- println(1 /* ERROR overflows */ <<1000)
+ println(1 << /* ERROR constant shift overflow */ 1000)
println(nil /* ERROR untyped nil */ )
var s []int
@@ -695,7 +695,7 @@ func Alignof1() {
_ = unsafe.Alignof(42)
_ = unsafe.Alignof(new(struct{}))
_ = unsafe.Alignof(1<<10)
- _ = unsafe.Alignof(1 /* ERROR overflows */ <<1000)
+ _ = unsafe.Alignof(1 << /* ERROR constant shift overflow */ 1000)
_ = unsafe.Alignof(nil /* ERROR "untyped nil */ )
unsafe /* ERROR not used */ .Alignof(x)
@@ -783,7 +783,7 @@ func Sizeof1() {
_ = unsafe.Sizeof(42)
_ = unsafe.Sizeof(new(complex128))
_ = unsafe.Sizeof(1<<10)
- _ = unsafe.Sizeof(1 /* ERROR overflows */ <<1000)
+ _ = unsafe.Sizeof(1 << /* ERROR constant shift overflow */ 1000)
_ = unsafe.Sizeof(nil /* ERROR untyped nil */ )
unsafe /* ERROR not used */ .Sizeof(x)
diff --git a/src/go/types/testdata/const0.src b/src/go/types/testdata/const0.src
index adbbf2863b03e..5608b1549ba8d 100644
--- a/src/go/types/testdata/const0.src
+++ b/src/go/types/testdata/const0.src
@@ -348,3 +348,16 @@ const _ = unsafe.Sizeof(func() {
assert(one == 1)
assert(iota == 0)
})
+
+// untyped constants must not get arbitrarily large
+const prec = 512 // internal maximum precision for integers
+const maxInt = (1<<(prec/2) - 1) * (1<<(prec/2) + 1) // == 1<> s // ERROR "signed shift count"
+)
+
diff --git a/src/go/types/testdata/go1_13.src b/src/go/types/testdata/go1_13.src
new file mode 100644
index 0000000000000..6aa1364e8a473
--- /dev/null
+++ b/src/go/types/testdata/go1_13.src
@@ -0,0 +1,22 @@
+// 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.
+
+// Check Go language version-specific errors.
+
+package go1_13 // go1.13
+
+// interface embedding
+
+type I interface { m() }
+
+type _ interface {
+ m()
+ I // ERROR "duplicate method m"
+}
+
+type _ interface {
+ I
+ I // ERROR "duplicate method m"
+}
+
diff --git a/src/go/types/testdata/go1_8.src b/src/go/types/testdata/go1_8.src
new file mode 100644
index 0000000000000..3ead1e981b71f
--- /dev/null
+++ b/src/go/types/testdata/go1_8.src
@@ -0,0 +1,11 @@
+// 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.
+
+// Check Go language version-specific errors.
+
+package go1_8 // go1.8
+
+// type alias declarations
+type any = /* ERROR type aliases requires go1.9 or later */ interface{}
+
diff --git a/src/go/types/testdata/importdecl0a.src b/src/go/types/testdata/importdecl0/importdecl0a.src
similarity index 95%
rename from src/go/types/testdata/importdecl0a.src
rename to src/go/types/testdata/importdecl0/importdecl0a.src
index e96fca3cdd56f..5ceb96e1fada6 100644
--- a/src/go/types/testdata/importdecl0a.src
+++ b/src/go/types/testdata/importdecl0/importdecl0a.src
@@ -10,7 +10,7 @@ import (
// we can have multiple blank imports (was bug)
_ "math"
_ "net/rpc"
- init /* ERROR "cannot declare init" */ "fmt"
+ init /* ERROR "cannot import package as init" */ "fmt"
// reflect defines a type "flag" which shows up in the gc export data
"reflect"
. /* ERROR "imported but not used" */ "reflect"
diff --git a/src/go/types/testdata/importdecl0b.src b/src/go/types/testdata/importdecl0/importdecl0b.src
similarity index 93%
rename from src/go/types/testdata/importdecl0b.src
rename to src/go/types/testdata/importdecl0/importdecl0b.src
index 6844e7098233e..55690423b66ec 100644
--- a/src/go/types/testdata/importdecl0b.src
+++ b/src/go/types/testdata/importdecl0/importdecl0b.src
@@ -8,7 +8,7 @@ import "math"
import m "math"
import . "testing" // declares T in file scope
-import . /* ERROR "imported but not used" */ "unsafe"
+import . /* ERROR .unsafe. imported but not used */ "unsafe"
import . "fmt" // declares Println in file scope
import (
diff --git a/src/go/types/testdata/importdecl1a.src b/src/go/types/testdata/importdecl1/importdecl1a.src
similarity index 100%
rename from src/go/types/testdata/importdecl1a.src
rename to src/go/types/testdata/importdecl1/importdecl1a.src
diff --git a/src/go/types/testdata/importdecl1b.src b/src/go/types/testdata/importdecl1/importdecl1b.src
similarity index 77%
rename from src/go/types/testdata/importdecl1b.src
rename to src/go/types/testdata/importdecl1/importdecl1b.src
index ee70bbd8e73f7..43a7bcd75396c 100644
--- a/src/go/types/testdata/importdecl1b.src
+++ b/src/go/types/testdata/importdecl1/importdecl1b.src
@@ -4,7 +4,7 @@
package importdecl1
-import . /* ERROR "imported but not used" */ "unsafe"
+import . /* ERROR .unsafe. imported but not used */ "unsafe"
type B interface {
A
diff --git a/src/go/types/testdata/issue25008a.src b/src/go/types/testdata/issue25008/issue25008a.src
similarity index 100%
rename from src/go/types/testdata/issue25008a.src
rename to src/go/types/testdata/issue25008/issue25008a.src
diff --git a/src/go/types/testdata/issue25008b.src b/src/go/types/testdata/issue25008/issue25008b.src
similarity index 100%
rename from src/go/types/testdata/issue25008b.src
rename to src/go/types/testdata/issue25008/issue25008b.src
diff --git a/src/go/types/testdata/main.src b/src/go/types/testdata/main.src
new file mode 100644
index 0000000000000..f892938d4acfa
--- /dev/null
+++ b/src/go/types/testdata/main.src
@@ -0,0 +1,9 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main()
+func /* ERROR "no arguments and no return values" */ main /* ERROR redeclared */ (int)
+func /* ERROR "no arguments and no return values" */ main /* ERROR redeclared */ () int
diff --git a/src/go/types/testdata/shifts.src b/src/go/types/testdata/shifts.src
index c9a38ae169ea0..4d3c59a50fd9b 100644
--- a/src/go/types/testdata/shifts.src
+++ b/src/go/types/testdata/shifts.src
@@ -20,7 +20,7 @@ func shifts0() {
// This depends on the exact spec wording which is not
// done yet.
// TODO(gri) revisit and adjust when spec change is done
- _ = 1<<- /* ERROR "truncated to uint" */ 1.0
+ _ = 1<<- /* ERROR "negative shift count" */ 1.0
_ = 1<<1075 /* ERROR "invalid shift" */
_ = 2.0<<1
_ = 1<<1.0
@@ -60,11 +60,13 @@ func shifts1() {
_ uint = 1 << u
_ float32 = 1 /* ERROR "must be integer" */ << u
- // for issue 14822
+ // issue #14822
+ _ = 1<<( /* ERROR "overflows uint" */ 1<<64)
_ = 1<<( /* ERROR "invalid shift count" */ 1<<64-1)
- _ = 1<<( /* ERROR "invalid shift count" */ 1<<64)
- _ = u<<(1<<63) // valid
- _ = u<<(1<<64) // valid
+
+ // issue #43697
+ _ = u<<( /* ERROR "overflows uint" */ 1<<64)
+ _ = u<<(1<<64-1)
)
}
diff --git a/src/go/types/testdata/stmt0.src b/src/go/types/testdata/stmt0.src
index 13777292a9439..de8f936a617ae 100644
--- a/src/go/types/testdata/stmt0.src
+++ b/src/go/types/testdata/stmt0.src
@@ -90,7 +90,7 @@ func assignments1() {
// assignments to _
_ = nil /* ERROR "use of untyped nil" */
- _ = 1 /* ERROR overflow */ <<1000
+ _ = 1 << /* ERROR constant shift overflow */ 1000
(_) = 0
}
diff --git a/src/go/types/testdata/vardecl.src b/src/go/types/testdata/vardecl.src
index 54f5ef1e10d09..6e2d1b5bd5e3b 100644
--- a/src/go/types/testdata/vardecl.src
+++ b/src/go/types/testdata/vardecl.src
@@ -158,6 +158,18 @@ func _() {
}
}
+
+// Invalid variable declarations must not lead to "declared but not used errors".
+func _() {
+ var a x // ERROR undeclared name: x
+ var b = x // ERROR undeclared name: x
+ var c int = x // ERROR undeclared name: x
+ var d, e, f x /* ERROR x */ /* ERROR x */ /* ERROR x */
+ var g, h, i = x /* ERROR x */, x /* ERROR x */, x /* ERROR x */
+ var j, k, l float32 = x /* ERROR x */, x /* ERROR x */, x /* ERROR x */
+ // but no "declared but not used" errors
+}
+
// Invalid (unused) expressions must not lead to spurious "declared but not used errors"
func _() {
var a, b, c int
@@ -203,4 +215,4 @@ func _() {
_, _, _ = x, y, z
}
-// TODO(gri) consolidate other var decl checks in this file
\ No newline at end of file
+// TODO(gri) consolidate other var decl checks in this file
diff --git a/src/go/types/type.go b/src/go/types/type.go
index 087cda429d689..66e194e967920 100644
--- a/src/go/types/type.go
+++ b/src/go/types/type.go
@@ -4,8 +4,6 @@
package types
-import "sort"
-
// A Type represents a type of Go.
// All types implement the Type interface.
type Type interface {
@@ -301,8 +299,8 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
}
// sort for API stability
- sort.Sort(byUniqueMethodName(methods))
- sort.Stable(byUniqueTypeName(embeddeds))
+ sortMethods(methods)
+ sortTypes(embeddeds)
typ.methods = methods
typ.embeddeds = embeddeds
@@ -396,7 +394,7 @@ func (t *Interface) Complete() *Interface {
}
if methods != nil {
- sort.Sort(byUniqueMethodName(methods))
+ sortMethods(methods)
t.allMethods = methods
}
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index 2b398010f4d04..b9249494fa16d 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -51,12 +51,12 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
}
assert(typ != nil)
- // The object may be dot-imported: If so, remove its package from
- // the map of unused dot imports for the respective file scope.
+ // The object may have been dot-imported.
+ // If so, mark the respective package as used.
// (This code is only needed for dot-imports. Without them,
// we only have to mark variables, see *Var case below).
- if pkg := obj.Pkg(); pkg != check.pkg && pkg != nil {
- delete(check.unusedDotImports[scope], pkg)
+ if pkgName := check.dotImportMap[dotImportKey{scope, obj}]; pkgName != nil {
+ pkgName.used = true
}
switch obj := obj.(type) {
@@ -518,8 +518,8 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
}
// sort for API stability
- sort.Sort(byUniqueMethodName(ityp.methods))
- sort.Stable(byUniqueTypeName(ityp.embeddeds))
+ sortMethods(ityp.methods)
+ sortTypes(ityp.embeddeds)
check.later(func() { check.completeInterface(ityp) })
}
@@ -578,9 +578,13 @@ func (check *Checker) completeInterface(ityp *Interface) {
check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
default:
- // check method signatures after all types are computed (issue #33656)
+ // We have a duplicate method name in an embedded (not explicitly declared) method.
+ // Check method signatures after all types are computed (issue #33656).
+ // If we're pre-go1.14 (overlapping embeddings are not permitted), report that
+ // error here as well (even though we could do it eagerly) because it's the same
+ // error message.
check.atEnd(func() {
- if !check.identical(m.typ, other.Type()) {
+ if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
}
@@ -613,6 +617,10 @@ func (check *Checker) completeInterface(ityp *Interface) {
}
}
+func sortTypes(list []Type) {
+ sort.Stable(byUniqueTypeName(list))
+}
+
// byUniqueTypeName named type lists can be sorted by their unique type names.
type byUniqueTypeName []Type
@@ -627,6 +635,19 @@ func sortName(t Type) string {
return ""
}
+func sortMethods(list []*Func) {
+ sort.Sort(byUniqueMethodName(list))
+}
+
+func assertSortedMethods(list []*Func) {
+ if !debug {
+ panic("internal error: assertSortedMethods called outside debug mode")
+ }
+ if !sort.IsSorted(byUniqueMethodName(list)) {
+ panic("internal error: methods not sorted")
+ }
+}
+
// byUniqueMethodName method lists can be sorted by their unique method names.
type byUniqueMethodName []*Func
diff --git a/src/go/types/version.go b/src/go/types/version.go
new file mode 100644
index 0000000000000..154694169b49d
--- /dev/null
+++ b/src/go/types/version.go
@@ -0,0 +1,82 @@
+// 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 types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// langCompat reports an error if the representation of a numeric
+// literal is not compatible with the current language version.
+func (check *Checker) langCompat(lit *ast.BasicLit) {
+ s := lit.Value
+ if len(s) <= 2 || check.allowVersion(check.pkg, 1, 13) {
+ return
+ }
+ // len(s) > 2
+ if strings.Contains(s, "_") {
+ check.errorf(lit, _InvalidLit, "underscores in numeric literals requires go1.13 or later")
+ return
+ }
+ if s[0] != '0' {
+ return
+ }
+ radix := s[1]
+ if radix == 'b' || radix == 'B' {
+ check.errorf(lit, _InvalidLit, "binary literals requires go1.13 or later")
+ return
+ }
+ if radix == 'o' || radix == 'O' {
+ check.errorf(lit, _InvalidLit, "0o/0O-style octal literals requires go1.13 or later")
+ return
+ }
+ if lit.Kind != token.INT && (radix == 'x' || radix == 'X') {
+ check.errorf(lit, _InvalidLit, "hexadecimal floating-point literals requires go1.13 or later")
+ }
+}
+
+// allowVersion reports whether the given package
+// is allowed to use version major.minor.
+func (check *Checker) allowVersion(pkg *Package, major, minor int) bool {
+ // We assume that imported packages have all been checked,
+ // so we only have to check for the local package.
+ if pkg != check.pkg {
+ return true
+ }
+ ma, mi := check.version.major, check.version.minor
+ return ma == 0 && mi == 0 || ma > major || ma == major && mi >= minor
+}
+
+type version struct {
+ major, minor int
+}
+
+// parseGoVersion parses a Go version string (such as "go1.12")
+// and returns the version, or an error. If s is the empty
+// string, the version is 0.0.
+func parseGoVersion(s string) (v version, err error) {
+ if s == "" {
+ return
+ }
+ matches := goVersionRx.FindStringSubmatch(s)
+ if matches == nil {
+ err = fmt.Errorf(`should be something like "go1.12"`)
+ return
+ }
+ v.major, err = strconv.Atoi(matches[1])
+ if err != nil {
+ return
+ }
+ v.minor, err = strconv.Atoi(matches[2])
+ return
+}
+
+// goVersionRx matches a Go version string, e.g. "go1.12".
+var goVersionRx = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
diff --git a/src/internal/abi/abi.go b/src/internal/abi/abi.go
new file mode 100644
index 0000000000000..6700facc04300
--- /dev/null
+++ b/src/internal/abi/abi.go
@@ -0,0 +1,53 @@
+// 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 abi
+
+import "unsafe"
+
+// RegArgs is a struct that has space for each argument
+// and return value register on the current architecture.
+//
+// Assembly code knows the layout of the first two fields
+// of RegArgs.
+//
+// RegArgs also contains additional space to hold pointers
+// when it may not be safe to keep them only in the integer
+// register space otherwise.
+type RegArgs struct {
+ Ints [IntArgRegs]uintptr // untyped integer registers
+ Floats [FloatArgRegs]uint64 // untyped float registers
+
+ // Fields above this point are known to assembly.
+
+ // Ptrs is a space that duplicates Ints but with pointer type,
+ // used to make pointers passed or returned in registers
+ // visible to the GC by making the type unsafe.Pointer.
+ Ptrs [IntArgRegs]unsafe.Pointer
+
+ // ReturnIsPtr is a bitmap that indicates which registers
+ // contain or will contain pointers on the return path from
+ // a reflectcall. The i'th bit indicates whether the i'th
+ // register contains or will contain a valid Go pointer.
+ ReturnIsPtr IntArgRegBitmap
+}
+
+// IntArgRegBitmap is a bitmap large enough to hold one bit per
+// integer argument/return register.
+type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8
+
+// Set sets the i'th bit of the bitmap to 1.
+func (b *IntArgRegBitmap) Set(i int) {
+ b[i/8] |= uint8(1) << (i % 8)
+}
+
+// Get returns whether the i'th bit of the bitmap is set.
+//
+// nosplit because it's called in extremely sensitive contexts, like
+// on the reflectcall return path.
+//
+//go:nosplit
+func (b *IntArgRegBitmap) Get(i int) bool {
+ return b[i/8]&(uint8(1)<<(i%8)) != 0
+}
diff --git a/src/internal/abi/abi_amd64.go b/src/internal/abi/abi_amd64.go
new file mode 100644
index 0000000000000..70e2ed1feb945
--- /dev/null
+++ b/src/internal/abi/abi_amd64.go
@@ -0,0 +1,24 @@
+// 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 goexperiment.regabi
+
+package abi
+
+const (
+ // See abi_generic.go.
+
+ // Currently these values are zero because whatever uses
+ // them will expect the register ABI, which isn't ready
+ // yet.
+
+ // RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11.
+ IntArgRegs = 0 // 9
+
+ // X0 -> X14.
+ FloatArgRegs = 0 // 15
+
+ // We use SSE2 registers which support 64-bit float operations.
+ EffectiveFloatRegSize = 0 // 8
+)
diff --git a/src/internal/abi/abi_generic.go b/src/internal/abi/abi_generic.go
new file mode 100644
index 0000000000000..5ef9883dc6e66
--- /dev/null
+++ b/src/internal/abi/abi_generic.go
@@ -0,0 +1,38 @@
+// 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 !goexperiment.regabi
+
+package abi
+
+const (
+ // ABI-related constants.
+ //
+ // In the generic case, these are all zero
+ // which lets them gracefully degrade to ABI0.
+
+ // IntArgRegs is the number of registers dedicated
+ // to passing integer argument values. Result registers are identical
+ // to argument registers, so this number is used for those too.
+ IntArgRegs = 0
+
+ // FloatArgRegs is the number of registers dedicated
+ // to passing floating-point argument values. Result registers are
+ // identical to argument registers, so this number is used for
+ // those too.
+ FloatArgRegs = 0
+
+ // EffectiveFloatRegSize describes the width of floating point
+ // registers on the current platform from the ABI's perspective.
+ //
+ // Since Go only supports 32-bit and 64-bit floating point primitives,
+ // this number should be either 0, 4, or 8. 0 indicates no floating
+ // point registers for the ABI or that floating point values will be
+ // passed via the softfloat ABI.
+ //
+ // For platforms that support larger floating point register widths,
+ // such as x87's 80-bit "registers" (not that we support x87 currently),
+ // use 8.
+ EffectiveFloatRegSize = 0
+)
diff --git a/src/reflect/abi.go b/src/reflect/abi.go
new file mode 100644
index 0000000000000..88af212717d23
--- /dev/null
+++ b/src/reflect/abi.go
@@ -0,0 +1,403 @@
+// 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 reflect
+
+import (
+ "internal/abi"
+ "unsafe"
+)
+
+// abiStep represents an ABI "instruction." Each instruction
+// describes one part of how to translate between a Go value
+// in memory and a call frame.
+type abiStep struct {
+ kind abiStepKind
+
+ // offset and size together describe a part of a Go value
+ // in memory.
+ offset uintptr
+ size uintptr // size in bytes of the part
+
+ // These fields describe the ABI side of the translation.
+ stkOff uintptr // stack offset, used if kind == abiStepStack
+ ireg int // integer register index, used if kind == abiStepIntReg or kind == abiStepPointer
+ freg int // FP register index, used if kind == abiStepFloatReg
+}
+
+// abiStepKind is the "op-code" for an abiStep instruction.
+type abiStepKind int
+
+const (
+ abiStepBad abiStepKind = iota
+ abiStepStack // copy to/from stack
+ abiStepIntReg // copy to/from integer register
+ abiStepPointer // copy pointer to/from integer register
+ abiStepFloatReg // copy to/from FP register
+)
+
+// abiSeq represents a sequence of ABI instructions for copying
+// from a series of reflect.Values to a call frame (for call arguments)
+// or vice-versa (for call results).
+//
+// An abiSeq should be populated by calling its addArg method.
+type abiSeq struct {
+ // steps is the set of instructions.
+ //
+ // The instructions are grouped together by whole arguments,
+ // with the starting index for the instructions
+ // of the i'th Go value available in valueStart.
+ //
+ // For instance, if this abiSeq represents 3 arguments
+ // passed to a function, then the 2nd argument's steps
+ // begin at steps[valueStart[1]].
+ //
+ // Because reflect accepts Go arguments in distinct
+ // Values and each Value is stored separately, each abiStep
+ // that begins a new argument will have its offset
+ // field == 0.
+ steps []abiStep
+ valueStart []int
+
+ stackBytes uintptr // stack space used
+ iregs, fregs int // registers used
+}
+
+func (a *abiSeq) dump() {
+ for i, p := range a.steps {
+ println("part", i, p.kind, p.offset, p.size, p.stkOff, p.ireg, p.freg)
+ }
+ print("values ")
+ for _, i := range a.valueStart {
+ print(i, " ")
+ }
+ println()
+ println("stack", a.stackBytes)
+ println("iregs", a.iregs)
+ println("fregs", a.fregs)
+}
+
+// stepsForValue returns the ABI instructions for translating
+// the i'th Go argument or return value represented by this
+// abiSeq to the Go ABI.
+func (a *abiSeq) stepsForValue(i int) []abiStep {
+ s := a.valueStart[i]
+ var e int
+ if i == len(a.valueStart)-1 {
+ e = len(a.steps)
+ } else {
+ e = a.valueStart[i+1]
+ }
+ return a.steps[s:e]
+}
+
+// addArg extends the abiSeq with a new Go value of type t.
+//
+// If the value was stack-assigned, returns the single
+// abiStep describing that translation, and nil otherwise.
+func (a *abiSeq) addArg(t *rtype) *abiStep {
+ pStart := len(a.steps)
+ a.valueStart = append(a.valueStart, pStart)
+ if !a.regAssign(t, 0) {
+ a.steps = a.steps[:pStart]
+ a.stackAssign(t.size, uintptr(t.align))
+ return &a.steps[len(a.steps)-1]
+ }
+ return nil
+}
+
+// addRcvr extends the abiSeq with a new method call
+// receiver according to the interface calling convention.
+//
+// If the receiver was stack-assigned, returns the single
+// abiStep describing that translation, and nil otherwise.
+// Returns true if the receiver is a pointer.
+func (a *abiSeq) addRcvr(rcvr *rtype) (*abiStep, bool) {
+ // The receiver is always one word.
+ a.valueStart = append(a.valueStart, len(a.steps))
+ var ok, ptr bool
+ if ifaceIndir(rcvr) || rcvr.pointers() {
+ ok = a.assignIntN(0, ptrSize, 1, 0b1)
+ ptr = true
+ } else {
+ // TODO(mknyszek): Is this case even possible?
+ // The interface data work never contains a non-pointer
+ // value. This case was copied over from older code
+ // in the reflect package which only conditionally added
+ // a pointer bit to the reflect.(Value).Call stack frame's
+ // GC bitmap.
+ ok = a.assignIntN(0, ptrSize, 1, 0b0)
+ ptr = false
+ }
+ if !ok {
+ a.stackAssign(ptrSize, ptrSize)
+ return &a.steps[len(a.steps)-1], ptr
+ }
+ return nil, ptr
+}
+
+// regAssign attempts to reserve argument registers for a value of
+// type t, stored at some offset.
+//
+// It returns whether or not the assignment succeeded, but
+// leaves any changes it made to a.steps behind, so the caller
+// must undo that work by adjusting a.steps if it fails.
+//
+// This method along with the assign* methods represent the
+// complete register-assignment algorithm for the Go ABI.
+func (a *abiSeq) regAssign(t *rtype, offset uintptr) bool {
+ switch t.Kind() {
+ case UnsafePointer, Ptr, Chan, Map, Func:
+ return a.assignIntN(offset, t.size, 1, 0b1)
+ case Bool, Int, Uint, Int8, Uint8, Int16, Uint16, Int32, Uint32, Uintptr:
+ return a.assignIntN(offset, t.size, 1, 0b0)
+ case Int64, Uint64:
+ switch ptrSize {
+ case 4:
+ return a.assignIntN(offset, 4, 2, 0b0)
+ case 8:
+ return a.assignIntN(offset, 8, 1, 0b0)
+ }
+ case Float32, Float64:
+ return a.assignFloatN(offset, t.size, 1)
+ case Complex64:
+ return a.assignFloatN(offset, 4, 2)
+ case Complex128:
+ return a.assignFloatN(offset, 8, 2)
+ case String:
+ return a.assignIntN(offset, ptrSize, 2, 0b01)
+ case Interface:
+ return a.assignIntN(offset, ptrSize, 2, 0b10)
+ case Slice:
+ return a.assignIntN(offset, ptrSize, 3, 0b001)
+ case Array:
+ tt := (*arrayType)(unsafe.Pointer(t))
+ switch tt.len {
+ case 0:
+ // There's nothing to assign, so don't modify
+ // a.steps but succeed so the caller doesn't
+ // try to stack-assign this value.
+ return true
+ case 1:
+ return a.regAssign(tt.elem, offset)
+ default:
+ return false
+ }
+ case Struct:
+ if t.size == 0 {
+ // There's nothing to assign, so don't modify
+ // a.steps but succeed so the caller doesn't
+ // try to stack-assign this value.
+ return true
+ }
+ st := (*structType)(unsafe.Pointer(t))
+ for i := range st.fields {
+ f := &st.fields[i]
+ if f.typ.Size() == 0 {
+ // Ignore zero-sized fields.
+ continue
+ }
+ if !a.regAssign(f.typ, offset+f.offset()) {
+ return false
+ }
+ }
+ return true
+ default:
+ print("t.Kind == ", t.Kind(), "\n")
+ panic("unknown type kind")
+ }
+ panic("unhandled register assignment path")
+}
+
+// assignIntN assigns n values to registers, each "size" bytes large,
+// from the data at [offset, offset+n*size) in memory. Each value at
+// [offset+i*size, offset+(i+1)*size) for i < n is assigned to the
+// next n integer registers.
+//
+// Bit i in ptrMap indicates whether the i'th value is a pointer.
+// n must be <= 8.
+//
+// Returns whether assignment succeeded.
+func (a *abiSeq) assignIntN(offset, size uintptr, n int, ptrMap uint8) bool {
+ if n > 8 || n < 0 {
+ panic("invalid n")
+ }
+ if ptrMap != 0 && size != ptrSize {
+ panic("non-empty pointer map passed for non-pointer-size values")
+ }
+ if a.iregs+n > abi.IntArgRegs {
+ return false
+ }
+ for i := 0; i < n; i++ {
+ kind := abiStepIntReg
+ if ptrMap&(uint8(1)< abi.FloatArgRegs || abi.EffectiveFloatRegSize < size {
+ return false
+ }
+ for i := 0; i < n; i++ {
+ a.steps = append(a.steps, abiStep{
+ kind: abiStepFloatReg,
+ offset: offset + uintptr(i)*size,
+ size: size,
+ freg: a.fregs,
+ })
+ a.fregs++
+ }
+ return true
+}
+
+// stackAssign reserves space for one value that is "size" bytes
+// large with alignment "alignment" to the stack.
+//
+// Should not be called directly; use addArg instead.
+func (a *abiSeq) stackAssign(size, alignment uintptr) {
+ a.stackBytes = align(a.stackBytes, alignment)
+ a.steps = append(a.steps, abiStep{
+ kind: abiStepStack,
+ offset: 0, // Only used for whole arguments, so the memory offset is 0.
+ size: size,
+ stkOff: a.stackBytes,
+ })
+ a.stackBytes += size
+}
+
+// abiDesc describes the ABI for a function or method.
+type abiDesc struct {
+ // call and ret represent the translation steps for
+ // the call and return paths of a Go function.
+ call, ret abiSeq
+
+ // These fields describe the stack space allocated
+ // for the call. stackCallArgsSize is the amount of space
+ // reserved for arguments but not return values. retOffset
+ // is the offset at which return values begin, and
+ // spill is the size in bytes of additional space reserved
+ // to spill argument registers into in case of preemption in
+ // reflectcall's stack frame.
+ stackCallArgsSize, retOffset, spill uintptr
+
+ // stackPtrs is a bitmap that indicates whether
+ // each word in the ABI stack space (stack-assigned
+ // args + return values) is a pointer. Used
+ // as the heap pointer bitmap for stack space
+ // passed to reflectcall.
+ stackPtrs *bitVector
+
+ // outRegPtrs is a bitmap whose i'th bit indicates
+ // whether the i'th integer result register contains
+ // a pointer. Used by reflectcall to make result
+ // pointers visible to the GC.
+ outRegPtrs abi.IntArgRegBitmap
+}
+
+func (a *abiDesc) dump() {
+ println("ABI")
+ println("call")
+ a.call.dump()
+ println("ret")
+ a.ret.dump()
+ println("stackCallArgsSize", a.stackCallArgsSize)
+ println("retOffset", a.retOffset)
+ println("spill", a.spill)
+}
+
+func newAbiDesc(t *funcType, rcvr *rtype) abiDesc {
+ // We need to add space for this argument to
+ // the frame so that it can spill args into it.
+ //
+ // The size of this space is just the sum of the sizes
+ // of each register-allocated type.
+ //
+ // TODO(mknyszek): Remove this when we no longer have
+ // caller reserved spill space.
+ spillInt := uintptr(0)
+ spillFloat := uintptr(0)
+
+ // Compute gc program & stack bitmap for stack arguments
+ stackPtrs := new(bitVector)
+
+ // Compute abiSeq for input parameters.
+ var in abiSeq
+ if rcvr != nil {
+ stkStep, isPtr := in.addRcvr(rcvr)
+ if stkStep != nil {
+ if isPtr {
+ stackPtrs.append(1)
+ } else {
+ stackPtrs.append(0)
+ }
+ } else {
+ spillInt += ptrSize
+ }
+ }
+ for _, arg := range t.in() {
+ i, f := in.iregs, in.fregs
+ stkStep := in.addArg(arg)
+ if stkStep != nil {
+ addTypeBits(stackPtrs, stkStep.stkOff, arg)
+ } else {
+ i, f = in.iregs-i, in.fregs-f
+ spillInt += uintptr(i) * ptrSize
+ spillFloat += uintptr(f) * abi.EffectiveFloatRegSize
+ }
+ }
+ spill := align(spillInt+spillFloat, ptrSize)
+
+ // From the input parameters alone, we now know
+ // the stackCallArgsSize and retOffset.
+ stackCallArgsSize := in.stackBytes
+ retOffset := align(in.stackBytes, ptrSize)
+
+ // Compute the stack frame pointer bitmap and register
+ // pointer bitmap for return values.
+ outRegPtrs := abi.IntArgRegBitmap{}
+
+ // Compute abiSeq for output parameters.
+ var out abiSeq
+ // Stack-assigned return values do not share
+ // space with arguments like they do with registers,
+ // so we need to inject a stack offset here.
+ // Fake it by artifically extending stackBytes by
+ // the return offset.
+ out.stackBytes = retOffset
+ for i, res := range t.out() {
+ stkStep := out.addArg(res)
+ if stkStep != nil {
+ addTypeBits(stackPtrs, stkStep.stkOff, res)
+ } else {
+ for _, st := range out.stepsForValue(i) {
+ if st.kind == abiStepPointer {
+ outRegPtrs.Set(st.ireg)
+ }
+ }
+ }
+ }
+ // Undo the faking from earlier so that stackBytes
+ // is accurate.
+ out.stackBytes -= retOffset
+ return abiDesc{in, out, stackCallArgsSize, retOffset, spill, stackPtrs, outRegPtrs}
+}
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index de426b58a8a2c..ddcfca9dee45c 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -23,15 +23,17 @@ const PtrSize = ptrSize
func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack []byte, gc []byte, ptrs bool) {
var ft *rtype
- var s *bitVector
+ var abi abiDesc
if rcvr != nil {
- ft, argSize, retOffset, s, _ = funcLayout((*funcType)(unsafe.Pointer(t.(*rtype))), rcvr.(*rtype))
+ ft, _, abi = funcLayout((*funcType)(unsafe.Pointer(t.(*rtype))), rcvr.(*rtype))
} else {
- ft, argSize, retOffset, s, _ = funcLayout((*funcType)(unsafe.Pointer(t.(*rtype))), nil)
+ ft, _, abi = funcLayout((*funcType)(unsafe.Pointer(t.(*rtype))), nil)
}
+ argSize = abi.stackCallArgsSize
+ retOffset = abi.retOffset
frametype = ft
- for i := uint32(0); i < s.n; i++ {
- stack = append(stack, s.data[i/8]>>(i%8)&1)
+ for i := uint32(0); i < abi.stackPtrs.n; i++ {
+ stack = append(stack, abi.stackPtrs.data[i/8]>>(i%8)&1)
}
if ft.kind&kindGCProg != 0 {
panic("can't handle gc programs")
diff --git a/src/reflect/makefunc.go b/src/reflect/makefunc.go
index 67dc4859b9736..e17d4ea758c3c 100644
--- a/src/reflect/makefunc.go
+++ b/src/reflect/makefunc.go
@@ -60,9 +60,9 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
code := **(**uintptr)(unsafe.Pointer(&dummy))
// makeFuncImpl contains a stack map for use by the runtime
- _, argLen, _, stack, _ := funcLayout(ftyp, nil)
+ _, _, abi := funcLayout(ftyp, nil)
- impl := &makeFuncImpl{code: code, stack: stack, argLen: argLen, ftyp: ftyp, fn: fn}
+ impl := &makeFuncImpl{code: code, stack: abi.stackPtrs, argLen: abi.stackCallArgsSize, ftyp: ftyp, fn: fn}
return Value{t, unsafe.Pointer(impl), flag(Func)}
}
@@ -112,12 +112,12 @@ func makeMethodValue(op string, v Value) Value {
code := **(**uintptr)(unsafe.Pointer(&dummy))
// methodValue contains a stack map for use by the runtime
- _, argLen, _, stack, _ := funcLayout(ftyp, nil)
+ _, _, abi := funcLayout(ftyp, nil)
fv := &methodValue{
fn: code,
- stack: stack,
- argLen: argLen,
+ stack: abi.stackPtrs,
+ argLen: abi.stackCallArgsSize,
method: int(v.flag) >> flagMethodShift,
rcvr: rcvr,
}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index a1cdf45e151ad..d52816628f0d1 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1865,7 +1865,7 @@ func MapOf(key, elem Type) Type {
// Make a map type.
// Note: flag values must match those used in the TMAP case
- // in ../cmd/compile/internal/gc/reflect.go:dtypesym.
+ // in ../cmd/compile/internal/gc/reflect.go:writeType.
var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
mt := **(**mapType)(unsafe.Pointer(&imap))
mt.str = resolveReflectName(newName(s, "", false))
@@ -2984,21 +2984,20 @@ type layoutKey struct {
type layoutType struct {
t *rtype
- argSize uintptr // size of arguments
- retOffset uintptr // offset of return values.
- stack *bitVector
framePool *sync.Pool
+ abi abiDesc
}
var layoutCache sync.Map // map[layoutKey]layoutType
// funcLayout computes a struct type representing the layout of the
-// function arguments and return values for the function type t.
+// stack-assigned function arguments and return values for the function
+// type t.
// If rcvr != nil, rcvr specifies the type of the receiver.
// The returned type exists only for GC, so we only fill out GC relevant info.
// Currently, that's just size and the GC program. We also fill in
// the name for possible debugging use.
-func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr, stk *bitVector, framePool *sync.Pool) {
+func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Pool, abi abiDesc) {
if t.Kind() != Func {
panic("reflect: funcLayout of non-func type " + t.String())
}
@@ -3008,46 +3007,24 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, argSize, retOffset
k := layoutKey{t, rcvr}
if lti, ok := layoutCache.Load(k); ok {
lt := lti.(layoutType)
- return lt.t, lt.argSize, lt.retOffset, lt.stack, lt.framePool
+ return lt.t, lt.framePool, lt.abi
}
- // compute gc program & stack bitmap for arguments
- ptrmap := new(bitVector)
- var offset uintptr
- if rcvr != nil {
- // Reflect uses the "interface" calling convention for
- // methods, where receivers take one word of argument
- // space no matter how big they actually are.
- if ifaceIndir(rcvr) || rcvr.pointers() {
- ptrmap.append(1)
- } else {
- ptrmap.append(0)
- }
- offset += ptrSize
- }
- for _, arg := range t.in() {
- offset += -offset & uintptr(arg.align-1)
- addTypeBits(ptrmap, offset, arg)
- offset += arg.size
- }
- argSize = offset
- offset += -offset & (ptrSize - 1)
- retOffset = offset
- for _, res := range t.out() {
- offset += -offset & uintptr(res.align-1)
- addTypeBits(ptrmap, offset, res)
- offset += res.size
- }
- offset += -offset & (ptrSize - 1)
+ // Compute the ABI layout.
+ abi = newAbiDesc(t, rcvr)
// build dummy rtype holding gc program
x := &rtype{
- align: ptrSize,
- size: offset,
- ptrdata: uintptr(ptrmap.n) * ptrSize,
+ align: ptrSize,
+ // Don't add spill space here; it's only necessary in
+ // reflectcall's frame, not in the allocated frame.
+ // TODO(mknyszek): Remove this comment when register
+ // spill space in the frame is no longer required.
+ size: align(abi.retOffset+abi.ret.stackBytes, ptrSize),
+ ptrdata: uintptr(abi.stackPtrs.n) * ptrSize,
}
- if ptrmap.n > 0 {
- x.gcdata = &ptrmap.data[0]
+ if abi.stackPtrs.n > 0 {
+ x.gcdata = &abi.stackPtrs.data[0]
}
var s string
@@ -3064,13 +3041,11 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, argSize, retOffset
}}
lti, _ := layoutCache.LoadOrStore(k, layoutType{
t: x,
- argSize: argSize,
- retOffset: retOffset,
- stack: ptrmap,
framePool: framePool,
+ abi: abi,
})
lt := lti.(layoutType)
- return lt.t, lt.argSize, lt.retOffset, lt.stack, lt.framePool
+ return lt.t, lt.framePool, lt.abi
}
// ifaceIndir reports whether t is stored indirectly in an interface value.
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 1f185b52e49e3..eae1b9bf294b7 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -5,6 +5,7 @@
package reflect
import (
+ "internal/abi"
"internal/unsafeheader"
"math"
"runtime"
@@ -352,6 +353,8 @@ func (v Value) CallSlice(in []Value) []Value {
var callGC bool // for testing; see TestCallMethodJump
+const debugReflectCall = false
+
func (v Value) call(op string, in []Value) []Value {
// Get function pointer, type.
t := (*funcType)(unsafe.Pointer(v.typ))
@@ -430,50 +433,112 @@ func (v Value) call(op string, in []Value) []Value {
}
nout := t.NumOut()
+ // Register argument space.
+ var regArgs abi.RegArgs
+
// Compute frame type.
- frametype, _, retOffset, _, framePool := funcLayout(t, rcvrtype)
+ frametype, framePool, abi := funcLayout(t, rcvrtype)
- // Allocate a chunk of memory for frame.
- var args unsafe.Pointer
- if nout == 0 {
- args = framePool.Get().(unsafe.Pointer)
- } else {
- // Can't use pool if the function has return values.
- // We will leak pointer to args in ret, so its lifetime is not scoped.
- args = unsafe_New(frametype)
+ // Allocate a chunk of memory for frame if needed.
+ var stackArgs unsafe.Pointer
+ if frametype.size != 0 {
+ if nout == 0 {
+ stackArgs = framePool.Get().(unsafe.Pointer)
+ } else {
+ // Can't use pool if the function has return values.
+ // We will leak pointer to args in ret, so its lifetime is not scoped.
+ stackArgs = unsafe_New(frametype)
+ }
+ }
+ frameSize := frametype.size
+
+ if debugReflectCall {
+ println("reflect.call", t.String())
+ abi.dump()
}
- off := uintptr(0)
// Copy inputs into args.
+
+ // Handle receiver.
+ inStart := 0
if rcvrtype != nil {
- storeRcvr(rcvr, args)
- off = ptrSize
+ // Guaranteed to only be one word in size,
+ // so it will only take up exactly 1 abiStep (either
+ // in a register or on the stack).
+ switch st := abi.call.steps[0]; st.kind {
+ case abiStepStack:
+ storeRcvr(rcvr, stackArgs)
+ case abiStepIntReg, abiStepPointer:
+ // Even pointers can go into the uintptr slot because
+ // they'll be kept alive by the Values referenced by
+ // this frame. Reflection forces these to be heap-allocated,
+ // so we don't need to worry about stack copying.
+ storeRcvr(rcvr, unsafe.Pointer(®Args.Ints[st.ireg]))
+ case abiStepFloatReg:
+ storeRcvr(rcvr, unsafe.Pointer(®Args.Floats[st.freg]))
+ default:
+ panic("unknown ABI parameter kind")
+ }
+ inStart = 1
}
+
+ // Handle arguments.
for i, v := range in {
v.mustBeExported()
targ := t.In(i).(*rtype)
- a := uintptr(targ.align)
- off = (off + a - 1) &^ (a - 1)
- n := targ.size
- if n == 0 {
- // Not safe to compute args+off pointing at 0 bytes,
- // because that might point beyond the end of the frame,
- // but we still need to call assignTo to check assignability.
- v.assignTo("reflect.Value.Call", targ, nil)
- continue
- }
- addr := add(args, off, "n > 0")
- v = v.assignTo("reflect.Value.Call", targ, addr)
- if v.flag&flagIndir != 0 {
- typedmemmove(targ, addr, v.ptr)
- } else {
- *(*unsafe.Pointer)(addr) = v.ptr
+ // TODO(mknyszek): Figure out if it's possible to get some
+ // scratch space for this assignment check. Previously, it
+ // was possible to use space in the argument frame.
+ v = v.assignTo("reflect.Value.Call", targ, nil)
+ stepsLoop:
+ for _, st := range abi.call.stepsForValue(i + inStart) {
+ switch st.kind {
+ case abiStepStack:
+ // Copy values to the "stack."
+ addr := add(stackArgs, st.stkOff, "precomputed stack arg offset")
+ if v.flag&flagIndir != 0 {
+ typedmemmove(targ, addr, v.ptr)
+ } else {
+ *(*unsafe.Pointer)(addr) = v.ptr
+ }
+ // There's only one step for a stack-allocated value.
+ break stepsLoop
+ case abiStepIntReg, abiStepPointer:
+ // Copy values to "integer registers."
+ if v.flag&flagIndir != 0 {
+ offset := add(v.ptr, st.offset, "precomputed value offset")
+ memmove(unsafe.Pointer(®Args.Ints[st.ireg]), offset, st.size)
+ } else {
+ if st.kind == abiStepPointer {
+ // Duplicate this pointer in the pointer area of the
+ // register space. Otherwise, there's the potential for
+ // this to be the last reference to v.ptr.
+ regArgs.Ptrs[st.ireg] = v.ptr
+ }
+ regArgs.Ints[st.ireg] = uintptr(v.ptr)
+ }
+ case abiStepFloatReg:
+ // Copy values to "float registers."
+ if v.flag&flagIndir == 0 {
+ panic("attempted to copy pointer to FP register")
+ }
+ offset := add(v.ptr, st.offset, "precomputed value offset")
+ memmove(unsafe.Pointer(®Args.Floats[st.freg]), offset, st.size)
+ default:
+ panic("unknown ABI part kind")
+ }
}
- off += n
}
+ // TODO(mknyszek): Remove this when we no longer have
+ // caller reserved spill space.
+ frameSize = align(frameSize, ptrSize)
+ frameSize += abi.spill
+
+ // Mark pointers in registers for the return path.
+ regArgs.ReturnIsPtr = abi.outRegPtrs
// Call.
- call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
+ call(frametype, fn, stackArgs, uint32(frametype.size), uint32(abi.retOffset), uint32(frameSize), ®Args)
// For testing; see TestCallMethodJump.
if callGC {
@@ -482,34 +547,82 @@ func (v Value) call(op string, in []Value) []Value {
var ret []Value
if nout == 0 {
- typedmemclr(frametype, args)
- framePool.Put(args)
+ if stackArgs != nil {
+ typedmemclr(frametype, stackArgs)
+ framePool.Put(stackArgs)
+ }
} else {
- // Zero the now unused input area of args,
- // because the Values returned by this function contain pointers to the args object,
- // and will thus keep the args object alive indefinitely.
- typedmemclrpartial(frametype, args, 0, retOffset)
+ if stackArgs != nil {
+ // Zero the now unused input area of args,
+ // because the Values returned by this function contain pointers to the args object,
+ // and will thus keep the args object alive indefinitely.
+ typedmemclrpartial(frametype, stackArgs, 0, abi.retOffset)
+ }
// Wrap Values around return values in args.
ret = make([]Value, nout)
- off = retOffset
for i := 0; i < nout; i++ {
tv := t.Out(i)
- a := uintptr(tv.Align())
- off = (off + a - 1) &^ (a - 1)
- if tv.Size() != 0 {
+ if tv.Size() == 0 {
+ // For zero-sized return value, args+off may point to the next object.
+ // In this case, return the zero value instead.
+ ret[i] = Zero(tv)
+ continue
+ }
+ steps := abi.ret.stepsForValue(i)
+ if st := steps[0]; st.kind == abiStepStack {
+ // This value is on the stack. If part of a value is stack
+ // allocated, the entire value is according to the ABI. So
+ // just make an indirection into the allocated frame.
fl := flagIndir | flag(tv.Kind())
- ret[i] = Value{tv.common(), add(args, off, "tv.Size() != 0"), fl}
+ ret[i] = Value{tv.common(), add(stackArgs, st.stkOff, "tv.Size() != 0"), fl}
// Note: this does introduce false sharing between results -
// if any result is live, they are all live.
// (And the space for the args is live as well, but as we've
// cleared that space it isn't as big a deal.)
- } else {
- // For zero-sized return value, args+off may point to the next object.
- // In this case, return the zero value instead.
- ret[i] = Zero(tv)
+ continue
+ }
+
+ // Handle pointers passed in registers.
+ if !ifaceIndir(tv.common()) {
+ // Pointer-valued data gets put directly
+ // into v.ptr.
+ if steps[0].kind != abiStepPointer {
+ print("kind=", steps[0].kind, ", type=", tv.String(), "\n")
+ panic("mismatch between ABI description and types")
+ }
+ ret[i] = Value{tv.common(), regArgs.Ptrs[steps[0].ireg], flag(t.Kind())}
+ continue
+ }
+
+ // All that's left is values passed in registers that we need to
+ // create space for and copy values back into.
+ //
+ // TODO(mknyszek): We make a new allocation for each register-allocated
+ // value, but previously we could always point into the heap-allocated
+ // stack frame. This is a regression that could be fixed by adding
+ // additional space to the allocated stack frame and storing the
+ // register-allocated return values into the allocated stack frame and
+ // referring there in the resulting Value.
+ s := unsafe_New(tv.common())
+ for _, st := range steps {
+ switch st.kind {
+ case abiStepIntReg:
+ offset := add(s, st.offset, "precomputed value offset")
+ memmove(offset, unsafe.Pointer(®Args.Ints[st.ireg]), st.size)
+ case abiStepPointer:
+ s := add(s, st.offset, "precomputed value offset")
+ *((*unsafe.Pointer)(s)) = regArgs.Ptrs[st.ireg]
+ case abiStepFloatReg:
+ offset := add(s, st.offset, "precomputed value offset")
+ memmove(offset, unsafe.Pointer(®Args.Floats[st.freg]), st.size)
+ case abiStepStack:
+ panic("register-based return value has stack component")
+ default:
+ panic("unknown ABI part kind")
+ }
}
- off += tv.Size()
+ ret[i] = Value{tv.common(), s, flagIndir | flag(tv.Kind())}
}
}
@@ -709,7 +822,8 @@ func align(x, n uintptr) uintptr {
func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool) {
rcvr := ctxt.rcvr
rcvrtype, t, fn := methodReceiver("call", rcvr, ctxt.method)
- frametype, argSize, retOffset, _, framePool := funcLayout(t, rcvrtype)
+ frametype, framePool, abid := funcLayout(t, rcvrtype)
+ argSize, retOffset := abid.stackCallArgsSize, abid.retOffset
// Make a new frame that is one word bigger so we can store the receiver.
// This space is used for both arguments and return values.
@@ -727,10 +841,19 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool) {
typedmemmovepartial(frametype, add(scratch, argOffset, "argSize > argOffset"), frame, argOffset, argSize-argOffset)
}
+ frameSize := frametype.size
+ // TODO(mknyszek): Remove this when we no longer have
+ // caller reserved spill space.
+ frameSize = align(frameSize, ptrSize)
+ frameSize += abid.spill
+
// Call.
// Call copies the arguments from scratch to the stack, calls fn,
// and then copies the results back into scratch.
- call(frametype, fn, scratch, uint32(frametype.size), uint32(retOffset))
+ //
+ // TODO(mknyszek): Have this actually support the register-based ABI.
+ var regs abi.RegArgs
+ call(frametype, fn, scratch, uint32(frametype.size), uint32(retOffset), uint32(frameSize), ®s)
// Copy return values.
// Ignore any changes to args and just copy return values.
@@ -2802,14 +2925,32 @@ func mapiternext(it unsafe.Pointer)
//go:noescape
func maplen(m unsafe.Pointer) int
-// call calls fn with a copy of the n argument bytes pointed at by arg.
-// After fn returns, reflectcall copies n-retoffset result bytes
-// back into arg+retoffset before returning. If copying result bytes back,
-// the caller must pass the argument frame type as argtype, so that
-// call can execute appropriate write barriers during the copy.
+// call calls fn with "stackArgsSize" bytes of stack arguments laid out
+// at stackArgs and register arguments laid out in regArgs. frameSize is
+// the total amount of stack space that will be reserved by call, so this
+// should include enough space to spill register arguments to the stack in
+// case of preemption.
+//
+// After fn returns, call copies stackArgsSize-stackRetOffset result bytes
+// back into stackArgs+stackRetOffset before returning, for any return
+// values passed on the stack. Register-based return values will be found
+// in the same regArgs structure.
+//
+// regArgs must also be prepared with an appropriate ReturnIsPtr bitmap
+// indicating which registers will contain pointer-valued return values. The
+// purpose of this bitmap is to keep pointers visible to the GC between
+// returning from reflectcall and actually using them.
//
+// If copying result bytes back from the stack, the caller must pass the
+// argument frame type as stackArgsType, so that call can execute appropriate
+// write barriers during the copy.
+//
+// Arguments passed through to call do not escape. The type is used only in a
+// very limited callee of call, the stackArgs are copied, and regArgs is only
+// used in the call frame.
+//go:noescape
//go:linkname call runtime.reflectcall
-func call(argtype *rtype, fn, arg unsafe.Pointer, n uint32, retoffset uint32)
+func call(stackArgsType *rtype, f, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs)
func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer)
diff --git a/src/runtime/asm.s b/src/runtime/asm.s
index 27d8df9e063ec..72c744925d89a 100644
--- a/src/runtime/asm.s
+++ b/src/runtime/asm.s
@@ -11,3 +11,8 @@
DATA runtime·no_pointers_stackmap+0x00(SB)/4, $2
DATA runtime·no_pointers_stackmap+0x04(SB)/4, $0
GLOBL runtime·no_pointers_stackmap(SB),RODATA, $8
+
+#ifndef GOARCH_amd64
+TEXT ·sigpanic0(SB),NOSPLIT,$0-0
+ JMP ·sigpanic(SB)
+#endif
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s
index fa3b1be339259..471451df282c0 100644
--- a/src/runtime/asm_386.s
+++ b/src/runtime/asm_386.s
@@ -273,25 +273,6 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
* go-routine
*/
-// void gosave(Gobuf*)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT, $0-4
- MOVL buf+0(FP), AX // gobuf
- LEAL buf+0(FP), BX // caller's SP
- MOVL BX, gobuf_sp(AX)
- MOVL 0(SP), BX // caller's PC
- MOVL BX, gobuf_pc(AX)
- MOVL $0, gobuf_ret(AX)
- // Assert ctxt is zero. See func save.
- MOVL gobuf_ctxt(AX), BX
- TESTL BX, BX
- JZ 2(PC)
- CALL runtime·badctxt(SB)
- get_tls(CX)
- MOVL g(CX), BX
- MOVL BX, gobuf_g(AX)
- RET
-
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $8-4
@@ -477,7 +458,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
JMP runtime·morestack(SB)
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -489,8 +470,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
JMP AX
// Note: can't just "JMP NAME(SB)" - bad inlining results.
-TEXT ·reflectcall(SB), NOSPLIT, $0-20
- MOVL argsize+12(FP), CX
+TEXT ·reflectcall(SB), NOSPLIT, $0-28
+ MOVL frameSize+20(FP), CX
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -522,11 +503,11 @@ TEXT ·reflectcall(SB), NOSPLIT, $0-20
JMP AX
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-28; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVL argptr+8(FP), SI; \
- MOVL argsize+12(FP), CX; \
+ MOVL stackArgs+8(FP), SI; \
+ MOVL stackArgsSize+12(FP), CX; \
MOVL SP, DI; \
REP;MOVSB; \
/* call function */ \
@@ -535,10 +516,10 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
PCDATA $PCDATA_StackMapIndex, $0; \
CALL AX; \
/* copy return values back */ \
- MOVL argtype+0(FP), DX; \
- MOVL argptr+8(FP), DI; \
- MOVL argsize+12(FP), CX; \
- MOVL retoffset+16(FP), BX; \
+ MOVL stackArgsType+0(FP), DX; \
+ MOVL stackArgs+8(FP), DI; \
+ MOVL stackArgsSize+12(FP), CX; \
+ MOVL stackRetOffset+16(FP), BX; \
MOVL SP, SI; \
ADDL BX, DI; \
ADDL BX, SI; \
@@ -550,11 +531,12 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $16-0
+TEXT callRet<>(SB), NOSPLIT, $20-0
MOVL DX, 0(SP)
MOVL DI, 4(SP)
MOVL SI, 8(SP)
MOVL CX, 12(SP)
+ MOVL $0, 16(SP)
CALL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 4ac87089f20df..05422c9699c45 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -84,9 +84,7 @@ GLOBL _rt0_amd64_lib_argc<>(SB),NOPTR, $8
DATA _rt0_amd64_lib_argv<>(SB)/8, $0
GLOBL _rt0_amd64_lib_argv<>(SB),NOPTR, $8
-// Defined as ABIInternal since it does not use the stack-based Go ABI (and
-// in addition there are no calls to this entry point from Go code).
-TEXT runtime·rt0_go(SB),NOSPLIT,$0
+TEXT runtime·rt0_go(SB),NOSPLIT,$0
// copy arguments forward on an even stack
MOVQ DI, AX // argc
MOVQ SI, BX // argv
@@ -256,26 +254,6 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
* go-routine
*/
-// func gosave(buf *gobuf)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT, $0-8
- MOVQ buf+0(FP), AX // gobuf
- LEAQ buf+0(FP), BX // caller's SP
- MOVQ BX, gobuf_sp(AX)
- MOVQ 0(SP), BX // caller's PC
- MOVQ BX, gobuf_pc(AX)
- MOVQ $0, gobuf_ret(AX)
- MOVQ BP, gobuf_bp(AX)
- // Assert ctxt is zero. See func save.
- MOVQ gobuf_ctxt(AX), BX
- TESTQ BX, BX
- JZ 2(PC)
- CALL runtime·badctxt(SB)
- get_tls(CX)
- MOVQ g(CX), BX
- MOVQ BX, gobuf_g(AX)
- RET
-
// func gogo(buf *gobuf)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $16-8
@@ -284,6 +262,7 @@ TEXT runtime·gogo(SB), NOSPLIT, $16-8
MOVQ 0(DX), CX // make sure g != nil
get_tls(CX)
MOVQ DX, g(CX)
+ MOVQ DX, R14 // set the g register
MOVQ gobuf_sp(BX), SP // restore SP
MOVQ gobuf_ret(BX), AX
MOVQ gobuf_ctxt(BX), DX
@@ -320,6 +299,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $0-8
MOVQ $runtime·badmcall(SB), AX
JMP AX
MOVQ SI, g(CX) // g = m->g0
+ MOVQ SI, R14 // set the g register
MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp
PUSHQ AX
MOVQ DI, DX
@@ -366,6 +346,7 @@ TEXT runtime·systemstack(SB), NOSPLIT, $0-8
// switch to g0
MOVQ DX, g(CX)
+ MOVQ DX, R14 // set the g register
MOVQ (g_sched+gobuf_sp)(DX), BX
// make it look like mstart called systemstack on g0, to stop traceback
SUBQ $8, BX
@@ -464,8 +445,77 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
MOVL $0, DX
JMP runtime·morestack(SB)
+// REFLECTCALL_USE_REGABI is not defined. It must be defined in conjunction with the
+// register constants in the internal/abi package.
+
+#ifdef REFLECTCALL_USE_REGABI
+// spillArgs stores return values from registers to a *internal/abi.RegArgs in R12.
+TEXT spillArgs<>(SB),NOSPLIT,$0-0
+ MOVQ AX, 0(R12)
+ MOVQ BX, 8(R12)
+ MOVQ CX, 16(R12)
+ MOVQ DI, 24(R12)
+ MOVQ SI, 32(R12)
+ MOVQ R8, 40(R12)
+ MOVQ R9, 48(R12)
+ MOVQ R10, 56(R12)
+ MOVQ R11, 64(R12)
+ MOVQ X0, 72(R12)
+ MOVQ X1, 80(R12)
+ MOVQ X2, 88(R12)
+ MOVQ X3, 96(R12)
+ MOVQ X4, 104(R12)
+ MOVQ X5, 112(R12)
+ MOVQ X6, 120(R12)
+ MOVQ X7, 128(R12)
+ MOVQ X8, 136(R12)
+ MOVQ X9, 144(R12)
+ MOVQ X10, 152(R12)
+ MOVQ X11, 160(R12)
+ MOVQ X12, 168(R12)
+ MOVQ X13, 176(R12)
+ MOVQ X14, 184(R12)
+ RET
+
+// unspillArgs loads args into registers from a *internal/abi.RegArgs in R12.
+TEXT unspillArgs<>(SB),NOSPLIT,$0-0
+ MOVQ 0(R12), AX
+ MOVQ 8(R12), BX
+ MOVQ 16(R12), CX
+ MOVQ 24(R12), DI
+ MOVQ 32(R12), SI
+ MOVQ 40(R12), R8
+ MOVQ 48(R12), R9
+ MOVQ 56(R12), R10
+ MOVQ 64(R12), R11
+ MOVQ 72(R12), X0
+ MOVQ 80(R12), X1
+ MOVQ 88(R12), X2
+ MOVQ 96(R12), X3
+ MOVQ 104(R12), X4
+ MOVQ 112(R12), X5
+ MOVQ 120(R12), X6
+ MOVQ 128(R12), X7
+ MOVQ 136(R12), X8
+ MOVQ 144(R12), X9
+ MOVQ 152(R12), X10
+ MOVQ 160(R12), X11
+ MOVQ 168(R12), X12
+ MOVQ 176(R12), X13
+ MOVQ 184(R12), X14
+ RET
+#else
+// spillArgs stores return values from registers to a pointer in R12.
+TEXT spillArgs<>(SB),NOSPLIT,$0-0
+ RET
+
+// unspillArgs loads args into registers from a pointer in R12.
+TEXT unspillArgs<>(SB),NOSPLIT,$0-0
+ RET
+#endif
+
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -477,8 +527,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0
JMP AX
// Note: can't just "JMP NAME(SB)" - bad inlining results.
-TEXT ·reflectcall(SB), NOSPLIT, $0-32
- MOVLQZX argsize+24(FP), CX
+TEXT ·reflectcall(SB), NOSPLIT, $0-48
+ MOVLQZX frameSize+32(FP), CX
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -510,23 +560,28 @@ TEXT ·reflectcall(SB), NOSPLIT, $0-32
JMP AX
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVQ argptr+16(FP), SI; \
- MOVLQZX argsize+24(FP), CX; \
+ MOVQ stackArgs+16(FP), SI; \
+ MOVLQZX stackArgsSize+24(FP), CX; \
MOVQ SP, DI; \
REP;MOVSB; \
+ /* set up argument registers */ \
+ MOVQ regArgs+40(FP), R12; \
+ CALL unspillArgs<>(SB); \
/* call function */ \
MOVQ f+8(FP), DX; \
PCDATA $PCDATA_StackMapIndex, $0; \
- MOVQ (DX), AX; \
- CALL AX; \
- /* copy return values back */ \
- MOVQ argtype+0(FP), DX; \
- MOVQ argptr+16(FP), DI; \
- MOVLQZX argsize+24(FP), CX; \
- MOVLQZX retoffset+28(FP), BX; \
+ MOVQ (DX), R12; \
+ CALL R12; \
+ /* copy register return values back */ \
+ MOVQ regArgs+40(FP), R12; \
+ CALL spillArgs<>(SB); \
+ MOVLQZX stackArgsSize+24(FP), CX; \
+ MOVLQZX stackRetOffset+28(FP), BX; \
+ MOVQ stackArgs+16(FP), DI; \
+ MOVQ stackArgsType+0(FP), DX; \
MOVQ SP, SI; \
ADDQ BX, DI; \
ADDQ BX, SI; \
@@ -538,12 +593,13 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $32-0
+TEXT callRet<>(SB), NOSPLIT, $40-0
NO_LOCAL_POINTERS
MOVQ DX, 0(SP)
MOVQ DI, 8(SP)
MOVQ SI, 16(SP)
MOVQ CX, 24(SP)
+ MOVQ R12, 32(SP)
CALL runtime·reflectcallmove(SB)
RET
@@ -604,18 +660,20 @@ TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16
MOVQ 0(DX), BX
JMP BX // but first run the deferred function
-// Save state of caller into g->sched. Smashes R8, R9.
+// Save state of caller into g->sched. Smashes R9.
TEXT gosave<>(SB),NOSPLIT,$0
- get_tls(R8)
- MOVQ g(R8), R8
+#ifndef GOEXPERIMENT_REGABI
+ get_tls(R14)
+ MOVQ g(R14), R14
+#endif
MOVQ 0(SP), R9
- MOVQ R9, (g_sched+gobuf_pc)(R8)
+ MOVQ R9, (g_sched+gobuf_pc)(R14)
LEAQ 8(SP), R9
- MOVQ R9, (g_sched+gobuf_sp)(R8)
- MOVQ $0, (g_sched+gobuf_ret)(R8)
- MOVQ BP, (g_sched+gobuf_bp)(R8)
+ MOVQ R9, (g_sched+gobuf_sp)(R14)
+ MOVQ $0, (g_sched+gobuf_ret)(R14)
+ MOVQ BP, (g_sched+gobuf_bp)(R14)
// Assert ctxt is zero. See func save.
- MOVQ (g_sched+gobuf_ctxt)(R8), R9
+ MOVQ (g_sched+gobuf_ctxt)(R14), R9
TESTQ R9, R9
JZ 2(PC)
CALL runtime·badctxt(SB)
@@ -846,6 +904,7 @@ settls:
TEXT setg_gcc<>(SB),NOSPLIT,$0
get_tls(AX)
MOVQ DI, g(AX)
+ MOVQ DI, R14 // set the g register
RET
TEXT runtime·abort(SB),NOSPLIT,$0-0
@@ -1382,6 +1441,18 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
POPQ R15
RET
+// Initialize special registers then jump to sigpanic.
+// This function is injected from the signal handler for panicking
+// signals. It is quite painful to set X15 in the signal context,
+// so we do it here.
+TEXT ·sigpanic0(SB),NOSPLIT,$0-0
+#ifdef GOEXPERIMENT_REGABI
+ get_tls(R14)
+ MOVQ g(R14), R14
+ XORPS X15, X15
+#endif
+ JMP ·sigpanic(SB)
+
// gcWriteBarrier performs a heap pointer write and informs the GC.
//
// gcWriteBarrier does NOT follow the Go ABI. It takes two arguments:
@@ -1390,24 +1461,28 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
// It clobbers FLAGS. It does not clobber any general-purpose registers,
// but may clobber others (e.g., SSE registers).
// Defined as ABIInternal since it does not use the stack-based Go ABI.
-TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120
+TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$112
// Save the registers clobbered by the fast path. This is slightly
// faster than having the caller spill these.
- MOVQ R14, 104(SP)
- MOVQ R13, 112(SP)
+ MOVQ R12, 96(SP)
+ MOVQ R13, 104(SP)
// TODO: Consider passing g.m.p in as an argument so they can be shared
// across a sequence of write barriers.
+#ifdef GOEXPERIMENT_REGABI
+ MOVQ g_m(R14), R13
+#else
get_tls(R13)
MOVQ g(R13), R13
MOVQ g_m(R13), R13
+#endif
MOVQ m_p(R13), R13
- MOVQ (p_wbBuf+wbBuf_next)(R13), R14
+ MOVQ (p_wbBuf+wbBuf_next)(R13), R12
// Increment wbBuf.next position.
- LEAQ 16(R14), R14
- MOVQ R14, (p_wbBuf+wbBuf_next)(R13)
- CMPQ R14, (p_wbBuf+wbBuf_end)(R13)
+ LEAQ 16(R12), R12
+ MOVQ R12, (p_wbBuf+wbBuf_next)(R13)
+ CMPQ R12, (p_wbBuf+wbBuf_end)(R13)
// Record the write.
- MOVQ AX, -16(R14) // Record value
+ MOVQ AX, -16(R12) // Record value
// Note: This turns bad pointer writes into bad
// pointer reads, which could be confusing. We could avoid
// reading from obviously bad pointers, which would
@@ -1415,12 +1490,12 @@ TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$120
// patch this up in the signal handler, or use XCHG to
// combine the read and the write.
MOVQ (DI), R13
- MOVQ R13, -8(R14) // Record *slot
+ MOVQ R13, -8(R12) // Record *slot
// Is the buffer full? (flags set in CMPQ above)
JEQ flush
ret:
- MOVQ 104(SP), R14
- MOVQ 112(SP), R13
+ MOVQ 96(SP), R12
+ MOVQ 104(SP), R13
// Do the write.
MOVQ AX, (DI)
RET
@@ -1450,10 +1525,10 @@ flush:
MOVQ R9, 64(SP)
MOVQ R10, 72(SP)
MOVQ R11, 80(SP)
- MOVQ R12, 88(SP)
+ // R12 already saved
// R13 already saved
- // R14 already saved
- MOVQ R15, 96(SP)
+ // R14 is g
+ MOVQ R15, 88(SP)
// This takes arguments DI and AX
CALL runtime·wbBufFlush(SB)
@@ -1469,8 +1544,7 @@ flush:
MOVQ 64(SP), R9
MOVQ 72(SP), R10
MOVQ 80(SP), R11
- MOVQ 88(SP), R12
- MOVQ 96(SP), R15
+ MOVQ 88(SP), R15
JMP ret
// gcWriteBarrierCX is gcWriteBarrier, but with args in DI and CX.
@@ -1732,67 +1806,67 @@ TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16
TEXT runtime·panicIndex(SB),NOSPLIT,$0-16
MOVQ AX, x+0(FP)
MOVQ CX, y+8(FP)
- JMP runtime·goPanicIndex(SB)
+ JMP runtime·goPanicIndex(SB)
TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16
MOVQ AX, x+0(FP)
MOVQ CX, y+8(FP)
- JMP runtime·goPanicIndexU(SB)
+ JMP runtime·goPanicIndexU(SB)
TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16
MOVQ CX, x+0(FP)
MOVQ DX, y+8(FP)
- JMP runtime·goPanicSliceAlen(SB)
+ JMP runtime·goPanicSliceAlen(SB)
TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16
MOVQ CX, x+0(FP)
MOVQ DX, y+8(FP)
- JMP runtime·goPanicSliceAlenU(SB)
+ JMP runtime·goPanicSliceAlenU(SB)
TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16
MOVQ CX, x+0(FP)
MOVQ DX, y+8(FP)
- JMP runtime·goPanicSliceAcap(SB)
+ JMP runtime·goPanicSliceAcap(SB)
TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16
MOVQ CX, x+0(FP)
MOVQ DX, y+8(FP)
- JMP runtime·goPanicSliceAcapU(SB)
+ JMP runtime·goPanicSliceAcapU(SB)
TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16
MOVQ AX, x+0(FP)
MOVQ CX, y+8(FP)
- JMP runtime·goPanicSliceB(SB)
+ JMP runtime·goPanicSliceB(SB)
TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16
MOVQ AX, x+0(FP)
MOVQ CX, y+8(FP)
- JMP runtime·goPanicSliceBU(SB)
+ JMP runtime·goPanicSliceBU(SB)
TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16
MOVQ DX, x+0(FP)
MOVQ BX, y+8(FP)
- JMP runtime·goPanicSlice3Alen(SB)
+ JMP runtime·goPanicSlice3Alen(SB)
TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16
MOVQ DX, x+0(FP)
MOVQ BX, y+8(FP)
- JMP runtime·goPanicSlice3AlenU(SB)
+ JMP runtime·goPanicSlice3AlenU(SB)
TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16
MOVQ DX, x+0(FP)
MOVQ BX, y+8(FP)
- JMP runtime·goPanicSlice3Acap(SB)
+ JMP runtime·goPanicSlice3Acap(SB)
TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16
MOVQ DX, x+0(FP)
MOVQ BX, y+8(FP)
- JMP runtime·goPanicSlice3AcapU(SB)
+ JMP runtime·goPanicSlice3AcapU(SB)
TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16
MOVQ CX, x+0(FP)
MOVQ DX, y+8(FP)
- JMP runtime·goPanicSlice3B(SB)
+ JMP runtime·goPanicSlice3B(SB)
TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16
MOVQ CX, x+0(FP)
MOVQ DX, y+8(FP)
- JMP runtime·goPanicSlice3BU(SB)
+ JMP runtime·goPanicSlice3BU(SB)
TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16
MOVQ AX, x+0(FP)
MOVQ CX, y+8(FP)
- JMP runtime·goPanicSlice3C(SB)
+ JMP runtime·goPanicSlice3C(SB)
TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16
MOVQ AX, x+0(FP)
MOVQ CX, y+8(FP)
- JMP runtime·goPanicSlice3CU(SB)
+ JMP runtime·goPanicSlice3CU(SB)
#ifdef GOOS_android
// Use the free TLS_SLOT_APP slot #2 on Android Q.
diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s
index c54b4eb0061e6..23619b1408496 100644
--- a/src/runtime/asm_arm.s
+++ b/src/runtime/asm_arm.s
@@ -206,23 +206,6 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
* go-routine
*/
-// void gosave(Gobuf*)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB),NOSPLIT|NOFRAME,$0-4
- MOVW buf+0(FP), R0
- MOVW R13, gobuf_sp(R0)
- MOVW LR, gobuf_pc(R0)
- MOVW g, gobuf_g(R0)
- MOVW $0, R11
- MOVW R11, gobuf_lr(R0)
- MOVW R11, gobuf_ret(R0)
- // Assert ctxt is zero. See func save.
- MOVW gobuf_ctxt(R0), R0
- CMP R0, R11
- B.EQ 2(PC)
- CALL runtime·badctxt(SB)
- RET
-
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB),NOSPLIT,$8-4
@@ -421,7 +404,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
B runtime·morestack(SB)
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -432,8 +415,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
MOVW $NAME(SB), R1; \
B (R1)
-TEXT ·reflectcall(SB),NOSPLIT|NOFRAME,$0-20
- MOVW argsize+12(FP), R0
+TEXT ·reflectcall(SB),NOSPLIT|NOFRAME,$0-28
+ MOVW frameSize+20(FP), R0
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -465,11 +448,11 @@ TEXT ·reflectcall(SB),NOSPLIT|NOFRAME,$0-20
B (R1)
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-28; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVW argptr+8(FP), R0; \
- MOVW argsize+12(FP), R2; \
+ MOVW stackArgs+8(FP), R0; \
+ MOVW stackArgsSize+12(FP), R2; \
ADD $4, R13, R1; \
CMP $0, R2; \
B.EQ 5(PC); \
@@ -483,10 +466,10 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
PCDATA $PCDATA_StackMapIndex, $0; \
BL (R0); \
/* copy return values back */ \
- MOVW argtype+0(FP), R4; \
- MOVW argptr+8(FP), R0; \
- MOVW argsize+12(FP), R2; \
- MOVW retoffset+16(FP), R3; \
+ MOVW stackArgsType+0(FP), R4; \
+ MOVW stackArgs+8(FP), R0; \
+ MOVW stackArgsSize+12(FP), R2; \
+ MOVW stackArgsRetOffset+16(FP), R3; \
ADD $4, R13, R1; \
ADD R3, R1; \
ADD R3, R0; \
@@ -498,11 +481,13 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $16-0
+TEXT callRet<>(SB), NOSPLIT, $20-0
MOVW R4, 4(R13)
MOVW R0, 8(R13)
MOVW R1, 12(R13)
MOVW R2, 16(R13)
+ MOVW $0, R7
+ MOVW R7, 20(R13)
BL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s
index a09172f0c964d..0ab92be1e4b95 100644
--- a/src/runtime/asm_arm64.s
+++ b/src/runtime/asm_arm64.s
@@ -113,23 +113,6 @@ TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
* go-routine
*/
-// void gosave(Gobuf*)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8
- MOVD buf+0(FP), R3
- MOVD RSP, R0
- MOVD R0, gobuf_sp(R3)
- MOVD R29, gobuf_bp(R3)
- MOVD LR, gobuf_pc(R3)
- MOVD g, gobuf_g(R3)
- MOVD ZR, gobuf_lr(R3)
- MOVD ZR, gobuf_ret(R3)
- // Assert ctxt is zero. See func save.
- MOVD gobuf_ctxt(R3), R0
- CBZ R0, 2(PC)
- CALL runtime·badctxt(SB)
- RET
-
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $24-8
@@ -329,7 +312,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
B runtime·morestack(SB)
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -342,8 +325,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
B (R27)
// Note: can't just "B NAME(SB)" - bad inlining results.
-TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
- MOVWU argsize+24(FP), R16
+TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48
+ MOVWU frameSize+32(FP), R16
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -375,11 +358,11 @@ TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
B (R0)
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVD arg+16(FP), R3; \
- MOVWU argsize+24(FP), R4; \
+ MOVD stackArgs+16(FP), R3; \
+ MOVWU stackArgsSize+24(FP), R4; \
ADD $8, RSP, R5; \
BIC $0xf, R4, R6; \
CBZ R6, 6(PC); \
@@ -405,10 +388,10 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
PCDATA $PCDATA_StackMapIndex, $0; \
BL (R0); \
/* copy return values back */ \
- MOVD argtype+0(FP), R7; \
- MOVD arg+16(FP), R3; \
- MOVWU n+24(FP), R4; \
- MOVWU retoffset+28(FP), R6; \
+ MOVD stackArgsType+0(FP), R7; \
+ MOVD stackArgs+16(FP), R3; \
+ MOVWU stackArgsSize+24(FP), R4; \
+ MOVWU stackRetOffset+28(FP), R6; \
ADD $8, RSP, R5; \
ADD R6, R5; \
ADD R6, R3; \
@@ -420,11 +403,12 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $40-0
+TEXT callRet<>(SB), NOSPLIT, $48-0
MOVD R7, 8(RSP)
MOVD R3, 16(RSP)
MOVD R5, 24(RSP)
MOVD R4, 32(RSP)
+ MOVD $0, 40(RSP)
BL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s
index 19781f7885fda..694950663a920 100644
--- a/src/runtime/asm_mips64x.s
+++ b/src/runtime/asm_mips64x.s
@@ -89,21 +89,6 @@ TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
* go-routine
*/
-// void gosave(Gobuf*)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8
- MOVV buf+0(FP), R1
- MOVV R29, gobuf_sp(R1)
- MOVV R31, gobuf_pc(R1)
- MOVV g, gobuf_g(R1)
- MOVV R0, gobuf_lr(R1)
- MOVV R0, gobuf_ret(R1)
- // Assert ctxt is zero. See func save.
- MOVV gobuf_ctxt(R1), R1
- BEQ R1, 2(PC)
- JAL runtime·badctxt(SB)
- RET
-
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $16-8
@@ -279,7 +264,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
JMP runtime·morestack(SB)
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -292,8 +277,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
JMP (R4)
// Note: can't just "BR NAME(SB)" - bad inlining results.
-TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
- MOVWU argsize+24(FP), R1
+TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48
+ MOVWU frameSize+32(FP), R1
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -325,11 +310,11 @@ TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
JMP (R4)
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVV arg+16(FP), R1; \
- MOVWU argsize+24(FP), R2; \
+ MOVV stackArgs+16(FP), R1; \
+ MOVWU stackArgsSize+24(FP), R2; \
MOVV R29, R3; \
ADDV $8, R3; \
ADDV R3, R2; \
@@ -345,10 +330,10 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
PCDATA $PCDATA_StackMapIndex, $0; \
JAL (R4); \
/* copy return values back */ \
- MOVV argtype+0(FP), R5; \
- MOVV arg+16(FP), R1; \
- MOVWU n+24(FP), R2; \
- MOVWU retoffset+28(FP), R4; \
+ MOVV stackArgsType+0(FP), R5; \
+ MOVV stackArgs+16(FP), R1; \
+ MOVWU stackArgsSize+24(FP), R2; \
+ MOVWU stackRetOffset+28(FP), R4; \
ADDV $8, R29, R3; \
ADDV R4, R3; \
ADDV R4, R1; \
@@ -360,11 +345,12 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $32-0
+TEXT callRet<>(SB), NOSPLIT, $40-0
MOVV R5, 8(R29)
MOVV R1, 16(R29)
MOVV R3, 24(R29)
MOVV R2, 32(R29)
+ MOVV $0, 40(R29)
JAL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_mipsx.s b/src/runtime/asm_mipsx.s
index ee87d81436304..8e5753d255728 100644
--- a/src/runtime/asm_mipsx.s
+++ b/src/runtime/asm_mipsx.s
@@ -90,21 +90,6 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
* go-routine
*/
-// void gosave(Gobuf*)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB),NOSPLIT|NOFRAME,$0-4
- MOVW buf+0(FP), R1
- MOVW R29, gobuf_sp(R1)
- MOVW R31, gobuf_pc(R1)
- MOVW g, gobuf_g(R1)
- MOVW R0, gobuf_lr(R1)
- MOVW R0, gobuf_ret(R1)
- // Assert ctxt is zero. See func save.
- MOVW gobuf_ctxt(R1), R1
- BEQ R1, 2(PC)
- JAL runtime·badctxt(SB)
- RET
-
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB),NOSPLIT,$8-4
@@ -280,7 +265,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
JMP runtime·morestack(SB)
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
@@ -291,8 +276,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
MOVW $NAME(SB), R4; \
JMP (R4)
-TEXT ·reflectcall(SB),NOSPLIT|NOFRAME,$0-20
- MOVW argsize+12(FP), R1
+TEXT ·reflectcall(SB),NOSPLIT|NOFRAME,$0-28
+ MOVW frameSize+20(FP), R1
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -325,11 +310,11 @@ TEXT ·reflectcall(SB),NOSPLIT|NOFRAME,$0-20
JMP (R4)
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB),WRAPPER,$MAXSIZE-20; \
+TEXT NAME(SB),WRAPPER,$MAXSIZE-28; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVW arg+8(FP), R1; \
- MOVW argsize+12(FP), R2; \
+ MOVW stackArgs+8(FP), R1; \
+ MOVW stackArgsSize+12(FP), R2; \
MOVW R29, R3; \
ADDU $4, R3; \
ADDU R3, R2; \
@@ -345,10 +330,10 @@ TEXT NAME(SB),WRAPPER,$MAXSIZE-20; \
PCDATA $PCDATA_StackMapIndex, $0; \
JAL (R4); \
/* copy return values back */ \
- MOVW argtype+0(FP), R5; \
- MOVW arg+8(FP), R1; \
- MOVW n+12(FP), R2; \
- MOVW retoffset+16(FP), R4; \
+ MOVW stackArgsType+0(FP), R5; \
+ MOVW stackArgs+8(FP), R1; \
+ MOVW stackArgsSize+12(FP), R2; \
+ MOVW stackRetOffset+16(FP), R4; \
ADDU $4, R29, R3; \
ADDU R4, R3; \
ADDU R4, R1; \
@@ -360,11 +345,12 @@ TEXT NAME(SB),WRAPPER,$MAXSIZE-20; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $16-0
+TEXT callRet<>(SB), NOSPLIT, $20-0
MOVW R5, 4(R29)
MOVW R1, 8(R29)
MOVW R3, 12(R29)
MOVW R2, 16(R29)
+ MOVW $0, 20(R29)
JAL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s
index dc34c0e4c876b..834023cce19fb 100644
--- a/src/runtime/asm_ppc64x.s
+++ b/src/runtime/asm_ppc64x.s
@@ -128,23 +128,6 @@ TEXT runtime·reginit(SB),NOSPLIT|NOFRAME,$0-0
* go-routine
*/
-// void gosave(Gobuf*)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8
- MOVD buf+0(FP), R3
- MOVD R1, gobuf_sp(R3)
- MOVD LR, R31
- MOVD R31, gobuf_pc(R3)
- MOVD g, gobuf_g(R3)
- MOVD R0, gobuf_lr(R3)
- MOVD R0, gobuf_ret(R3)
- // Assert ctxt is zero. See func save.
- MOVD gobuf_ctxt(R3), R3
- CMP R0, R3
- BEQ 2(PC)
- BL runtime·badctxt(SB)
- RET
-
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $16-8
@@ -356,7 +339,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
BR runtime·morestack(SB)
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -370,8 +353,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
BR (CTR)
// Note: can't just "BR NAME(SB)" - bad inlining results.
-TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
- MOVWZ argsize+24(FP), R3
+TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48
+ MOVWZ frameSize+32(FP), R3
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -404,11 +387,11 @@ TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
BR (CTR)
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVD arg+16(FP), R3; \
- MOVWZ argsize+24(FP), R4; \
+ MOVD stackArgs+16(FP), R3; \
+ MOVWZ stackArgsSize+24(FP), R4; \
MOVD R1, R5; \
CMP R4, $8; \
BLT tailsetup; \
@@ -456,10 +439,10 @@ callfn: \
MOVD 24(R1), R2; \
#endif \
/* copy return values back */ \
- MOVD argtype+0(FP), R7; \
- MOVD arg+16(FP), R3; \
- MOVWZ n+24(FP), R4; \
- MOVWZ retoffset+28(FP), R6; \
+ MOVD stackArgsType+0(FP), R7; \
+ MOVD stackArgs+16(FP), R3; \
+ MOVWZ stackArgsSize+24(FP), R4; \
+ MOVWZ stackRetOffset+28(FP), R6; \
ADD $FIXED_FRAME, R1, R5; \
ADD R6, R5; \
ADD R6, R3; \
@@ -471,11 +454,12 @@ callfn: \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $32-0
+TEXT callRet<>(SB), NOSPLIT, $40-0
MOVD R7, FIXED_FRAME+0(R1)
MOVD R3, FIXED_FRAME+8(R1)
MOVD R5, FIXED_FRAME+16(R1)
MOVD R4, FIXED_FRAME+24(R1)
+ MOVD $0, FIXED_FRAME+32(R1)
BL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s
index 01b42dc3debe6..31e324d677592 100644
--- a/src/runtime/asm_riscv64.s
+++ b/src/runtime/asm_riscv64.s
@@ -297,21 +297,6 @@ TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8
JALR RA, T1
JMP runtime·badmcall2(SB)
-// func gosave(buf *gobuf)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8
- MOV buf+0(FP), T1
- MOV X2, gobuf_sp(T1)
- MOV RA, gobuf_pc(T1)
- MOV g, gobuf_g(T1)
- MOV ZERO, gobuf_lr(T1)
- MOV ZERO, gobuf_ret(T1)
- // Assert ctxt is zero. See func save.
- MOV gobuf_ctxt(T1), T1
- BEQ T1, ZERO, 2(PC)
- CALL runtime·badctxt(SB)
- RET
-
// Save state of caller into g->sched. Smashes X31.
TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0
MOV X1, (g_sched+gobuf_pc)(g)
@@ -374,7 +359,7 @@ TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
RET
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -386,13 +371,13 @@ TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
JALR ZERO, T2
// Note: can't just "BR NAME(SB)" - bad inlining results.
-// func call(argtype *rtype, fn, arg unsafe.Pointer, n uint32, retoffset uint32)
+// func call(stackArgsType *rtype, fn, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
TEXT reflect·call(SB), NOSPLIT, $0-0
JMP ·reflectcall(SB)
-// func reflectcall(argtype *_type, fn, arg unsafe.Pointer, argsize uint32, retoffset uint32)
-TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
- MOVWU argsize+24(FP), T0
+// func call(stackArgsType *_type, fn, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
+TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48
+ MOVWU frameSize+32(FP), T0
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -424,11 +409,11 @@ TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
JALR ZERO, T2
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOV arg+16(FP), A1; \
- MOVWU argsize+24(FP), A2; \
+ MOV stackArgs+16(FP), A1; \
+ MOVWU stackArgsSize+24(FP), A2; \
MOV X2, A3; \
ADD $8, A3; \
ADD A3, A2; \
@@ -444,10 +429,10 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
PCDATA $PCDATA_StackMapIndex, $0; \
JALR RA, A4; \
/* copy return values back */ \
- MOV argtype+0(FP), A5; \
- MOV arg+16(FP), A1; \
- MOVWU n+24(FP), A2; \
- MOVWU retoffset+28(FP), A4; \
+ MOV stackArgsType+0(FP), A5; \
+ MOV stackArgs+16(FP), A1; \
+ MOVWU stackArgsSize+24(FP), A2; \
+ MOVWU stackRetOffset+28(FP), A4; \
ADD $8, X2, A3; \
ADD A4, A3; \
ADD A4, A1; \
@@ -459,11 +444,12 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $32-0
+TEXT callRet<>(SB), NOSPLIT, $40-0
MOV A5, 8(X2)
MOV A1, 16(X2)
MOV A3, 24(X2)
MOV A2, 32(X2)
+ MOV $0, 40(X2)
CALL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s
index 7baef37324755..fbd185c3533d1 100644
--- a/src/runtime/asm_s390x.s
+++ b/src/runtime/asm_s390x.s
@@ -174,21 +174,6 @@ TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
* go-routine
*/
-// void gosave(Gobuf*)
-// save state in Gobuf; setjmp
-TEXT runtime·gosave(SB), NOSPLIT, $-8-8
- MOVD buf+0(FP), R3
- MOVD R15, gobuf_sp(R3)
- MOVD LR, gobuf_pc(R3)
- MOVD g, gobuf_g(R3)
- MOVD $0, gobuf_lr(R3)
- MOVD $0, gobuf_ret(R3)
- // Assert ctxt is zero. See func save.
- MOVD gobuf_ctxt(R3), R3
- CMPBEQ R3, $0, 2(PC)
- BL runtime·badctxt(SB)
- RET
-
// void gogo(Gobuf*)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $16-8
@@ -368,7 +353,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
BR runtime·morestack(SB)
// reflectcall: call a function with the given argument list
-// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs).
// we don't have variable-sized frames, so we use a small number
// of constant-sized-frame functions to encode a few bits of size in the pc.
// Caution: ugly multiline assembly macros in your future!
@@ -381,8 +366,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0
BR (R5)
// Note: can't just "BR NAME(SB)" - bad inlining results.
-TEXT ·reflectcall(SB), NOSPLIT, $-8-32
- MOVWZ argsize+24(FP), R3
+TEXT ·reflectcall(SB), NOSPLIT, $-8-48
+ MOVWZ frameSize+32(FP), R3
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
DISPATCH(runtime·call64, 64)
@@ -414,11 +399,11 @@ TEXT ·reflectcall(SB), NOSPLIT, $-8-32
BR (R5)
#define CALLFN(NAME,MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
NO_LOCAL_POINTERS; \
/* copy arguments to stack */ \
- MOVD arg+16(FP), R4; \
- MOVWZ argsize+24(FP), R5; \
+ MOVD stackArgs+16(FP), R4; \
+ MOVWZ stackArgsSize+24(FP), R5; \
MOVD $stack-MAXSIZE(SP), R6; \
loopArgs: /* copy 256 bytes at a time */ \
CMP R5, $256; \
@@ -439,11 +424,11 @@ callFunction: \
PCDATA $PCDATA_StackMapIndex, $0; \
BL (R8); \
/* copy return values back */ \
- MOVD argtype+0(FP), R7; \
- MOVD arg+16(FP), R6; \
- MOVWZ n+24(FP), R5; \
+ MOVD stackArgsType+0(FP), R7; \
+ MOVD stackArgs+16(FP), R6; \
+ MOVWZ stackArgsSize+24(FP), R5; \
MOVD $stack-MAXSIZE(SP), R4; \
- MOVWZ retoffset+28(FP), R1; \
+ MOVWZ stackRetOffset+28(FP), R1; \
ADD R1, R4; \
ADD R1, R6; \
SUB R1, R5; \
@@ -454,11 +439,12 @@ callFunction: \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $32-0
+TEXT callRet<>(SB), NOSPLIT, $40-0
MOVD R7, 8(R15)
MOVD R6, 16(R15)
MOVD R4, 24(R15)
MOVD R5, 32(R15)
+ MOVD $0, 40(R15)
BL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s
index fcb780f1dc830..cf3d961b749bd 100644
--- a/src/runtime/asm_wasm.s
+++ b/src/runtime/asm_wasm.s
@@ -296,14 +296,14 @@ TEXT ·asmcgocall(SB), NOSPLIT, $0-0
JMP NAME(SB); \
End
-TEXT ·reflectcall(SB), NOSPLIT, $0-32
+TEXT ·reflectcall(SB), NOSPLIT, $0-48
I64Load fn+8(FP)
I64Eqz
If
CALLNORESUME runtime·sigpanic(SB)
End
- MOVW argsize+24(FP), R0
+ MOVW frameSize+32(FP), R0
DISPATCH(runtime·call16, 16)
DISPATCH(runtime·call32, 32)
@@ -335,18 +335,18 @@ TEXT ·reflectcall(SB), NOSPLIT, $0-32
JMP runtime·badreflectcall(SB)
#define CALLFN(NAME, MAXSIZE) \
-TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
+TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \
NO_LOCAL_POINTERS; \
- MOVW argsize+24(FP), R0; \
+ MOVW stackArgsSize+24(FP), R0; \
\
Get R0; \
I64Eqz; \
Not; \
If; \
Get SP; \
- I64Load argptr+16(FP); \
+ I64Load stackArgs+16(FP); \
I32WrapI64; \
- I64Load argsize+24(FP); \
+ I64Load stackArgsSize+24(FP); \
I64Const $3; \
I64ShrU; \
I32WrapI64; \
@@ -359,12 +359,12 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
I64Load $0; \
CALL; \
\
- I64Load32U retoffset+28(FP); \
+ I64Load32U stackRetOffset+28(FP); \
Set R0; \
\
- MOVD argtype+0(FP), RET0; \
+ MOVD stackArgsType+0(FP), RET0; \
\
- I64Load argptr+16(FP); \
+ I64Load stackArgs+16(FP); \
Get R0; \
I64Add; \
Set RET1; \
@@ -375,7 +375,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
I64Add; \
Set RET2; \
\
- I64Load32U argsize+24(FP); \
+ I64Load32U stackArgsSize+24(FP); \
Get R0; \
I64Sub; \
Set RET3; \
@@ -387,12 +387,13 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
// separate function so it can allocate stack space for the arguments
// to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers.
-TEXT callRet<>(SB), NOSPLIT, $32-0
+TEXT callRet<>(SB), NOSPLIT, $40-0
NO_LOCAL_POINTERS
MOVD RET0, 0(SP)
MOVD RET1, 8(SP)
MOVD RET2, 16(SP)
MOVD RET3, 24(SP)
+ MOVD $0, 32(SP)
CALL runtime·reflectcallmove(SB)
RET
diff --git a/src/runtime/cgo/gcc_amd64.S b/src/runtime/cgo/gcc_amd64.S
index 17d9d47ef40cb..d75f8646663ab 100644
--- a/src/runtime/cgo/gcc_amd64.S
+++ b/src/runtime/cgo/gcc_amd64.S
@@ -30,9 +30,14 @@ EXT(crosscall_amd64):
pushq %r15
#if defined(_WIN64)
+ movq %r8, %rdi /* arg of setg_gcc */
+ call *%rdx /* setg_gcc */
call *%rcx /* fn */
#else
- call *%rdi /* fn */
+ movq %rdi, %rbx
+ movq %rdx, %rdi /* arg of setg_gcc */
+ call *%rsi /* setg_gcc */
+ call *%rbx /* fn */
#endif
popq %r15
diff --git a/src/runtime/cgo/gcc_darwin_amd64.c b/src/runtime/cgo/gcc_darwin_amd64.c
index 51410d50269da..d5b7fd8fd802b 100644
--- a/src/runtime/cgo/gcc_darwin_amd64.c
+++ b/src/runtime/cgo/gcc_darwin_amd64.c
@@ -9,13 +9,16 @@
#include "libcgo_unix.h"
static void* threadentry(void*);
+static void (*setg_gcc)(void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{
pthread_attr_t attr;
size_t size;
+ setg_gcc = setg;
+
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
g->stacklo = (uintptr)&attr - size + 4096;
@@ -57,10 +60,6 @@ threadentry(void *v)
ts = *(ThreadStart*)v;
free(v);
- // Move the g pointer into the slot reserved in thread local storage.
- // Constant must match the one in cmd/link/internal/ld/sym.go.
- asm volatile("movq %0, %%gs:0x30" :: "r"(ts.g));
-
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
diff --git a/src/runtime/cgo/gcc_dragonfly_amd64.c b/src/runtime/cgo/gcc_dragonfly_amd64.c
index d25db91900071..0003414bf8665 100644
--- a/src/runtime/cgo/gcc_dragonfly_amd64.c
+++ b/src/runtime/cgo/gcc_dragonfly_amd64.c
@@ -61,11 +61,6 @@ threadentry(void *v)
ts = *(ThreadStart*)v;
free(v);
- /*
- * Set specific keys.
- */
- setg_gcc((void*)ts.g);
-
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
diff --git a/src/runtime/cgo/gcc_freebsd_amd64.c b/src/runtime/cgo/gcc_freebsd_amd64.c
index 514a2f8a235ee..6071ec3909378 100644
--- a/src/runtime/cgo/gcc_freebsd_amd64.c
+++ b/src/runtime/cgo/gcc_freebsd_amd64.c
@@ -69,11 +69,6 @@ threadentry(void *v)
free(v);
_cgo_tsan_release();
- /*
- * Set specific keys.
- */
- setg_gcc((void*)ts.g);
-
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
diff --git a/src/runtime/cgo/gcc_linux_amd64.c b/src/runtime/cgo/gcc_linux_amd64.c
index f2bf6482cb5f7..c25e7e769ba23 100644
--- a/src/runtime/cgo/gcc_linux_amd64.c
+++ b/src/runtime/cgo/gcc_linux_amd64.c
@@ -89,11 +89,6 @@ threadentry(void *v)
free(v);
_cgo_tsan_release();
- /*
- * Set specific keys.
- */
- setg_gcc((void*)ts.g);
-
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
diff --git a/src/runtime/cgo/gcc_netbsd_amd64.c b/src/runtime/cgo/gcc_netbsd_amd64.c
index dc966fc45b4ba..9f4b031a08954 100644
--- a/src/runtime/cgo/gcc_netbsd_amd64.c
+++ b/src/runtime/cgo/gcc_netbsd_amd64.c
@@ -62,11 +62,6 @@ threadentry(void *v)
ts = *(ThreadStart*)v;
free(v);
- /*
- * Set specific keys.
- */
- setg_gcc((void*)ts.g);
-
// On NetBSD, a new thread inherits the signal stack of the
// creating thread. That confuses minit, so we remove that
// signal stack here before calling the regular mstart. It's
@@ -78,6 +73,6 @@ threadentry(void *v)
ss.ss_flags = SS_DISABLE;
sigaltstack(&ss, nil);
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
diff --git a/src/runtime/cgo/gcc_openbsd_amd64.c b/src/runtime/cgo/gcc_openbsd_amd64.c
index 34319fb0b80e3..09d2750f3ad8f 100644
--- a/src/runtime/cgo/gcc_openbsd_amd64.c
+++ b/src/runtime/cgo/gcc_openbsd_amd64.c
@@ -60,11 +60,6 @@ threadentry(void *v)
ts = *(ThreadStart*)v;
free(v);
- /*
- * Set specific keys.
- */
- setg_gcc((void*)ts.g);
-
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
diff --git a/src/runtime/cgo/gcc_solaris_amd64.c b/src/runtime/cgo/gcc_solaris_amd64.c
index 079bd12898e25..e89e844b1e0ad 100644
--- a/src/runtime/cgo/gcc_solaris_amd64.c
+++ b/src/runtime/cgo/gcc_solaris_amd64.c
@@ -72,11 +72,6 @@ threadentry(void *v)
ts = *(ThreadStart*)v;
free(v);
- /*
- * Set specific keys.
- */
- setg_gcc((void*)ts.g);
-
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
return nil;
}
diff --git a/src/runtime/cgo/gcc_windows_amd64.c b/src/runtime/cgo/gcc_windows_amd64.c
index 0f8c817f0e4d1..25cfd086ddc33 100644
--- a/src/runtime/cgo/gcc_windows_amd64.c
+++ b/src/runtime/cgo/gcc_windows_amd64.c
@@ -12,10 +12,12 @@
#include "libcgo_windows.h"
static void threadentry(void*);
+static void (*setg_gcc)(void*);
void
-x_cgo_init(G *g)
+x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase)
{
+ setg_gcc = setg;
}
@@ -46,10 +48,8 @@ threadentry(void *v)
*/
asm volatile (
"movq %0, %%gs:0x28\n" // MOVL tls0, 0x28(GS)
- "movq %%gs:0x28, %%rax\n" // MOVQ 0x28(GS), tmp
- "movq %1, 0(%%rax)\n" // MOVQ g, 0(GS)
- :: "r"(ts.tls), "r"(ts.g) : "%rax"
+ :: "r"(ts.tls)
);
- crosscall_amd64(ts.fn);
+ crosscall_amd64(ts.fn, setg_gcc, (void*)ts.g);
}
diff --git a/src/runtime/cgo/libcgo.h b/src/runtime/cgo/libcgo.h
index aba500a301c66..af4960e7e9522 100644
--- a/src/runtime/cgo/libcgo.h
+++ b/src/runtime/cgo/libcgo.h
@@ -66,7 +66,7 @@ uintptr_t _cgo_wait_runtime_init_done(void);
/*
* Call fn in the 6c world.
*/
-void crosscall_amd64(void (*fn)(void));
+void crosscall_amd64(void (*fn)(void), void (*setg_gcc)(void*), void *g);
/*
* Call fn in the 8c world.
diff --git a/src/runtime/duff_amd64.s b/src/runtime/duff_amd64.s
index 2ff5bf6dbcd66..df010f58533be 100644
--- a/src/runtime/duff_amd64.s
+++ b/src/runtime/duff_amd64.s
@@ -5,100 +5,100 @@
#include "textflag.h"
TEXT runtime·duffzero(SB), NOSPLIT, $0-0
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
- MOVUPS X0,(DI)
- MOVUPS X0,16(DI)
- MOVUPS X0,32(DI)
- MOVUPS X0,48(DI)
+ MOVUPS X15,(DI)
+ MOVUPS X15,16(DI)
+ MOVUPS X15,32(DI)
+ MOVUPS X15,48(DI)
LEAQ 64(DI),DI
RET
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index 2b5affce5219a..4994347bdeb35 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -14,6 +14,7 @@
package runtime
import (
+ "internal/abi"
"runtime/internal/sys"
"unsafe"
)
@@ -223,11 +224,18 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size
// stack map of reflectcall is wrong.
//
//go:nosplit
-func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr) {
+func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr, regs *abi.RegArgs) {
if writeBarrier.needed && typ != nil && typ.ptrdata != 0 && size >= sys.PtrSize {
bulkBarrierPreWrite(uintptr(dst), uintptr(src), size)
}
memmove(dst, src, size)
+
+ // Move pointers returned in registers to a place where the GC can see them.
+ for i := range regs.Ints {
+ if regs.ReturnIsPtr.Get(i) {
+ regs.Ptrs[i] = unsafe.Pointer(regs.Ints[i])
+ }
+ }
}
//go:nosplit
diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go
index f4dbd77252890..7d0313be12026 100644
--- a/src/runtime/mfinal.go
+++ b/src/runtime/mfinal.go
@@ -7,6 +7,7 @@
package runtime
import (
+ "internal/abi"
"runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
@@ -219,7 +220,11 @@ func runfinq() {
throw("bad kind in runfinq")
}
fingRunning = true
- reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))
+ // Pass a dummy RegArgs for now.
+ //
+ // TODO(mknyszek): Pass arguments in registers.
+ var regs abi.RegArgs
+ reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz), uint32(framesz), ®s)
fingRunning = false
// Drop finalizer queue heap references
diff --git a/src/runtime/mkduff.go b/src/runtime/mkduff.go
index 94ae75fbfe5ff..ef297f073e099 100644
--- a/src/runtime/mkduff.go
+++ b/src/runtime/mkduff.go
@@ -62,15 +62,15 @@ func gen(arch string, tags, zero, copy func(io.Writer)) {
func notags(w io.Writer) { fmt.Fprintln(w) }
func zeroAMD64(w io.Writer) {
- // X0: zero
+ // X15: zero
// DI: ptr to memory to be zeroed
// DI is updated as a side effect.
- fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT, $0-0")
+ fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT, $0-0")
for i := 0; i < 16; i++ {
- fmt.Fprintln(w, "\tMOVUPS\tX0,(DI)")
- fmt.Fprintln(w, "\tMOVUPS\tX0,16(DI)")
- fmt.Fprintln(w, "\tMOVUPS\tX0,32(DI)")
- fmt.Fprintln(w, "\tMOVUPS\tX0,48(DI)")
+ fmt.Fprintln(w, "\tMOVUPS\tX15,(DI)")
+ fmt.Fprintln(w, "\tMOVUPS\tX15,16(DI)")
+ fmt.Fprintln(w, "\tMOVUPS\tX15,32(DI)")
+ fmt.Fprintln(w, "\tMOVUPS\tX15,48(DI)")
fmt.Fprintln(w, "\tLEAQ\t64(DI),DI") // We use lea instead of add, to avoid clobbering flags
fmt.Fprintln(w)
}
@@ -84,7 +84,7 @@ func copyAMD64(w io.Writer) {
//
// This is equivalent to a sequence of MOVSQ but
// for some reason that is 3.5x slower than this code.
- fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT, $0-0")
+ fmt.Fprintln(w, "TEXT runtime·duffcopy