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(SB), NOSPLIT, $0-0") for i := 0; i < 64; i++ { fmt.Fprintln(w, "\tMOVUPS\t(SI), X0") fmt.Fprintln(w, "\tADDQ\t$16, SI") diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go index 428ff7f225c37..abd1010be93d7 100644 --- a/src/runtime/os2_aix.go +++ b/src/runtime/os2_aix.go @@ -18,11 +18,11 @@ import ( //go:cgo_import_dynamic libc___n_pthreads __n_pthreads "libpthread.a/shr_xpg5_64.o" //go:cgo_import_dynamic libc___mod_init __mod_init "libc.a/shr_64.o" -//go:linkname libc___n_pthreads libc___n_pthread +//go:linkname libc___n_pthreads libc___n_pthreads //go:linkname libc___mod_init libc___mod_init var ( - libc___n_pthread, + libc___n_pthreads, libc___mod_init libFunc ) diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 5b2ccdd8740f8..e320eaa59666d 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/abi" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -874,7 +875,13 @@ func reflectcallSave(p *_panic, fn, arg unsafe.Pointer, argsize uint32) { p.pc = getcallerpc() p.sp = unsafe.Pointer(getcallersp()) } - reflectcall(nil, fn, arg, argsize, argsize) + // Pass a dummy RegArgs for now since no function actually implements + // the register-based ABI. + // + // TODO(mknyszek): Implement this properly, setting up arguments in + // registers as necessary in the caller. + var regs abi.RegArgs + reflectcall(nil, fn, arg, argsize, argsize, argsize, ®s) if p != nil { p.pc = 0 p.sp = unsafe.Pointer(nil) @@ -968,7 +975,9 @@ func gopanic(e interface{}) { } } else { p.argp = unsafe.Pointer(getargp(0)) - reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz)) + + var regs abi.RegArgs + reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz), uint32(d.siz), ®s) } p.argp = nil diff --git a/src/runtime/race/output_test.go b/src/runtime/race/output_test.go index 69496874c64aa..17dc32013f3ee 100644 --- a/src/runtime/race/output_test.go +++ b/src/runtime/race/output_test.go @@ -7,6 +7,7 @@ package race_test import ( + "fmt" "internal/testenv" "os" "os/exec" @@ -71,9 +72,24 @@ func TestOutput(t *testing.T) { "GORACE="+test.gorace, ) got, _ := cmd.CombinedOutput() - if !regexp.MustCompile(test.re).MatchString(string(got)) { - t.Fatalf("failed test case %v, expect:\n%v\ngot:\n%s", - test.name, test.re, got) + matched := false + for _, re := range test.re { + if regexp.MustCompile(re).MatchString(string(got)) { + matched = true + break + } + } + if !matched { + exp := fmt.Sprintf("expect:\n%v\n", test.re[0]) + if len(test.re) > 1 { + exp = fmt.Sprintf("expected one of %d patterns:\n", + len(test.re)) + for k, re := range test.re { + exp += fmt.Sprintf("pattern %d:\n%v\n", k, re) + } + } + t.Fatalf("failed test case %v, %sgot:\n%s", + test.name, exp, got) } } } @@ -84,7 +100,7 @@ var tests = []struct { goos string gorace string source string - re string + re []string }{ {"simple", "run", "", "atexit_sleep_ms=0", ` package main @@ -107,7 +123,7 @@ func racer(x *int, done chan bool) { store(x, 42) done <- true } -`, `================== +`, []string{`================== WARNING: DATA RACE Write at 0x[0-9,a-f]+ by goroutine [0-9]: main\.store\(\) @@ -129,7 +145,7 @@ Goroutine [0-9] \(running\) created at: ================== Found 1 data race\(s\) exit status 66 -`}, +`}}, {"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", ` package main @@ -143,7 +159,7 @@ func main() { x = 43 <-done } -`, `exit status 13`}, +`, []string{`exit status 13`}}, {"strip_path_prefix", "run", "", "atexit_sleep_ms=0 strip_path_prefix=/main.", ` package main @@ -157,9 +173,9 @@ func main() { x = 43 <-done } -`, ` +`, []string{` go:7 \+0x[0-9,a-f]+ -`}, +`}}, {"halt_on_error", "run", "", "atexit_sleep_ms=0 halt_on_error=1", ` package main @@ -173,10 +189,10 @@ func main() { x = 43 <-done } -`, ` +`, []string{` ================== exit status 66 -`}, +`}}, {"test_fails_on_race", "test", "", "atexit_sleep_ms=0", ` package main_test @@ -193,12 +209,12 @@ func TestFail(t *testing.T) { <-done t.Log(t.Failed()) } -`, ` +`, []string{` ================== --- FAIL: TestFail \(0...s\) .*main_test.go:14: true .*testing.go:.*: race detected during execution of test -FAIL`}, +FAIL`}}, {"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", ` package main @@ -211,11 +227,11 @@ func main() { data[0] = 1 <-done } -`, ` +`, []string{` runtime\.slicebytetostring\(\) .*/runtime/string\.go:.* main\.main\.func1\(\) - .*/main.go:7`}, + .*/main.go:7`}}, // Test for https://golang.org/issue/33309 {"midstack_inlining_traceback", "run", "linux", "atexit_sleep_ms=0", ` @@ -241,7 +257,7 @@ func g(c chan int) { func h(c chan int) { c <- x } -`, `================== +`, []string{`================== WARNING: DATA RACE Read at 0x[0-9,a-f]+ by goroutine [0-9]: main\.h\(\) @@ -261,7 +277,7 @@ Goroutine [0-9] \(running\) created at: ================== Found 1 data race\(s\) exit status 66 -`}, +`}}, // Test for https://golang.org/issue/17190 {"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", ` @@ -300,7 +316,25 @@ func main() { racy++ <- done } -`, `================== +`, []string{`================== +WARNING: DATA RACE +Read at 0x[0-9,a-f]+ by main goroutine: + main\.main\(\) + .*/main\.go:34 \+0x[0-9,a-f]+ + +Previous write at 0x[0-9,a-f]+ by goroutine [0-9]: + main\.goCallback\(\) + .*/main\.go:27 \+0x[0-9,a-f]+ + _cgoexp_[0-9a-z]+_goCallback\(\) + .*_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+ + _cgoexp_[0-9a-z]+_goCallback\(\) + :1 \+0x[0-9,a-f]+ + +Goroutine [0-9] \(running\) created at: + runtime\.newextram\(\) + .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+ +==================`, + `================== WARNING: DATA RACE Read at 0x[0-9,a-f]+ by .*: main\..* @@ -313,7 +347,7 @@ Previous write at 0x[0-9,a-f]+ by .*: Goroutine [0-9] \(running\) created at: runtime\.newextram\(\) .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+ -==================`}, +==================`}}, {"second_test_passes", "test", "", "atexit_sleep_ms=0", ` package main_test import "testing" @@ -331,11 +365,11 @@ func TestFail(t *testing.T) { func TestPass(t *testing.T) { } -`, ` +`, []string{` ================== --- FAIL: TestFail \(0...s\) .*testing.go:.*: race detected during execution of test -FAIL`}, +FAIL`}}, {"mutex", "run", "", "atexit_sleep_ms=0", ` package main import ( @@ -366,7 +400,7 @@ func main() { } wg.Wait() if (data == iterations*(threads+1)) { fmt.Println("pass") } -}`, `pass`}, +}`, []string{`pass`}}, // Test for https://github.com/golang/go/issues/37355 {"chanmm", "run", "", "atexit_sleep_ms=0", ` package main @@ -395,7 +429,7 @@ func main() { wg.Wait() _ = data } -`, `================== +`, []string{`================== WARNING: DATA RACE Write at 0x[0-9,a-f]+ by goroutine [0-9]: main\.main\.func2\(\) @@ -408,5 +442,5 @@ Previous write at 0x[0-9,a-f]+ by main goroutine: Goroutine [0-9] \(running\) created at: main\.main\(\) .*/main.go:[0-9]+ \+0x[0-9,a-f]+ -==================`}, +==================`}}, } diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s index 9818bc6ddff97..c3b7bbfbfe2e8 100644 --- a/src/runtime/race_amd64.s +++ b/src/runtime/race_amd64.s @@ -146,8 +146,10 @@ TEXT runtime·racewriterangepc1(SB), NOSPLIT, $0-24 // If addr (RARG1) is out of range, do nothing. // Otherwise, setup goroutine context and invoke racecall. Other arguments already set. TEXT racecalladdr<>(SB), NOSPLIT, $0-0 +#ifndef GOEXPERIMENT_REGABI get_tls(R12) MOVQ g(R12), R14 +#endif MOVQ g_racectx(R14), RARG0 // goroutine context // Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend). CMPQ RARG1, runtime·racearenastart(SB) @@ -183,8 +185,10 @@ TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8 // R11 = caller's return address TEXT racefuncenter<>(SB), NOSPLIT, $0-0 MOVQ DX, R15 // save function entry context (for closures) +#ifndef GOEXPERIMENT_REGABI get_tls(R12) MOVQ g(R12), R14 +#endif MOVQ g_racectx(R14), RARG0 // goroutine context MOVQ R11, RARG1 // void __tsan_func_enter(ThreadState *thr, void *pc); @@ -197,8 +201,10 @@ TEXT racefuncenter<>(SB), NOSPLIT, $0-0 // func runtime·racefuncexit() // Called from instrumented code. TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0 +#ifndef GOEXPERIMENT_REGABI get_tls(R12) MOVQ g(R12), R14 +#endif MOVQ g_racectx(R14), RARG0 // goroutine context // void __tsan_func_exit(ThreadState *thr); MOVQ $__tsan_func_exit(SB), AX @@ -357,8 +363,10 @@ racecallatomic_data: JAE racecallatomic_ignore racecallatomic_ok: // Addr is within the good range, call the atomic function. +#ifndef GOEXPERIMENT_REGABI get_tls(R12) MOVQ g(R12), R14 +#endif MOVQ g_racectx(R14), RARG0 // goroutine context MOVQ 8(SP), RARG1 // caller pc MOVQ (SP), RARG2 // pc @@ -370,8 +378,10 @@ racecallatomic_ignore: // An attempt to synchronize on the address would cause crash. MOVQ AX, R15 // remember the original function MOVQ $__tsan_go_ignore_sync_begin(SB), AX +#ifndef GOEXPERIMENT_REGABI get_tls(R12) MOVQ g(R12), R14 +#endif MOVQ g_racectx(R14), RARG0 // goroutine context CALL racecall<>(SB) MOVQ R15, AX // restore the original function @@ -399,8 +409,10 @@ TEXT runtime·racecall(SB), NOSPLIT, $0-0 // Switches SP to g0 stack and calls (AX). Arguments already set. TEXT racecall<>(SB), NOSPLIT, $0-0 +#ifndef GOEXPERIMENT_REGABI get_tls(R12) MOVQ g(R12), R14 +#endif MOVQ g_m(R14), R13 // Switch to g0 stack. MOVQ SP, R12 // callee-saved, preserved across the CALL @@ -412,6 +424,9 @@ call: ANDQ $~15, SP // alignment for gcc ABI CALL AX MOVQ R12, SP + // Back to Go world, set special registers. + // The g register (R14) is preserved in C. + XORPS X15, X15 RET // C->Go callback thunk that allows to call runtime·racesymbolize from C code. @@ -419,7 +434,9 @@ call: // The overall effect of Go->C->Go call chain is similar to that of mcall. // RARG0 contains command code. RARG1 contains command-specific context. // See racecallback for command codes. -TEXT runtime·racecallbackthunk(SB), NOSPLIT, $56-8 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// because its address is passed to C via funcPC. +TEXT runtime·racecallbackthunk(SB), NOSPLIT, $56-8 // Handle command raceGetProcCmd (0) here. // First, code below assumes that we are on curg, while raceGetProcCmd // can be executed on g0. Second, it is called frequently, so will @@ -447,12 +464,13 @@ rest: PUSHQ R15 // Set g = g0. get_tls(R12) - MOVQ g(R12), R13 - MOVQ g_m(R13), R14 - MOVQ m_g0(R14), R15 + MOVQ g(R12), R14 + MOVQ g_m(R14), R13 + MOVQ m_g0(R13), R15 CMPQ R13, R15 JEQ noswitch // branch if already on g0 MOVQ R15, g(R12) // g = m->g0 + MOVQ R15, R14 // set g register PUSHQ RARG1 // func arg PUSHQ RARG0 // func arg CALL runtime·racecallback(SB) diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 109f0da131195..b7c7b4cff24c7 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -853,7 +853,7 @@ type funcinl struct { // layout of Itab known to compilers // allocated in non-garbage-collected memory // Needs to be in sync with -// ../cmd/compile/internal/gc/reflect.go:/^func.dumptabs. +// ../cmd/compile/internal/gc/reflect.go:/^func.WriteTabs. type itab struct { inter *interfacetype _type *_type diff --git a/src/runtime/signal_amd64.go b/src/runtime/signal_amd64.go index 6ab1f758c2293..3eeb5e044ff0f 100644 --- a/src/runtime/signal_amd64.go +++ b/src/runtime/signal_amd64.go @@ -65,11 +65,14 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { pc := uintptr(c.rip()) sp := uintptr(c.rsp()) + // In case we are panicking from external code, we need to initialize + // Go special registers. We inject sigpanic0 (instead of sigpanic), + // which takes care of that. if shouldPushSigpanic(gp, pc, *(*uintptr)(unsafe.Pointer(sp))) { - c.pushCall(funcPC(sigpanic), pc) + c.pushCall(funcPC(sigpanic0), pc) } else { // Not safe to push the call. Just clobber the frame. - c.set_rip(uint64(funcPC(sigpanic))) + c.set_rip(uint64(funcPC(sigpanic0))) } } diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 2ee2c74dfe07e..c0cc95ec65f45 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -4,7 +4,10 @@ package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) // Should be a built-in for unsafe.Pointer? //go:nosplit @@ -167,7 +170,6 @@ func noescape(p unsafe.Pointer) unsafe.Pointer { // pointer-declared arguments. func cgocallback(fn, frame, ctxt uintptr) func gogo(buf *gobuf) -func gosave(buf *gobuf) //go:noescape func jmpdefer(fv *funcval, argp uintptr) @@ -175,19 +177,50 @@ func asminit() func setg(gg *g) func breakpoint() -// reflectcall 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 should pass the argument frame type as argtype, so that -// call can execute appropriate write barriers during the copy. +// reflectcall calls fn with arguments described by stackArgs, stackArgsSize, +// frameSize, and regArgs. // -// Package reflect always passes a frame type. In package runtime, -// Windows callbacks are the only use of this that copies results -// back, and those cannot have pointers in their results, so runtime -// passes nil for the frame type. +// Arguments passed on the stack and space for return values passed on the stack +// must be laid out at the space pointed to by stackArgs (with total length +// stackArgsSize) according to the ABI. +// +// stackRetOffset must be some value <= stackArgsSize that indicates the +// offset within stackArgs where the return value space begins. +// +// frameSize is the total size of the argument frame at stackArgs and must +// therefore be >= stackArgsSize. It must include additional space for spilling +// register arguments for stack growth and preemption. +// +// TODO(mknyszek): Once we don't need the additional spill space, remove frameSize, +// since frameSize will be redundant with stackArgsSize. +// +// Arguments passed in registers must be laid out in regArgs according to the ABI. +// regArgs will hold any return values passed in registers after the call. +// +// reflectcall copies stack arguments from stackArgs to the goroutine stack, and +// then copies back stackArgsSize-stackRetOffset bytes back to the return space +// in stackArgs once fn has completed. It also "unspills" argument registers from +// regArgs before calling fn, and spills them back into regArgs immediately +// following the call to fn. If there are results being returned on the stack, +// the caller should pass the argument frame type as stackArgsType so that +// reflectcall can execute appropriate write barriers during the copy. +// +// reflectcall expects regArgs.ReturnIsPtr to be populated indicating which +// registers on the return path will contain Go pointers. It will then store +// these pointers in regArgs.Ptrs such that they are visible to the GC. +// +// Package reflect passes a frame type. In package runtime, there is only +// one call that copies results back, in callbackWrap in syscall_windows.go, and it +// does NOT pass a frame type, meaning there are no write barriers invoked. See that +// call site for justification. // // Package reflect accesses this symbol through a linkname. -func reflectcall(argtype *_type, fn, arg unsafe.Pointer, argsize uint32, retoffset uint32) +// +// Arguments passed through to reflectcall do not escape. The type is used +// only in a very limited callee of reflectcall, the stackArgs are copied, and +// regArgs is only used in the reflectcall frame. +//go:noescape +func reflectcall(stackArgsType *_type, fn, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs) func procyield(cycles uint32) @@ -357,3 +390,7 @@ func duffcopy() // Called from linker-generated .initarray; declared for go vet; do NOT call from Go. func addmoduledata() + +// Injected by the signal handler for panicking signals. On many platforms it just +// jumps to sigpanic. +func sigpanic0() diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s index 630fb5df64fd2..0fe8c7e172f5b 100644 --- a/src/runtime/sys_darwin_amd64.s +++ b/src/runtime/sys_darwin_amd64.s @@ -5,6 +5,8 @@ // System calls and other sys.stuff for AMD64, Darwin // System calls are implemented in libSystem, this file contains // trampolines that convert from Go to C calling convention. +// The trampolines are ABIInternal as they are referenced from +// Go code with funcPC. #include "go_asm.h" #include "go_tls.h" @@ -13,7 +15,7 @@ #define CLOCK_REALTIME 0 // Exit the entire program (like C exit) -TEXT runtime·exit_trampoline(SB),NOSPLIT,$0 +TEXT runtime·exit_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 0(DI), DI // arg 1 exit status @@ -22,7 +24,7 @@ TEXT runtime·exit_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·open_trampoline(SB),NOSPLIT,$0 +TEXT runtime·open_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 8(DI), SI // arg 2 flags @@ -33,7 +35,7 @@ TEXT runtime·open_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·close_trampoline(SB),NOSPLIT,$0 +TEXT runtime·close_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 0(DI), DI // arg 1 fd @@ -41,7 +43,7 @@ TEXT runtime·close_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·read_trampoline(SB),NOSPLIT,$0 +TEXT runtime·read_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 buf @@ -57,7 +59,7 @@ noerr: POPQ BP RET -TEXT runtime·write_trampoline(SB),NOSPLIT,$0 +TEXT runtime·write_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 buf @@ -73,7 +75,7 @@ noerr: POPQ BP RET -TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP CALL libc_pipe(SB) // pointer already in DI @@ -84,7 +86,7 @@ TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0 +TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 new @@ -94,7 +96,7 @@ TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·madvise_trampoline(SB), NOSPLIT, $0 +TEXT runtime·madvise_trampoline(SB), NOSPLIT, $0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 len @@ -105,12 +107,12 @@ TEXT runtime·madvise_trampoline(SB), NOSPLIT, $0 POPQ BP RET -TEXT runtime·mlock_trampoline(SB), NOSPLIT, $0 +TEXT runtime·mlock_trampoline(SB), NOSPLIT, $0 UNDEF // unimplemented GLOBL timebase<>(SB),NOPTR,$(machTimebaseInfo__size) -TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$0 +TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ DI, BX @@ -139,7 +141,7 @@ initialized: POPQ BP RET -TEXT runtime·walltime_trampoline(SB),NOSPLIT,$0 +TEXT runtime·walltime_trampoline(SB),NOSPLIT,$0 PUSHQ BP // make a frame; keep stack aligned MOVQ SP, BP MOVQ DI, SI // arg 2 timespec @@ -148,7 +150,7 @@ TEXT runtime·walltime_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·sigaction_trampoline(SB),NOSPLIT,$0 +TEXT runtime·sigaction_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 new @@ -161,7 +163,7 @@ TEXT runtime·sigaction_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0 +TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 new @@ -174,7 +176,7 @@ TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·sigaltstack_trampoline(SB),NOSPLIT,$0 +TEXT runtime·sigaltstack_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 old @@ -186,7 +188,7 @@ TEXT runtime·sigaltstack_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·raiseproc_trampoline(SB),NOSPLIT,$0 +TEXT runtime·raiseproc_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 0(DI), BX // signal @@ -212,7 +214,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 // This is the function registered during sigaction and is invoked when // a signal is received. It just redirects to the Go function sigtrampgo. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT,$0 // This runs on the signal stack, so we have lots of stack available. // We allocate our own stack space, because if we tell the linker // how much we're using, the NOSPLIT check fails. @@ -246,7 +248,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0 // Used instead of sigtramp in programs that use cgo. // Arguments from kernel are in DI, SI, DX. -TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // If no traceback function, do usual sigtramp. MOVQ runtime·cgoTraceback(SB), AX TESTQ AX, AX @@ -289,12 +291,12 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // The first three arguments, and the fifth, are already in registers. // Set the two remaining arguments now. MOVQ runtime·cgoTraceback(SB), CX - MOVQ $runtime·sigtramp(SB), R9 + MOVQ $runtime·sigtramp(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX sigtramp: - JMP runtime·sigtramp(SB) + JMP runtime·sigtramp(SB) sigtrampnog: // Signal arrived on a non-Go thread. If this is SIGPROF, get a @@ -320,7 +322,7 @@ sigtrampnog: MOVQ _cgo_callers(SB), AX JMP AX -TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0 +TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0 PUSHQ BP // make a frame; keep stack aligned MOVQ SP, BP MOVQ DI, BX @@ -343,7 +345,7 @@ ok: POPQ BP RET -TEXT runtime·munmap_trampoline(SB),NOSPLIT,$0 +TEXT runtime·munmap_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 len @@ -355,7 +357,7 @@ TEXT runtime·munmap_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·usleep_trampoline(SB),NOSPLIT,$0 +TEXT runtime·usleep_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 0(DI), DI // arg 1 usec @@ -367,7 +369,7 @@ TEXT runtime·settls(SB),NOSPLIT,$32 // Nothing to do on Darwin, pthread already set thread-local storage up. RET -TEXT runtime·sysctl_trampoline(SB),NOSPLIT,$0 +TEXT runtime·sysctl_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 8(DI), SI // arg 2 miblen @@ -380,7 +382,7 @@ TEXT runtime·sysctl_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·sysctlbyname_trampoline(SB),NOSPLIT,$0 +TEXT runtime·sysctlbyname_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 oldp @@ -392,14 +394,14 @@ TEXT runtime·sysctlbyname_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·kqueue_trampoline(SB),NOSPLIT,$0 +TEXT runtime·kqueue_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP CALL libc_kqueue(SB) POPQ BP RET -TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0 +TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 keventt @@ -418,7 +420,7 @@ ok: POPQ BP RET -TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0 +TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 4(DI), SI // arg 2 cmd @@ -475,7 +477,7 @@ TEXT runtime·mstart_stub(SB),NOSPLIT,$0 // A pointer to the arguments is passed in DI. // A single int32 result is returned in AX. // (For more results, make an args/results structure.) -TEXT runtime·pthread_attr_init_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_attr_init_trampoline(SB),NOSPLIT,$0 PUSHQ BP // make frame, keep stack 16-byte aligned. MOVQ SP, BP MOVQ 0(DI), DI // arg 1 attr @@ -483,7 +485,7 @@ TEXT runtime·pthread_attr_init_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_attr_getstacksize_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_attr_getstacksize_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 size @@ -492,7 +494,7 @@ TEXT runtime·pthread_attr_getstacksize_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_attr_setdetachstate_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_attr_setdetachstate_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 state @@ -501,7 +503,7 @@ TEXT runtime·pthread_attr_setdetachstate_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_create_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_create_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP @@ -514,7 +516,7 @@ TEXT runtime·pthread_create_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·raise_trampoline(SB),NOSPLIT,$0 +TEXT runtime·raise_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVL 0(DI), DI // arg 1 signal @@ -522,7 +524,7 @@ TEXT runtime·raise_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_mutex_init_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_mutex_init_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 attr @@ -531,7 +533,7 @@ TEXT runtime·pthread_mutex_init_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_mutex_lock_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_mutex_lock_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 0(DI), DI // arg 1 mutex @@ -539,7 +541,7 @@ TEXT runtime·pthread_mutex_lock_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_mutex_unlock_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_mutex_unlock_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 0(DI), DI // arg 1 mutex @@ -547,7 +549,7 @@ TEXT runtime·pthread_mutex_unlock_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_cond_init_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_cond_init_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 attr @@ -556,7 +558,7 @@ TEXT runtime·pthread_cond_init_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_cond_wait_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_cond_wait_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 mutex @@ -565,7 +567,7 @@ TEXT runtime·pthread_cond_wait_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_cond_timedwait_relative_np_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_cond_timedwait_relative_np_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 mutex @@ -575,7 +577,7 @@ TEXT runtime·pthread_cond_timedwait_relative_np_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_cond_signal_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_cond_signal_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 0(DI), DI // arg 1 cond @@ -583,7 +585,7 @@ TEXT runtime·pthread_cond_signal_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_self_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_self_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ DI, BX // BX is caller-save @@ -592,7 +594,7 @@ TEXT runtime·pthread_self_trampoline(SB),NOSPLIT,$0 POPQ BP RET -TEXT runtime·pthread_kill_trampoline(SB),NOSPLIT,$0 +TEXT runtime·pthread_kill_trampoline(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP MOVQ 8(DI), SI // arg 2 sig @@ -617,7 +619,7 @@ TEXT runtime·pthread_kill_trampoline(SB),NOSPLIT,$0 // // syscall expects a 32-bit result and tests for 32-bit -1 // to decide there was an error. -TEXT runtime·syscall(SB),NOSPLIT,$0 +TEXT runtime·syscall(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP @@ -667,7 +669,7 @@ ok: // // syscallX is like syscall but expects a 64-bit result // and tests for 64-bit -1 to decide there was an error. -TEXT runtime·syscallX(SB),NOSPLIT,$0 +TEXT runtime·syscallX(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP @@ -703,7 +705,7 @@ ok: // syscallPtr is like syscallX except that the libc function reports an // error by returning NULL and setting errno. -TEXT runtime·syscallPtr(SB),NOSPLIT,$0 +TEXT runtime·syscallPtr(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP @@ -756,7 +758,7 @@ ok: // // syscall6 expects a 32-bit result and tests for 32-bit -1 // to decide there was an error. -TEXT runtime·syscall6(SB),NOSPLIT,$0 +TEXT runtime·syscall6(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP @@ -809,7 +811,7 @@ ok: // // syscall6X is like syscall6 but expects a 64-bit result // and tests for 64-bit -1 to decide there was an error. -TEXT runtime·syscall6X(SB),NOSPLIT,$0 +TEXT runtime·syscall6X(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP @@ -845,7 +847,7 @@ ok: // syscallNoErr is like syscall6 but does not check for errors, and // only returns one value, for use with standard C ABI library functions. -TEXT runtime·syscallNoErr(SB),NOSPLIT,$0 +TEXT runtime·syscallNoErr(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index 37cb8dad0369e..d48573c2c5922 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -215,9 +215,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$16-12 MOVQ SP, R12 // Save old SP; R12 unchanged by C code. +#ifdef GOEXPERIMENT_REGABI + MOVQ g_m(R14), BX // BX unchanged by C code. +#else get_tls(CX) MOVQ g(CX), AX MOVQ g_m(AX), BX // BX unchanged by C code. +#endif // Set vdsoPC and vdsoSP for SIGPROF traceback. // Save the old values on stack and restore them on exit, @@ -232,7 +236,11 @@ TEXT runtime·walltime1(SB),NOSPLIT,$16-12 MOVQ CX, m_vdsoPC(BX) MOVQ DX, m_vdsoSP(BX) +#ifdef GOEXPERIMENT_REGABI + CMPQ R14, m_curg(BX) // Only switch if on curg. +#else CMPQ AX, m_curg(BX) // Only switch if on curg. +#endif JNE noswitch MOVQ m_g0(BX), DX @@ -275,9 +283,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVQ SP, R12 // Save old SP; R12 unchanged by C code. +#ifdef GOEXPERIMENT_REGABI + MOVQ g_m(R14), BX // BX unchanged by C code. +#else get_tls(CX) MOVQ g(CX), AX MOVQ g_m(AX), BX // BX unchanged by C code. +#endif // Set vdsoPC and vdsoSP for SIGPROF traceback. // Save the old values on stack and restore them on exit, @@ -292,7 +304,11 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVQ CX, m_vdsoPC(BX) MOVQ DX, m_vdsoSP(BX) +#ifdef GOEXPERIMENT_REGABI + CMPQ R14, m_curg(BX) // Only switch if on curg. +#else CMPQ AX, m_curg(BX) // Only switch if on curg. +#endif JNE noswitch MOVQ m_g0(BX), DX @@ -632,6 +648,7 @@ nog1: get_tls(CX) MOVQ R13, g_m(R9) MOVQ R9, g(CX) + MOVQ R9, R14 // set g register CALL runtime·stackcheck(SB) nog2: diff --git a/src/runtime/syscall2_solaris.go b/src/runtime/syscall2_solaris.go index e098e8006a681..3310489202122 100644 --- a/src/runtime/syscall2_solaris.go +++ b/src/runtime/syscall2_solaris.go @@ -15,7 +15,6 @@ import _ "unsafe" // for go:linkname //go:cgo_import_dynamic libc_gethostname gethostname "libc.so" //go:cgo_import_dynamic libc_getpid getpid "libc.so" //go:cgo_import_dynamic libc_ioctl ioctl "libc.so" -//go:cgo_import_dynamic libc_pipe pipe "libc.so" //go:cgo_import_dynamic libc_setgid setgid "libc.so" //go:cgo_import_dynamic libc_setgroups setgroups "libc.so" //go:cgo_import_dynamic libc_setsid setsid "libc.so" @@ -33,7 +32,6 @@ import _ "unsafe" // for go:linkname //go:linkname libc_gethostname libc_gethostname //go:linkname libc_getpid libc_getpid //go:linkname libc_ioctl libc_ioctl -//go:linkname libc_pipe libc_pipe //go:linkname libc_setgid libc_setgid //go:linkname libc_setgroups libc_setgroups //go:linkname libc_setsid libc_setsid diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go index 7835b492f7814..add40bb0b345b 100644 --- a/src/runtime/syscall_windows.go +++ b/src/runtime/syscall_windows.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/abi" "runtime/internal/sys" "unsafe" ) @@ -242,7 +243,11 @@ func callbackWrap(a *callbackArgs) { // Even though this is copying back results, we can pass a nil // type because those results must not require write barriers. - reflectcall(nil, unsafe.Pointer(c.fn), noescape(goArgs), uint32(c.retOffset)+sys.PtrSize, uint32(c.retOffset)) + // + // Pass a dummy RegArgs for now. + // TODO(mknyszek): Pass arguments in registers. + var regs abi.RegArgs + reflectcall(nil, unsafe.Pointer(c.fn), noescape(goArgs), uint32(c.retOffset)+sys.PtrSize, uint32(c.retOffset), uint32(c.retOffset)+sys.PtrSize, ®s) // Extract the result. a.result = *(*uintptr)(unsafe.Pointer(&frame[c.retOffset])) diff --git a/src/runtime/textflag.h b/src/runtime/textflag.h index daca36d948330..e727208cd03a0 100644 --- a/src/runtime/textflag.h +++ b/src/runtime/textflag.h @@ -35,3 +35,5 @@ // Function is the top of the call stack. Call stack unwinders should stop // at this function. #define TOPFRAME 2048 +// Function is an ABI wrapper. +#define ABIWRAPPER 4096 diff --git a/src/runtime/type.go b/src/runtime/type.go index 81455f3532db8..18fc4bbfad754 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -383,7 +383,7 @@ type maptype struct { } // 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. func (mt *maptype) indirectkey() bool { // store ptr to key instead of key itself return mt.flags&1 != 0 } diff --git a/src/strconv/bytealg.go b/src/strconv/bytealg.go new file mode 100644 index 0000000000000..7f66f2a8bbc8c --- /dev/null +++ b/src/strconv/bytealg.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. + +// +build !compiler_bootstrap + +package strconv + +import "internal/bytealg" + +// contains reports whether the string contains the byte c. +func contains(s string, c byte) bool { + return bytealg.IndexByteString(s, c) != -1 +} diff --git a/src/strconv/bytealg_bootstrap.go b/src/strconv/bytealg_bootstrap.go new file mode 100644 index 0000000000000..a3a547d1b639a --- /dev/null +++ b/src/strconv/bytealg_bootstrap.go @@ -0,0 +1,17 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build compiler_bootstrap + +package strconv + +// contains reports whether the string contains the byte c. +func contains(s string, c byte) bool { + for i := 0; i < len(s); i++ { + if s[i] == c { + return true + } + } + return false +} diff --git a/src/strconv/eisel_lemire.go b/src/strconv/eisel_lemire.go index 6c7f852eba806..fecd1b93451d0 100644 --- a/src/strconv/eisel_lemire.go +++ b/src/strconv/eisel_lemire.go @@ -29,7 +29,7 @@ func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) { // Exp10 Range. if man == 0 { if neg { - f = math.Float64frombits(0x80000000_00000000) // Negative zero. + f = math.Float64frombits(0x8000000000000000) // Negative zero. } return f, true } @@ -39,7 +39,7 @@ func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) { // Normalization. clz := bits.LeadingZeros64(man) - man <<= clz + man <<= uint(clz) const float64ExponentBias = 1023 retExp2 := uint64(217706*exp10>>16+64+float64ExponentBias) - uint64(clz) @@ -84,9 +84,9 @@ func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) { if retExp2-1 >= 0x7FF-1 { return 0, false } - retBits := retExp2<<52 | retMantissa&0x000FFFFF_FFFFFFFF + retBits := retExp2<<52 | retMantissa&0x000FFFFFFFFFFFFF if neg { - retBits |= 0x80000000_00000000 + retBits |= 0x8000000000000000 } return math.Float64frombits(retBits), true } @@ -114,7 +114,7 @@ func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) { // Normalization. clz := bits.LeadingZeros64(man) - man <<= clz + man <<= uint(clz) const float32ExponentBias = 127 retExp2 := uint64(217706*exp10>>16+64+float32ExponentBias) - uint64(clz) @@ -122,13 +122,13 @@ func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) { xHi, xLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][1]) // Wider Approximation. - if xHi&0x3F_FFFFFFFF == 0x3F_FFFFFFFF && xLo+man < man { + if xHi&0x3FFFFFFFFF == 0x3FFFFFFFFF && xLo+man < man { yHi, yLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][0]) mergedHi, mergedLo := xHi, xLo+yHi if mergedLo < xLo { mergedHi++ } - if mergedHi&0x3F_FFFFFFFF == 0x3F_FFFFFFFF && mergedLo+1 == 0 && yLo+man < man { + if mergedHi&0x3FFFFFFFFF == 0x3FFFFFFFFF && mergedLo+1 == 0 && yLo+man < man { return 0, false } xHi, xLo = mergedHi, mergedLo @@ -140,7 +140,7 @@ func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) { retExp2 -= 1 ^ msb // Half-way Ambiguity. - if xLo == 0 && xHi&0x3F_FFFFFFFF == 0 && retMantissa&3 == 1 { + if xLo == 0 && xHi&0x3FFFFFFFFF == 0 && retMantissa&3 == 1 { return 0, false } diff --git a/src/strconv/quote.go b/src/strconv/quote.go index bcbdbc514d21a..4ffa10b72efc9 100644 --- a/src/strconv/quote.go +++ b/src/strconv/quote.go @@ -7,7 +7,6 @@ package strconv import ( - "internal/bytealg" "unicode/utf8" ) @@ -436,11 +435,6 @@ func Unquote(s string) (string, error) { return string(buf), nil } -// contains reports whether the string contains the byte c. -func contains(s string, c byte) bool { - return bytealg.IndexByteString(s, c) != -1 -} - // bsearch16 returns the smallest i such that a[i] >= x. // If there is no such i, bsearch16 returns len(a). func bsearch16(a []uint16, x uint16) int { diff --git a/src/syscall/mkasm.go b/src/syscall/mkasm.go index 2ebaf8d3513e5..e53d14bed10b2 100644 --- a/src/syscall/mkasm.go +++ b/src/syscall/mkasm.go @@ -53,7 +53,8 @@ func main() { fn := line[5 : len(line)-13] if !trampolines[fn] { trampolines[fn] = true - fmt.Fprintf(&out, "TEXT ·%s_trampoline(SB),NOSPLIT,$0-0\n", fn) + // The trampolines are ABIInternal as they are address-taken in Go code. + fmt.Fprintf(&out, "TEXT ·%s_trampoline(SB),NOSPLIT,$0-0\n", fn) fmt.Fprintf(&out, "\tJMP\t%s(SB)\n", fn) } } diff --git a/src/syscall/mksyscall.pl b/src/syscall/mksyscall.pl index 67e8d1d99efc0..c1ed3a35d9770 100755 --- a/src/syscall/mksyscall.pl +++ b/src/syscall/mksyscall.pl @@ -351,9 +351,6 @@ ($) $trampolines{$funcname} = 1; # The assembly trampoline that jumps to the libc routine. $text .= "func ${funcname}_trampoline()\n"; - # Map syscall.funcname to just plain funcname. - # (The jump to this function is in the assembly trampoline, generated by mkasm.go.) - $text .= "//go:linkname $funcname $funcname\n"; # Tell the linker that funcname can be found in libSystem using varname without the libc_ prefix. my $basename = substr $funcname, 5; my $libc = "libc.so"; diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_darwin.go index afdadbf89468d..162e94479fa31 100644 --- a/src/syscall/syscall_darwin.go +++ b/src/syscall/syscall_darwin.go @@ -115,7 +115,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { func libc_getfsstat_trampoline() -//go:linkname libc_getfsstat libc_getfsstat //go:cgo_import_dynamic libc_getfsstat getfsstat "/usr/lib/libSystem.B.dylib" func setattrlistTimes(path string, times []Timespec) error { @@ -148,7 +147,6 @@ func setattrlistTimes(path string, times []Timespec) error { func libc_setattrlist_trampoline() -//go:linkname libc_setattrlist libc_setattrlist //go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib" func utimensat(dirfd int, path string, times *[2]Timespec, flag int) error { @@ -276,7 +274,6 @@ func fdopendir(fd int) (dir uintptr, err error) { func libc_fdopendir_trampoline() -//go:linkname libc_fdopendir libc_fdopendir //go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib" func readlen(fd int, buf *byte, nbuf int) (n int, err error) { diff --git a/src/syscall/syscall_darwin_amd64.go b/src/syscall/syscall_darwin_amd64.go index 22ddb78ae5bd2..687efff77073f 100644 --- a/src/syscall/syscall_darwin_amd64.go +++ b/src/syscall/syscall_darwin_amd64.go @@ -56,7 +56,6 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e func libc_sendfile_trampoline() -//go:linkname libc_sendfile libc_sendfile //go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib" // Implemented in the runtime package (runtime/sys_darwin_64.go) diff --git a/src/syscall/syscall_darwin_arm64.go b/src/syscall/syscall_darwin_arm64.go index ecb9ffff492ce..ab57117475824 100644 --- a/src/syscall/syscall_darwin_arm64.go +++ b/src/syscall/syscall_darwin_arm64.go @@ -56,7 +56,6 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e func libc_sendfile_trampoline() -//go:linkname libc_sendfile libc_sendfile //go:cgo_import_dynamic libc_sendfile sendfile "/usr/lib/libSystem.B.dylib" // Implemented in the runtime package (runtime/sys_darwin_64.go) diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go index c246c3a2675ff..4f2cdf861e246 100644 --- a/src/syscall/zsyscall_darwin_amd64.go +++ b/src/syscall/zsyscall_darwin_amd64.go @@ -20,7 +20,6 @@ func getgroups(ngid int, gid *_Gid_t) (n int, err error) { func libc_getgroups_trampoline() -//go:linkname libc_getgroups libc_getgroups //go:cgo_import_dynamic libc_getgroups getgroups "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -35,7 +34,6 @@ func setgroups(ngid int, gid *_Gid_t) (err error) { func libc_setgroups_trampoline() -//go:linkname libc_setgroups libc_setgroups //go:cgo_import_dynamic libc_setgroups setgroups "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -51,7 +49,6 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err func libc_wait4_trampoline() -//go:linkname libc_wait4 libc_wait4 //go:cgo_import_dynamic libc_wait4 wait4 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -67,7 +64,6 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { func libc_accept_trampoline() -//go:linkname libc_accept libc_accept //go:cgo_import_dynamic libc_accept accept "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -82,7 +78,6 @@ func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { func libc_bind_trampoline() -//go:linkname libc_bind libc_bind //go:cgo_import_dynamic libc_bind bind "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -97,7 +92,6 @@ func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { func libc_connect_trampoline() -//go:linkname libc_connect libc_connect //go:cgo_import_dynamic libc_connect connect "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -113,7 +107,6 @@ func socket(domain int, typ int, proto int) (fd int, err error) { func libc_socket_trampoline() -//go:linkname libc_socket libc_socket //go:cgo_import_dynamic libc_socket socket "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -128,7 +121,6 @@ func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen func libc_getsockopt_trampoline() -//go:linkname libc_getsockopt libc_getsockopt //go:cgo_import_dynamic libc_getsockopt getsockopt "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -143,7 +135,6 @@ func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) func libc_setsockopt_trampoline() -//go:linkname libc_setsockopt libc_setsockopt //go:cgo_import_dynamic libc_setsockopt setsockopt "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -158,7 +149,6 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { func libc_getpeername_trampoline() -//go:linkname libc_getpeername libc_getpeername //go:cgo_import_dynamic libc_getpeername getpeername "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -173,7 +163,6 @@ func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { func libc_getsockname_trampoline() -//go:linkname libc_getsockname libc_getsockname //go:cgo_import_dynamic libc_getsockname getsockname "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -188,7 +177,6 @@ func Shutdown(s int, how int) (err error) { func libc_shutdown_trampoline() -//go:linkname libc_shutdown libc_shutdown //go:cgo_import_dynamic libc_shutdown shutdown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -203,7 +191,6 @@ func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { func libc_socketpair_trampoline() -//go:linkname libc_socketpair libc_socketpair //go:cgo_import_dynamic libc_socketpair socketpair "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -225,7 +212,6 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl func libc_recvfrom_trampoline() -//go:linkname libc_recvfrom libc_recvfrom //go:cgo_import_dynamic libc_recvfrom recvfrom "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -246,7 +232,6 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) ( func libc_sendto_trampoline() -//go:linkname libc_sendto libc_sendto //go:cgo_import_dynamic libc_sendto sendto "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -262,7 +247,6 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { func libc_recvmsg_trampoline() -//go:linkname libc_recvmsg libc_recvmsg //go:cgo_import_dynamic libc_recvmsg recvmsg "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -278,7 +262,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { func libc_sendmsg_trampoline() -//go:linkname libc_sendmsg libc_sendmsg //go:cgo_import_dynamic libc_sendmsg sendmsg "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -294,7 +277,6 @@ func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, ne func libc_kevent_trampoline() -//go:linkname libc_kevent libc_kevent //go:cgo_import_dynamic libc_kevent kevent "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -314,7 +296,6 @@ func utimes(path string, timeval *[2]Timeval) (err error) { func libc_utimes_trampoline() -//go:linkname libc_utimes libc_utimes //go:cgo_import_dynamic libc_utimes utimes "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -329,7 +310,6 @@ func futimes(fd int, timeval *[2]Timeval) (err error) { func libc_futimes_trampoline() -//go:linkname libc_futimes libc_futimes //go:cgo_import_dynamic libc_futimes futimes "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -345,7 +325,6 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { func libc_fcntl_trampoline() -//go:linkname libc_fcntl libc_fcntl //go:cgo_import_dynamic libc_fcntl fcntl "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -360,7 +339,6 @@ func pipe(p *[2]int32) (err error) { func libc_pipe_trampoline() -//go:linkname libc_pipe libc_pipe //go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -375,7 +353,6 @@ func kill(pid int, signum int, posix int) (err error) { func libc_kill_trampoline() -//go:linkname libc_kill libc_kill //go:cgo_import_dynamic libc_kill kill "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -395,7 +372,6 @@ func Access(path string, mode uint32) (err error) { func libc_access_trampoline() -//go:linkname libc_access libc_access //go:cgo_import_dynamic libc_access access "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -410,7 +386,6 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { func libc_adjtime_trampoline() -//go:linkname libc_adjtime libc_adjtime //go:cgo_import_dynamic libc_adjtime adjtime "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -430,7 +405,6 @@ func Chdir(path string) (err error) { func libc_chdir_trampoline() -//go:linkname libc_chdir libc_chdir //go:cgo_import_dynamic libc_chdir chdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -450,7 +424,6 @@ func Chflags(path string, flags int) (err error) { func libc_chflags_trampoline() -//go:linkname libc_chflags libc_chflags //go:cgo_import_dynamic libc_chflags chflags "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -470,7 +443,6 @@ func Chmod(path string, mode uint32) (err error) { func libc_chmod_trampoline() -//go:linkname libc_chmod libc_chmod //go:cgo_import_dynamic libc_chmod chmod "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -490,7 +462,6 @@ func Chown(path string, uid int, gid int) (err error) { func libc_chown_trampoline() -//go:linkname libc_chown libc_chown //go:cgo_import_dynamic libc_chown chown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -510,7 +481,6 @@ func Chroot(path string) (err error) { func libc_chroot_trampoline() -//go:linkname libc_chroot libc_chroot //go:cgo_import_dynamic libc_chroot chroot "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -525,7 +495,6 @@ func Close(fd int) (err error) { func libc_close_trampoline() -//go:linkname libc_close libc_close //go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -540,7 +509,6 @@ func closedir(dir uintptr) (err error) { func libc_closedir_trampoline() -//go:linkname libc_closedir libc_closedir //go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -556,7 +524,6 @@ func Dup(fd int) (nfd int, err error) { func libc_dup_trampoline() -//go:linkname libc_dup libc_dup //go:cgo_import_dynamic libc_dup dup "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -571,7 +538,6 @@ func Dup2(from int, to int) (err error) { func libc_dup2_trampoline() -//go:linkname libc_dup2 libc_dup2 //go:cgo_import_dynamic libc_dup2 dup2 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -596,7 +562,6 @@ func Exchangedata(path1 string, path2 string, options int) (err error) { func libc_exchangedata_trampoline() -//go:linkname libc_exchangedata libc_exchangedata //go:cgo_import_dynamic libc_exchangedata exchangedata "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -611,7 +576,6 @@ func Fchdir(fd int) (err error) { func libc_fchdir_trampoline() -//go:linkname libc_fchdir libc_fchdir //go:cgo_import_dynamic libc_fchdir fchdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -626,7 +590,6 @@ func Fchflags(fd int, flags int) (err error) { func libc_fchflags_trampoline() -//go:linkname libc_fchflags libc_fchflags //go:cgo_import_dynamic libc_fchflags fchflags "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -641,7 +604,6 @@ func Fchmod(fd int, mode uint32) (err error) { func libc_fchmod_trampoline() -//go:linkname libc_fchmod libc_fchmod //go:cgo_import_dynamic libc_fchmod fchmod "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -656,7 +618,6 @@ func Fchown(fd int, uid int, gid int) (err error) { func libc_fchown_trampoline() -//go:linkname libc_fchown libc_fchown //go:cgo_import_dynamic libc_fchown fchown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -671,7 +632,6 @@ func Flock(fd int, how int) (err error) { func libc_flock_trampoline() -//go:linkname libc_flock libc_flock //go:cgo_import_dynamic libc_flock flock "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -687,7 +647,6 @@ func Fpathconf(fd int, name int) (val int, err error) { func libc_fpathconf_trampoline() -//go:linkname libc_fpathconf libc_fpathconf //go:cgo_import_dynamic libc_fpathconf fpathconf "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -702,7 +661,6 @@ func Fsync(fd int) (err error) { func libc_fsync_trampoline() -//go:linkname libc_fsync libc_fsync //go:cgo_import_dynamic libc_fsync fsync "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -717,7 +675,6 @@ func Ftruncate(fd int, length int64) (err error) { func libc_ftruncate_trampoline() -//go:linkname libc_ftruncate libc_ftruncate //go:cgo_import_dynamic libc_ftruncate ftruncate "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -730,7 +687,6 @@ func Getdtablesize() (size int) { func libc_getdtablesize_trampoline() -//go:linkname libc_getdtablesize libc_getdtablesize //go:cgo_import_dynamic libc_getdtablesize getdtablesize "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -743,7 +699,6 @@ func Getegid() (egid int) { func libc_getegid_trampoline() -//go:linkname libc_getegid libc_getegid //go:cgo_import_dynamic libc_getegid getegid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -756,7 +711,6 @@ func Geteuid() (uid int) { func libc_geteuid_trampoline() -//go:linkname libc_geteuid libc_geteuid //go:cgo_import_dynamic libc_geteuid geteuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -769,7 +723,6 @@ func Getgid() (gid int) { func libc_getgid_trampoline() -//go:linkname libc_getgid libc_getgid //go:cgo_import_dynamic libc_getgid getgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -785,7 +738,6 @@ func Getpgid(pid int) (pgid int, err error) { func libc_getpgid_trampoline() -//go:linkname libc_getpgid libc_getpgid //go:cgo_import_dynamic libc_getpgid getpgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -798,7 +750,6 @@ func Getpgrp() (pgrp int) { func libc_getpgrp_trampoline() -//go:linkname libc_getpgrp libc_getpgrp //go:cgo_import_dynamic libc_getpgrp getpgrp "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -811,7 +762,6 @@ func Getpid() (pid int) { func libc_getpid_trampoline() -//go:linkname libc_getpid libc_getpid //go:cgo_import_dynamic libc_getpid getpid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -824,7 +774,6 @@ func Getppid() (ppid int) { func libc_getppid_trampoline() -//go:linkname libc_getppid libc_getppid //go:cgo_import_dynamic libc_getppid getppid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -840,7 +789,6 @@ func Getpriority(which int, who int) (prio int, err error) { func libc_getpriority_trampoline() -//go:linkname libc_getpriority libc_getpriority //go:cgo_import_dynamic libc_getpriority getpriority "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -855,7 +803,6 @@ func Getrlimit(which int, lim *Rlimit) (err error) { func libc_getrlimit_trampoline() -//go:linkname libc_getrlimit libc_getrlimit //go:cgo_import_dynamic libc_getrlimit getrlimit "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -870,7 +817,6 @@ func Getrusage(who int, rusage *Rusage) (err error) { func libc_getrusage_trampoline() -//go:linkname libc_getrusage libc_getrusage //go:cgo_import_dynamic libc_getrusage getrusage "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -886,7 +832,6 @@ func Getsid(pid int) (sid int, err error) { func libc_getsid_trampoline() -//go:linkname libc_getsid libc_getsid //go:cgo_import_dynamic libc_getsid getsid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -899,7 +844,6 @@ func Getuid() (uid int) { func libc_getuid_trampoline() -//go:linkname libc_getuid libc_getuid //go:cgo_import_dynamic libc_getuid getuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -912,7 +856,6 @@ func Issetugid() (tainted bool) { func libc_issetugid_trampoline() -//go:linkname libc_issetugid libc_issetugid //go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -928,7 +871,6 @@ func Kqueue() (fd int, err error) { func libc_kqueue_trampoline() -//go:linkname libc_kqueue libc_kqueue //go:cgo_import_dynamic libc_kqueue kqueue "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -948,7 +890,6 @@ func Lchown(path string, uid int, gid int) (err error) { func libc_lchown_trampoline() -//go:linkname libc_lchown libc_lchown //go:cgo_import_dynamic libc_lchown lchown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -973,7 +914,6 @@ func Link(path string, link string) (err error) { func libc_link_trampoline() -//go:linkname libc_link libc_link //go:cgo_import_dynamic libc_link link "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -988,7 +928,6 @@ func Listen(s int, backlog int) (err error) { func libc_listen_trampoline() -//go:linkname libc_listen libc_listen //go:cgo_import_dynamic libc_listen listen "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1008,7 +947,6 @@ func Mkdir(path string, mode uint32) (err error) { func libc_mkdir_trampoline() -//go:linkname libc_mkdir libc_mkdir //go:cgo_import_dynamic libc_mkdir mkdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1028,7 +966,6 @@ func Mkfifo(path string, mode uint32) (err error) { func libc_mkfifo_trampoline() -//go:linkname libc_mkfifo libc_mkfifo //go:cgo_import_dynamic libc_mkfifo mkfifo "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1048,7 +985,6 @@ func Mknod(path string, mode uint32, dev int) (err error) { func libc_mknod_trampoline() -//go:linkname libc_mknod libc_mknod //go:cgo_import_dynamic libc_mknod mknod "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1069,7 +1005,6 @@ func Mlock(b []byte) (err error) { func libc_mlock_trampoline() -//go:linkname libc_mlock libc_mlock //go:cgo_import_dynamic libc_mlock mlock "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1084,7 +1019,6 @@ func Mlockall(flags int) (err error) { func libc_mlockall_trampoline() -//go:linkname libc_mlockall libc_mlockall //go:cgo_import_dynamic libc_mlockall mlockall "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1105,7 +1039,6 @@ func Mprotect(b []byte, prot int) (err error) { func libc_mprotect_trampoline() -//go:linkname libc_mprotect libc_mprotect //go:cgo_import_dynamic libc_mprotect mprotect "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1126,7 +1059,6 @@ func Munlock(b []byte) (err error) { func libc_munlock_trampoline() -//go:linkname libc_munlock libc_munlock //go:cgo_import_dynamic libc_munlock munlock "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1141,7 +1073,6 @@ func Munlockall() (err error) { func libc_munlockall_trampoline() -//go:linkname libc_munlockall libc_munlockall //go:cgo_import_dynamic libc_munlockall munlockall "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1162,7 +1093,6 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { func libc_open_trampoline() -//go:linkname libc_open libc_open //go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1183,7 +1113,6 @@ func Pathconf(path string, name int) (val int, err error) { func libc_pathconf_trampoline() -//go:linkname libc_pathconf libc_pathconf //go:cgo_import_dynamic libc_pathconf pathconf "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1205,7 +1134,6 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { func libc_pread_trampoline() -//go:linkname libc_pread libc_pread //go:cgo_import_dynamic libc_pread pread "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1227,7 +1155,6 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { func libc_pwrite_trampoline() -//go:linkname libc_pwrite libc_pwrite //go:cgo_import_dynamic libc_pwrite pwrite "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1249,7 +1176,6 @@ func read(fd int, p []byte) (n int, err error) { func libc_read_trampoline() -//go:linkname libc_read libc_read //go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1262,7 +1188,6 @@ func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) { func libc_readdir_r_trampoline() -//go:linkname libc_readdir_r libc_readdir_r //go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1289,7 +1214,6 @@ func Readlink(path string, buf []byte) (n int, err error) { func libc_readlink_trampoline() -//go:linkname libc_readlink libc_readlink //go:cgo_import_dynamic libc_readlink readlink "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1314,7 +1238,6 @@ func Rename(from string, to string) (err error) { func libc_rename_trampoline() -//go:linkname libc_rename libc_rename //go:cgo_import_dynamic libc_rename rename "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1334,7 +1257,6 @@ func Revoke(path string) (err error) { func libc_revoke_trampoline() -//go:linkname libc_revoke libc_revoke //go:cgo_import_dynamic libc_revoke revoke "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1354,7 +1276,6 @@ func Rmdir(path string) (err error) { func libc_rmdir_trampoline() -//go:linkname libc_rmdir libc_rmdir //go:cgo_import_dynamic libc_rmdir rmdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1370,7 +1291,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { func libc_lseek_trampoline() -//go:linkname libc_lseek libc_lseek //go:cgo_import_dynamic libc_lseek lseek "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1385,7 +1305,6 @@ func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) { func libc_select_trampoline() -//go:linkname libc_select libc_select //go:cgo_import_dynamic libc_select select "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1400,7 +1319,6 @@ func Setegid(egid int) (err error) { func libc_setegid_trampoline() -//go:linkname libc_setegid libc_setegid //go:cgo_import_dynamic libc_setegid setegid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1415,7 +1333,6 @@ func Seteuid(euid int) (err error) { func libc_seteuid_trampoline() -//go:linkname libc_seteuid libc_seteuid //go:cgo_import_dynamic libc_seteuid seteuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1430,7 +1347,6 @@ func Setgid(gid int) (err error) { func libc_setgid_trampoline() -//go:linkname libc_setgid libc_setgid //go:cgo_import_dynamic libc_setgid setgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1450,7 +1366,6 @@ func Setlogin(name string) (err error) { func libc_setlogin_trampoline() -//go:linkname libc_setlogin libc_setlogin //go:cgo_import_dynamic libc_setlogin setlogin "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1465,7 +1380,6 @@ func Setpgid(pid int, pgid int) (err error) { func libc_setpgid_trampoline() -//go:linkname libc_setpgid libc_setpgid //go:cgo_import_dynamic libc_setpgid setpgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1480,7 +1394,6 @@ func Setpriority(which int, who int, prio int) (err error) { func libc_setpriority_trampoline() -//go:linkname libc_setpriority libc_setpriority //go:cgo_import_dynamic libc_setpriority setpriority "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1495,7 +1408,6 @@ func Setprivexec(flag int) (err error) { func libc_setprivexec_trampoline() -//go:linkname libc_setprivexec libc_setprivexec //go:cgo_import_dynamic libc_setprivexec setprivexec "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1510,7 +1422,6 @@ func Setregid(rgid int, egid int) (err error) { func libc_setregid_trampoline() -//go:linkname libc_setregid libc_setregid //go:cgo_import_dynamic libc_setregid setregid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1525,7 +1436,6 @@ func Setreuid(ruid int, euid int) (err error) { func libc_setreuid_trampoline() -//go:linkname libc_setreuid libc_setreuid //go:cgo_import_dynamic libc_setreuid setreuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1540,7 +1450,6 @@ func Setrlimit(which int, lim *Rlimit) (err error) { func libc_setrlimit_trampoline() -//go:linkname libc_setrlimit libc_setrlimit //go:cgo_import_dynamic libc_setrlimit setrlimit "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1556,7 +1465,6 @@ func Setsid() (pid int, err error) { func libc_setsid_trampoline() -//go:linkname libc_setsid libc_setsid //go:cgo_import_dynamic libc_setsid setsid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1571,7 +1479,6 @@ func Settimeofday(tp *Timeval) (err error) { func libc_settimeofday_trampoline() -//go:linkname libc_settimeofday libc_settimeofday //go:cgo_import_dynamic libc_settimeofday settimeofday "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1586,7 +1493,6 @@ func Setuid(uid int) (err error) { func libc_setuid_trampoline() -//go:linkname libc_setuid libc_setuid //go:cgo_import_dynamic libc_setuid setuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1611,7 +1517,6 @@ func Symlink(path string, link string) (err error) { func libc_symlink_trampoline() -//go:linkname libc_symlink libc_symlink //go:cgo_import_dynamic libc_symlink symlink "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1626,7 +1531,6 @@ func Sync() (err error) { func libc_sync_trampoline() -//go:linkname libc_sync libc_sync //go:cgo_import_dynamic libc_sync sync "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1646,7 +1550,6 @@ func Truncate(path string, length int64) (err error) { func libc_truncate_trampoline() -//go:linkname libc_truncate libc_truncate //go:cgo_import_dynamic libc_truncate truncate "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1659,7 +1562,6 @@ func Umask(newmask int) (oldmask int) { func libc_umask_trampoline() -//go:linkname libc_umask libc_umask //go:cgo_import_dynamic libc_umask umask "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1679,7 +1581,6 @@ func Undelete(path string) (err error) { func libc_undelete_trampoline() -//go:linkname libc_undelete libc_undelete //go:cgo_import_dynamic libc_undelete undelete "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1699,7 +1600,6 @@ func Unlink(path string) (err error) { func libc_unlink_trampoline() -//go:linkname libc_unlink libc_unlink //go:cgo_import_dynamic libc_unlink unlink "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1719,7 +1619,6 @@ func Unmount(path string, flags int) (err error) { func libc_unmount_trampoline() -//go:linkname libc_unmount libc_unmount //go:cgo_import_dynamic libc_unmount unmount "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1741,7 +1640,6 @@ func write(fd int, p []byte) (n int, err error) { func libc_write_trampoline() -//go:linkname libc_write libc_write //go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1763,7 +1661,6 @@ func writev(fd int, iovecs []Iovec) (cnt uintptr, err error) { func libc_writev_trampoline() -//go:linkname libc_writev libc_writev //go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1779,7 +1676,6 @@ func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) ( func libc_mmap_trampoline() -//go:linkname libc_mmap libc_mmap //go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1794,7 +1690,6 @@ func munmap(addr uintptr, length uintptr) (err error) { func libc_munmap_trampoline() -//go:linkname libc_munmap libc_munmap //go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1810,7 +1705,6 @@ func fork() (pid int, err error) { func libc_fork_trampoline() -//go:linkname libc_fork libc_fork //go:cgo_import_dynamic libc_fork fork "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1825,7 +1719,6 @@ func ioctl(fd int, req int, arg int) (err error) { func libc_ioctl_trampoline() -//go:linkname libc_ioctl libc_ioctl //go:cgo_import_dynamic libc_ioctl ioctl "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1850,7 +1743,6 @@ func execve(path *byte, argv **byte, envp **byte) (err error) { func libc_execve_trampoline() -//go:linkname libc_execve libc_execve //go:cgo_import_dynamic libc_execve execve "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1865,7 +1757,6 @@ func exit(res int) (err error) { func libc_exit_trampoline() -//go:linkname libc_exit libc_exit //go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1886,7 +1777,6 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) func libc_sysctl_trampoline() -//go:linkname libc_sysctl libc_sysctl //go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1917,7 +1807,6 @@ func unlinkat(fd int, path string, flags int) (err error) { func libc_unlinkat_trampoline() -//go:linkname libc_unlinkat libc_unlinkat //go:cgo_import_dynamic libc_unlinkat unlinkat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1938,7 +1827,6 @@ func openat(fd int, path string, flags int, perm uint32) (fdret int, err error) func libc_openat_trampoline() -//go:linkname libc_openat libc_openat //go:cgo_import_dynamic libc_openat openat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1960,7 +1848,6 @@ func getcwd(buf []byte) (n int, err error) { func libc_getcwd_trampoline() -//go:linkname libc_getcwd libc_getcwd //go:cgo_import_dynamic libc_getcwd getcwd "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1975,7 +1862,6 @@ func Fstat(fd int, stat *Stat_t) (err error) { func libc_fstat64_trampoline() -//go:linkname libc_fstat64 libc_fstat64 //go:cgo_import_dynamic libc_fstat64 fstat64 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1990,7 +1876,6 @@ func Fstatfs(fd int, stat *Statfs_t) (err error) { func libc_fstatfs64_trampoline() -//go:linkname libc_fstatfs64 libc_fstatfs64 //go:cgo_import_dynamic libc_fstatfs64 fstatfs64 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2005,7 +1890,6 @@ func Gettimeofday(tp *Timeval) (err error) { func libc_gettimeofday_trampoline() -//go:linkname libc_gettimeofday libc_gettimeofday //go:cgo_import_dynamic libc_gettimeofday gettimeofday "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2025,7 +1909,6 @@ func Lstat(path string, stat *Stat_t) (err error) { func libc_lstat64_trampoline() -//go:linkname libc_lstat64 libc_lstat64 //go:cgo_import_dynamic libc_lstat64 lstat64 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2045,7 +1928,6 @@ func Stat(path string, stat *Stat_t) (err error) { func libc_stat64_trampoline() -//go:linkname libc_stat64 libc_stat64 //go:cgo_import_dynamic libc_stat64 stat64 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2065,7 +1947,6 @@ func Statfs(path string, stat *Statfs_t) (err error) { func libc_statfs64_trampoline() -//go:linkname libc_statfs64 libc_statfs64 //go:cgo_import_dynamic libc_statfs64 statfs64 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2085,7 +1966,6 @@ func fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { func libc_fstatat64_trampoline() -//go:linkname libc_fstatat64 libc_fstatat64 //go:cgo_import_dynamic libc_fstatat64 fstatat64 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2101,5 +1981,4 @@ func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) { func libc_ptrace_trampoline() -//go:linkname libc_ptrace libc_ptrace //go:cgo_import_dynamic libc_ptrace ptrace "/usr/lib/libSystem.B.dylib" diff --git a/src/syscall/zsyscall_darwin_amd64.s b/src/syscall/zsyscall_darwin_amd64.s index 492f947855487..5eb48cee447a3 100644 --- a/src/syscall/zsyscall_darwin_amd64.s +++ b/src/syscall/zsyscall_darwin_amd64.s @@ -1,253 +1,253 @@ // go run mkasm.go darwin amd64 // Code generated by the command above; DO NOT EDIT. #include "textflag.h" -TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_getfsstat(SB) -TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0 JMP libc_setattrlist(SB) -TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fdopendir(SB) -TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) -TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgroups(SB) -TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgroups(SB) -TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 JMP libc_wait4(SB) -TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 JMP libc_accept(SB) -TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 JMP libc_bind(SB) -TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 JMP libc_connect(SB) -TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 JMP libc_socket(SB) -TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockopt(SB) -TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsockopt(SB) -TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpeername(SB) -TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockname(SB) -TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 JMP libc_shutdown(SB) -TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 JMP libc_socketpair(SB) -TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvfrom(SB) -TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendto(SB) -TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvmsg(SB) -TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendmsg(SB) -TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 JMP libc_kevent(SB) -TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_utimes(SB) -TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_futimes(SB) -TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) -TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0 JMP libc_pipe(SB) -TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 JMP libc_kill(SB) -TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 JMP libc_access(SB) -TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 JMP libc_adjtime(SB) -TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_chdir(SB) -TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_chflags(SB) -TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_chmod(SB) -TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 JMP libc_chown(SB) -TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 JMP libc_chroot(SB) -TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 JMP libc_close(SB) -TEXT ·libc_closedir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_closedir_trampoline(SB),NOSPLIT,$0-0 JMP libc_closedir(SB) -TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) -TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) -TEXT ·libc_exchangedata_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_exchangedata_trampoline(SB),NOSPLIT,$0-0 JMP libc_exchangedata(SB) -TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) -TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchflags(SB) -TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchmod(SB) -TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchown(SB) -TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 JMP libc_flock(SB) -TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_fpathconf(SB) -TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 JMP libc_fsync(SB) -TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_ftruncate(SB) -TEXT ·libc_getdtablesize_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getdtablesize_trampoline(SB),NOSPLIT,$0-0 JMP libc_getdtablesize(SB) -TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getegid(SB) -TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_geteuid(SB) -TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgid(SB) -TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgid(SB) -TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgrp(SB) -TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpid(SB) -TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getppid(SB) -TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpriority(SB) -TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrlimit(SB) -TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrusage(SB) -TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsid(SB) -TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getuid(SB) -TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 JMP libc_issetugid(SB) -TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 JMP libc_kqueue(SB) -TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_lchown(SB) -TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 JMP libc_link(SB) -TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 JMP libc_listen(SB) -TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkdir(SB) -TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkfifo(SB) -TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 JMP libc_mknod(SB) -TEXT ·libc_mlock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mlock_trampoline(SB),NOSPLIT,$0-0 JMP libc_mlock(SB) -TEXT ·libc_mlockall_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mlockall_trampoline(SB),NOSPLIT,$0-0 JMP libc_mlockall(SB) -TEXT ·libc_mprotect_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mprotect_trampoline(SB),NOSPLIT,$0-0 JMP libc_mprotect(SB) -TEXT ·libc_munlock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munlock_trampoline(SB),NOSPLIT,$0-0 JMP libc_munlock(SB) -TEXT ·libc_munlockall_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munlockall_trampoline(SB),NOSPLIT,$0-0 JMP libc_munlockall(SB) -TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 JMP libc_open(SB) -TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_pathconf(SB) -TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 JMP libc_pread(SB) -TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 JMP libc_pwrite(SB) -TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 JMP libc_read(SB) -TEXT ·libc_readdir_r_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_readdir_r_trampoline(SB),NOSPLIT,$0-0 JMP libc_readdir_r(SB) -TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_readlink(SB) -TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 JMP libc_rename(SB) -TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 JMP libc_revoke(SB) -TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_rmdir(SB) -TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 JMP libc_lseek(SB) -TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 JMP libc_select(SB) -TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setegid(SB) -TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_seteuid(SB) -TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgid(SB) -TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 JMP libc_setlogin(SB) -TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpgid(SB) -TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpriority(SB) -TEXT ·libc_setprivexec_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setprivexec_trampoline(SB),NOSPLIT,$0-0 JMP libc_setprivexec(SB) -TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setregid(SB) -TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setreuid(SB) -TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_setrlimit(SB) -TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsid(SB) -TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_settimeofday(SB) -TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setuid(SB) -TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_symlink(SB) -TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 JMP libc_sync(SB) -TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_truncate(SB) -TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 JMP libc_umask(SB) -TEXT ·libc_undelete_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_undelete_trampoline(SB),NOSPLIT,$0-0 JMP libc_undelete(SB) -TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlink(SB) -TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 JMP libc_unmount(SB) -TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 JMP libc_write(SB) -TEXT ·libc_writev_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_writev_trampoline(SB),NOSPLIT,$0-0 JMP libc_writev(SB) -TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_mmap(SB) -TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_munmap(SB) -TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 JMP libc_fork(SB) -TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) -TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 JMP libc_execve(SB) -TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 JMP libc_exit(SB) -TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_sysctl(SB) -TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlinkat(SB) -TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 JMP libc_openat(SB) -TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 JMP libc_getcwd(SB) -TEXT ·libc_fstat64_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstat64_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstat64(SB) -TEXT ·libc_fstatfs64_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatfs64_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatfs64(SB) -TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_gettimeofday(SB) -TEXT ·libc_lstat64_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lstat64_trampoline(SB),NOSPLIT,$0-0 JMP libc_lstat64(SB) -TEXT ·libc_stat64_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_stat64_trampoline(SB),NOSPLIT,$0-0 JMP libc_stat64(SB) -TEXT ·libc_statfs64_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_statfs64_trampoline(SB),NOSPLIT,$0-0 JMP libc_statfs64(SB) -TEXT ·libc_fstatat64_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatat64_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatat64(SB) -TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 JMP libc_ptrace(SB) diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go index ede0091de2483..0d8598d8165e2 100644 --- a/src/syscall/zsyscall_darwin_arm64.go +++ b/src/syscall/zsyscall_darwin_arm64.go @@ -20,7 +20,6 @@ func getgroups(ngid int, gid *_Gid_t) (n int, err error) { func libc_getgroups_trampoline() -//go:linkname libc_getgroups libc_getgroups //go:cgo_import_dynamic libc_getgroups getgroups "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -35,7 +34,6 @@ func setgroups(ngid int, gid *_Gid_t) (err error) { func libc_setgroups_trampoline() -//go:linkname libc_setgroups libc_setgroups //go:cgo_import_dynamic libc_setgroups setgroups "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -51,7 +49,6 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err func libc_wait4_trampoline() -//go:linkname libc_wait4 libc_wait4 //go:cgo_import_dynamic libc_wait4 wait4 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -67,7 +64,6 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { func libc_accept_trampoline() -//go:linkname libc_accept libc_accept //go:cgo_import_dynamic libc_accept accept "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -82,7 +78,6 @@ func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { func libc_bind_trampoline() -//go:linkname libc_bind libc_bind //go:cgo_import_dynamic libc_bind bind "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -97,7 +92,6 @@ func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { func libc_connect_trampoline() -//go:linkname libc_connect libc_connect //go:cgo_import_dynamic libc_connect connect "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -113,7 +107,6 @@ func socket(domain int, typ int, proto int) (fd int, err error) { func libc_socket_trampoline() -//go:linkname libc_socket libc_socket //go:cgo_import_dynamic libc_socket socket "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -128,7 +121,6 @@ func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen func libc_getsockopt_trampoline() -//go:linkname libc_getsockopt libc_getsockopt //go:cgo_import_dynamic libc_getsockopt getsockopt "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -143,7 +135,6 @@ func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) func libc_setsockopt_trampoline() -//go:linkname libc_setsockopt libc_setsockopt //go:cgo_import_dynamic libc_setsockopt setsockopt "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -158,7 +149,6 @@ func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { func libc_getpeername_trampoline() -//go:linkname libc_getpeername libc_getpeername //go:cgo_import_dynamic libc_getpeername getpeername "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -173,7 +163,6 @@ func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { func libc_getsockname_trampoline() -//go:linkname libc_getsockname libc_getsockname //go:cgo_import_dynamic libc_getsockname getsockname "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -188,7 +177,6 @@ func Shutdown(s int, how int) (err error) { func libc_shutdown_trampoline() -//go:linkname libc_shutdown libc_shutdown //go:cgo_import_dynamic libc_shutdown shutdown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -203,7 +191,6 @@ func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { func libc_socketpair_trampoline() -//go:linkname libc_socketpair libc_socketpair //go:cgo_import_dynamic libc_socketpair socketpair "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -225,7 +212,6 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl func libc_recvfrom_trampoline() -//go:linkname libc_recvfrom libc_recvfrom //go:cgo_import_dynamic libc_recvfrom recvfrom "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -246,7 +232,6 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) ( func libc_sendto_trampoline() -//go:linkname libc_sendto libc_sendto //go:cgo_import_dynamic libc_sendto sendto "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -262,7 +247,6 @@ func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { func libc_recvmsg_trampoline() -//go:linkname libc_recvmsg libc_recvmsg //go:cgo_import_dynamic libc_recvmsg recvmsg "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -278,7 +262,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { func libc_sendmsg_trampoline() -//go:linkname libc_sendmsg libc_sendmsg //go:cgo_import_dynamic libc_sendmsg sendmsg "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -294,7 +277,6 @@ func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, ne func libc_kevent_trampoline() -//go:linkname libc_kevent libc_kevent //go:cgo_import_dynamic libc_kevent kevent "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -314,7 +296,6 @@ func utimes(path string, timeval *[2]Timeval) (err error) { func libc_utimes_trampoline() -//go:linkname libc_utimes libc_utimes //go:cgo_import_dynamic libc_utimes utimes "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -329,7 +310,6 @@ func futimes(fd int, timeval *[2]Timeval) (err error) { func libc_futimes_trampoline() -//go:linkname libc_futimes libc_futimes //go:cgo_import_dynamic libc_futimes futimes "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -345,7 +325,6 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { func libc_fcntl_trampoline() -//go:linkname libc_fcntl libc_fcntl //go:cgo_import_dynamic libc_fcntl fcntl "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -360,7 +339,6 @@ func pipe(p *[2]int32) (err error) { func libc_pipe_trampoline() -//go:linkname libc_pipe libc_pipe //go:cgo_import_dynamic libc_pipe pipe "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -375,7 +353,6 @@ func kill(pid int, signum int, posix int) (err error) { func libc_kill_trampoline() -//go:linkname libc_kill libc_kill //go:cgo_import_dynamic libc_kill kill "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -395,7 +372,6 @@ func Access(path string, mode uint32) (err error) { func libc_access_trampoline() -//go:linkname libc_access libc_access //go:cgo_import_dynamic libc_access access "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -410,7 +386,6 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { func libc_adjtime_trampoline() -//go:linkname libc_adjtime libc_adjtime //go:cgo_import_dynamic libc_adjtime adjtime "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -430,7 +405,6 @@ func Chdir(path string) (err error) { func libc_chdir_trampoline() -//go:linkname libc_chdir libc_chdir //go:cgo_import_dynamic libc_chdir chdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -450,7 +424,6 @@ func Chflags(path string, flags int) (err error) { func libc_chflags_trampoline() -//go:linkname libc_chflags libc_chflags //go:cgo_import_dynamic libc_chflags chflags "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -470,7 +443,6 @@ func Chmod(path string, mode uint32) (err error) { func libc_chmod_trampoline() -//go:linkname libc_chmod libc_chmod //go:cgo_import_dynamic libc_chmod chmod "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -490,7 +462,6 @@ func Chown(path string, uid int, gid int) (err error) { func libc_chown_trampoline() -//go:linkname libc_chown libc_chown //go:cgo_import_dynamic libc_chown chown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -510,7 +481,6 @@ func Chroot(path string) (err error) { func libc_chroot_trampoline() -//go:linkname libc_chroot libc_chroot //go:cgo_import_dynamic libc_chroot chroot "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -525,7 +495,6 @@ func Close(fd int) (err error) { func libc_close_trampoline() -//go:linkname libc_close libc_close //go:cgo_import_dynamic libc_close close "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -540,7 +509,6 @@ func closedir(dir uintptr) (err error) { func libc_closedir_trampoline() -//go:linkname libc_closedir libc_closedir //go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -556,7 +524,6 @@ func Dup(fd int) (nfd int, err error) { func libc_dup_trampoline() -//go:linkname libc_dup libc_dup //go:cgo_import_dynamic libc_dup dup "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -571,7 +538,6 @@ func Dup2(from int, to int) (err error) { func libc_dup2_trampoline() -//go:linkname libc_dup2 libc_dup2 //go:cgo_import_dynamic libc_dup2 dup2 "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -596,7 +562,6 @@ func Exchangedata(path1 string, path2 string, options int) (err error) { func libc_exchangedata_trampoline() -//go:linkname libc_exchangedata libc_exchangedata //go:cgo_import_dynamic libc_exchangedata exchangedata "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -611,7 +576,6 @@ func Fchdir(fd int) (err error) { func libc_fchdir_trampoline() -//go:linkname libc_fchdir libc_fchdir //go:cgo_import_dynamic libc_fchdir fchdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -626,7 +590,6 @@ func Fchflags(fd int, flags int) (err error) { func libc_fchflags_trampoline() -//go:linkname libc_fchflags libc_fchflags //go:cgo_import_dynamic libc_fchflags fchflags "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -641,7 +604,6 @@ func Fchmod(fd int, mode uint32) (err error) { func libc_fchmod_trampoline() -//go:linkname libc_fchmod libc_fchmod //go:cgo_import_dynamic libc_fchmod fchmod "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -656,7 +618,6 @@ func Fchown(fd int, uid int, gid int) (err error) { func libc_fchown_trampoline() -//go:linkname libc_fchown libc_fchown //go:cgo_import_dynamic libc_fchown fchown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -671,7 +632,6 @@ func Flock(fd int, how int) (err error) { func libc_flock_trampoline() -//go:linkname libc_flock libc_flock //go:cgo_import_dynamic libc_flock flock "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -687,7 +647,6 @@ func Fpathconf(fd int, name int) (val int, err error) { func libc_fpathconf_trampoline() -//go:linkname libc_fpathconf libc_fpathconf //go:cgo_import_dynamic libc_fpathconf fpathconf "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -702,7 +661,6 @@ func Fsync(fd int) (err error) { func libc_fsync_trampoline() -//go:linkname libc_fsync libc_fsync //go:cgo_import_dynamic libc_fsync fsync "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -717,7 +675,6 @@ func Ftruncate(fd int, length int64) (err error) { func libc_ftruncate_trampoline() -//go:linkname libc_ftruncate libc_ftruncate //go:cgo_import_dynamic libc_ftruncate ftruncate "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -730,7 +687,6 @@ func Getdtablesize() (size int) { func libc_getdtablesize_trampoline() -//go:linkname libc_getdtablesize libc_getdtablesize //go:cgo_import_dynamic libc_getdtablesize getdtablesize "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -743,7 +699,6 @@ func Getegid() (egid int) { func libc_getegid_trampoline() -//go:linkname libc_getegid libc_getegid //go:cgo_import_dynamic libc_getegid getegid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -756,7 +711,6 @@ func Geteuid() (uid int) { func libc_geteuid_trampoline() -//go:linkname libc_geteuid libc_geteuid //go:cgo_import_dynamic libc_geteuid geteuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -769,7 +723,6 @@ func Getgid() (gid int) { func libc_getgid_trampoline() -//go:linkname libc_getgid libc_getgid //go:cgo_import_dynamic libc_getgid getgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -785,7 +738,6 @@ func Getpgid(pid int) (pgid int, err error) { func libc_getpgid_trampoline() -//go:linkname libc_getpgid libc_getpgid //go:cgo_import_dynamic libc_getpgid getpgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -798,7 +750,6 @@ func Getpgrp() (pgrp int) { func libc_getpgrp_trampoline() -//go:linkname libc_getpgrp libc_getpgrp //go:cgo_import_dynamic libc_getpgrp getpgrp "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -811,7 +762,6 @@ func Getpid() (pid int) { func libc_getpid_trampoline() -//go:linkname libc_getpid libc_getpid //go:cgo_import_dynamic libc_getpid getpid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -824,7 +774,6 @@ func Getppid() (ppid int) { func libc_getppid_trampoline() -//go:linkname libc_getppid libc_getppid //go:cgo_import_dynamic libc_getppid getppid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -840,7 +789,6 @@ func Getpriority(which int, who int) (prio int, err error) { func libc_getpriority_trampoline() -//go:linkname libc_getpriority libc_getpriority //go:cgo_import_dynamic libc_getpriority getpriority "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -855,7 +803,6 @@ func Getrlimit(which int, lim *Rlimit) (err error) { func libc_getrlimit_trampoline() -//go:linkname libc_getrlimit libc_getrlimit //go:cgo_import_dynamic libc_getrlimit getrlimit "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -870,7 +817,6 @@ func Getrusage(who int, rusage *Rusage) (err error) { func libc_getrusage_trampoline() -//go:linkname libc_getrusage libc_getrusage //go:cgo_import_dynamic libc_getrusage getrusage "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -886,7 +832,6 @@ func Getsid(pid int) (sid int, err error) { func libc_getsid_trampoline() -//go:linkname libc_getsid libc_getsid //go:cgo_import_dynamic libc_getsid getsid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -899,7 +844,6 @@ func Getuid() (uid int) { func libc_getuid_trampoline() -//go:linkname libc_getuid libc_getuid //go:cgo_import_dynamic libc_getuid getuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -912,7 +856,6 @@ func Issetugid() (tainted bool) { func libc_issetugid_trampoline() -//go:linkname libc_issetugid libc_issetugid //go:cgo_import_dynamic libc_issetugid issetugid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -928,7 +871,6 @@ func Kqueue() (fd int, err error) { func libc_kqueue_trampoline() -//go:linkname libc_kqueue libc_kqueue //go:cgo_import_dynamic libc_kqueue kqueue "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -948,7 +890,6 @@ func Lchown(path string, uid int, gid int) (err error) { func libc_lchown_trampoline() -//go:linkname libc_lchown libc_lchown //go:cgo_import_dynamic libc_lchown lchown "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -973,7 +914,6 @@ func Link(path string, link string) (err error) { func libc_link_trampoline() -//go:linkname libc_link libc_link //go:cgo_import_dynamic libc_link link "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -988,7 +928,6 @@ func Listen(s int, backlog int) (err error) { func libc_listen_trampoline() -//go:linkname libc_listen libc_listen //go:cgo_import_dynamic libc_listen listen "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1008,7 +947,6 @@ func Mkdir(path string, mode uint32) (err error) { func libc_mkdir_trampoline() -//go:linkname libc_mkdir libc_mkdir //go:cgo_import_dynamic libc_mkdir mkdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1028,7 +966,6 @@ func Mkfifo(path string, mode uint32) (err error) { func libc_mkfifo_trampoline() -//go:linkname libc_mkfifo libc_mkfifo //go:cgo_import_dynamic libc_mkfifo mkfifo "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1048,7 +985,6 @@ func Mknod(path string, mode uint32, dev int) (err error) { func libc_mknod_trampoline() -//go:linkname libc_mknod libc_mknod //go:cgo_import_dynamic libc_mknod mknod "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1069,7 +1005,6 @@ func Mlock(b []byte) (err error) { func libc_mlock_trampoline() -//go:linkname libc_mlock libc_mlock //go:cgo_import_dynamic libc_mlock mlock "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1084,7 +1019,6 @@ func Mlockall(flags int) (err error) { func libc_mlockall_trampoline() -//go:linkname libc_mlockall libc_mlockall //go:cgo_import_dynamic libc_mlockall mlockall "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1105,7 +1039,6 @@ func Mprotect(b []byte, prot int) (err error) { func libc_mprotect_trampoline() -//go:linkname libc_mprotect libc_mprotect //go:cgo_import_dynamic libc_mprotect mprotect "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1126,7 +1059,6 @@ func Munlock(b []byte) (err error) { func libc_munlock_trampoline() -//go:linkname libc_munlock libc_munlock //go:cgo_import_dynamic libc_munlock munlock "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1141,7 +1073,6 @@ func Munlockall() (err error) { func libc_munlockall_trampoline() -//go:linkname libc_munlockall libc_munlockall //go:cgo_import_dynamic libc_munlockall munlockall "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1162,7 +1093,6 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { func libc_open_trampoline() -//go:linkname libc_open libc_open //go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1183,7 +1113,6 @@ func Pathconf(path string, name int) (val int, err error) { func libc_pathconf_trampoline() -//go:linkname libc_pathconf libc_pathconf //go:cgo_import_dynamic libc_pathconf pathconf "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1205,7 +1134,6 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { func libc_pread_trampoline() -//go:linkname libc_pread libc_pread //go:cgo_import_dynamic libc_pread pread "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1227,7 +1155,6 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { func libc_pwrite_trampoline() -//go:linkname libc_pwrite libc_pwrite //go:cgo_import_dynamic libc_pwrite pwrite "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1249,7 +1176,6 @@ func read(fd int, p []byte) (n int, err error) { func libc_read_trampoline() -//go:linkname libc_read libc_read //go:cgo_import_dynamic libc_read read "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1262,7 +1188,6 @@ func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) { func libc_readdir_r_trampoline() -//go:linkname libc_readdir_r libc_readdir_r //go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1289,7 +1214,6 @@ func Readlink(path string, buf []byte) (n int, err error) { func libc_readlink_trampoline() -//go:linkname libc_readlink libc_readlink //go:cgo_import_dynamic libc_readlink readlink "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1314,7 +1238,6 @@ func Rename(from string, to string) (err error) { func libc_rename_trampoline() -//go:linkname libc_rename libc_rename //go:cgo_import_dynamic libc_rename rename "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1334,7 +1257,6 @@ func Revoke(path string) (err error) { func libc_revoke_trampoline() -//go:linkname libc_revoke libc_revoke //go:cgo_import_dynamic libc_revoke revoke "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1354,7 +1276,6 @@ func Rmdir(path string) (err error) { func libc_rmdir_trampoline() -//go:linkname libc_rmdir libc_rmdir //go:cgo_import_dynamic libc_rmdir rmdir "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1370,7 +1291,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { func libc_lseek_trampoline() -//go:linkname libc_lseek libc_lseek //go:cgo_import_dynamic libc_lseek lseek "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1385,7 +1305,6 @@ func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) { func libc_select_trampoline() -//go:linkname libc_select libc_select //go:cgo_import_dynamic libc_select select "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1400,7 +1319,6 @@ func Setegid(egid int) (err error) { func libc_setegid_trampoline() -//go:linkname libc_setegid libc_setegid //go:cgo_import_dynamic libc_setegid setegid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1415,7 +1333,6 @@ func Seteuid(euid int) (err error) { func libc_seteuid_trampoline() -//go:linkname libc_seteuid libc_seteuid //go:cgo_import_dynamic libc_seteuid seteuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1430,7 +1347,6 @@ func Setgid(gid int) (err error) { func libc_setgid_trampoline() -//go:linkname libc_setgid libc_setgid //go:cgo_import_dynamic libc_setgid setgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1450,7 +1366,6 @@ func Setlogin(name string) (err error) { func libc_setlogin_trampoline() -//go:linkname libc_setlogin libc_setlogin //go:cgo_import_dynamic libc_setlogin setlogin "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1465,7 +1380,6 @@ func Setpgid(pid int, pgid int) (err error) { func libc_setpgid_trampoline() -//go:linkname libc_setpgid libc_setpgid //go:cgo_import_dynamic libc_setpgid setpgid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1480,7 +1394,6 @@ func Setpriority(which int, who int, prio int) (err error) { func libc_setpriority_trampoline() -//go:linkname libc_setpriority libc_setpriority //go:cgo_import_dynamic libc_setpriority setpriority "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1495,7 +1408,6 @@ func Setprivexec(flag int) (err error) { func libc_setprivexec_trampoline() -//go:linkname libc_setprivexec libc_setprivexec //go:cgo_import_dynamic libc_setprivexec setprivexec "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1510,7 +1422,6 @@ func Setregid(rgid int, egid int) (err error) { func libc_setregid_trampoline() -//go:linkname libc_setregid libc_setregid //go:cgo_import_dynamic libc_setregid setregid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1525,7 +1436,6 @@ func Setreuid(ruid int, euid int) (err error) { func libc_setreuid_trampoline() -//go:linkname libc_setreuid libc_setreuid //go:cgo_import_dynamic libc_setreuid setreuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1540,7 +1450,6 @@ func Setrlimit(which int, lim *Rlimit) (err error) { func libc_setrlimit_trampoline() -//go:linkname libc_setrlimit libc_setrlimit //go:cgo_import_dynamic libc_setrlimit setrlimit "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1556,7 +1465,6 @@ func Setsid() (pid int, err error) { func libc_setsid_trampoline() -//go:linkname libc_setsid libc_setsid //go:cgo_import_dynamic libc_setsid setsid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1571,7 +1479,6 @@ func Settimeofday(tp *Timeval) (err error) { func libc_settimeofday_trampoline() -//go:linkname libc_settimeofday libc_settimeofday //go:cgo_import_dynamic libc_settimeofday settimeofday "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1586,7 +1493,6 @@ func Setuid(uid int) (err error) { func libc_setuid_trampoline() -//go:linkname libc_setuid libc_setuid //go:cgo_import_dynamic libc_setuid setuid "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1611,7 +1517,6 @@ func Symlink(path string, link string) (err error) { func libc_symlink_trampoline() -//go:linkname libc_symlink libc_symlink //go:cgo_import_dynamic libc_symlink symlink "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1626,7 +1531,6 @@ func Sync() (err error) { func libc_sync_trampoline() -//go:linkname libc_sync libc_sync //go:cgo_import_dynamic libc_sync sync "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1646,7 +1550,6 @@ func Truncate(path string, length int64) (err error) { func libc_truncate_trampoline() -//go:linkname libc_truncate libc_truncate //go:cgo_import_dynamic libc_truncate truncate "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1659,7 +1562,6 @@ func Umask(newmask int) (oldmask int) { func libc_umask_trampoline() -//go:linkname libc_umask libc_umask //go:cgo_import_dynamic libc_umask umask "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1679,7 +1581,6 @@ func Undelete(path string) (err error) { func libc_undelete_trampoline() -//go:linkname libc_undelete libc_undelete //go:cgo_import_dynamic libc_undelete undelete "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1699,7 +1600,6 @@ func Unlink(path string) (err error) { func libc_unlink_trampoline() -//go:linkname libc_unlink libc_unlink //go:cgo_import_dynamic libc_unlink unlink "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1719,7 +1619,6 @@ func Unmount(path string, flags int) (err error) { func libc_unmount_trampoline() -//go:linkname libc_unmount libc_unmount //go:cgo_import_dynamic libc_unmount unmount "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1741,7 +1640,6 @@ func write(fd int, p []byte) (n int, err error) { func libc_write_trampoline() -//go:linkname libc_write libc_write //go:cgo_import_dynamic libc_write write "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1763,7 +1661,6 @@ func writev(fd int, iovecs []Iovec) (cnt uintptr, err error) { func libc_writev_trampoline() -//go:linkname libc_writev libc_writev //go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1779,7 +1676,6 @@ func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) ( func libc_mmap_trampoline() -//go:linkname libc_mmap libc_mmap //go:cgo_import_dynamic libc_mmap mmap "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1794,7 +1690,6 @@ func munmap(addr uintptr, length uintptr) (err error) { func libc_munmap_trampoline() -//go:linkname libc_munmap libc_munmap //go:cgo_import_dynamic libc_munmap munmap "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1810,7 +1705,6 @@ func fork() (pid int, err error) { func libc_fork_trampoline() -//go:linkname libc_fork libc_fork //go:cgo_import_dynamic libc_fork fork "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1825,7 +1719,6 @@ func ioctl(fd int, req int, arg int) (err error) { func libc_ioctl_trampoline() -//go:linkname libc_ioctl libc_ioctl //go:cgo_import_dynamic libc_ioctl ioctl "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1850,7 +1743,6 @@ func execve(path *byte, argv **byte, envp **byte) (err error) { func libc_execve_trampoline() -//go:linkname libc_execve libc_execve //go:cgo_import_dynamic libc_execve execve "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1865,7 +1757,6 @@ func exit(res int) (err error) { func libc_exit_trampoline() -//go:linkname libc_exit libc_exit //go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1886,7 +1777,6 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) func libc_sysctl_trampoline() -//go:linkname libc_sysctl libc_sysctl //go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1917,7 +1807,6 @@ func unlinkat(fd int, path string, flags int) (err error) { func libc_unlinkat_trampoline() -//go:linkname libc_unlinkat libc_unlinkat //go:cgo_import_dynamic libc_unlinkat unlinkat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1938,7 +1827,6 @@ func openat(fd int, path string, flags int, perm uint32) (fdret int, err error) func libc_openat_trampoline() -//go:linkname libc_openat libc_openat //go:cgo_import_dynamic libc_openat openat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1960,7 +1848,6 @@ func getcwd(buf []byte) (n int, err error) { func libc_getcwd_trampoline() -//go:linkname libc_getcwd libc_getcwd //go:cgo_import_dynamic libc_getcwd getcwd "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1975,7 +1862,6 @@ func Fstat(fd int, stat *Stat_t) (err error) { func libc_fstat_trampoline() -//go:linkname libc_fstat libc_fstat //go:cgo_import_dynamic libc_fstat fstat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1990,7 +1876,6 @@ func Fstatfs(fd int, stat *Statfs_t) (err error) { func libc_fstatfs_trampoline() -//go:linkname libc_fstatfs libc_fstatfs //go:cgo_import_dynamic libc_fstatfs fstatfs "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2005,7 +1890,6 @@ func Gettimeofday(tp *Timeval) (err error) { func libc_gettimeofday_trampoline() -//go:linkname libc_gettimeofday libc_gettimeofday //go:cgo_import_dynamic libc_gettimeofday gettimeofday "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2025,7 +1909,6 @@ func Lstat(path string, stat *Stat_t) (err error) { func libc_lstat_trampoline() -//go:linkname libc_lstat libc_lstat //go:cgo_import_dynamic libc_lstat lstat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2045,7 +1928,6 @@ func Stat(path string, stat *Stat_t) (err error) { func libc_stat_trampoline() -//go:linkname libc_stat libc_stat //go:cgo_import_dynamic libc_stat stat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2065,7 +1947,6 @@ func Statfs(path string, stat *Statfs_t) (err error) { func libc_statfs_trampoline() -//go:linkname libc_statfs libc_statfs //go:cgo_import_dynamic libc_statfs statfs "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2085,7 +1966,6 @@ func fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { func libc_fstatat_trampoline() -//go:linkname libc_fstatat libc_fstatat //go:cgo_import_dynamic libc_fstatat fstatat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2101,5 +1981,4 @@ func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) { func libc_ptrace_trampoline() -//go:linkname libc_ptrace libc_ptrace //go:cgo_import_dynamic libc_ptrace ptrace "/usr/lib/libSystem.B.dylib" diff --git a/src/syscall/zsyscall_darwin_arm64.s b/src/syscall/zsyscall_darwin_arm64.s index b606c6e49e35b..73e4a3fd8dff2 100644 --- a/src/syscall/zsyscall_darwin_arm64.s +++ b/src/syscall/zsyscall_darwin_arm64.s @@ -1,253 +1,253 @@ // go run mkasm.go darwin arm64 // Code generated by the command above; DO NOT EDIT. #include "textflag.h" -TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_getfsstat(SB) -TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0 JMP libc_setattrlist(SB) -TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fdopendir(SB) -TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) -TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgroups(SB) -TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgroups(SB) -TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 JMP libc_wait4(SB) -TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 JMP libc_accept(SB) -TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 JMP libc_bind(SB) -TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 JMP libc_connect(SB) -TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 JMP libc_socket(SB) -TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockopt(SB) -TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsockopt(SB) -TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpeername(SB) -TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockname(SB) -TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 JMP libc_shutdown(SB) -TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 JMP libc_socketpair(SB) -TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvfrom(SB) -TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendto(SB) -TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvmsg(SB) -TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendmsg(SB) -TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 JMP libc_kevent(SB) -TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_utimes(SB) -TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_futimes(SB) -TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) -TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0 JMP libc_pipe(SB) -TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 JMP libc_kill(SB) -TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 JMP libc_access(SB) -TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 JMP libc_adjtime(SB) -TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_chdir(SB) -TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_chflags(SB) -TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_chmod(SB) -TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 JMP libc_chown(SB) -TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 JMP libc_chroot(SB) -TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 JMP libc_close(SB) -TEXT ·libc_closedir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_closedir_trampoline(SB),NOSPLIT,$0-0 JMP libc_closedir(SB) -TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) -TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) -TEXT ·libc_exchangedata_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_exchangedata_trampoline(SB),NOSPLIT,$0-0 JMP libc_exchangedata(SB) -TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) -TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchflags(SB) -TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchmod(SB) -TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchown(SB) -TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 JMP libc_flock(SB) -TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_fpathconf(SB) -TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 JMP libc_fsync(SB) -TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_ftruncate(SB) -TEXT ·libc_getdtablesize_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getdtablesize_trampoline(SB),NOSPLIT,$0-0 JMP libc_getdtablesize(SB) -TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getegid(SB) -TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_geteuid(SB) -TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgid(SB) -TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgid(SB) -TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgrp(SB) -TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpid(SB) -TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getppid(SB) -TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpriority(SB) -TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrlimit(SB) -TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrusage(SB) -TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsid(SB) -TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getuid(SB) -TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 JMP libc_issetugid(SB) -TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 JMP libc_kqueue(SB) -TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_lchown(SB) -TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 JMP libc_link(SB) -TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 JMP libc_listen(SB) -TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkdir(SB) -TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkfifo(SB) -TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 JMP libc_mknod(SB) -TEXT ·libc_mlock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mlock_trampoline(SB),NOSPLIT,$0-0 JMP libc_mlock(SB) -TEXT ·libc_mlockall_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mlockall_trampoline(SB),NOSPLIT,$0-0 JMP libc_mlockall(SB) -TEXT ·libc_mprotect_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mprotect_trampoline(SB),NOSPLIT,$0-0 JMP libc_mprotect(SB) -TEXT ·libc_munlock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munlock_trampoline(SB),NOSPLIT,$0-0 JMP libc_munlock(SB) -TEXT ·libc_munlockall_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munlockall_trampoline(SB),NOSPLIT,$0-0 JMP libc_munlockall(SB) -TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 JMP libc_open(SB) -TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_pathconf(SB) -TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 JMP libc_pread(SB) -TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 JMP libc_pwrite(SB) -TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 JMP libc_read(SB) -TEXT ·libc_readdir_r_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_readdir_r_trampoline(SB),NOSPLIT,$0-0 JMP libc_readdir_r(SB) -TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_readlink(SB) -TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 JMP libc_rename(SB) -TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 JMP libc_revoke(SB) -TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_rmdir(SB) -TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 JMP libc_lseek(SB) -TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 JMP libc_select(SB) -TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setegid(SB) -TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_seteuid(SB) -TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgid(SB) -TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 JMP libc_setlogin(SB) -TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpgid(SB) -TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpriority(SB) -TEXT ·libc_setprivexec_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setprivexec_trampoline(SB),NOSPLIT,$0-0 JMP libc_setprivexec(SB) -TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setregid(SB) -TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setreuid(SB) -TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_setrlimit(SB) -TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsid(SB) -TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_settimeofday(SB) -TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setuid(SB) -TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_symlink(SB) -TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 JMP libc_sync(SB) -TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_truncate(SB) -TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 JMP libc_umask(SB) -TEXT ·libc_undelete_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_undelete_trampoline(SB),NOSPLIT,$0-0 JMP libc_undelete(SB) -TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlink(SB) -TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 JMP libc_unmount(SB) -TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 JMP libc_write(SB) -TEXT ·libc_writev_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_writev_trampoline(SB),NOSPLIT,$0-0 JMP libc_writev(SB) -TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_mmap(SB) -TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_munmap(SB) -TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 JMP libc_fork(SB) -TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) -TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 JMP libc_execve(SB) -TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 JMP libc_exit(SB) -TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_sysctl(SB) -TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlinkat(SB) -TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 JMP libc_openat(SB) -TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 JMP libc_getcwd(SB) -TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstat(SB) -TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatfs(SB) -TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_gettimeofday(SB) -TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_lstat(SB) -TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0 JMP libc_stat(SB) -TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0 JMP libc_statfs(SB) -TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatat(SB) -TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 JMP libc_ptrace(SB) diff --git a/src/syscall/zsyscall_openbsd_amd64.s b/src/syscall/zsyscall_openbsd_amd64.s index e5c5dde930bb6..8256a451f5f86 100644 --- a/src/syscall/zsyscall_openbsd_amd64.s +++ b/src/syscall/zsyscall_openbsd_amd64.s @@ -1,233 +1,233 @@ // go run mkasm.go openbsd amd64 // Code generated by the command above; DO NOT EDIT. #include "textflag.h" -TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgroups(SB) -TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgroups(SB) -TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 JMP libc_wait4(SB) -TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 JMP libc_accept(SB) -TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 JMP libc_bind(SB) -TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 JMP libc_connect(SB) -TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 JMP libc_socket(SB) -TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockopt(SB) -TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsockopt(SB) -TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpeername(SB) -TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockname(SB) -TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 JMP libc_shutdown(SB) -TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 JMP libc_socketpair(SB) -TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvfrom(SB) -TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendto(SB) -TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvmsg(SB) -TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendmsg(SB) -TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 JMP libc_kevent(SB) -TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_utimes(SB) -TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_futimes(SB) -TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) -TEXT ·libc_pipe2_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pipe2_trampoline(SB),NOSPLIT,$0-0 JMP libc_pipe2(SB) -TEXT ·libc_accept4_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_accept4_trampoline(SB),NOSPLIT,$0-0 JMP libc_accept4(SB) -TEXT ·libc_getdents_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getdents_trampoline(SB),NOSPLIT,$0-0 JMP libc_getdents(SB) -TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 JMP libc_access(SB) -TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 JMP libc_adjtime(SB) -TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_chdir(SB) -TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_chflags(SB) -TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_chmod(SB) -TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 JMP libc_chown(SB) -TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 JMP libc_chroot(SB) -TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 JMP libc_close(SB) -TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) -TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) -TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) -TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchflags(SB) -TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchmod(SB) -TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchown(SB) -TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 JMP libc_flock(SB) -TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_fpathconf(SB) -TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstat(SB) -TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatfs(SB) -TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 JMP libc_fsync(SB) -TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_ftruncate(SB) -TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getegid(SB) -TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_geteuid(SB) -TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgid(SB) -TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgid(SB) -TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgrp(SB) -TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpid(SB) -TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getppid(SB) -TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpriority(SB) -TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrlimit(SB) -TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrusage(SB) -TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsid(SB) -TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_gettimeofday(SB) -TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getuid(SB) -TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 JMP libc_issetugid(SB) -TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 JMP libc_kill(SB) -TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 JMP libc_kqueue(SB) -TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_lchown(SB) -TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 JMP libc_link(SB) -TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 JMP libc_listen(SB) -TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_lstat(SB) -TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkdir(SB) -TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkfifo(SB) -TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 JMP libc_mknod(SB) -TEXT ·libc_nanosleep_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_nanosleep_trampoline(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) -TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 JMP libc_open(SB) -TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_pathconf(SB) -TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 JMP libc_pread(SB) -TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 JMP libc_pwrite(SB) -TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 JMP libc_read(SB) -TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_readlink(SB) -TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 JMP libc_rename(SB) -TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 JMP libc_revoke(SB) -TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_rmdir(SB) -TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 JMP libc_select(SB) -TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setegid(SB) -TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_seteuid(SB) -TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgid(SB) -TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 JMP libc_setlogin(SB) -TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpgid(SB) -TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpriority(SB) -TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setregid(SB) -TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setreuid(SB) -TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_setrlimit(SB) -TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsid(SB) -TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_settimeofday(SB) -TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setuid(SB) -TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0 JMP libc_stat(SB) -TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0 JMP libc_statfs(SB) -TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_symlink(SB) -TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 JMP libc_sync(SB) -TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_truncate(SB) -TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 JMP libc_umask(SB) -TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlink(SB) -TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 JMP libc_unmount(SB) -TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 JMP libc_write(SB) -TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_mmap(SB) -TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_munmap(SB) -TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) -TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0 JMP libc_syscall(SB) -TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 JMP libc_lseek(SB) -TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 JMP libc_getcwd(SB) -TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_sysctl(SB) -TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 JMP libc_fork(SB) -TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) -TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 JMP libc_execve(SB) -TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 JMP libc_exit(SB) -TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 JMP libc_ptrace(SB) -TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0 JMP libc_getentropy(SB) -TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatat(SB) -TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlinkat(SB) -TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 JMP libc_openat(SB) diff --git a/src/syscall/zsyscall_openbsd_arm64.s b/src/syscall/zsyscall_openbsd_arm64.s index 37778b1db5417..f6e0a8da94c4f 100644 --- a/src/syscall/zsyscall_openbsd_arm64.s +++ b/src/syscall/zsyscall_openbsd_arm64.s @@ -1,233 +1,233 @@ // go run mkasm.go openbsd arm64 // Code generated by the command above; DO NOT EDIT. #include "textflag.h" -TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgroups(SB) -TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgroups(SB) -TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0 JMP libc_wait4(SB) -TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0 JMP libc_accept(SB) -TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0 JMP libc_bind(SB) -TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0 JMP libc_connect(SB) -TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0 JMP libc_socket(SB) -TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockopt(SB) -TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsockopt(SB) -TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpeername(SB) -TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsockname(SB) -TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0 JMP libc_shutdown(SB) -TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0 JMP libc_socketpair(SB) -TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvfrom(SB) -TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendto(SB) -TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_recvmsg(SB) -TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0 JMP libc_sendmsg(SB) -TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0 JMP libc_kevent(SB) -TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_utimes(SB) -TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0 JMP libc_futimes(SB) -TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) -TEXT ·libc_pipe2_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pipe2_trampoline(SB),NOSPLIT,$0-0 JMP libc_pipe2(SB) -TEXT ·libc_accept4_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_accept4_trampoline(SB),NOSPLIT,$0-0 JMP libc_accept4(SB) -TEXT ·libc_getdents_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getdents_trampoline(SB),NOSPLIT,$0-0 JMP libc_getdents(SB) -TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 JMP libc_access(SB) -TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0 JMP libc_adjtime(SB) -TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_chdir(SB) -TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_chflags(SB) -TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_chmod(SB) -TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0 JMP libc_chown(SB) -TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0 JMP libc_chroot(SB) -TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0 JMP libc_close(SB) -TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) -TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) -TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) -TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchflags(SB) -TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchmod(SB) -TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchown(SB) -TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0 JMP libc_flock(SB) -TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_fpathconf(SB) -TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstat(SB) -TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatfs(SB) -TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0 JMP libc_fsync(SB) -TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_ftruncate(SB) -TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getegid(SB) -TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_geteuid(SB) -TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getgid(SB) -TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgid(SB) -TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpgrp(SB) -TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpid(SB) -TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getppid(SB) -TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_getpriority(SB) -TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrlimit(SB) -TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0 JMP libc_getrusage(SB) -TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getsid(SB) -TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_gettimeofday(SB) -TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_getuid(SB) -TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0 JMP libc_issetugid(SB) -TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 JMP libc_kill(SB) -TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0 JMP libc_kqueue(SB) -TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0 JMP libc_lchown(SB) -TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0 JMP libc_link(SB) -TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0 JMP libc_listen(SB) -TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_lstat(SB) -TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkdir(SB) -TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkfifo(SB) -TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0 JMP libc_mknod(SB) -TEXT ·libc_nanosleep_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_nanosleep_trampoline(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) -TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0 JMP libc_open(SB) -TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0 JMP libc_pathconf(SB) -TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0 JMP libc_pread(SB) -TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0 JMP libc_pwrite(SB) -TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0 JMP libc_read(SB) -TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_readlink(SB) -TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0 JMP libc_rename(SB) -TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0 JMP libc_revoke(SB) -TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_rmdir(SB) -TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0 JMP libc_select(SB) -TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setegid(SB) -TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_seteuid(SB) -TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setgid(SB) -TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0 JMP libc_setlogin(SB) -TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpgid(SB) -TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0 JMP libc_setpriority(SB) -TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setregid(SB) -TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setreuid(SB) -TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0 JMP libc_setrlimit(SB) -TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setsid(SB) -TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0 JMP libc_settimeofday(SB) -TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0 JMP libc_setuid(SB) -TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0 JMP libc_stat(SB) -TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0 JMP libc_statfs(SB) -TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_symlink(SB) -TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0 JMP libc_sync(SB) -TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0 JMP libc_truncate(SB) -TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0 JMP libc_umask(SB) -TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlink(SB) -TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0 JMP libc_unmount(SB) -TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0 JMP libc_write(SB) -TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_mmap(SB) -TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0 JMP libc_munmap(SB) -TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) -TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0 JMP libc_syscall(SB) -TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0 JMP libc_lseek(SB) -TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0 JMP libc_getcwd(SB) -TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_sysctl(SB) -TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0 JMP libc_fork(SB) -TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) -TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0 JMP libc_execve(SB) -TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0 JMP libc_exit(SB) -TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0 JMP libc_ptrace(SB) -TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0 JMP libc_getentropy(SB) -TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0 JMP libc_fstatat(SB) -TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0 JMP libc_unlinkat(SB) -TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 +TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0 JMP libc_openat(SB) diff --git a/test/abi/regabipragma.dir/main.go b/test/abi/regabipragma.dir/main.go new file mode 100644 index 0000000000000..d663337a10315 --- /dev/null +++ b/test/abi/regabipragma.dir/main.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 main + +import ( + "fmt" + "regabipragma.dir/tmp" +) + +type S string + +//go:noinline +func (s S) ff(t string) string { + return string(s) + " " + t +} + +//go:noinline +//go:registerparams +func f(s,t string) string { // ERROR "Declared function f has register params" + return s + " " + t +} + +func check(s string) { + if s != "Hello world!" { + fmt.Printf("FAIL, wanted 'Hello world!' but got '%s'\n", s) + } +} + +func main() { + check(f("Hello", "world!")) // ERROR "Called function ...f has register params" + check(tmp.F("Hello", "world!")) // ERROR "Called function regabipragma.dir/tmp.F has register params" + check(S("Hello").ff("world!")) + check(tmp.S("Hello").FF("world!")) +} diff --git a/test/abi/regabipragma.dir/tmp/foo.go b/test/abi/regabipragma.dir/tmp/foo.go new file mode 100644 index 0000000000000..cff989bbcdba9 --- /dev/null +++ b/test/abi/regabipragma.dir/tmp/foo.go @@ -0,0 +1,19 @@ +// 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 tmp + + +type S string + +//go:noinline +func (s S) FF(t string) string { + return string(s) + " " + t +} + +//go:noinline +//go:registerparams +func F(s,t string) string { + return s + " " + t +} diff --git a/test/abi/regabipragma.go b/test/abi/regabipragma.go new file mode 100644 index 0000000000000..e7ecd58fc8937 --- /dev/null +++ b/test/abi/regabipragma.go @@ -0,0 +1,10 @@ +// runindir -gcflags=-c=1 +// +build !windows + +// 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. + +// TODO May delete or adapt this test once regabi is the default + +package ignore diff --git a/test/abi/regabipragma.out b/test/abi/regabipragma.out new file mode 100644 index 0000000000000..321b1adfccc1a --- /dev/null +++ b/test/abi/regabipragma.out @@ -0,0 +1,6 @@ +# regabipragma.dir/tmp +tmp/foo.go:17:6: declared function F has register params +# regabipragma.dir +./main.go:21:6: declared function f has register params +./main.go:32:9: called function f has register params +./main.go:33:13: called function tmp.F has register params diff --git a/test/closure2.go b/test/closure2.go index e4db05d8846ce..812d41f8ce02c 100644 --- a/test/closure2.go +++ b/test/closure2.go @@ -9,6 +9,8 @@ package main +var never bool + func main() { { type X struct { @@ -115,4 +117,16 @@ func main() { panic("g() != 2") } } + + { + var g func() int + q := 0 + q, g = 1, func() int { return q } + if never { + g = func() int { return 2 } + } + if g() != 1 { + panic("g() != 1") + } + } } diff --git a/test/closure3.dir/main.go b/test/closure3.dir/main.go index 5694673f1ec64..2fc33753ed7f8 100644 --- a/test/closure3.dir/main.go +++ b/test/closure3.dir/main.go @@ -93,11 +93,11 @@ func main() { y := func(x int) int { // ERROR "can inline main.func11" "func literal does not escape" return x + 2 } - y, sink = func() (func(int) int, int) { // ERROR "func literal does not escape" - return func(x int) int { // ERROR "can inline main.func12" "func literal escapes" + y, sink = func() (func(int) int, int) { // ERROR "can inline main.func12" + return func(x int) int { // ERROR "can inline main.func12" return x + 1 }, 42 - }() + }() // ERROR "func literal does not escape" "inlining call to main.func12" if y(40) != 41 { ppanic("y(40) != 41") } @@ -105,14 +105,14 @@ func main() { { func() { // ERROR "func literal does not escape" - y := func(x int) int { // ERROR "can inline main.func13.1" "func literal does not escape" + y := func(x int) int { // ERROR "func literal does not escape" "can inline main.func13.1" return x + 2 } - y, sink = func() (func(int) int, int) { // ERROR "func literal does not escape" - return func(x int) int { // ERROR "can inline main.func13.2" "func literal escapes" + y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2" + return func(x int) int { // ERROR "can inline main.func13.2" return x + 1 }, 42 - }() + }() // ERROR "inlining call to main.func13.2" "func literal does not escape" if y(40) != 41 { ppanic("y(40) != 41") } @@ -187,29 +187,29 @@ func main() { { x := 42 - if z := func(y int) int { // ERROR "func literal does not escape" - return func() int { // ERROR "can inline main.func22.1" + if z := func(y int) int { // ERROR "can inline main.func22" + return func() int { // ERROR "can inline main.func22.1" "can inline main.func30" return x + y }() // ERROR "inlining call to main.func22.1" - }(1); z != 43 { + }(1); z != 43 { // ERROR "inlining call to main.func22" "inlining call to main.func30" ppanic("z != 43") } - if z := func(y int) int { // ERROR "func literal does not escape" - return func() int { // ERROR "can inline main.func23.1" + if z := func(y int) int { // ERROR "func literal does not escape" "can inline main.func23" + return func() int { // ERROR "can inline main.func23.1" "can inline main.func31" return x + y }() // ERROR "inlining call to main.func23.1" - }; z(1) != 43 { + }; z(1) != 43 { // ERROR "inlining call to main.func23" "inlining call to main.func31" ppanic("z(1) != 43") } } { a := 1 - func() { // ERROR "func literal does not escape" - func() { // ERROR "can inline main.func24" + func() { // ERROR "can inline main.func24" + func() { // ERROR "can inline main.func24" "can inline main.func32" a = 2 }() // ERROR "inlining call to main.func24" - }() + }() // ERROR "inlining call to main.func24" "inlining call to main.func32" if a != 2 { ppanic("a != 2") } @@ -250,12 +250,12 @@ func main() { a := 2 if r := func(x int) int { // ERROR "func literal does not escape" b := 3 - return func(y int) int { // ERROR "func literal does not escape" + return func(y int) int { // ERROR "can inline main.func27.1" c := 5 - return func(z int) int { // ERROR "can inline main.func27.1.1" + return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.func27.2" return a*x + b*y + c*z }(10) // ERROR "inlining call to main.func27.1.1" - }(100) + }(100) // ERROR "inlining call to main.func27.1" "inlining call to main.func27.2" }(1000); r != 2350 { ppanic("r != 2350") } @@ -265,15 +265,15 @@ func main() { a := 2 if r := func(x int) int { // ERROR "func literal does not escape" b := 3 - return func(y int) int { // ERROR "func literal does not escape" + return func(y int) int { // ERROR "can inline main.func28.1" c := 5 - func(z int) { // ERROR "can inline main.func28.1.1" + func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.2" a = a * x b = b * y c = c * z }(10) // ERROR "inlining call to main.func28.1.1" return a + c - }(100) + b + }(100) + b // ERROR "inlining call to main.func28.1" "inlining call to main.func28.2" }(1000); r != 2350 { ppanic("r != 2350") } @@ -285,5 +285,5 @@ func main() { //go:noinline func ppanic(s string) { // ERROR "leaking param: s" - panic(s) + panic(s) // ERROR "s escapes to heap" } diff --git a/test/closure5.dir/a.go b/test/closure5.dir/a.go new file mode 100644 index 0000000000000..de8082b7b17a7 --- /dev/null +++ b/test/closure5.dir/a.go @@ -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 correctness of various closure corner cases +// that are expected to be inlined + +package a + +func f() bool { return true } +func G() func() func() bool { return func() func() bool { return f } } diff --git a/test/closure5.dir/main.go b/test/closure5.dir/main.go new file mode 100644 index 0000000000000..ee5dba648156c --- /dev/null +++ b/test/closure5.dir/main.go @@ -0,0 +1,15 @@ +// 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 correctness of various closure corner cases +// that are expected to be inlined +package main + +import "a" + +func main() { + if !a.G()()() { + panic("FAIL") + } +} diff --git a/test/closure5.go b/test/closure5.go new file mode 100644 index 0000000000000..a7022b27a68f7 --- /dev/null +++ b/test/closure5.go @@ -0,0 +1,10 @@ +// compiledir + +// 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 correctness of various closure corner cases +// that are expected to be inlined + +package ignored diff --git a/test/closure6.go b/test/closure6.go new file mode 100644 index 0000000000000..b5592ad3d3e07 --- /dev/null +++ b/test/closure6.go @@ -0,0 +1,18 @@ +// compile + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Float64Slice []float64 + +func (a Float64Slice) Search1(x float64) int { + f := func(q int) bool { return a[q] >= x } + i := 0 + if !f(3) { + i = 5 + } + return i +} diff --git a/test/closure7.go b/test/closure7.go new file mode 100644 index 0000000000000..823333f45fd25 --- /dev/null +++ b/test/closure7.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func g(f func()) { +} + +// Must have exportable name +func F() { + g(func() { + ch := make(chan int) + for { + select { + case <-ch: + return + default: + } + } + }) +} + +func main() { + F() +} diff --git a/test/codegen/condmove.go b/test/codegen/condmove.go index f86da3459a325..7579dd1890ed2 100644 --- a/test/codegen/condmove.go +++ b/test/codegen/condmove.go @@ -31,7 +31,7 @@ func cmovuintptr(x, y uintptr) uintptr { if x < y { x = -y } - // amd64:"CMOVQCS" + // amd64:"CMOVQ(HI|CS)" // arm64:"CSEL\t(LO|HI)" // wasm:"Select" return x @@ -41,7 +41,7 @@ func cmov32bit(x, y uint32) uint32 { if x < y { x = -y } - // amd64:"CMOVLCS" + // amd64:"CMOVL(HI|CS)" // arm64:"CSEL\t(LO|HI)" // wasm:"Select" return x @@ -51,7 +51,7 @@ func cmov16bit(x, y uint16) uint16 { if x < y { x = -y } - // amd64:"CMOVWCS" + // amd64:"CMOVW(HI|CS)" // arm64:"CSEL\t(LO|HI)" // wasm:"Select" return x diff --git a/test/codegen/spectre.go b/test/codegen/spectre.go index 3753498d09f76..d845da35ced92 100644 --- a/test/codegen/spectre.go +++ b/test/codegen/spectre.go @@ -13,12 +13,12 @@ func IndexArray(x *[10]int, i int) int { } func IndexString(x string, i int) byte { - // amd64:`CMOVQCC` + // amd64:`CMOVQLS` return x[i] } func IndexSlice(x []float64, i int) float64 { - // amd64:`CMOVQCC` + // amd64:`CMOVQLS` return x[i] } diff --git a/test/codegen/structs.go b/test/codegen/structs.go index 9eddc5b16ec3e..c4bcb55c637cd 100644 --- a/test/codegen/structs.go +++ b/test/codegen/structs.go @@ -18,7 +18,7 @@ type Z1 struct { } func Zero1(t *Z1) { // Issue #18370 - // amd64:`XORPS\tX., X`,`MOVUPS\tX., \(.*\)`,`MOVQ\t\$0, 16\(.*\)` + // amd64:`MOVUPS\tX[0-9]+, \(.*\)`,`MOVQ\t\$0, 16\(.*\)` *t = Z1{} } @@ -27,7 +27,7 @@ type Z2 struct { } func Zero2(t *Z2) { - // amd64:`XORPS\tX., X`,`MOVUPS\tX., \(.*\)`,`MOVQ\t\$0, 16\(.*\)` + // amd64:`MOVUPS\tX[0-9]+, \(.*\)`,`MOVQ\t\$0, 16\(.*\)` // amd64:`.*runtime[.]gcWriteBarrier.*\(SB\)` *t = Z2{} } diff --git a/test/const2.go b/test/const2.go index 048d0cb9f3976..d104a2fa71941 100644 --- a/test/const2.go +++ b/test/const2.go @@ -19,3 +19,14 @@ const LargeB = LargeA * LargeA * LargeA const LargeC = LargeB * LargeB * LargeB // GC_ERROR "constant multiplication overflow" const AlsoLargeA = LargeA << 400 << 400 >> 400 >> 400 // GC_ERROR "constant shift overflow" + +// Issue #42732. + +const a = 1e+500000000 +const b = a * a // ERROR "constant multiplication overflow" +const c = b * b + +const MaxInt512 = (1<<256 - 1) * (1<<256 + 1) +const _ = MaxInt512 + 1 // ERROR "constant addition overflow" +const _ = MaxInt512 ^ -1 // ERROR "constant bitwise XOR overflow" +const _ = ^MaxInt512 // ERROR "constant bitwise complement overflow" diff --git a/test/deferfin.go b/test/deferfin.go index 80372916d20b3..1312bbbe71b04 100644 --- a/test/deferfin.go +++ b/test/deferfin.go @@ -18,12 +18,8 @@ import ( var sink func() func main() { - // Does not work on 32-bits due to partially conservative GC. + // Does not work with gccgo, due to partially conservative GC. // Try to enable when we have fully precise GC. - if runtime.GOARCH != "amd64" { - return - } - // Likewise for gccgo. if runtime.Compiler == "gccgo" { return } @@ -60,4 +56,3 @@ func main() { panic("not all finalizers are called") } } - diff --git a/test/escape2.go b/test/escape2.go index 5c6eb559faf06..b9b723d866686 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -1547,7 +1547,7 @@ func foo153(v interface{}) *int { // ERROR "v does not escape" case int: // ERROR "moved to heap: x$" return &x } - panic(0) + panic(0) // ERROR "0 escapes to heap" } // issue 8185 - &result escaping into result diff --git a/test/escape2n.go b/test/escape2n.go index 46e58f85661a7..7c8208aa73ca1 100644 --- a/test/escape2n.go +++ b/test/escape2n.go @@ -1547,7 +1547,7 @@ func foo153(v interface{}) *int { // ERROR "v does not escape" case int: // ERROR "moved to heap: x$" return &x } - panic(0) + panic(0) // ERROR "0 escapes to heap" } // issue 8185 - &result escaping into result diff --git a/test/escape4.go b/test/escape4.go index a4a9c14a3e0cc..4e50231bf959b 100644 --- a/test/escape4.go +++ b/test/escape4.go @@ -35,14 +35,14 @@ func f1() { func f2() {} // ERROR "can inline f2" // No inline for recover; panic now allowed to inline. -func f3() { panic(1) } // ERROR "can inline f3" +func f3() { panic(1) } // ERROR "can inline f3" "1 escapes to heap" func f4() { recover() } func f5() *byte { type T struct { x [1]byte } - t := new(T) // ERROR "new.T. escapes to heap" + t := new(T) // ERROR "new.T. escapes to heap" return &t.x[0] } @@ -52,6 +52,6 @@ func f6() *byte { y byte } } - t := new(T) // ERROR "new.T. escapes to heap" + t := new(T) // ERROR "new.T. escapes to heap" return &t.x.y } diff --git a/test/escape_param.go b/test/escape_param.go index 993e914e1d0d4..dc93f689cf9fb 100644 --- a/test/escape_param.go +++ b/test/escape_param.go @@ -212,7 +212,7 @@ func caller7() { // **in -> heap func param8(i **int) { // ERROR "i does not escape$" - sink = **i // ERROR "\* \(\*i\) escapes to heap" + sink = **i // ERROR "\*\(\*i\) escapes to heap" } func caller8() { @@ -402,7 +402,7 @@ func caller13h() { var p *int v := &Val{&p} // ERROR "&Val{...} does not escape$" v.param13(&i) - sink = **v.p // ERROR "\* \(\*v\.p\) escapes to heap" + sink = **v.p // ERROR "\*\(\*v\.p\) escapes to heap" } type Node struct { diff --git a/test/fixedbugs/bug340.go b/test/fixedbugs/bug340.go index 8c543c98d97e3..542a6eab03d2d 100644 --- a/test/fixedbugs/bug340.go +++ b/test/fixedbugs/bug340.go @@ -12,6 +12,7 @@ func main() { var x interface{} switch t := x.(type) { case 0: // ERROR "type" - t.x = 1 // ERROR "type interface \{\}|reference to undefined field or method|interface with no methods" + t.x = 1 + x.x = 1 // ERROR "type interface \{\}|reference to undefined field or method|interface with no methods" } } diff --git a/test/fixedbugs/bug462.go b/test/fixedbugs/bug462.go index 3df63b091ddbb..bae5ee0aeb019 100644 --- a/test/fixedbugs/bug462.go +++ b/test/fixedbugs/bug462.go @@ -13,7 +13,7 @@ type T struct { } func main() { - _ = T { - os.File: 1, // ERROR "unknown T? ?field" + _ = T{ + os.File: 1, // ERROR "invalid field name os.File|unknown field" } } diff --git a/test/fixedbugs/issue11362.go b/test/fixedbugs/issue11362.go index 964e5fdf6b7ce..9492ec12739af 100644 --- a/test/fixedbugs/issue11362.go +++ b/test/fixedbugs/issue11362.go @@ -8,7 +8,7 @@ package main -import _ "unicode//utf8" // GC_ERROR "non-canonical import path .unicode//utf8. \(should be .unicode/utf8.\)" "can't find import: .unicode//utf8." +import _ "unicode//utf8" // GC_ERROR "non-canonical import path .unicode//utf8. \(should be .unicode/utf8.\)" func main() { } diff --git a/test/fixedbugs/issue13799.go b/test/fixedbugs/issue13799.go index fbdd4c32bc85a..c8ecfc54e4464 100644 --- a/test/fixedbugs/issue13799.go +++ b/test/fixedbugs/issue13799.go @@ -60,7 +60,7 @@ func test1(iter int) { } if len(m) != maxI { - panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" + panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap" } } @@ -84,7 +84,7 @@ func test2(iter int) { } if len(m) != maxI { - panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" + panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap" } } @@ -110,7 +110,7 @@ func test3(iter int) { } if *m != maxI { - panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" + panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap" } } @@ -136,7 +136,7 @@ func test4(iter int) { } if *m != maxI { - panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" + panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap" } } @@ -167,7 +167,7 @@ func test5(iter int) { } if *m != maxI { - panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" + panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap" } } @@ -185,6 +185,6 @@ func test6(iter int) { } if *m != maxI { - panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" + panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap" } } diff --git a/test/fixedbugs/issue15055.go b/test/fixedbugs/issue15055.go index e58047e411caa..33cf63aaad8d1 100644 --- a/test/fixedbugs/issue15055.go +++ b/test/fixedbugs/issue15055.go @@ -8,10 +8,12 @@ package main func main() { type name string - _ = []byte("abc", "def", 12) // ERROR "too many arguments to conversion to \[\]byte: \(\[\]byte\)\(.abc., .def., 12\)" + _ = []byte("abc", "def", 12) // ERROR "too many arguments to conversion to \[\]byte: \[\]byte\(.abc., .def., 12\)" _ = string("a", "b", nil) // ERROR "too many arguments to conversion to string: string\(.a., .b., nil\)" - _ = []byte() // ERROR "missing argument to conversion to \[\]byte: \(\[\]byte\)\(\)" + _ = []byte() // ERROR "missing argument to conversion to \[\]byte: \[\]byte\(\)" _ = string() // ERROR "missing argument to conversion to string: string\(\)" + _ = *int() // ERROR "missing argument to conversion to int: int\(\)" + _ = (*int)() // ERROR "missing argument to conversion to \*int: \(\*int\)\(\)" _ = name("a", 1, 3.3) // ERROR "too many arguments to conversion to name: name\(.a., 1, 3.3\)" - _ = map[string]string(nil, nil) // ERROR "too many arguments to conversion to map\[string\]string: \(map\[string\]string\)\(nil, nil\)" + _ = map[string]string(nil, nil) // ERROR "too many arguments to conversion to map\[string\]string: map\[string\]string\(nil, nil\)" } diff --git a/test/fixedbugs/issue20232.go b/test/fixedbugs/issue20232.go index f91c74936b13f..7a0300a4c42d1 100644 --- a/test/fixedbugs/issue20232.go +++ b/test/fixedbugs/issue20232.go @@ -6,6 +6,7 @@ package main -const _ = 6e5518446744 // ERROR "malformed constant: 6e5518446744 \(exponent overflow\)" +const x = 6e5518446744 // ERROR "malformed constant: 6e5518446744" +const _ = x * x const _ = 1e-1000000000 -const _ = 1e+1000000000 // ERROR "constant too large" +const _ = 1e+1000000000 // ERROR "malformed constant: 1e\+1000000000" diff --git a/test/fixedbugs/issue20250.go b/test/fixedbugs/issue20250.go index c190515274af2..1a513bea56df1 100644 --- a/test/fixedbugs/issue20250.go +++ b/test/fixedbugs/issue20250.go @@ -1,4 +1,4 @@ -// errorcheck -0 -live -l -d=compilelater +// errorcheck -0 -live -l // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/issue20415.go b/test/fixedbugs/issue20415.go index 9e7649fc9589d..1d9a745ab47fe 100644 --- a/test/fixedbugs/issue20415.go +++ b/test/fixedbugs/issue20415.go @@ -11,7 +11,7 @@ package p // 1 var f byte -var f interface{} // ERROR "previous declaration at issue20415.go:12|redefinition" +var f interface{} // ERROR "issue20415.go:12: previous declaration|redefinition" func _(f int) { } @@ -22,7 +22,7 @@ var g byte func _(g int) { } -var g interface{} // ERROR "previous declaration at issue20415.go:20|redefinition" +var g interface{} // ERROR "issue20415.go:20: previous declaration|redefinition" // 3 func _(h int) { @@ -30,4 +30,4 @@ func _(h int) { var h byte -var h interface{} // ERROR "previous declaration at issue20415.go:31|redefinition" +var h interface{} // ERROR "issue20415.go:31: previous declaration|redefinition" diff --git a/test/fixedbugs/issue20780.go b/test/fixedbugs/issue20780.go index 53c4f615e17fa..f73e6d1f794bb 100644 --- a/test/fixedbugs/issue20780.go +++ b/test/fixedbugs/issue20780.go @@ -9,11 +9,17 @@ package main +type Big = [400e6]byte + func f() { // GC_ERROR "stack frame too large" - var x [800e6]byte - g(x) - return + // Note: This test relies on the fact that we currently always + // spill function-results to the stack, even if they're so + // large that we would normally heap allocate them. If we ever + // improve the backend to spill temporaries to the heap, this + // test will probably need updating to find some new way to + // construct an overly large stack frame. + g(h(), h()) } -//go:noinline -func g([800e6]byte) {} +func g(Big, Big) +func h() Big diff --git a/test/fixedbugs/issue20780b.go b/test/fixedbugs/issue20780b.go new file mode 100644 index 0000000000000..c8bf1f83499f4 --- /dev/null +++ b/test/fixedbugs/issue20780b.go @@ -0,0 +1,62 @@ +// +build cgo,linux,amd64 +// run -race + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that CL 281293 doesn't interfere with race detector +// instrumentation. + +package main + +import "fmt" + +const N = 2e6 + +type Big = [N]int + +var sink interface{} + +func main() { + g(0, f(0)) + + x1 := f(1) + sink = &x1 + g(1, x1) + g(7, f(7)) + g(1, x1) + + x3 := f(3) + sink = &x3 + g(1, x1) + g(3, x3) + + h(f(0), x1, f(2), x3, f(4)) +} + +//go:noinline +func f(k int) (x Big) { + for i := range x { + x[i] = k*N + i + } + return +} + +//go:noinline +func g(k int, x Big) { + for i := range x { + if x[i] != k*N+i { + panic(fmt.Sprintf("x%d[%d] = %d", k, i, x[i])) + } + } +} + +//go:noinline +func h(x0, x1, x2, x3, x4 Big) { + g(0, x0) + g(1, x1) + g(2, x2) + g(3, x3) + g(4, x4) +} diff --git a/test/fixedbugs/issue22822.go b/test/fixedbugs/issue22822.go index ea53452f09b76..dc86c97fe5706 100644 --- a/test/fixedbugs/issue22822.go +++ b/test/fixedbugs/issue22822.go @@ -12,5 +12,7 @@ package main func F() { slice := []int{1, 2, 3} len := int(2) - println(len(slice)) // ERROR "cannot call non-function len .type int., declared at|expected function" + println(len(slice)) // ERROR "cannot call non-function len .type int., declared at LINE-1|expected function" + const iota = 1 + println(iota(slice)) // ERROR "cannot call non-function iota .type int., declared at LINE-1|expected function" } diff --git a/test/fixedbugs/issue23017.go b/test/fixedbugs/issue23017.go new file mode 100644 index 0000000000000..770c48ef26bd9 --- /dev/null +++ b/test/fixedbugs/issue23017.go @@ -0,0 +1,113 @@ +// run + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// assignment order in multiple assignments. +// See issue #23017 + +package main + +import "fmt" + +func main() {} + +func init() { + var m = map[int]int{} + var p *int + + defer func() { + recover() + check(1, len(m)) + check(42, m[2]) + }() + m[2], *p = 42, 2 +} + +func init() { + var m = map[int]int{} + p := []int{} + + defer func() { + recover() + check(1, len(m)) + check(2, m[2]) + }() + m[2], p[1] = 2, 2 +} + +func init() { + type P struct{ i int } + var m = map[int]int{} + var p *P + + defer func() { + recover() + check(1, len(m)) + check(3, m[2]) + }() + m[2], p.i = 3, 2 +} + +func init() { + type T struct{ i int } + var x T + p := &x + p, p.i = new(T), 4 + check(4, x.i) +} + +func init() { + var m map[int]int + var a int + var p = &a + + defer func() { + recover() + check(5, *p) + }() + *p, m[2] = 5, 2 +} + +var g int + +func init() { + var m map[int]int + defer func() { + recover() + check(0, g) + }() + m[0], g = 1, 2 +} + +func init() { + type T struct{ x struct{ y int } } + var x T + p := &x + p, p.x.y = new(T), 7 + check(7, x.x.y) + check(0, p.x.y) +} + +func init() { + type T *struct{ x struct{ y int } } + x := struct{ y int }{0} + var q T = &struct{ x struct{ y int } }{x} + p := q + p, p.x.y = nil, 7 + check(7, q.x.y) +} + +func init() { + x, y := 1, 2 + x, y = y, x + check(2, x) + check(1, y) +} + +func check(want, got int) { + if want != got { + panic(fmt.Sprintf("wanted %d, but got %d", want, got)) + } +} diff --git a/test/fixedbugs/issue27595.go b/test/fixedbugs/issue27595.go index af5c7a10d9b01..b9328a68132a1 100644 --- a/test/fixedbugs/issue27595.go +++ b/test/fixedbugs/issue27595.go @@ -8,7 +8,7 @@ package main var a = twoResults() // ERROR "assignment mismatch: 1 variable but twoResults returns 2 values" var b, c, d = twoResults() // ERROR "assignment mismatch: 3 variables but twoResults returns 2 values" -var e, f = oneResult() // ERROR "assignment mismatch: 2 variables but oneResult returns 1 values" +var e, f = oneResult() // ERROR "assignment mismatch: 2 variables but oneResult returns 1 value" func twoResults() (int, int) { return 1, 2 diff --git a/test/fixedbugs/issue28079b.go b/test/fixedbugs/issue28079b.go index d1992e1d0942a..54c9db994b8fa 100644 --- a/test/fixedbugs/issue28079b.go +++ b/test/fixedbugs/issue28079b.go @@ -13,5 +13,5 @@ import "unsafe" type T [uintptr(unsafe.Pointer(nil))]int // ERROR "non-constant array bound|array bound is not constant" func f() { - _ = complex(1< 10 { - foo = func() int { return 2 } // ERROR "can inline o.func2" "func literal escapes" + foo = func() int { return 2 } // ERROR "can inline o.func2" } - }(11) + }(11) // ERROR "func literal does not escape" "inlining call to o.func2" return foo() } -func p() int { +func p() int { // ERROR "can inline p" return func() int { return 42 }() // ERROR "can inline p.func1" "inlining call to p.func1" } -func q(x int) int { +func q(x int) int { // ERROR "can inline q" foo := func() int { return x * 2 } // ERROR "can inline q.func1" "func literal does not escape" return foo() // ERROR "inlining call to q.func1" } @@ -111,15 +111,15 @@ func r(z int) int { foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape" return x + z } - bar := func(x int) int { // ERROR "func literal does not escape" - return x + func(y int) int { // ERROR "can inline r.func2.1" + bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2" + return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3" return 2*y + x*z }(x) // ERROR "inlining call to r.func2.1" } - return foo(42) + bar(42) // ERROR "inlining call to r.func1" + return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3" } -func s0(x int) int { +func s0(x int) int { // ERROR "can inline s0" foo := func() { // ERROR "can inline s0.func1" "func literal does not escape" x = x + 1 } @@ -127,7 +127,7 @@ func s0(x int) int { return x } -func s1(x int) int { +func s1(x int) int { // ERROR "can inline s1" foo := func() int { // ERROR "can inline s1.func1" "func literal does not escape" return x } diff --git a/test/interface/explicit.go b/test/interface/explicit.go index 3f9451e8d2faa..b705b97676007 100644 --- a/test/interface/explicit.go +++ b/test/interface/explicit.go @@ -100,6 +100,7 @@ type T2 struct{} func (t *T2) M() {} func (t *T2) _() {} -// Check that nothing satisfies an interface with blank methods. -var b1 B1 = &T2{} // ERROR "incompatible|missing _ method" -var b2 B2 = &T2{} // ERROR "incompatible|missing _ method" +// Already reported about the invalid blank interface method above; +// no need to report about not implementing it. +var b1 B1 = &T2{} +var b2 B2 = &T2{} diff --git a/test/linkname2.go b/test/linkname2.go new file mode 100644 index 0000000000000..43e66a584958b --- /dev/null +++ b/test/linkname2.go @@ -0,0 +1,30 @@ +// errorcheck + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests that errors are reported for misuse of linkname. +package p + +import _ "unsafe" + +type t int + +var x, y int + +//go:linkname x ok + +// ERROR "//go:linkname requires linkname argument or -p compiler flag" +// BAD: want error "//go:linkname must refer to declared function or variable" +// BAD: want error "//go:linkname must refer to declared function or variable" +// ERROR "duplicate //go:linkname for x" + +// The two BAD lines are just waiting for #42938 before we can +// re-enable the errors. + +//line linkname2.go:18 +//go:linkname y +//go:linkname nonexist nonexist +//go:linkname t notvarfunc +//go:linkname x duplicate diff --git a/test/live.go b/test/live.go index 3df7ab01af47f..d52ce7f007501 100644 --- a/test/live.go +++ b/test/live.go @@ -718,5 +718,5 @@ func f44(f func() [2]*int) interface{} { // ERROR "live at entry to f44: f" } ret := T{} ret.s[0] = f() - return ret // ERROR "stack object .autotmp_5 T" + return ret // ERROR "stack object .autotmp_[0-9]+ T" } diff --git a/test/mainsig.go b/test/mainsig.go new file mode 100644 index 0000000000000..d006d9cda3142 --- /dev/null +++ b/test/mainsig.go @@ -0,0 +1,13 @@ +// errorcheck + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main(int) {} // ERROR "func main must have no arguments and no return values" +func main() int { return 1 } // ERROR "func main must have no arguments and no return values" "main redeclared in this block" + +func init(int) {} // ERROR "func init must have no arguments and no return values" +func init() int { return 1 } // ERROR "func init must have no arguments and no return values" diff --git a/test/nilptr3.go b/test/nilptr3.go index e0f2ed9767659..3345cfa5ab38a 100644 --- a/test/nilptr3.go +++ b/test/nilptr3.go @@ -214,14 +214,6 @@ func p1() byte { return p[5] // ERROR "removed nil check" } -// make sure not to do nil check for access of PAUTOHEAP -//go:noinline -func (p *Struct) m() {} -func c1() { - var x Struct - func() { x.m() }() // ERROR "removed nil check" -} - type SS struct { x byte } diff --git a/test/nosplit.go b/test/nosplit.go index faa7b8c2d8583..8a3fa9bf3538e 100644 --- a/test/nosplit.go +++ b/test/nosplit.go @@ -353,7 +353,14 @@ TestCases: log.Fatal(err) } - cmd := exec.Command("go", "build") + // Turn off ABI0 wrapper generation for now. The problem here is + // that in these test cases main.main is an assembly routine, + // thus calls to it will have to go through an ABI wrapper. The + // ABI wrapper will consume some stack space, which throws off + // the numbers. + workaround := "-gcflags=-abiwrap=0" + + cmd := exec.Command("go", "build", workaround) cmd.Dir = dir output, err := cmd.CombinedOutput() if err == nil { diff --git a/test/reorder.go b/test/reorder.go index 3a87d025c2a95..57892f882fa5b 100644 --- a/test/reorder.go +++ b/test/reorder.go @@ -20,6 +20,8 @@ func main() { p7() p8() p9() + p10() + p11() } var gx []int @@ -149,3 +151,17 @@ func checkOAS2XXX(x bool, s string) { panic("failed") } } + +//go:noinline +func fp() (*int, int) { return nil, 42 } + +func p10() { + p := new(int) + p, *p = fp() +} + +func p11() { + var i interface{} + p := new(bool) + p, *p = i.(*bool) +} diff --git a/test/run.go b/test/run.go index 624f2236a116a..dba4d16d63e6e 100644 --- a/test/run.go +++ b/test/run.go @@ -59,7 +59,7 @@ var ( // dirs are the directories to look for *.go files in. // TODO(bradfitz): just use all directories? - dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime"} + dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi"} // ratec controls the max number of tests running at a time. ratec chan bool @@ -491,7 +491,7 @@ func (t *test) run() { // Execution recipe stops at first blank line. pos := strings.Index(t.src, "\n\n") if pos == -1 { - t.err = errors.New("double newline not found") + t.err = fmt.Errorf("double newline ending execution recipe not found in %s", t.goFileName()) return } action := t.src[:pos] @@ -868,9 +868,7 @@ func (t *test) run() { t.err = err return } - if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { - t.err = fmt.Errorf("incorrect output\n%s", out) - } + t.checkExpectedOutput(out) } } @@ -904,15 +902,14 @@ func (t *test) run() { if *linkshared { cmd = append(cmd, "-linkshared") } + cmd = append(cmd, flags...) cmd = append(cmd, ".") out, err := runcmd(cmd...) if err != nil { t.err = err return } - if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { - t.err = fmt.Errorf("incorrect output\n%s", out) - } + t.checkExpectedOutput(out) case "build": // Build Go file. @@ -997,9 +994,7 @@ func (t *test) run() { t.err = err break } - if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { - t.err = fmt.Errorf("incorrect output\n%s", out) - } + t.checkExpectedOutput(out) } case "buildrun": @@ -1025,9 +1020,7 @@ func (t *test) run() { return } - if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { - t.err = fmt.Errorf("incorrect output\n%s", out) - } + t.checkExpectedOutput(out) case "run": // Run Go file if no special go command flags are provided; @@ -1070,9 +1063,7 @@ func (t *test) run() { t.err = err return } - if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { - t.err = fmt.Errorf("incorrect output\n%s", out) - } + t.checkExpectedOutput(out) case "runoutput": // Run Go file and write its output into temporary Go file. @@ -1107,9 +1098,7 @@ func (t *test) run() { t.err = err return } - if string(out) != t.expectedOutput() { - t.err = fmt.Errorf("incorrect output\n%s", out) - } + t.checkExpectedOutput(out) case "errorcheckoutput": // Run Go file and write its output into temporary Go file. @@ -1183,12 +1172,24 @@ func (t *test) makeTempDir() { } } -func (t *test) expectedOutput() string { +// checkExpectedOutput compares the output from compiling and/or running with the contents +// of the corresponding reference output file, if any (replace ".go" with ".out"). +// If they don't match, fail with an informative message. +func (t *test) checkExpectedOutput(gotBytes []byte) { + got := string(gotBytes) filename := filepath.Join(t.dir, t.gofile) filename = filename[:len(filename)-len(".go")] filename += ".out" - b, _ := ioutil.ReadFile(filename) - return string(b) + b, err := ioutil.ReadFile(filename) + // File is allowed to be missing (err != nil) in which case output should be empty. + got = strings.Replace(got, "\r\n", "\n", -1) + if got != string(b) { + if err == nil { + t.err = fmt.Errorf("output does not match expected in %s. Instead saw\n%s", filename, got) + } else { + t.err = fmt.Errorf("output should be empty when (optional) expected-output file %s is not present. Instead saw\n%s", filename, got) + } + } } func splitOutput(out string, wantAuto bool) []string { diff --git a/test/used.go b/test/used.go new file mode 100644 index 0000000000000..76f3fc91cccd2 --- /dev/null +++ b/test/used.go @@ -0,0 +1,144 @@ +// errorcheck + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +const C = 1 + +var x, x1, x2 int +var b bool +var s string +var c chan int +var cp complex128 +var slice []int +var array [2]int +var bytes []byte +var runes []rune +var r rune + +func f0() {} +func f1() int { return 1 } +func f2() (int, int) { return 1, 1 } + +type T struct{ X int } + +func (T) M1() int { return 1 } +func (T) M0() {} +func (T) M() {} + +var t T +var tp *T + +type I interface{ M() } + +var i I + +var m map[int]int + +func _() { + // Note: if the next line changes to x, the error silences the x+x etc below! + x1 // ERROR "x1 evaluated but not used" + + nil // ERROR "nil evaluated but not used" + C // ERROR "C evaluated but not used" + 1 // ERROR "1 evaluated but not used" + x + x // ERROR "x \+ x evaluated but not used" + x - x // ERROR "x - x evaluated but not used" + x | x // ERROR "x \| x evaluated but not used" + "a" + s // ERROR ".a. \+ s evaluated but not used" + &x // ERROR "&x evaluated but not used" + b && b // ERROR "b && b evaluated but not used" + append(slice, 1) // ERROR "append\(slice, 1\) evaluated but not used" + string(bytes) // ERROR "string\(bytes\) evaluated but not used" + string(runes) // ERROR "string\(runes\) evaluated but not used" + f0() // ok + f1() // ok + f2() // ok + _ = f0() // ERROR "f0\(\) used as value" + _ = f1() // ok + _, _ = f2() // ok + _ = f2() // ERROR "assignment mismatch: 1 variable but f2 returns 2 values" + _ = f1(), 0 // ERROR "assignment mismatch: 1 variable but 2 values" + T.M0 // ERROR "T.M0 evaluated but not used" + t.M0 // ERROR "t.M0 evaluated but not used" + cap // ERROR "use of builtin cap not in function call" + cap(slice) // ERROR "cap\(slice\) evaluated but not used" + close(c) // ok + _ = close(c) // ERROR "close\(c\) used as value" + func() {} // ERROR "func literal evaluated but not used" + X{} // ERROR "undefined: X" + map[string]int{} // ERROR "map\[string\]int{} evaluated but not used" + struct{}{} // ERROR "struct ?{}{} evaluated but not used" + [1]int{} // ERROR "\[1\]int{} evaluated but not used" + []int{} // ERROR "\[\]int{} evaluated but not used" + &struct{}{} // ERROR "&struct ?{}{} evaluated but not used" + float32(x) // ERROR "float32\(x\) evaluated but not used" + I(t) // ERROR "I\(t\) evaluated but not used" + int(x) // ERROR "int\(x\) evaluated but not used" + copy(slice, slice) // ok + _ = copy(slice, slice) // ok + delete(m, 1) // ok + _ = delete(m, 1) // ERROR "delete\(m, 1\) used as value" + t.X // ERROR "t.X evaluated but not used" + tp.X // ERROR "tp.X evaluated but not used" + t.M // ERROR "t.M evaluated but not used" + I.M // ERROR "I.M evaluated but not used" + i.(T) // ERROR "i.\(T\) evaluated but not used" + x == x // ERROR "x == x evaluated but not used" + x != x // ERROR "x != x evaluated but not used" + x != x // ERROR "x != x evaluated but not used" + x < x // ERROR "x < x evaluated but not used" + x >= x // ERROR "x >= x evaluated but not used" + x > x // ERROR "x > x evaluated but not used" + *tp // ERROR "\*tp evaluated but not used" + slice[0] // ERROR "slice\[0\] evaluated but not used" + m[1] // ERROR "m\[1\] evaluated but not used" + len(slice) // ERROR "len\(slice\) evaluated but not used" + make(chan int) // ERROR "make\(chan int\) evaluated but not used" + make(map[int]int) // ERROR "make\(map\[int\]int\) evaluated but not used" + make([]int, 1) // ERROR "make\(\[\]int, 1\) evaluated but not used" + x * x // ERROR "x \* x evaluated but not used" + x / x // ERROR "x / x evaluated but not used" + x % x // ERROR "x % x evaluated but not used" + x << x // ERROR "x << x evaluated but not used" + x >> x // ERROR "x >> x evaluated but not used" + x & x // ERROR "x & x evaluated but not used" + x &^ x // ERROR "x &\^ x evaluated but not used" + new(int) // ERROR "new\(int\) evaluated but not used" + !b // ERROR "!b evaluated but not used" + ^x // ERROR "\^x evaluated but not used" + +x // ERROR "\+x evaluated but not used" + -x // ERROR "-x evaluated but not used" + b || b // ERROR "b \|\| b evaluated but not used" + panic(1) // ok + _ = panic(1) // ERROR "panic\(1\) used as value" + print(1) // ok + _ = print(1) // ERROR "print\(1\) used as value" + println(1) // ok + _ = println(1) // ERROR "println\(1\) used as value" + c <- 1 // ok + slice[1:1] // ERROR "slice\[1:1\] evaluated but not used" + array[1:1] // ERROR "array\[1:1\] evaluated but not used" + s[1:1] // ERROR "s\[1:1\] evaluated but not used" + slice[1:1:1] // ERROR "slice\[1:1:1\] evaluated but not used" + array[1:1:1] // ERROR "array\[1:1:1\] evaluated but not used" + recover() // ok + <-c // ok + string(r) // ERROR "string\(r\) evaluated but not used" + iota // ERROR "undefined: iota" + real(cp) // ERROR "real\(cp\) evaluated but not used" + imag(cp) // ERROR "imag\(cp\) evaluated but not used" + complex(1, 2) // ERROR "complex\(1, 2\) evaluated but not used" + unsafe.Alignof(t.X) // ERROR "unsafe.Alignof\(t.X\) evaluated but not used" + unsafe.Offsetof(t.X) // ERROR "unsafe.Offsetof\(t.X\) evaluated but not used" + unsafe.Sizeof(t) // ERROR "unsafe.Sizeof\(t\) evaluated but not used" + _ = int // ERROR "type int is not an expression" + (x) // ERROR "x evaluated but not used" + _ = new(x2) // ERROR "x2 is not a type" + _ = new(1 + 1) // ERROR "1 \+ 1 is not a type" +}