all: merge branch dev.regabi (d3cd4830ad) into master

This CL merges the dev.regabi branch to the master branch.

In the dev.regabi branch we have refactored the compiler, and laid
some preliminary work for enabling a register-based ABI (issue #40724),
including improved late call/return lowering, improved ABI wrapper
generation, reflect call prepared for the new ABI, and reserving
special registers in the internal ABI. The actual register-based ABI
has not been enabled for the moment. The ABI-related changes are behind
GOEXPERIMENT=regabi and currently off by default.

Updates #40724, #44222.
Fixes #44224.

Change-Id: Id5de9f734d14099267ab717167aaaeef31fdba70
diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go
index a62e551..026d8ab 100644
--- a/src/cmd/asm/internal/arch/arch.go
+++ b/src/cmd/asm/internal/arch/arch.go
@@ -109,6 +109,10 @@
 	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 2e83e17..c6def15 100644
--- a/src/cmd/asm/internal/asm/operand_test.go
+++ b/src/cmd/asm/internal/asm/operand_test.go
@@ -259,6 +259,7 @@
 	{"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 154cf9c..f1d37bc 100644
--- a/src/cmd/asm/internal/asm/parse.go
+++ b/src/cmd/asm/internal/asm/parse.go
@@ -305,7 +305,7 @@
 // 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 6625ccf..0000000
--- 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 0811df7..0000000
--- 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 0000000..f4ef2cc
--- /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
+t<sub>1</sub>, t<sub>2</sub>, ..., t<sub>N</sub>.
+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 |
+    | <pointer-sized alignment>    |
+    |             . . .            |
+    | 2nd stack-assigned result    |
+    | 1st stack-assigned result    |
+    | <pointer-sized alignment>    |
+    |             . . .            |
+    | 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 0000000..e935821
--- /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 af58440..ce1c402 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 0c1456f..aefdb14 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 @@
 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 @@
 	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 @@
 		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 5ff05a0..4938e4b 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -8,16 +8,18 @@
 	"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 @@
 //     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 @@
 	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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 		}
 		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 @@
 			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 @@
 		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:
-		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
+		if base.Flag.ABIWrap {
+			v.Fatalf("LoweredGetG should not appear in new ABI")
 		}
-	case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLclosure, ssa.OpAMD64CALLinter:
+		r := v.Reg()
+		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.OpAMD64CALLclosure, ssa.OpAMD64CALLinter:
 		s.Call(v)
 
 	case ssa.OpAMD64LoweredGetCallerPC:
@@ -1012,12 +1050,12 @@
 	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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 		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 20e2f43..81959ae 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 @@
 	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 bd8d7ff..f2c6763 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 765a771..729d2da 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -9,9 +9,11 @@
 	"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 @@
 }
 
 // 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 @@
 }
 
 // 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 @@
 	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 @@
 			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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 }
 
 // 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 @@
 		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 40d6e17..d3db37e 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 @@
 	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 f3fec03..8364535 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 @@
 	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 4358851..73e74e1 100644
--- a/src/cmd/compile/internal/arm64/ssa.go
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -7,9 +7,11 @@
 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 @@
 }
 
 // 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 @@
 	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 @@
 			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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 }
 
 // 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 @@
 		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/base/base.go b/src/cmd/compile/internal/base/base.go
new file mode 100644
index 0000000..3b9bc3a
--- /dev/null
+++ b/src/cmd/compile/internal/base/base.go
@@ -0,0 +1,75 @@
+// 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 (
+	"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
+// racefuncexit at the start and end (respectively) of each
+// function. This is handled below.
+//
+// Second, during buildssa, it inserts appropriate instrumentation
+// calls immediately before each memory load or store. This is handled
+// by the (*state).instrument method in ssa.go, so here we just set
+// the Func.InstrumentBody flag as needed. For background on why this
+// is done during SSA construction rather than a separate SSA pass,
+// see issue #19054.
+//
+// Third we remove calls to racefuncenter and racefuncexit, for leaf
+// functions without instrumented operations. This is done as part of
+// ssa opt pass via special rule.
+
+// TODO(dvyukov): do not instrument initialization as writes:
+// a := make([]int, 10)
+
+// Do not instrument the following packages at all,
+// at best instrumentation would cause infinite recursion.
+var NoInstrumentPkgs = []string{
+	"runtime/internal/atomic",
+	"runtime/internal/sys",
+	"runtime/internal/math",
+	"runtime",
+	"runtime/race",
+	"runtime/msan",
+	"internal/cpu",
+}
+
+// Don't insert racefuncenterfp/racefuncexit into the following packages.
+// Memory accesses in the packages are either uninteresting or will cause false positives.
+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 0000000..164941b
--- /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 <key>[=<value>]
+
+<key> is one of:
+
+`
+
+const debugHelpFooter = `
+<value> 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 0000000..c38bbe6
--- /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 0000000..49fe435
--- /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 0000000..668c600
--- /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 56b3899..f599f4e 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 @@
 	"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 0000000..bcac1fe
--- /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 0000000..5202037
--- /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 0000000..60ba208
--- /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 0000000..dd22c03
--- /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 85%
rename from src/cmd/compile/internal/gc/dwinl.go
rename to src/cmd/compile/internal/dwarfgen/dwinl.go
index bb5ae61..d5687cb 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 @@
 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 @@
 			}
 			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 @@
 	}
 
 	// Debugging
-	if Debug_gendwarfinl != 0 {
+	if base.Debug.DwarfInl != 0 {
 		dumpInlCalls(inlcalls)
 		dumpInlVars(dwVars)
 	}
@@ -204,16 +207,17 @@
 // 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 @@
 	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 @@
 	// 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 @@
 // 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 @@
 	// 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 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 @@
 		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 @@
 
 	// 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 @@
 	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 0000000..ec6ce45
--- /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 e66b859..1c040ed 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 @@
 	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 @@
 }
 
 // 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 @@
 
 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 b0e038d..fcfcf85 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 @@
 	"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 0000000..58cad73
--- /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 = &note{
+			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 2f7fa27..0000000
--- 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/align.go b/src/cmd/compile/internal/gc/align.go
deleted file mode 100644
index a3a0c8f..0000000
--- a/src/cmd/compile/internal/gc/align.go
+++ /dev/null
@@ -1,531 +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"
-	"fmt"
-	"sort"
-)
-
-// sizeCalculationDisabled indicates whether it is safe
-// to calculate Types' widths and alignments. See dowidth.
-var sizeCalculationDisabled bool
-
-// machine size and rounding alignment is dictated around
-// the size of a pointer, set in betypeinit (see ../amd64/galign.go).
-var defercalc int
-
-func Rnd(o int64, r int64) int64 {
-	if r < 1 || r > 8 || r&(r-1) != 0 {
-		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
-
-	addMethod := func(m *types.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):
-			return
-		default:
-			yyerrorl(m.Pos, "duplicate method %s", m.Sym.Name)
-		}
-		methods = append(methods, m)
-	}
-
-	for _, m := range t.Methods().Slice() {
-		if m.Sym == nil {
-			continue
-		}
-
-		checkwidth(m.Type)
-		addMethod(m, true)
-	}
-
-	for _, m := range t.Methods().Slice() {
-		if m.Sym != nil {
-			continue
-		}
-
-		if !m.Type.IsInterface() {
-			yyerrorl(m.Pos, "interface contains embedded non-interface %v", m.Type)
-			m.SetBroke(true)
-			t.SetBroke(true)
-			// Add to fields so that error messages
-			// include the broken embedded type when
-			// printing t.
-			// TODO(mdempsky): Revisit this.
-			methods = append(methods, m)
-			continue
-		}
-
-		// Embedded interface: duplicate all methods
-		// (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())
-			addMethod(f, false)
-		}
-	}
-
-	sort.Sort(methcmp(methods))
-
-	if int64(len(methods)) >= thearch.MAXWIDTH/int64(Widthptr) {
-		yyerrorl(typePos(t), "interface too large")
-	}
-	for i, m := range methods {
-		m.Offset = int64(i) * int64(Widthptr)
-	}
-
-	// Access fields directly to avoid recursively calling dowidth
-	// within Type.Fields().
-	t.Extra.(*types.Interface).Fields.Set(methods)
-}
-
-func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
-	starto := o
-	maxalign := int32(flag)
-	if maxalign < 1 {
-		maxalign = 1
-	}
-	lastzero := int64(0)
-	for _, f := range t.Fields().Slice() {
-		if f.Type == nil {
-			// broken field, just skip it so that other valid fields
-			// get a width.
-			continue
-		}
-
-		dowidth(f.Type)
-		if int32(f.Type.Align) > maxalign {
-			maxalign = int32(f.Type.Align)
-		}
-		if f.Type.Align > 0 {
-			o = Rnd(o, int64(f.Type.Align))
-		}
-		f.Offset = o
-		if n := asNode(f.Nname); n != nil {
-			// addrescapes has similar code to update these offsets.
-			// Usually addrescapes runs after widstruct,
-			// 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
-			}
-		}
-
-		w := f.Type.Width
-		if w < 0 {
-			Fatalf("invalid width %d", f.Type.Width)
-		}
-		if w == 0 {
-			lastzero = o
-		}
-		o += w
-		maxwidth := thearch.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)
-			o = 8 // small but nonzero
-		}
-	}
-
-	// For nonzero-sized structs which end in a zero-sized thing, we add
-	// an extra byte of padding to the type. This padding ensures that
-	// taking the address of the zero-sized thing can't manufacture a
-	// pointer to the next object in the heap. See issue 9401.
-	if flag == 1 && o > starto && o == lastzero {
-		o++
-	}
-
-	// final width is rounded
-	if flag != 0 {
-		o = Rnd(o, int64(maxalign))
-	}
-	t.Align = uint8(maxalign)
-
-	// type width only includes back to first field's offset
-	t.Width = o - starto
-
-	return o
-}
-
-// findTypeLoop searches for an invalid type declaration loop involving
-// type t and reports whether one is found. If so, path contains the
-// loop.
-//
-// 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 {
-	// We implement a simple DFS loop-finding algorithm. This
-	// could be faster, but type cycles are rare.
-
-	if t.Sym != nil {
-		// Declared type. Check for loops and otherwise
-		// recurse on the type expression used in the type
-		// declaration.
-
-		for i, x := range *path {
-			if x == t {
-				*path = (*path)[i:]
-				return true
-			}
-		}
-
-		*path = append(*path, t)
-		if p := asNode(t.Nod).Name.Param; p != nil && findTypeLoop(p.Ntype.Type, path) {
-			return true
-		}
-		*path = (*path)[:len(*path)-1]
-	} else {
-		// Anonymous type. Recurse on contained types.
-
-		switch t.Etype {
-		case TARRAY:
-			if findTypeLoop(t.Elem(), path) {
-				return true
-			}
-		case TSTRUCT:
-			for _, f := range t.Fields().Slice() {
-				if findTypeLoop(f.Type, path) {
-					return true
-				}
-			}
-		case TINTER:
-			for _, m := range t.Methods().Slice() {
-				if m.Type.IsInterface() { // embedded interface
-					if findTypeLoop(m.Type, path) {
-						return true
-					}
-				}
-			}
-		}
-	}
-
-	return false
-}
-
-func reportTypeLoop(t *types.Type) {
-	if t.Broke() {
-		return
-	}
-
-	var l []*types.Type
-	if !findTypeLoop(t, &l) {
-		Fatalf("failed to find type loop for: %v", t)
-	}
-
-	// Rotate loop so that the earliest type declaration is first.
-	i := 0
-	for j, t := range l[1:] {
-		if typePos(t).Before(typePos(l[i])) {
-			i = j + 1
-		}
-	}
-	l = append(l[i:], l[:i]...)
-
-	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)
-		t.SetBroke(true)
-	}
-	fmt.Fprintf(&msg, "\t%v: %v", linestr(typePos(l[0])), l[0])
-	yyerrorl(typePos(l[0]), msg.String())
-}
-
-// dowidth calculates and stores the size and alignment for t.
-// If sizeCalculationDisabled 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.
-	// See issue #33658.
-	if enableTrace && skipDowidthForTracing {
-		return
-	}
-	if Widthptr == 0 {
-		Fatalf("dowidth without betypeinit")
-	}
-
-	if t == nil {
-		return
-	}
-
-	if t.Width == -2 {
-		reportTypeLoop(t)
-		t.Width = 0
-		t.Align = 1
-		return
-	}
-
-	if t.WidthCalculated() {
-		return
-	}
-
-	if sizeCalculationDisabled {
-		if t.Broke() {
-			// break infinite recursion from Fatal call below
-			return
-		}
-		t.SetBroke(true)
-		Fatalf("width not calculated: %v", t)
-	}
-
-	// break infinite recursion if the broken recursive type
-	// is referenced again
-	if t.Broke() && t.Width == 0 {
-		return
-	}
-
-	// defer checkwidth calls until after we're done
-	defercheckwidth()
-
-	lno := lineno
-	if asNode(t.Nod) != nil {
-		lineno = asNode(t.Nod).Pos
-	}
-
-	t.Width = -2
-	t.Align = 0 // 0 means use t.Width, below
-
-	et := t.Etype
-	switch et {
-	case TFUNC, TCHAN, TMAP, TSTRING:
-		break
-
-	// simtype == 0 during bootstrap
-	default:
-		if simtype[t.Etype] != 0 {
-			et = simtype[t.Etype]
-		}
-	}
-
-	var w int64
-	switch et {
-	default:
-		Fatalf("dowidth: unknown type: %v", t)
-
-	// compiler-specific stuff
-	case TINT8, TUINT8, TBOOL:
-		// bool is int8
-		w = 1
-
-	case TINT16, TUINT16:
-		w = 2
-
-	case TINT32, TUINT32, TFLOAT32:
-		w = 4
-
-	case TINT64, TUINT64, TFLOAT64:
-		w = 8
-		t.Align = uint8(Widthreg)
-
-	case TCOMPLEX64:
-		w = 8
-		t.Align = 4
-
-	case TCOMPLEX128:
-		w = 16
-		t.Align = uint8(Widthreg)
-
-	case TPTR:
-		w = int64(Widthptr)
-		checkwidth(t.Elem())
-
-	case TUNSAFEPTR:
-		w = int64(Widthptr)
-
-	case TINTER: // implemented as 2 pointers
-		w = 2 * int64(Widthptr)
-		t.Align = uint8(Widthptr)
-		expandiface(t)
-
-	case TCHAN: // implemented as pointer
-		w = int64(Widthptr)
-
-		checkwidth(t.Elem())
-
-		// make fake type to check later to
-		// trigger channel argument check.
-		t1 := types.NewChanArgs(t)
-		checkwidth(t1)
-
-	case TCHANARGS:
-		t1 := t.ChanArgs()
-		dowidth(t1) // just in case
-		if t1.Elem().Width >= 1<<16 {
-			yyerrorl(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())
-
-	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")
-
-	case TSTRING:
-		if sizeofString == 0 {
-			Fatalf("early dowidth string")
-		}
-		w = sizeofString
-		t.Align = uint8(Widthptr)
-
-	case TARRAY:
-		if t.Elem() == nil {
-			break
-		}
-
-		dowidth(t.Elem())
-		if t.Elem().Width != 0 {
-			cap := (uint64(thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
-			if uint64(t.NumElem()) > cap {
-				yyerrorl(typePos(t), "type %L larger than address space", t)
-			}
-		}
-		w = t.NumElem() * t.Elem().Width
-		t.Align = t.Elem().Align
-
-	case TSLICE:
-		if t.Elem() == nil {
-			break
-		}
-		w = sizeofSlice
-		checkwidth(t.Elem())
-		t.Align = uint8(Widthptr)
-
-	case TSTRUCT:
-		if t.IsFuncArgStruct() {
-			Fatalf("dowidth fn struct %v", t)
-		}
-		w = widstruct(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
-
-	// 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)
-		}
-		t.Align = 1
-	}
-
-	if Widthptr == 4 && w != int64(int32(w)) {
-		yyerrorl(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)
-		}
-		t.Align = uint8(w)
-	}
-
-	lineno = lno
-
-	resumecheckwidth()
-}
-
-// when a type's width should be known, we call checkwidth
-// to compute it.  during a declaration like
-//
-//	type T *struct { next T }
-//
-// it is necessary to defer the calculation of the struct width
-// 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
-// size is evaluated eventually.
-
-var deferredTypeStack []*types.Type
-
-func checkwidth(t *types.Type) {
-	if t == nil {
-		return
-	}
-
-	// function arg structs should not be checked
-	// outside of the enclosing function.
-	if t.IsFuncArgStruct() {
-		Fatalf("checkwidth %v", t)
-	}
-
-	if defercalc == 0 {
-		dowidth(t)
-		return
-	}
-
-	// if type has not yet been pushed on deferredTypeStack yet, do it now
-	if !t.Deferwidth() {
-		t.SetDeferwidth(true)
-		deferredTypeStack = append(deferredTypeStack, t)
-	}
-}
-
-func defercheckwidth() {
-	defercalc++
-}
-
-func resumecheckwidth() {
-	if defercalc == 1 {
-		for len(deferredTypeStack) > 0 {
-			t := deferredTypeStack[len(deferredTypeStack)-1]
-			deferredTypeStack = deferredTypeStack[:len(deferredTypeStack)-1]
-			t.SetDeferwidth(false)
-			dowidth(t)
-		}
-	}
-
-	defercalc--
-}
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
deleted file mode 100644
index 10f21f8..0000000
--- 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 911ac4c0..0000000
--- 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 967f75a..2e13d6b 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 e04f23e..0000000
--- 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 e32ab97..0000000
--- 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 bd350f6..0000000
--- 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 0000000..ba67c58
--- /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 b92c8d6..0000000
--- 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 6e90eb4..0000000
--- 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 f45796c..0000000
--- 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 6f328ab..0000000
--- 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 618bdf7..0000000
--- 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 c6917e0..356fcfa 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -5,225 +5,68 @@
 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
-	}
-
-	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) {
+	p := &exporter{marked: make(map[*types.Type]bool)}
+	for _, n := range typecheck.Target.Exports {
+		p.markObject(n)
+	}
+
 	// 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))
-	}
-	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)
-	}
-	return t
-}
-
-// 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))
-		}
-		return nil
-	}
-
-	n.Op = op
-	n.Pos = pos
-	n.SetClass(ctxt)
-	if ctxt == PFUNC {
-		n.Sym.SetFunc(true)
-	}
-	n.Type = t
-	return n
-}
-
-// 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)
-	}
-}
-
-// 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
-	}
-
-	n.Func = new(Func)
-	t.SetNname(asTypesNode(n))
-
-	if Debug.E != 0 {
-		fmt.Printf("import func %v%S\n", s, t)
-	}
-}
-
-// 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 {
-		return
-	}
-
-	if Debug.E != 0 {
-		fmt.Printf("import var %v %L\n", s, t)
-	}
-}
-
-// 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
-	}
-
-	if Debug.E != 0 {
-		fmt.Printf("import type %v = %L\n", s, t)
+	if base.Debug.Export != 0 {
+		fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, size)
 	}
 }
 
 func dumpasmhdr() {
-	b, err := bio.Create(asmhdr)
+	b, err := bio.Create(base.Flag.AsmHdr)
 	if err != nil {
-		Fatalf("%v", err)
+		base.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() {
+	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 OLITERAL:
-			t := n.Val().Ctype()
-			if t == CTFLT || t == CTCPLX {
+		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())
+			fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym().Name, n.Val())
 
-		case OTYPE:
-			t := n.Type
+		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))
+			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))
+					fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym().Name, f.Sym.Name, int(f.Offset))
 				}
 			}
 		}
@@ -231,3 +74,83 @@
 
 	b.Close()
 }
+
+type exporter struct {
+	marked map[*types.Type]bool // types already seen by markType
+}
+
+// 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)
+		}
+	}
+
+	p.markType(n.Type())
+}
+
+// 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.Kind() != types.TINTER {
+		for _, m := range t.Methods().Slice() {
+			if types.IsExported(m.Sym.Name) {
+				p.markObject(ir.AsNode(m.Nname))
+			}
+		}
+	}
+
+	// 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())
+		}
+
+	case types.TMAP:
+		p.markType(t.Key())
+		p.markType(t.Elem())
+
+	case types.TSTRUCT:
+		for _, f := range t.FieldSlice() {
+			if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
+				p.markType(f.Type)
+			}
+		}
+
+	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)
+			}
+		}
+	}
+}
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
deleted file mode 100644
index f92f5d0..0000000
--- 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, "<ctype=%d>", 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("<T>")
-		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 = <N>" initializing statements, hope they're always
-	// preceded by the DCL which will be re-parsed and typechecked to reproduce
-	// the "v = <N>" 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, "<N>")
-		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, "<struct>")
-
-	case OTINTER:
-		fmt.Fprint(s, "<inter>")
-
-	case OTFUNC:
-		fmt.Fprint(s, "<func>")
-
-	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, ".<nil>")
-			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, ".<nil>")
-			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, "<node %v>", 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 "<S>"
-	}
-
-	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("<S>")
-		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("<T>")
-		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, "<N>")
-		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, "<nil>")
-		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 929653e..0000000
--- 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 274930b..0000000
--- 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 d599a38..0000000
--- 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/iexport.go b/src/cmd/compile/internal/gc/iexport.go
deleted file mode 100644
index 1f53d8c..0000000
--- a/src/cmd/compile/internal/gc/iexport.go
+++ /dev/null
@@ -1,1515 +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 export.
-//
-// The indexed export data format is an evolution of the previous
-// binary export data format. Its chief contribution is introducing an
-// index table, which allows efficient random access of individual
-// declarations and inline function bodies. In turn, this allows
-// avoiding unnecessary work for compilation units that import large
-// packages.
-//
-//
-// The top-level data format is structured as:
-//
-//     Header struct {
-//         Tag        byte   // 'i'
-//         Version    uvarint
-//         StringSize uvarint
-//         DataSize   uvarint
-//     }
-//
-//     Strings [StringSize]byte
-//     Data    [DataSize]byte
-//
-//     MainIndex []struct{
-//         PkgPath   stringOff
-//         PkgName   stringOff
-//         PkgHeight uvarint
-//
-//         Decls []struct{
-//             Name   stringOff
-//             Offset declOff
-//         }
-//     }
-//
-//     Fingerprint [8]byte
-//
-// uvarint means a uint64 written out using uvarint encoding.
-//
-// []T means a uvarint followed by that many T objects. In other
-// words:
-//
-//     Len   uvarint
-//     Elems [Len]T
-//
-// stringOff means a uvarint that indicates an offset within the
-// Strings section. At that offset is another uvarint, followed by
-// that many bytes, which form the string value.
-//
-// declOff means a uvarint that indicates an offset within the Data
-// section where the associated declaration can be found.
-//
-//
-// There are five kinds of declarations, distinguished by their first
-// byte:
-//
-//     type Var struct {
-//         Tag  byte // 'V'
-//         Pos  Pos
-//         Type typeOff
-//     }
-//
-//     type Func struct {
-//         Tag       byte // 'F'
-//         Pos       Pos
-//         Signature Signature
-//     }
-//
-//     type Const struct {
-//         Tag   byte // 'C'
-//         Pos   Pos
-//         Value Value
-//     }
-//
-//     type Type struct {
-//         Tag        byte // 'T'
-//         Pos        Pos
-//         Underlying typeOff
-//
-//         Methods []struct{  // omitted if Underlying is an interface type
-//             Pos       Pos
-//             Name      stringOff
-//             Recv      Param
-//             Signature Signature
-//         }
-//     }
-//
-//     type Alias struct {
-//         Tag  byte // 'A'
-//         Pos  Pos
-//         Type typeOff
-//     }
-//
-//
-// typeOff means a uvarint that either indicates a predeclared type,
-// or an offset into the Data section. If the uvarint is less than
-// predeclReserved, then it indicates the index into the predeclared
-// types list (see predeclared in bexport.go for order). Otherwise,
-// subtracting predeclReserved yields the offset of a type descriptor.
-//
-// Value means a type and type-specific value. See
-// (*exportWriter).value for details.
-//
-//
-// There are nine kinds of type descriptors, distinguished by an itag:
-//
-//     type DefinedType struct {
-//         Tag     itag // definedType
-//         Name    stringOff
-//         PkgPath stringOff
-//     }
-//
-//     type PointerType struct {
-//         Tag  itag // pointerType
-//         Elem typeOff
-//     }
-//
-//     type SliceType struct {
-//         Tag  itag // sliceType
-//         Elem typeOff
-//     }
-//
-//     type ArrayType struct {
-//         Tag  itag // arrayType
-//         Len  uint64
-//         Elem typeOff
-//     }
-//
-//     type ChanType struct {
-//         Tag  itag   // chanType
-//         Dir  uint64 // 1 RecvOnly; 2 SendOnly; 3 SendRecv
-//         Elem typeOff
-//     }
-//
-//     type MapType struct {
-//         Tag  itag // mapType
-//         Key  typeOff
-//         Elem typeOff
-//     }
-//
-//     type FuncType struct {
-//         Tag       itag // signatureType
-//         PkgPath   stringOff
-//         Signature Signature
-//     }
-//
-//     type StructType struct {
-//         Tag     itag // structType
-//         PkgPath stringOff
-//         Fields []struct {
-//             Pos      Pos
-//             Name     stringOff
-//             Type     typeOff
-//             Embedded bool
-//             Note     stringOff
-//         }
-//     }
-//
-//     type InterfaceType struct {
-//         Tag     itag // interfaceType
-//         PkgPath stringOff
-//         Embeddeds []struct {
-//             Pos  Pos
-//             Type typeOff
-//         }
-//         Methods []struct {
-//             Pos       Pos
-//             Name      stringOff
-//             Signature Signature
-//         }
-//     }
-//
-//
-//     type Signature struct {
-//         Params   []Param
-//         Results  []Param
-//         Variadic bool  // omitted if Results is empty
-//     }
-//
-//     type Param struct {
-//         Pos  Pos
-//         Name stringOff
-//         Type typOff
-//     }
-//
-//
-// Pos encodes a file:line:column triple, incorporating a simple delta
-// encoding scheme within a data object. See exportWriter.pos for
-// details.
-//
-//
-// Compiler-specific details.
-//
-// cmd/compile writes out a second index for inline bodies and also
-// appends additional compiler-specific details after declarations.
-// Third-party tools are not expected to depend on these details and
-// they're expected to change much more rapidly, so they're omitted
-// here. See exportWriter's varExt/funcExt/etc methods for details.
-
-package gc
-
-import (
-	"bufio"
-	"bytes"
-	"cmd/compile/internal/types"
-	"cmd/internal/goobj"
-	"cmd/internal/src"
-	"crypto/md5"
-	"encoding/binary"
-	"fmt"
-	"io"
-	"math/big"
-	"sort"
-	"strings"
-)
-
-// Current indexed export format version. Increase with each format change.
-// 1: added column details to Pos
-// 0: Go1.11 encoding
-const iexportVersion = 1
-
-// predeclReserved is the number of type offsets reserved for types
-// implicitly declared in the universe block.
-const predeclReserved = 32
-
-// An itag distinguishes the kind of type that was written into the
-// indexed export format.
-type itag uint64
-
-const (
-	// Types
-	definedType itag = iota
-	pointerType
-	sliceType
-	arrayType
-	chanType
-	mapType
-	signatureType
-	structType
-	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)
-		}
-	}
-
-	p := iexporter{
-		allPkgs:     map[*types.Pkg]bool{},
-		stringIndex: map[string]uint64{},
-		declIndex:   map[*Node]uint64{},
-		inlineIndex: map[*Node]uint64{},
-		typIndex:    map[*types.Type]uint64{},
-	}
-
-	for i, pt := range predeclared() {
-		p.typIndex[pt] = uint64(i)
-	}
-	if len(p.typIndex) > predeclReserved {
-		Fatalf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)
-	}
-
-	// Initialize work queue with exported declarations.
-	for _, n := range exportlist {
-		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())
-	}
-
-	// Append indices to data0 section.
-	dataLen := uint64(p.data0.Len())
-	w := p.newWriter()
-	w.writeIndex(p.declIndex, true)
-	w.writeIndex(p.inlineIndex, false)
-	w.flush()
-
-	// Assemble header.
-	var hdr intWriter
-	hdr.WriteByte('i')
-	hdr.uint64(iexportVersion)
-	hdr.uint64(uint64(p.strings.Len()))
-	hdr.uint64(dataLen)
-
-	// Flush output.
-	h := md5.New()
-	wr := io.MultiWriter(out, h)
-	io.Copy(wr, &hdr)
-	io.Copy(wr, &p.strings)
-	io.Copy(wr, &p.data0)
-
-	// 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[:])
-}
-
-// writeIndex writes out an object 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{}
-
-	// 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
-		for pkg := range w.p.allPkgs {
-			pkgObjs[pkg] = nil
-		}
-	}
-
-	for n := range index {
-		pkgObjs[n.Sym.Pkg] = append(pkgObjs[n.Sym.Pkg], n)
-	}
-
-	var pkgs []*types.Pkg
-	for pkg, objs := range pkgObjs {
-		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
-	})
-
-	w.uint64(uint64(len(pkgs)))
-	for _, pkg := range pkgs {
-		w.string(pkg.Path)
-		if mainIndex {
-			w.string(pkg.Name)
-			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])
-		}
-	}
-}
-
-type iexporter struct {
-	// allPkgs tracks all packages that have been referenced by
-	// the export data, so we can ensure to include them in the
-	// main index.
-	allPkgs map[*types.Pkg]bool
-
-	declTodo nodeQueue
-
-	strings     intWriter
-	stringIndex map[string]uint64
-
-	data0       intWriter
-	declIndex   map[*Node]uint64
-	inlineIndex map[*Node]uint64
-	typIndex    map[*types.Type]uint64
-}
-
-// stringOff returns the offset of s within the string section.
-// If not already present, it's added to the end.
-func (p *iexporter) stringOff(s string) uint64 {
-	off, ok := p.stringIndex[s]
-	if !ok {
-		off = uint64(p.strings.Len())
-		p.stringIndex[s] = off
-
-		p.strings.uint64(uint64(len(s)))
-		p.strings.WriteString(s)
-	}
-	return off
-}
-
-// 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)
-	}
-
-	// Don't export predeclared declarations.
-	if n.Sym.Pkg == builtinpkg || n.Sym.Pkg == unsafepkg {
-		return
-	}
-
-	if _, ok := p.declIndex[n]; ok {
-		return
-	}
-
-	p.declIndex[n] = ^uint64(0) // mark n present in work queue
-	p.declTodo.pushRight(n)
-}
-
-// exportWriter handles writing out individual data section chunks.
-type exportWriter struct {
-	p *iexporter
-
-	data       intWriter
-	currPkg    *types.Pkg
-	prevFile   string
-	prevLine   int64
-	prevColumn int64
-}
-
-func (p *iexporter) doDecl(n *Node) {
-	w := p.newWriter()
-	w.setPkg(n.Sym.Pkg, false)
-
-	switch n.Op {
-	case ONAME:
-		switch n.Class() {
-		case PEXTERN:
-			// Variable.
-			w.tag('V')
-			w.pos(n.Pos)
-			w.typ(n.Type)
-			w.varExt(n)
-
-		case PFUNC:
-			if n.IsMethod() {
-				Fatalf("unexpected method: %v", n)
-			}
-
-			// Function.
-			w.tag('F')
-			w.pos(n.Pos)
-			w.signature(n.Type)
-			w.funcExt(n)
-
-		default:
-			Fatalf("unexpected class: %v, %v", n, n.Class())
-		}
-
-	case OLITERAL:
-		// Constant.
-		n = typecheck(n, ctxExpr)
-		w.tag('C')
-		w.pos(n.Pos)
-		w.value(n.Type, n.Val())
-
-	case OTYPE:
-		if IsAlias(n.Sym) {
-			// Alias.
-			w.tag('A')
-			w.pos(n.Pos)
-			w.typ(n.Type)
-			break
-		}
-
-		// Defined type.
-		w.tag('T')
-		w.pos(n.Pos)
-
-		underlying := n.Type.Orig
-		if underlying == types.Errortype.Orig {
-			// 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
-		}
-		w.typ(underlying)
-
-		t := n.Type
-		if t.IsInterface() {
-			w.typeExt(t)
-			break
-		}
-
-		ms := t.Methods()
-		w.uint64(uint64(ms.Len()))
-		for _, m := range ms.Slice() {
-			w.pos(m.Pos)
-			w.selector(m.Sym)
-			w.param(m.Type.Recv())
-			w.signature(m.Type)
-		}
-
-		w.typeExt(t)
-		for _, m := range ms.Slice() {
-			w.methExt(m)
-		}
-
-	default:
-		Fatalf("unexpected node: %v", n)
-	}
-
-	p.declIndex[n] = w.flush()
-}
-
-func (w *exportWriter) tag(tag byte) {
-	w.data.WriteByte(tag)
-}
-
-func (p *iexporter) doInline(f *Node) {
-	w := p.newWriter()
-	w.setPkg(fnpkg(f), false)
-
-	w.stmtList(asNodes(f.Func.Inl.Body))
-
-	p.inlineIndex[f] = w.flush()
-}
-
-func (w *exportWriter) pos(pos src.XPos) {
-	p := Ctxt.PosTable.Pos(pos)
-	file := p.Base().AbsFilename()
-	line := int64(p.RelLine())
-	column := int64(p.RelCol())
-
-	// Encode position relative to the last position: column
-	// delta, then line delta, then file name. We reserve the
-	// bottom bit of the column and line deltas to encode whether
-	// the remaining fields are present.
-	//
-	// Note: Because data objects may be read out of order (or not
-	// at all), we can only apply delta encoding within a single
-	// object. This is handled implicitly by tracking prevFile,
-	// prevLine, and prevColumn as fields of exportWriter.
-
-	deltaColumn := (column - w.prevColumn) << 1
-	deltaLine := (line - w.prevLine) << 1
-
-	if file != w.prevFile {
-		deltaLine |= 1
-	}
-	if deltaLine != 0 {
-		deltaColumn |= 1
-	}
-
-	w.int64(deltaColumn)
-	if deltaColumn&1 != 0 {
-		w.int64(deltaLine)
-		if deltaLine&1 != 0 {
-			w.string(file)
-		}
-	}
-
-	w.prevFile = file
-	w.prevLine = line
-	w.prevColumn = column
-}
-
-func (w *exportWriter) pkg(pkg *types.Pkg) {
-	// 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) {
-	// Ensure any referenced declarations are written out too.
-	w.p.pushDecl(n)
-
-	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")
-	}
-
-	// 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)
-		}
-	}
-
-	w.string(name)
-}
-
-func (w *exportWriter) typ(t *types.Type) {
-	w.data.uint64(w.p.typOff(t))
-}
-
-func (p *iexporter) newWriter() *exportWriter {
-	return &exportWriter{p: p}
-}
-
-func (w *exportWriter) flush() uint64 {
-	off := uint64(w.p.data0.Len())
-	io.Copy(&w.p.data0, &w.data)
-	return off
-}
-
-func (p *iexporter) typOff(t *types.Type) uint64 {
-	off, ok := p.typIndex[t]
-	if !ok {
-		w := p.newWriter()
-		w.doTyp(t)
-		off = predeclReserved + w.flush()
-		p.typIndex[t] = off
-	}
-	return off
-}
-
-func (w *exportWriter) startType(k itag) {
-	w.data.uint64(uint64(k))
-}
-
-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)
-		}
-
-		w.startType(definedType)
-		w.qualifiedIdent(typenod(t))
-		return
-	}
-
-	switch t.Etype {
-	case TPTR:
-		w.startType(pointerType)
-		w.typ(t.Elem())
-
-	case TSLICE:
-		w.startType(sliceType)
-		w.typ(t.Elem())
-
-	case TARRAY:
-		w.startType(arrayType)
-		w.uint64(uint64(t.NumElem()))
-		w.typ(t.Elem())
-
-	case TCHAN:
-		w.startType(chanType)
-		w.uint64(uint64(t.ChanDir()))
-		w.typ(t.Elem())
-
-	case TMAP:
-		w.startType(mapType)
-		w.typ(t.Key())
-		w.typ(t.Elem())
-
-	case TFUNC:
-		w.startType(signatureType)
-		w.setPkg(t.Pkg(), true)
-		w.signature(t)
-
-	case TSTRUCT:
-		w.startType(structType)
-		w.setPkg(t.Pkg(), true)
-
-		w.uint64(uint64(t.NumFields()))
-		for _, f := range t.FieldSlice() {
-			w.pos(f.Pos)
-			w.selector(f.Sym)
-			w.typ(f.Type)
-			w.bool(f.Embedded != 0)
-			w.string(f.Note)
-		}
-
-	case TINTER:
-		var embeddeds, methods []*types.Field
-		for _, m := range t.Methods().Slice() {
-			if m.Sym != nil {
-				methods = append(methods, m)
-			} else {
-				embeddeds = append(embeddeds, m)
-			}
-		}
-
-		w.startType(interfaceType)
-		w.setPkg(t.Pkg(), true)
-
-		w.uint64(uint64(len(embeddeds)))
-		for _, f := range embeddeds {
-			w.pos(f.Pos)
-			w.typ(f.Type)
-		}
-
-		w.uint64(uint64(len(methods)))
-		for _, f := range methods {
-			w.pos(f.Pos)
-			w.selector(f.Sym)
-			w.signature(f.Type)
-		}
-
-	default:
-		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 write {
-		w.pkg(pkg)
-	}
-
-	w.currPkg = pkg
-}
-
-func (w *exportWriter) signature(t *types.Type) {
-	w.paramList(t.Params().FieldSlice())
-	w.paramList(t.Results().FieldSlice())
-	if n := t.Params().NumFields(); n > 0 {
-		w.bool(t.Params().Field(n - 1).IsDDD())
-	}
-}
-
-func (w *exportWriter) paramList(fs []*types.Field) {
-	w.uint64(uint64(len(fs)))
-	for _, f := range fs {
-		w.param(f)
-	}
-}
-
-func (w *exportWriter) param(f *types.Field) {
-	w.pos(f.Pos)
-	w.localIdent(origSym(f.Sym), 0)
-	w.typ(f.Type)
-}
-
-func constTypeOf(typ *types.Type) Ctype {
-	switch typ {
-	case types.UntypedInt, types.UntypedRune:
-		return CTINT
-	case types.UntypedFloat:
-		return CTFLT
-	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 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)
-	}
-	w.typ(typ)
-
-	// Each type has only one admissible constant representation,
-	// so we could type switch directly on v.U here. However,
-	// switching on the type increases symmetry with import logic
-	// 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)
-	}
-}
-
-func intSize(typ *types.Type) (signed bool, maxBytes uint) {
-	if typ.IsUntyped() {
-		return true, Mpprec / 8
-	}
-
-	switch typ.Etype {
-	case TFLOAT32, TCOMPLEX64:
-		return true, 3
-	case TFLOAT64, TCOMPLEX128:
-		return true, 7
-	}
-
-	signed = typ.IsSigned()
-	maxBytes = uint(typ.Size())
-
-	// 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:
-		maxBytes = 8
-	}
-
-	return
-}
-
-// mpint exports a multi-precision integer.
-//
-// For unsigned types, small values are written out as a single
-// byte. Larger values are written out as a length-prefixed big-endian
-// byte string, where the length prefix is encoded as its complement.
-// For example, bytes 0, 1, and 2 directly represent the integer
-// values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-,
-// 2-, and 3-byte big-endian string follow.
-//
-// Encoding for signed types use the same general approach as for
-// unsigned types, except small values use zig-zag encoding and the
-// bottom bit of length prefix byte for large values is reserved as a
-// sign bit.
-//
-// The exact boundary between small and large encodings varies
-// 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) {
-	signed, maxBytes := intSize(typ)
-
-	negative := x.Sign() < 0
-	if !signed && negative {
-		Fatalf("negative unsigned integer; type %v, value %v", typ, x)
-	}
-
-	b := x.Bytes()
-	if len(b) > 0 && b[0] == 0 {
-		Fatalf("leading zeros")
-	}
-	if uint(len(b)) > maxBytes {
-		Fatalf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)
-	}
-
-	maxSmall := 256 - maxBytes
-	if signed {
-		maxSmall = 256 - 2*maxBytes
-	}
-	if maxBytes == 1 {
-		maxSmall = 256
-	}
-
-	// Check if x can use small value encoding.
-	if len(b) <= 1 {
-		var ux uint
-		if len(b) == 1 {
-			ux = uint(b[0])
-		}
-		if signed {
-			ux <<= 1
-			if negative {
-				ux--
-			}
-		}
-		if ux < maxSmall {
-			w.data.WriteByte(byte(ux))
-			return
-		}
-	}
-
-	n := 256 - uint(len(b))
-	if signed {
-		n = 256 - 2*uint(len(b))
-		if negative {
-			n |= 1
-		}
-	}
-	if n < maxSmall || n >= 256 {
-		Fatalf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)
-	}
-
-	w.data.WriteByte(byte(n))
-	w.data.Write(b)
-}
-
-// mpfloat exports a multi-precision floating point number.
-//
-// The number's value is decomposed into mantissa × 2**exponent, where
-// 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) {
-	if f.IsInf() {
-		Fatalf("infinite constant")
-	}
-
-	// Break into f = mant × 2**exp, with 0.5 <= mant < 1.
-	var mant big.Float
-	exp := int64(f.MantExp(&mant))
-
-	// Scale so that mant is an integer.
-	prec := mant.MinPrec()
-	mant.SetMantExp(&mant, int(prec))
-	exp -= int64(prec)
-
-	manti, acc := mant.Int(nil)
-	if acc != big.Exact {
-		Fatalf("mantissa scaling failed for %f (%s)", f, acc)
-	}
-	w.mpint(manti, typ)
-	if manti.Sign() != 0 {
-		w.int64(exp)
-	}
-}
-
-func (w *exportWriter) bool(b bool) bool {
-	var x uint64
-	if b {
-		x = 1
-	}
-	w.uint64(x)
-	return b
-}
-
-func (w *exportWriter) int64(x int64)   { w.data.int64(x) }
-func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) }
-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) funcExt(n *Node) {
-	w.linkname(n.Sym)
-	w.symIdx(n.Sym)
-
-	// Escape analysis.
-	for _, fs := range &types.RecvsParams {
-		for _, f := range fs(n.Type).FieldSlice() {
-			w.string(f.Note)
-		}
-	}
-
-	// Inline body.
-	if n.Func.Inl != nil {
-		w.uint64(1 + uint64(n.Func.Inl.Cost))
-		if n.Func.ExportInline() {
-			w.p.doInline(n)
-		}
-
-		// 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)
-		}
-	} else {
-		w.uint64(0)
-	}
-}
-
-func (w *exportWriter) methExt(m *types.Field) {
-	w.bool(m.Nointerface())
-	w.funcExt(asNode(m.Type.Nname()))
-}
-
-func (w *exportWriter) linkname(s *types.Sym) {
-	w.string(s.Linkname)
-}
-
-func (w *exportWriter) symIdx(s *types.Sym) {
-	lsym := s.Linksym()
-	if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
-		// Don't export index for non-package symbols, linkname'd symbols,
-		// and symbols without an index. They can only be referenced by
-		// name.
-		w.int64(-1)
-	} else {
-		// For a defined symbol, export its index.
-		// For re-exporting an imported symbol, pass its index through.
-		w.int64(int64(lsym.SymIdx))
-	}
-}
-
-func (w *exportWriter) typeExt(t *types.Type) {
-	// Export whether this type is marked notinheap.
-	w.bool(t.NotInHeap())
-	// For type T, export the index of type descriptor symbols of T and *T.
-	if i, ok := typeSymIdx[t]; ok {
-		w.int64(i[0])
-		w.int64(i[1])
-		return
-	}
-	w.symIdx(typesym(t))
-	w.symIdx(typesym(t.PtrTo()))
-}
-
-// Inline bodies.
-
-func (w *exportWriter) stmtList(list Nodes) {
-	for _, n := range list.Slice() {
-		w.node(n)
-	}
-	w.op(OEND)
-}
-
-func (w *exportWriter) node(n *Node) {
-	if opprec[n.Op] < 0 {
-		w.stmt(n)
-	} else {
-		w.expr(n)
-	}
-}
-
-// 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) {
-		// can't use stmtList here since we don't want the final OEND
-		for _, n := range n.Ninit.Slice() {
-			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)
-
-	// case ODCLFIELD:
-	//	unimplemented - handled by default case
-
-	case OAS:
-		// Don't export "v = <N>" initializing statements, hope they're always
-		// preceded by the DCL which will be re-parsed and typecheck to reproduce
-		// the "v = <N>" again.
-		if n.Right != nil {
-			w.op(OAS)
-			w.pos(n.Pos)
-			w.expr(n.Left)
-			w.expr(n.Right)
-		}
-
-	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 OAS2:
-		w.op(OAS2)
-		w.pos(n.Pos)
-		w.exprList(n.List)
-		w.exprList(n.Rlist)
-
-	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 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 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)
-
-	default:
-		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
-
-	cases := sw.List.Slice()
-	w.uint64(uint64(len(cases)))
-	for _, cas := range cases {
-		if cas.Op != OCASE {
-			Fatalf("expected OCASE, got %v", cas)
-		}
-		w.pos(cas.Pos)
-		w.stmtList(cas.List)
-		if namedTypeSwitch {
-			w.localName(cas.Rlist.First())
-		}
-		w.stmtList(cas.Nbody)
-	}
-}
-
-func (w *exportWriter) exprList(list Nodes) {
-	for _, n := range list.Slice() {
-		w.expr(n)
-	}
-	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
-	// }
-
-	// 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
-	}
-
-	switch op := n.Op; 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
-		}
-
-		// Package scope name.
-		if (n.Class() == PEXTERN || n.Class() == PFUNC) && !n.isBlank() {
-			w.op(ONONAME)
-			w.qualifiedIdent(n)
-			break
-		}
-
-		// Function scope name.
-		w.op(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 OTYPESW:
-		w.op(OTYPESW)
-		w.pos(n.Pos)
-		var s *types.Sym
-		if n.Left != nil {
-			if n.Left.Op != ONONAME {
-				Fatalf("expected ONONAME, got %v", n.Left)
-			}
-			s = n.Left.Sym
-		}
-		w.localIdent(s, 0) // declared pseudo-variable, if any
-		w.exprsOrNil(n.Right, nil)
-
-	// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
-	// 	should have been resolved by typechecking - handled by default case
-
-	// case OCLOSURE:
-	//	unimplemented - handled by default case
-
-	// 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)
-		w.exprList(n.List)
-
-	case OKEY:
-		w.op(OKEY)
-		w.pos(n.Pos)
-		w.exprsOrNil(n.Left, n.Right)
-
-	// 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:
-		// 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
-		}
-		// only append() calls may contain '...' arguments
-		if op == OAPPEND {
-			w.bool(n.IsDDD())
-		} else if n.IsDDD() {
-			Fatalf("exporter: unexpected '...' with %v call", 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)
-		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)
-		}
-
-	// unary expressions
-	case OPLUS, ONEG, OADDR, OBITNOT, ODEREF, ONOT, ORECV:
-		w.op(op)
-		w.pos(n.Pos)
-		w.expr(n.Left)
-
-	// 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)
-		w.exprList(n.List)
-
-	case 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))
-	}
-}
-
-func (w *exportWriter) op(op Op) {
-	w.uint64(uint64(op))
-}
-
-func (w *exportWriter) exprsOrNil(a, b *Node) {
-	ab := 0
-	if a != nil {
-		ab |= 1
-	}
-	if b != nil {
-		ab |= 2
-	}
-	w.uint64(uint64(ab))
-	if ab&1 != 0 {
-		w.expr(a)
-	}
-	if ab&2 != 0 {
-		w.node(b)
-	}
-}
-
-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) 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
-	}
-
-	w.localIdent(n.Sym, v)
-}
-
-func (w *exportWriter) localIdent(s *types.Sym, v int32) {
-	// Anonymous parameters.
-	if s == nil {
-		w.string("")
-		return
-	}
-
-	name := s.Name
-	if name == "_" {
-		w.string("_")
-		return
-	}
-
-	// 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)
-	}
-
-	if !types.IsExported(name) && s.Pkg != w.currPkg {
-		Fatalf("weird package in name: %v => %v, not %q", s, name, w.currPkg.Path)
-	}
-
-	w.string(name)
-}
-
-type intWriter struct {
-	bytes.Buffer
-}
-
-func (w *intWriter) int64(x int64) {
-	var buf [binary.MaxVarintLen64]byte
-	n := binary.PutVarint(buf[:], x)
-	w.Write(buf[:n])
-}
-
-func (w *intWriter) uint64(x uint64) {
-	var buf [binary.MaxVarintLen64]byte
-	n := binary.PutUvarint(buf[:], x)
-	w.Write(buf[:n])
-}
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
deleted file mode 100644
index c0114d0..0000000
--- 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 ec9cc4b..0000000
--- 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 600d12b..0000000
--- 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 a6963a3..726a068 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 <key>[=<value>]
-
-<key> is one of:
-
-`
-
-const debugHelpFooter = `
-<value> 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")
+	dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
 
-	if smallFrames {
-		maxStackVarSize = 128 * 1024
-		maxImplicitStackVarSize = 16 * 1024
+	if !base.EnableTrace && base.Flag.LowerT {
+		log.Fatalf("compiler not built with support for -t")
 	}
 
-	Ctxt.Flag_shared = flag_dynlink || flag_shared
-	Ctxt.Flag_dynlink = flag_dynlink
-	Ctxt.Flag_optimize = Debug.N == 0
+	// 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
+	}
 
-	Ctxt.Debugasm = Debug.S
-	Ctxt.Debugvlog = Debug_vlog
-	if flagDWARF {
-		Ctxt.DebugInfo = debuginfo
-		Ctxt.GenAbstractFunc = genAbstractFunc
-		Ctxt.DwFixups = obj.NewDwarfFixupTable(Ctxt)
+	if base.Flag.SmallFrames {
+		ir.MaxStackVarSize = 128 * 1024
+		ir.MaxImplicitStackVarSize = 16 * 1024
+	}
+
+	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
-		genDwarfInline = 0
-		Ctxt.Flag_locationlists = false
+		base.Flag.GenDwarfInl = 0
+		base.Ctxt.Flag_locationlists = false
+	}
+	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 flag.NArg() < 1 && debugstr != "help" && debugstr != "ssa/help" {
-		usage()
+	types.ParseLangFlag()
+
+	if base.Flag.SymABIs != "" {
+		ssagen.ReadSymABIs(base.Flag.SymABIs, base.Ctxt.Pkgpath)
 	}
 
-	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.Compiling(base.NoInstrumentPkgs) {
+		base.Flag.Race = false
+		base.Flag.MSan = false
 	}
 
-	checkLang()
-
-	if symabisPath != "" {
-		readSymABIs(symabisPath, myimportpath)
-	}
-
-	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
-	}
-
+	ssagen.Arch.LinkArch.Init(base.Ctxt)
 	startProfile()
-
-	if flag_race && flag_msan {
-		log.Fatal("cannot use both -race and -msan")
+	if base.Flag.Race || base.Flag.MSan {
+		base.Flag.Cfg.Instrumenting = true
 	}
-	if flag_race || flag_msan {
-		// -race and -msan imply -d=checkptr for now.
-		Debug_checkptr = 1
+	if base.Flag.Dwarf {
+		dwarf.EnableLogging(base.Debug.DwarfInl != 0)
 	}
-	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 base.Debug.SoftFloat != 0 {
+		ssagen.Arch.SoftFloat = 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.JSON != "" { // parse version,destination from json logging optimization.
+		logopt.LogJsonOption(base.Flag.JSON)
 	}
 
-	// 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)
+	ir.EscFmt = escape.Fmt
+	ir.IsIntrinsicCall = ssagen.IsIntrinsicCall
+	inline.SSADumpInline = ssagen.DumpInline
+	ssagen.InitEnv()
+	ssagen.InitTables()
+
+	types.PtrSize = ssagen.Arch.LinkArch.PtrSize
+	types.RegSize = ssagen.Arch.LinkArch.RegSize
+	types.MaxWidth = ssagen.Arch.MAXWIDTH
+
+	typecheck.Target = new(ir.Package)
+
+	typecheck.NeedITab = func(t, iface *types.Type) { reflectdata.ITabAddr(t, iface) }
+	typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): TypeSym for lock?
+
+	base.AutogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
+
+	typecheck.InitUniverse()
+
+	// Parse and typecheck input.
+	noder.LoadPackage(flag.Args())
+
+	dwarfgen.RecordPackageName()
+	ssagen.CgoSymABIs()
+
+	// Build init task.
+	if initTask := pkginit.Task(); initTask != nil {
+		typecheck.Export(initTask)
+	}
+
+	// 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))
 		}
 	}
 
-	if compiling_runtime {
-		// Runtime can't use -d=checkptr, at least not yet.
-		Debug_checkptr = 0
-
-		// Fuzzing the runtime isn't interesting either.
-		Debug_libfuzzer = 0
+	// 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
 	}
+	typecheck.IncrementalAddrtaken = true
 
-	// set via a -d flag
-	Ctxt.Debugpcln = Debug_pctab
-	if flagDWARF {
-		dwarf.EnableLogging(Debug_gendwarfinl != 0)
-	}
-
-	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
-	}
-
-	if jsonLogOpt != "" { // parse version,destination from json logging optimization.
-		logopt.LogJsonOption(jsonLogOpt)
-	}
-
-	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]
-		}
-	}
-
-	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))
-	}
-	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
-
-	autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
-
-	timings.Start("fe", "loadsys")
-	loadsys()
-
-	timings.Start("fe", "parse")
-	lines := parseFiles(flag.Args())
-	timings.Stop()
-	timings.AddEvent(int64(lines), "lines")
-
-	finishUniverse()
-
-	recordPackageName()
-
-	typecheckok = 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 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)
-		}
-	}
-
-	// 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++
-		}
-	}
-	// 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)
-		}
-	}
-	capturevarscomplete = 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 @@
 	// 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()
+	ir.CurFunc = nil
+	reflectdata.CompileITabs()
 
-	// 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)
+	// 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)
+	logopt.FlushLoggedOpts(base.Ctxt, base.Ctxt.Pkgpath)
+	base.ExitIfErrors()
 
-	if nerrors+nsavederrors != 0 {
-		errorexit()
-	}
+	base.FlushErrors()
+	base.Timer.Stop()
 
-	flusherrors()
-	timings.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 @@
 	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 @@
 	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 == "!<arch>\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 401aef3..0000000
--- 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 340350b..0000000
--- 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 7494c3e..0000000
--- 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 32aa7c5..0472af7 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 @@
 )
 
 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("!<arch>\n")
@@ -78,17 +71,17 @@
 
 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 @@
 	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 dumpdata() {
-	externs := len(externdcl)
-	xtops := len(xtop)
+	numExterns := len(typecheck.Target.Externs)
+	numDecls := len(typecheck.Target.Decls)
 
-	dumpglobls()
-	addptabs()
-	exportlistLen := len(exportlist)
-	addsignats(externdcl)
-	dumpsignats()
-	dumptabs()
-	ptabsLen := len(ptabs)
-	itabsLen := len(itabs)
-	dumpimportstrings()
-	dumpbasictypes()
+	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 @@
 	// 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)
+	obj.WriteObjFile(base.Ctxt, bout)
 }
 
-func addptabs() {
-	if !Ctxt.Flag_dynlink || localpkg.Name != "main" {
+func dumpGlobal(n *ir.Name) {
+	if n.Type() == nil {
+		base.Fatalf("external %v nil type\n", n)
+	}
+	if n.Class == ir.PFUNC {
 		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)})
-		}
-	}
-}
-
-func dumpGlobal(n *Node) {
-	if n.Type == nil {
-		Fatalf("external %v nil type\n", n)
-	}
-	if n.Class() == PFUNC {
+	if n.Sym().Pkg != types.LocalPkg {
 		return
 	}
-	if n.Sym.Pkg != 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 {
-			return
-		}
-		// Ideal integers we export as int (if they fit).
-		t = types.Types[TINT]
-	default:
+	if !t.IsInteger() {
 		return
 	}
-	Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64Val())
+	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
+		}
+	}
+	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 @@
 // 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
+	if nam.Type() != nil && !nam.Type().HasPointers() {
+		flags |= obj.NOPTR
+	}
+	base.Ctxt.Globl(s, nam.Type().Width, flags)
+	if nam.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 duint8(s *obj.LSym, off int, v uint8) int {
-	return duintxx(s, off, uint64(v), 1)
+func dumpembeds() {
+	for _, v := range typecheck.Target.Embeds {
+		staticdata.WriteEmbed(v)
+	}
 }
 
-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)))
-	}
-	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)
-	}
-
-	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)
-	}
-
-	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
+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())
 		}
-		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)
-		}
-
-	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 41d5883..0000000
--- 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 30e1535..0000000
--- 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 353f4b0..0000000
--- 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 b1db298..0000000
--- 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/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
deleted file mode 100644
index 3552617..0000000
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ /dev/null
@@ -1,93 +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"
-	"cmd/internal/sys"
-)
-
-// The racewalk pass is currently handled in three parts.
-//
-// First, for flag_race, it inserts calls to racefuncenter and
-// racefuncexit at the start and end (respectively) of each
-// function. This is handled below.
-//
-// Second, during buildssa, it inserts appropriate instrumentation
-// calls immediately before each memory load or store. This is handled
-// by the (*state).instrument method in ssa.go, so here we just set
-// the Func.InstrumentBody flag as needed. For background on why this
-// is done during SSA construction rather than a separate SSA pass,
-// see issue #19054.
-//
-// Third we remove calls to racefuncenter and racefuncexit, for leaf
-// functions without instrumented operations. This is done as part of
-// ssa opt pass via special rule.
-
-// TODO(dvyukov): do not instrument initialization as writes:
-// a := make([]int, 10)
-
-// Do not instrument the following packages at all,
-// at best instrumentation would cause infinite recursion.
-var omit_pkgs = []string{
-	"runtime/internal/atomic",
-	"runtime/internal/sys",
-	"runtime/internal/math",
-	"runtime",
-	"runtime/race",
-	"runtime/msan",
-	"internal/cpu",
-}
-
-// 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
-	}
-}
diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
deleted file mode 100644
index 1b4d765..0000000
--- 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/reflect.go b/src/cmd/compile/internal/gc/reflect.go
deleted file mode 100644
index 9401eba..0000000
--- a/src/cmd/compile/internal/gc/reflect.go
+++ /dev/null
@@ -1,1901 +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/gcprog"
-	"cmd/internal/obj"
-	"cmd/internal/objabi"
-	"cmd/internal/src"
-	"fmt"
-	"os"
-	"sort"
-	"strings"
-	"sync"
-)
-
-type itabEntry struct {
-	t, itype *types.Type
-	lsym     *obj.LSym // symbol of the itab itself
-
-	// symbols of each method in
-	// the itab, sorted by byte offset;
-	// filled in by peekitabs
-	entries []*obj.LSym
-}
-
-type ptabEntry struct {
-	s *types.Sym
-	t *types.Type
-}
-
-// runtime interface and reflection data structures
-var (
-	signatmu    sync.Mutex // protects signatset and signatslice
-	signatset   = make(map[*types.Type]struct{})
-	signatslice []*types.Type
-
-	itabs []itabEntry
-	ptabs []ptabEntry
-)
-
-type Sig struct {
-	name  *types.Sym
-	isym  *types.Sym
-	tsym  *types.Sym
-	type_ *types.Type
-	mtype *types.Type
-}
-
-// Builds a type representing a Bucket structure for
-// the given map type. This type is not visible to users -
-// we include only enough information to generate a correct GC
-// program for it.
-// Make sure this stays in sync with runtime/map.go.
-const (
-	BUCKETSIZE  = 8
-	MAXKEYSIZE  = 128
-	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 uncommonSize(t *types.Type) int { // Sizeof(runtime.uncommontype{})
-	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
-}
-
-// bmap makes the map bucket type given the type of the map.
-func bmap(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)
-	if keytype.Width > MAXKEYSIZE {
-		keytype = types.NewPtr(keytype)
-	}
-	if elemtype.Width > MAXELEMSIZE {
-		elemtype = types.NewPtr(elemtype)
-	}
-
-	field := make([]*types.Field, 0, 5)
-
-	// The first field is: uint8 topbits[BUCKETSIZE].
-	arr := types.NewArray(types.Types[TUINT8], BUCKETSIZE)
-	field = append(field, makefield("topbits", arr))
-
-	arr = types.NewArray(keytype, BUCKETSIZE)
-	arr.SetNoalg(true)
-	keys := makefield("keys", arr)
-	field = append(field, keys)
-
-	arr = types.NewArray(elemtype, BUCKETSIZE)
-	arr.SetNoalg(true)
-	elems := makefield("elems", arr)
-	field = append(field, elems)
-
-	// If keys and elems have no pointers, the map implementation
-	// can keep a list of overflow pointers on the side so that
-	// buckets can be marked as having no pointers.
-	// 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)
-	if !elemtype.HasPointers() && !keytype.HasPointers() {
-		otyp = types.Types[TUINTPTR]
-	}
-	overflow := makefield("overflow", otyp)
-	field = append(field, overflow)
-
-	// link up fields
-	bucket.SetNoalg(true)
-	bucket.SetFields(field[:])
-	dowidth(bucket)
-
-	// Check invariants that map code depends on.
-	if !IsComparable(t.Key()) {
-		Fatalf("unsupported map key type for %v", t)
-	}
-	if BUCKETSIZE < 8 {
-		Fatalf("bucket size too small for proper alignment")
-	}
-	if keytype.Align > BUCKETSIZE {
-		Fatalf("key align too big for %v", t)
-	}
-	if elemtype.Align > BUCKETSIZE {
-		Fatalf("elem align too big for %v", t)
-	}
-	if keytype.Width > MAXKEYSIZE {
-		Fatalf("key size to large for %v", t)
-	}
-	if elemtype.Width > MAXELEMSIZE {
-		Fatalf("elem size to large for %v", t)
-	}
-	if t.Key().Width > MAXKEYSIZE && !keytype.IsPtr() {
-		Fatalf("key indirect incorrect for %v", t)
-	}
-	if t.Elem().Width > MAXELEMSIZE && !elemtype.IsPtr() {
-		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)
-	}
-	if elemtype.Width%int64(elemtype.Align) != 0 {
-		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)
-	}
-	if bucket.Align%elemtype.Align != 0 {
-		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)
-	}
-	if elems.Offset%int64(elemtype.Align) != 0 {
-		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)
-	}
-
-	t.MapType().Bucket = bucket
-
-	bucket.StructType().Map = t
-	return bucket
-}
-
-// hmap 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 {
-	if t.MapType().Hmap != nil {
-		return t.MapType().Hmap
-	}
-
-	bmap := bmap(t)
-
-	// build a struct:
-	// type hmap struct {
-	//    count      int
-	//    flags      uint8
-	//    B          uint8
-	//    noverflow  uint16
-	//    hash0      uint32
-	//    buckets    *bmap
-	//    oldbuckets *bmap
-	//    nevacuate  uintptr
-	//    extra      unsafe.Pointer // *mapextra
-	// }
-	// 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("oldbuckets", types.NewPtr(bmap)),
-		makefield("nevacuate", types.Types[TUINTPTR]),
-		makefield("extra", types.Types[TUNSAFEPTR]),
-	}
-
-	hmap := types.New(TSTRUCT)
-	hmap.SetNoalg(true)
-	hmap.SetFields(fields)
-	dowidth(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)
-	}
-
-	t.MapType().Hmap = hmap
-	hmap.StructType().Map = t
-	return hmap
-}
-
-// hiter 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 {
-	if t.MapType().Hiter != nil {
-		return t.MapType().Hiter
-	}
-
-	hmap := hmap(t)
-	bmap := bmap(t)
-
-	// build a struct:
-	// type hiter struct {
-	//    key         *Key
-	//    elem        *Elem
-	//    t           unsafe.Pointer // *MapType
-	//    h           *hmap
-	//    buckets     *bmap
-	//    bptr        *bmap
-	//    overflow    unsafe.Pointer // *[]*bmap
-	//    oldoverflow unsafe.Pointer // *[]*bmap
-	//    startBucket uintptr
-	//    offset      uint8
-	//    wrapped     bool
-	//    B           uint8
-	//    i           uint8
-	//    bucket      uintptr
-	//    checkBucket uintptr
-	// }
-	// must match runtime/map.go:hiter.
-	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("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]),
-	}
-
-	// build iterator struct holding the above fields
-	hiter := types.New(TSTRUCT)
-	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)
-	}
-	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 {
-	// method type
-	mt := methtype(t)
-
-	if mt == nil {
-		return nil
-	}
-	expandmeth(mt)
-
-	// type stored in interface word
-	it := t
-
-	if !isdirectiface(it) {
-		it = types.NewPtr(t)
-	}
-
-	// make list of methods for t,
-	// generating code if necessary.
-	var ms []*Sig
-	for _, f := range mt.AllMethods().Slice() {
-		if !f.IsMethod() {
-			Fatalf("non-method on %v method %v %v\n", mt, f.Sym, f)
-		}
-		if f.Type.Recv() == nil {
-			Fatalf("receiver with no type on %v method %v %v\n", 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) {
-			continue
-		}
-
-		sig := &Sig{
-			name:  method,
-			isym:  methodSym(it, method),
-			tsym:  methodSym(t, method),
-			type_: methodfunc(f.Type, t),
-			mtype: methodfunc(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
-	for _, f := range t.Fields().Slice() {
-		if f.Type.Etype != TFUNC || f.Sym == nil {
-			continue
-		}
-		if f.Sym.IsBlank() {
-			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)
-			}
-		}
-
-		sig := &Sig{
-			name:  f.Sym,
-			mtype: f.Type,
-			type_: methodfunc(f.Type, nil),
-		}
-		methods = append(methods, sig)
-
-		// NOTE(rsc): Perhaps an oversight that
-		// 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)
-		}
-	}
-
-	return methods
-}
-
-func dimportpath(p *types.Pkg) {
-	if p.Pathsym != nil {
-		return
-	}
-
-	// 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
-	// both of them, so just produce one for localpkg.
-	if myimportpath == "runtime" && p == Runtimepkg {
-		return
-	}
-
-	str := p.Path
-	if p == localpkg {
-		// Note: myimportpath != "", or else dgopkgpath won't call dimportpath.
-		str = myimportpath
-	}
-
-	s := Ctxt.Lookup("type..importpath." + p.Prefix + ".")
-	ot := dnameData(s, 0, str, "", nil, false)
-	ggloblsym(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)
-	}
-
-	if pkg == localpkg && myimportpath == "" {
-		// 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)
-	}
-
-	dimportpath(pkg)
-	return dsymptr(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)
-	}
-	if pkg == localpkg && myimportpath == "" {
-		// 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)
-	}
-
-	dimportpath(pkg)
-	return dsymptrOff(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)
-	}
-	nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name))
-	return dsymptr(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)
-	}
-	if len(tag) > 1<<16-1 {
-		Fatalf("tag too long: %s", tag)
-	}
-
-	// Encode name and tag. See reflect/type.go for details.
-	var bits byte
-	l := 1 + 2 + len(name)
-	if exported {
-		bits |= 1 << 0
-	}
-	if len(tag) > 0 {
-		l += 2 + len(tag)
-		bits |= 1 << 1
-	}
-	if pkg != nil {
-		bits |= 1 << 2
-	}
-	b := make([]byte, l)
-	b[0] = bits
-	b[1] = uint8(len(name) >> 8)
-	b[2] = uint8(len(name))
-	copy(b[3:], name)
-	if len(tag) > 0 {
-		tb := b[3+len(name):]
-		tb[0] = uint8(len(tag) >> 8)
-		tb[1] = uint8(len(tag))
-		copy(tb[2:], tag)
-	}
-
-	ot = int(s.WriteBytes(Ctxt, int64(ot), b))
-
-	if pkg != nil {
-		ot = dgopkgpathOff(s, ot, pkg)
-	}
-
-	return ot
-}
-
-var dnameCount int
-
-// dname creates a reflect.name for a struct field or method.
-func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym {
-	// Write out data as "type.." to signal two things to the
-	// linker, first that when dynamically linking, the symbol
-	// should be moved to a relro section, and second that the
-	// contents should not be decoded as a type.
-	sname := "type..namedata."
-	if pkg == nil {
-		// In the common case, share data with other packages.
-		if name == "" {
-			if exported {
-				sname += "-noname-exported." + tag
-			} else {
-				sname += "-noname-unexported." + tag
-			}
-		} else {
-			if exported {
-				sname += name + "." + tag
-			} else {
-				sname += name + "-" + tag
-			}
-		}
-	} else {
-		sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount)
-		dnameCount++
-	}
-	s := 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)
-	s.Set(obj.AttrContentAddressable, true)
-	return s
-}
-
-// dextratype dumps the fields of a runtime.uncommontype.
-// dataAdd is the offset in bytes after the header where the
-// 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 {
-		return ot
-	}
-	noff := int(Rnd(int64(ot), int64(Widthptr)))
-	if noff != ot {
-		Fatalf("unexpected alignment in dextratype for %v", t)
-	}
-
-	for _, a := range m {
-		dtypesym(a.type_)
-	}
-
-	ot = dgopkgpathOff(lsym, ot, typePkg(t))
-
-	dataAdd += uncommonSize(t)
-	mcount := len(m)
-	if mcount != int(uint16(mcount)) {
-		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)
-	}
-
-	ot = duint16(lsym, ot, uint16(mcount))
-	ot = duint16(lsym, ot, uint16(xcount))
-	ot = duint32(lsym, ot, uint32(dataAdd))
-	ot = duint32(lsym, ot, 0)
-	return ot
-}
-
-func typePkg(t *types.Type) *types.Pkg {
-	tsym := t.Sym
-	if tsym == nil {
-		switch t.Etype {
-		case TARRAY, TSLICE, TPTR, TCHAN:
-			if t.Elem() != nil {
-				tsym = t.Elem().Sym
-			}
-		}
-	}
-	if tsym != nil && t != types.Types[t.Etype] && t != types.Errortype {
-		return tsym.Pkg
-	}
-	return nil
-}
-
-// dextratypeData dumps the backing array for the []method field of
-// runtime.uncommontype.
-func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int {
-	for _, a := range methods(t) {
-		// ../../../../runtime/type.go:/method
-		exported := types.IsExported(a.name.Name)
-		var pkg *types.Pkg
-		if !exported && a.name.Pkg != typePkg(t) {
-			pkg = a.name.Pkg
-		}
-		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())
-	}
-	return ot
-}
-
-func dmethodptrOff(s *obj.LSym, ot int, x *obj.LSym) int {
-	duint32(s, ot, 0)
-	r := obj.Addrel(s)
-	r.Off = int32(ot)
-	r.Siz = 4
-	r.Sym = x
-	r.Type = objabi.R_METHODOFF
-	return ot + 4
-}
-
-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
-	}
-}
-
-// tflag is documented in reflect/type.go.
-//
-// tflag values must be kept in sync with copies in:
-//	cmd/compile/internal/gc/reflect.go
-//	cmd/link/internal/ld/decodesym.go
-//	reflect/type.go
-//	runtime/type.go
-const (
-	tflagUncommon      = 1 << 0
-	tflagExtraStar     = 1 << 1
-	tflagNamed         = 1 << 2
-	tflagRegularMemory = 1 << 3
-)
-
-var (
-	memhashvarlen  *obj.LSym
-	memequalvarlen *obj.LSym
-)
-
-// dcommontype dumps the contents of a reflect.rtype (runtime._type).
-func dcommontype(lsym *obj.LSym, t *types.Type) int {
-	dowidth(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 {
-			sptrWeak = false
-		}
-		sptr = dtypesym(tptr)
-	}
-
-	gcsym, useGCProg, ptrdata := dgcsym(t)
-
-	// ../../../../reflect/type.go:/^type.rtype
-	// actual type structure
-	//	type rtype struct {
-	//		size          uintptr
-	//		ptrdata       uintptr
-	//		hash          uint32
-	//		tflag         tflag
-	//		align         uint8
-	//		fieldAlign    uint8
-	//		kind          uint8
-	//		equal         func(unsafe.Pointer, unsafe.Pointer) bool
-	//		gcdata        *byte
-	//		str           nameOff
-	//		ptrToThis     typeOff
-	//	}
-	ot := 0
-	ot = duintptr(lsym, ot, uint64(t.Width))
-	ot = duintptr(lsym, ot, uint64(ptrdata))
-	ot = duint32(lsym, ot, typehash(t))
-
-	var tflag uint8
-	if uncommonSize(t) != 0 {
-		tflag |= tflagUncommon
-	}
-	if t.Sym != nil && t.Sym.Name != "" {
-		tflag |= tflagNamed
-	}
-	if IsRegularMemory(t) {
-		tflag |= tflagRegularMemory
-	}
-
-	exported := false
-	p := t.LongString()
-	// If we're writing out type T,
-	// we are very likely to write out type *T as well.
-	// Use the string "*T"[1:] for "T", so that the two
-	// share storage. This is a cheap way to reduce the
-	// amount of space taken up by reflect strings.
-	if !strings.HasPrefix(p, "*") {
-		p = "*" + p
-		tflag |= tflagExtraStar
-		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)
-		}
-	}
-
-	ot = duint8(lsym, ot, tflag)
-
-	// runtime (and common sense) expects alignment to be a power of two.
-	i := int(t.Align)
-
-	if i == 0 {
-		i = 1
-	}
-	if i&(i-1) != 0 {
-		Fatalf("invalid alignment %d for %v", t.Align, t)
-	}
-	ot = duint8(lsym, ot, t.Align) // align
-	ot = duint8(lsym, ot, t.Align) // fieldAlign
-
-	i = kinds[t.Etype]
-	if isdirectiface(t) {
-		i |= objabi.KindDirectIface
-	}
-	if useGCProg {
-		i |= objabi.KindGCProg
-	}
-	ot = duint8(lsym, ot, uint8(i)) // kind
-	if eqfunc != nil {
-		ot = dsymptr(lsym, ot, eqfunc, 0) // equality function
-	} else {
-		ot = duintptr(lsym, ot, 0) // type we can't do == with
-	}
-	ot = dsymptr(lsym, ot, gcsym, 0) // gcdata
-
-	nsym := dname(p, "", nil, exported)
-	ot = dsymptrOff(lsym, ot, nsym) // str
-	// ptrToThis
-	if sptr == nil {
-		ot = duint32(lsym, ot, 0)
-	} else if sptrWeak {
-		ot = dsymptrWeakOff(lsym, ot, sptr)
-	} else {
-		ot = dsymptrOff(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
-// 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 typesymprefix(prefix string, t *types.Type) *types.Sym {
-	p := prefix + "." + t.ShortString()
-	s := typeLookup(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)
-	signatmu.Unlock()
-
-	//print("algsym: %s -> %+S\n", p, s);
-
-	return s
-}
-
-func typenamesym(t *types.Type) *types.Sym {
-	if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() {
-		Fatalf("typenamesym %v", t)
-	}
-	s := typesym(t)
-	signatmu.Lock()
-	addsignat(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 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
-}
-
-// 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())
-
-	case TSTRUCT:
-		for _, t1 := range t.Fields().Slice() {
-			if !isreflexive(t1.Type) {
-				return false
-			}
-		}
-		return true
-
-	default:
-		Fatalf("bad type for map key: %v", t)
-		return false
-	}
-}
-
-// 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:
-		return false
-
-	case TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, // floats and complex can be +0/-0
-		TINTER,
-		TSTRING: // strings might have smaller backing stores
-		return true
-
-	case TARRAY:
-		return needkeyupdate(t.Elem())
-
-	case TSTRUCT:
-		for _, t1 := range t.Fields().Slice() {
-			if needkeyupdate(t1.Type) {
-				return true
-			}
-		}
-		return false
-
-	default:
-		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:
-		return true
-
-	case TARRAY:
-		return hashMightPanic(t.Elem())
-
-	case TSTRUCT:
-		for _, t1 := range t.Fields().Slice() {
-			if hashMightPanic(t1.Type) {
-				return true
-			}
-		}
-		return false
-
-	default:
-		return false
-	}
-}
-
-// formalType replaces byte and rune aliases with real types.
-// 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]
-	}
-	return t
-}
-
-func dtypesym(t *types.Type) *obj.LSym {
-	t = formalType(t)
-	if t.IsUntyped() {
-		Fatalf("dtypesym %v", t)
-	}
-
-	s := typesym(t)
-	lsym := s.Linksym()
-	if s.Siggen() {
-		return lsym
-	}
-	s.SetSiggen(true)
-
-	// special case (look for runtime below):
-	// when compiling package runtime,
-	// emit the type structures for int, float, etc.
-	tbase := t
-
-	if t.IsPtr() && t.Sym == nil && t.Elem().Sym != nil {
-		tbase = t.Elem()
-	}
-	dupok := 0
-	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
-		// 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])
-				}
-				lsym.Set(obj.AttrIndexed, true)
-			}
-			return lsym
-		}
-		// TODO(mdempsky): Investigate whether this can happen.
-		if tbase.Etype == TFORW {
-			return lsym
-		}
-	}
-
-	ot := 0
-	switch t.Etype {
-	default:
-		ot = dcommontype(lsym, t)
-		ot = dextratype(lsym, ot, t, 0)
-
-	case TARRAY:
-		// ../../../../runtime/type.go:/arrayType
-		s1 := dtypesym(t.Elem())
-		t2 := types.NewSlice(t.Elem())
-		s2 := dtypesym(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 = dextratype(lsym, ot, t, 0)
-
-	case TSLICE:
-		// ../../../../runtime/type.go:/sliceType
-		s1 := dtypesym(t.Elem())
-		ot = dcommontype(lsym, t)
-		ot = dsymptr(lsym, ot, s1, 0)
-		ot = dextratype(lsym, ot, t, 0)
-
-	case TCHAN:
-		// ../../../../runtime/type.go:/chanType
-		s1 := dtypesym(t.Elem())
-		ot = dcommontype(lsym, t)
-		ot = dsymptr(lsym, ot, s1, 0)
-		ot = duintptr(lsym, ot, uint64(t.ChanDir()))
-		ot = dextratype(lsym, ot, t, 0)
-
-	case TFUNC:
-		for _, t1 := range t.Recvs().Fields().Slice() {
-			dtypesym(t1.Type)
-		}
-		isddd := false
-		for _, t1 := range t.Params().Fields().Slice() {
-			isddd = t1.IsDDD()
-			dtypesym(t1.Type)
-		}
-		for _, t1 := range t.Results().Fields().Slice() {
-			dtypesym(t1.Type)
-		}
-
-		ot = dcommontype(lsym, t)
-		inCount := t.NumRecvs() + t.NumParams()
-		outCount := t.NumResults()
-		if isddd {
-			outCount |= 1 << 15
-		}
-		ot = duint16(lsym, ot, uint16(inCount))
-		ot = duint16(lsym, ot, uint16(outCount))
-		if Widthptr == 8 {
-			ot += 4 // align for *rtype
-		}
-
-		dataAdd := (inCount + t.NumResults()) * Widthptr
-		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)
-		}
-		for _, t1 := range t.Params().Fields().Slice() {
-			ot = dsymptr(lsym, ot, dtypesym(t1.Type), 0)
-		}
-		for _, t1 := range t.Results().Fields().Slice() {
-			ot = dsymptr(lsym, ot, dtypesym(t1.Type), 0)
-		}
-
-	case TINTER:
-		m := imethods(t)
-		n := len(m)
-		for _, a := range m {
-			dtypesym(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
-		}
-		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))
-		dataAdd := imethodSize() * n
-		ot = dextratype(lsym, ot, t, dataAdd)
-
-		for _, a := range m {
-			// ../../../../runtime/type.go:/imethod
-			exported := types.IsExported(a.name.Name)
-			var pkg *types.Pkg
-			if !exported && a.name.Pkg != tpkg {
-				pkg = a.name.Pkg
-			}
-			nsym := dname(a.name.Name, "", pkg, exported)
-
-			ot = dsymptrOff(lsym, ot, nsym)
-			ot = dsymptrOff(lsym, ot, dtypesym(a.type_))
-		}
-
-	// ../../../../runtime/type.go:/mapType
-	case TMAP:
-		s1 := dtypesym(t.Key())
-		s2 := dtypesym(t.Elem())
-		s3 := dtypesym(bmap(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)
-		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))
-			flags |= 1 // indirect key
-		} else {
-			ot = duint8(lsym, ot, uint8(t.Key().Width))
-		}
-
-		if t.Elem().Width > MAXELEMSIZE {
-			ot = duint8(lsym, ot, uint8(Widthptr))
-			flags |= 2 // indirect value
-		} else {
-			ot = duint8(lsym, ot, uint8(t.Elem().Width))
-		}
-		ot = duint16(lsym, ot, uint16(bmap(t).Width))
-		if isreflexive(t.Key()) {
-			flags |= 4 // reflexive key
-		}
-		if needkeyupdate(t.Key()) {
-			flags |= 8 // need key update
-		}
-		if hashMightPanic(t.Key()) {
-			flags |= 16 // hash might panic
-		}
-		ot = duint32(lsym, ot, flags)
-		ot = dextratype(lsym, ot, t, 0)
-
-	case TPTR:
-		if t.Elem().Etype == TANY {
-			// ../../../../runtime/type.go:/UnsafePointerType
-			ot = dcommontype(lsym, t)
-			ot = dextratype(lsym, ot, t, 0)
-
-			break
-		}
-
-		// ../../../../runtime/type.go:/ptrType
-		s1 := dtypesym(t.Elem())
-
-		ot = dcommontype(lsym, t)
-		ot = dsymptr(lsym, ot, s1, 0)
-		ot = dextratype(lsym, ot, t, 0)
-
-	// ../../../../runtime/type.go:/structType
-	// for security, only the exported fields.
-	case TSTRUCT:
-		fields := t.Fields().Slice()
-		for _, t1 := range fields {
-			dtypesym(t1.Type)
-		}
-
-		// All non-exported struct field names within a struct
-		// type must originate from a single package. By
-		// identifying and recording that package within the
-		// struct type descriptor, we can omit that
-		// information from the field descriptors.
-		var spkg *types.Pkg
-		for _, f := range fields {
-			if !types.IsExported(f.Sym.Name) {
-				spkg = f.Sym.Pkg
-				break
-			}
-		}
-
-		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)))
-
-		dataAdd := len(fields) * structfieldSize()
-		ot = dextratype(lsym, ot, t, dataAdd)
-
-		for _, f := range fields {
-			// ../../../../runtime/type.go:/structField
-			ot = dnameField(lsym, ot, spkg, f)
-			ot = dsymptr(lsym, ot, dtypesym(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)
-			}
-			if f.Embedded != 0 {
-				offsetAnon |= 1
-			}
-			ot = duintptr(lsym, ot, offsetAnon)
-		}
-	}
-
-	ot = dextratypeData(lsym, ot, t)
-	ggloblsym(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 {
-		// 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:
-			keep = true
-		}
-	}
-	// Do not put Noalg types in typelinks.  See issue #22605.
-	if typeHasNoAlg(t) {
-		keep = false
-	}
-	lsym.Set(obj.AttrMakeTypelink, keep)
-
-	return lsym
-}
-
-// ifaceMethodOffset returns the offset of the i-th method in the interface
-// type descriptor, ityp.
-func ifaceMethodOffset(ityp *types.Type, i int64) int64 {
-	// interface type descriptor layout is struct {
-	//   _type        // commonSize
-	//   pkgpath      // 1 word
-	//   []imethod    // 3 words (pointing to [...]imethod below)
-	//   uncommontype // uncommonSize
-	//   [...]imethod
-	// }
-	// The size of imethod is 8.
-	return int64(commonSize()+4*Widthptr+uncommonSize(ityp)) + i*8
-}
-
-// for each itabEntry, gather the methods on
-// the concrete type that implement the interface
-func peekitabs() {
-	for i := range itabs {
-		tab := &itabs[i]
-		methods := genfun(tab.t, tab.itype)
-		if len(methods) == 0 {
-			continue
-		}
-		tab.entries = methods
-	}
-}
-
-// for the given concrete type and interface
-// type, return the (sorted) set of methods
-// on the concrete type that implement the interface
-func genfun(t, it *types.Type) []*obj.LSym {
-	if t == nil || it == nil {
-		return nil
-	}
-	sigs := imethods(it)
-	methods := methods(t)
-	out := make([]*obj.LSym, 0, len(sigs))
-	// TODO(mdempsky): Short circuit before calling methods(t)?
-	// See discussion on CL 105039.
-	if len(sigs) == 0 {
-		return nil
-	}
-
-	// both sigs and methods are sorted by name,
-	// 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())
-			sigs = sigs[1:]
-			if len(sigs) == 0 {
-				break
-			}
-		}
-	}
-
-	if len(sigs) != 0 {
-		Fatalf("incomplete itab")
-	}
-
-	return out
-}
-
-// itabsym uses the information gathered in
-// peekitabs 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 {
-	var syms []*obj.LSym
-	if it == nil {
-		return nil
-	}
-
-	for i := range itabs {
-		e := &itabs[i]
-		if e.lsym == it {
-			syms = e.entries
-			break
-		}
-	}
-	if syms == nil {
-		return nil
-	}
-
-	// keep this arithmetic in sync with *itab layout
-	methodnum := int((offset - 2*int64(Widthptr) - 8) / int64(Widthptr))
-	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) {
-	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
-	// 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()})
-			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))
-			}
-		}
-	}
-}
-
-func dumptabs() {
-	// process itabs
-	for _, i := range itabs {
-		// dump empty itab symbol into i.sym
-		// type itab struct {
-		//   inter  *interfacetype
-		//   _type  *_type
-		//   hash   uint32
-		//   _      [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
-		for _, fn := range genfun(i.t, i.itype) {
-			o = dsymptr(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))
-		i.lsym.Set(obj.AttrContentAddressable, true)
-	}
-
-	// process ptabs
-	if localpkg.Name == "main" && len(ptabs) > 0 {
-		ot := 0
-		s := Ctxt.Lookup("go.plugin.tabs")
-		for _, p := range ptabs {
-			// Dump ptab symbol into go.pluginsym package.
-			//
-			// type ptab struct {
-			//	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)
-			// Plugin exports symbols as interfaces. Mark their types
-			// as UsedInIface.
-			tsym.Set(obj.AttrUsedInIface, true)
-		}
-		ggloblsym(s, int32(ot), int16(obj.RODATA))
-
-		ot = 0
-		s = Ctxt.Lookup("go.plugin.exports")
-		for _, p := range ptabs {
-			ot = dsymptr(s, ot, p.s.Linksym(), 0)
-		}
-		ggloblsym(s, int32(ot), int16(obj.RODATA))
-	}
-}
-
-func dumpimportstrings() {
-	// generate import strings for imported packages
-	for _, p := range types.ImportedPkgList() {
-		dimportpath(p)
-	}
-}
-
-func dumpbasictypes() {
-	// 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]))
-		}
-		dtypesym(types.NewPtr(types.Types[TSTRING]))
-		dtypesym(types.NewPtr(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))
-
-		dtypesym(functype(nil, []*Node{anonfield(types.Errortype)}, []*Node{anonfield(types.Types[TSTRING])}))
-
-		// add paths for runtime and main, which 6l imports implicitly.
-		dimportpath(Runtimepkg)
-
-		if flag_race {
-			dimportpath(racepkg)
-		}
-		if flag_msan {
-			dimportpath(msanpkg)
-		}
-		dimportpath(types.NewPkg("main", ""))
-	}
-}
-
-type typeAndStr struct {
-	t       *types.Type
-	short   string
-	regular string
-}
-
-type typesByString []typeAndStr
-
-func (a typesByString) Len() int { return len(a) }
-func (a typesByString) Less(i, j int) bool {
-	if a[i].short != a[j].short {
-		return a[i].short < a[j].short
-	}
-	// When the only difference between the types is whether
-	// they refer to byte or uint8, such as **byte vs **uint8,
-	// the types' ShortStrings can be identical.
-	// To preserve deterministic sort ordering, sort these by String().
-	if a[i].regular != a[j].regular {
-		return a[i].regular < a[j].regular
-	}
-	// Identical anonymous interfaces defined in different locations
-	// 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 {
-		return a[i].t.Methods().Index(0).Pos.Before(a[j].t.Methods().Index(0).Pos)
-	}
-	return false
-}
-func (a typesByString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-// maxPtrmaskBytes is the maximum length of a GC ptrmask bitmap,
-// which holds 1-bit entries describing where pointers are in a given type.
-// Above this length, the GC information is recorded as a GC program,
-// which can express repetition compactly. In either form, the
-// information is used by the runtime to initialize the heap bitmap,
-// and for large types (like 128 or more words), they are roughly the
-// same speed. GC programs are never much larger and often more
-// compact. (If large arrays are involved, they can be arbitrarily
-// more compact.)
-//
-// The cutoff must be large enough that any allocation large enough to
-// use a GC program is large enough that it does not share heap bitmap
-// bytes with any other objects, allowing the GC program execution to
-// assume an aligned start and not use atomic operations. In the current
-// runtime, this means all malloc size classes larger than the cutoff must
-// be multiples of four words. On 32-bit systems that's 16 bytes, and
-// all size classes >= 16 bytes are 16-byte aligned, so no real constraint.
-// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed
-// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated
-// is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
-// must be >= 4.
-//
-// We used to use 16 because the GC programs do have some constant overhead
-// to get started, and processing 128 pointers seems to be enough to
-// amortize that overhead well.
-//
-// To make sure that the runtime's chansend can call typeBitsBulkBarrier,
-// we raised the limit to 2048, so that even 32-bit systems are guaranteed to
-// use bitmaps for objects up to 64 kB in size.
-//
-// Also known to reflect/type.go.
-//
-const maxPtrmaskBytes = 2048
-
-// dgcsym emits and returns a data symbol containing GC information for type t,
-// 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 {
-		lsym = dgcptrmask(t)
-		return
-	}
-
-	useGCProg = true
-	lsym, ptrdata = dgcprog(t)
-	return
-}
-
-// 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)
-	fillptrmask(t, ptrmask)
-	p := fmt.Sprintf("gcbits.%x", ptrmask)
-
-	sym := Runtimepkg.Lookup(p)
-	lsym := sym.Linksym()
-	if !sym.Uniq() {
-		sym.SetUniq(true)
-		for i, x := range ptrmask {
-			duint8(lsym, i, x)
-		}
-		ggloblsym(lsym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL)
-		lsym.Set(obj.AttrContentAddressable, true)
-	}
-	return lsym
-}
-
-// fillptrmask fills in ptrmask with 1s corresponding to the
-// word offsets in t that hold pointers.
-// ptrmask is assumed to fit at least typeptrdata(t)/Widthptr bits.
-func fillptrmask(t *types.Type, ptrmask []byte) {
-	for i := range ptrmask {
-		ptrmask[i] = 0
-	}
-	if !t.HasPointers() {
-		return
-	}
-
-	vec := bvalloc(8 * int32(len(ptrmask)))
-	onebitwalktype1(t, 0, vec)
-
-	nptr := typeptrdata(t) / int64(Widthptr)
-	for i := int64(0); i < nptr; i++ {
-		if vec.Get(int32(i)) {
-			ptrmask[i/8] |= 1 << (uint(i) % 8)
-		}
-	}
-}
-
-// dgcprog emits and returns the symbol containing a GC program for type t
-// along with the size of the data described by the program (in the range [typeptrdata(t), t.Width]).
-// 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)
-	}
-	lsym := typesymprefix(".gcprog", t).Linksym()
-	var p GCProg
-	p.init(lsym)
-	p.emit(t, 0)
-	offset := p.w.BitIndex() * int64(Widthptr)
-	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)
-	}
-	return lsym, offset
-}
-
-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) {
-	p.lsym = lsym
-	p.symoff = 4 // first 4 bytes hold program length
-	p.w.Init(p.writeByte)
-	if 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) 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 {
-		fmt.Fprintf(os.Stderr, "compile: end GCProg for %v\n", p.lsym)
-	}
-}
-
-func (p *GCProg) emit(t *types.Type, offset int64) {
-	dowidth(t)
-	if !t.HasPointers() {
-		return
-	}
-	if t.Width == int64(Widthptr) {
-		p.w.Ptr(offset / int64(Widthptr))
-		return
-	}
-	switch t.Etype {
-	default:
-		Fatalf("GCProg.emit: unexpected type %v", t)
-
-	case TSTRING:
-		p.w.Ptr(offset / int64(Widthptr))
-
-	case TINTER:
-		// Note: the first word isn't a pointer. See comment in plive.go:onebitwalktype1.
-		p.w.Ptr(offset/int64(Widthptr) + 1)
-
-	case TSLICE:
-		p.w.Ptr(offset / int64(Widthptr))
-
-	case TARRAY:
-		if t.NumElem() == 0 {
-			// should have been handled by haspointers check above
-			Fatalf("GCProg.emit: empty array")
-		}
-
-		// Flatten array-of-array-of-array to just a big array by multiplying counts.
-		count := t.NumElem()
-		elem := t.Elem()
-		for elem.IsArray() {
-			count *= elem.NumElem()
-			elem = elem.Elem()
-		}
-
-		if !p.w.ShouldRepeat(elem.Width/int64(Widthptr), count) {
-			// Cheaper to just emit the bits.
-			for i := int64(0); i < count; i++ {
-				p.emit(elem, offset+i*elem.Width)
-			}
-			return
-		}
-		p.emit(elem, offset)
-		p.w.ZeroUntil((offset + elem.Width) / int64(Widthptr))
-		p.w.Repeat(elem.Width/int64(Widthptr), count-1)
-
-	case TSTRUCT:
-		for _, t1 := range t.Fields().Slice() {
-			p.emit(t1.Type, offset+t1.Offset)
-		}
-	}
-}
-
-// zeroaddr returns the address of a symbol with at least
-// size bytes of zeros.
-func zeroaddr(size int64) *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
-}
diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
deleted file mode 100644
index 97e0424..0000000
--- 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 212fcc0..0000000
--- 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/ssa.go b/src/cmd/compile/internal/gc/ssa.go
deleted file mode 100644
index 5b74754..0000000
--- a/src/cmd/compile/internal/gc/ssa.go
+++ /dev/null
@@ -1,7231 +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 (
-	"encoding/binary"
-	"fmt"
-	"html"
-	"os"
-	"path/filepath"
-	"sort"
-
-	"bufio"
-	"bytes"
-	"cmd/compile/internal/ssa"
-	"cmd/compile/internal/types"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-	"cmd/internal/objabi"
-	"cmd/internal/src"
-	"cmd/internal/sys"
-)
-
-var ssaConfig *ssa.Config
-var ssaCaches []ssa.Cache
-
-var ssaDump string     // early copy of $GOSSAFUNC; the func name to dump output for
-var ssaDir string      // optional destination for ssa dump file
-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
-
-func initssaconfig() {
-	types_ := ssa.NewTypes()
-
-	if thearch.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.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)
-
-	// 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")
-
-	// asm funcs with special ABI
-	if thearch.LinkArch.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")
-	} 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")
-	}
-
-	// 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")
-}
-
-// 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)
-	}
-	return t.Params().Field(i)
-}
-
-// dvarint writes a varint v to the funcdata in symbol x and returns the new offset
-func dvarint(x *obj.LSym, off int, v int64) int {
-	if v < 0 || v > 1e9 {
-		panic(fmt.Sprintf("dvarint: bad offset for funcdata - %v", v))
-	}
-	if v < 1<<7 {
-		return duint8(x, off, uint8(v))
-	}
-	off = duint8(x, off, uint8((v&127)|128))
-	if v < 1<<14 {
-		return duint8(x, off, uint8(v>>7))
-	}
-	off = duint8(x, off, uint8(((v>>7)&127)|128))
-	if v < 1<<21 {
-		return duint8(x, off, uint8(v>>14))
-	}
-	off = duint8(x, off, uint8(((v>>14)&127)|128))
-	if v < 1<<28 {
-		return duint8(x, off, uint8(v>>21))
-	}
-	off = duint8(x, off, uint8(((v>>21)&127)|128))
-	return duint8(x, off, uint8(v>>28))
-}
-
-// emitOpenDeferInfo emits FUNCDATA information about the defers in a function
-// that is using open-coded defers.  This funcdata is used to determine the active
-// defers in a function and execute those defers during panic processing.
-//
-// The funcdata is all encoded in varints (since values will almost always be less than
-// 128, but stack offsets could potentially be up to 2Gbyte). All "locations" (offsets)
-// for stack variables are specified as the number of bytes below varp (pointer to the
-// top of the local variables) for their starting address. The format is:
-//
-//  - Max total argument size among all the defers
-//  - Offset of the deferBits variable
-//  - Number of defers in the function
-//  - Information about each defer call, in reverse order of appearance in the function:
-//    - Total argument size of the call
-//    - Offset of the closure value to call
-//    - Number of arguments (including interface receiver or method receiver as first arg)
-//    - Information about each argument
-//      - Offset of the stored defer argument in this function's frame
-//      - 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
-	off := 0
-
-	// Compute maxargsize (max size of arguments for all defers)
-	// first, so we can output it first to the funcdata
-	var maxargsize int64
-	for i := len(s.openDefers) - 1; i >= 0; i-- {
-		r := s.openDefers[i]
-		argsize := r.n.Left.Type.ArgWidth()
-		if argsize > maxargsize {
-			maxargsize = argsize
-		}
-	}
-	off = dvarint(x, off, maxargsize)
-	off = dvarint(x, off, -s.deferBitsTemp.Xoffset)
-	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)
-		numArgs := len(r.argNodes)
-		if r.rcvrNode != nil {
-			// If there's an interface receiver, treat/place it as the first
-			// arg. (If there is a method receiver, it's already included as
-			// first arg in r.argNodes.)
-			numArgs++
-		}
-		off = dvarint(x, off, int64(numArgs))
-		if r.rcvrNode != nil {
-			off = dvarint(x, off, -r.rcvrNode.Xoffset)
-			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, f.Type.Size())
-			off = dvarint(x, off, f.Offset)
-		}
-	}
-}
-
-// 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()
-	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
-	}
-	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)
-		if ssaDumpStdout {
-			fmt.Println("generating SSA for", name)
-			fmt.Print(astBuf.String())
-		}
-	}
-
-	var s state
-	s.pushLine(fn.Pos)
-	defer s.popLine()
-
-	s.hasdefer = fn.Func.HasDefer()
-	if fn.Func.Pragma&CgoUnsafeArgs != 0 {
-		s.cgoUnsafeArgs = true
-	}
-
-	fe := ssafn{
-		curfn: fn,
-		log:   printssa && ssaDumpStdout,
-	}
-	s.curfn = fn
-
-	s.f = ssa.NewFunc(&fe)
-	s.config = ssaConfig
-	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 {
-		s.f.NoSplit = true
-	}
-	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
-
-	if printssa {
-		ssaDF := ssaDumpFile
-		if ssaDir != "" {
-			ssaDF = filepath.Join(ssaDir, myimportpath+"."+name+".html")
-			ssaD := filepath.Dir(ssaDF)
-			os.MkdirAll(ssaD, 0755)
-		}
-		s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDF, s.f, ssaDumpCFG)
-		// TODO: generate and print a mapping from nodes to values and blocks
-		dumpSourcesColumn(s.f.HTMLWriter, fn)
-		s.f.HTMLWriter.WriteAST("AST", astBuf)
-	}
-
-	// Allocate starting values
-	s.labels = map[string]*ssaLabel{}
-	s.labeledNodes = map[*Node]*ssaLabel{}
-	s.fwdVars = map[*Node]*ssa.Value{}
-	s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem)
-
-	s.hasOpenDefers = Debug.N == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed()
-	switch {
-	case s.hasOpenDefers && (Ctxt.Flag_shared || Ctxt.Flag_dynlink) && thearch.LinkArch.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 {
-		// 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.
-		s.hasOpenDefers = false
-	}
-	if s.hasOpenDefers &&
-		s.curfn.Func.numReturns*s.curfn.Func.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).
-		// Open-coded defers are most important for improving performance
-		// for smaller functions (which don't have many returns).
-		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.startBlock(s.f.Entry)
-	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])
-		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
-		s.deferBitsAddr = s.addr(deferBitsTemp)
-		s.store(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)
-	}
-
-	// Generate addresses of local declarations
-	s.decladdrs = map[*Node]*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:
-			// 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())
-		}
-	}
-
-	// Populate SSAable arguments.
-	for _, n := range fn.Func.Dcl {
-		if n.Class() == 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.
-		}
-	}
-
-	// Convert the AST-based IR to the SSA-based IR
-	s.stmtList(fn.Func.Enter)
-	s.stmtList(fn.Nbody)
-
-	// fallthrough to exit
-	if s.curBlock != nil {
-		s.pushLine(fn.Func.Endlineno)
-		s.exit()
-		s.popLine()
-	}
-
-	for _, b := range s.f.Blocks {
-		if b.Pos != src.NoXPos {
-			s.updateUnsetPredPos(b)
-		}
-	}
-
-	s.insertPhis()
-
-	// Main call to ssa package to compile function
-	ssa.Compile(s.f)
-
-	if s.hasOpenDefers {
-		s.emitOpenDeferInfo()
-	}
-
-	return s.f
-}
-
-func dumpSourcesColumn(writer *ssa.HTMLWriter, fn *Node) {
-	// Read sources of target function fn.
-	fname := Ctxt.PosTable.Pos(fn.Pos).Filename()
-	targetFn, err := readFuncLines(fname, fn.Pos.Line(), fn.Func.Endlineno.Line())
-	if err != nil {
-		writer.Logf("cannot read sources for function %v: %v", fn, err)
-	}
-
-	// 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())
-		if err != nil {
-			writer.Logf("cannot read sources for inlined function %v: %v", fi, err)
-			continue
-		}
-		inlFns = append(inlFns, fnLines)
-	}
-
-	sort.Sort(ssa.ByTopo(inlFns))
-	if targetFn != nil {
-		inlFns = append([]*ssa.FuncLines{targetFn}, inlFns...)
-	}
-
-	writer.WriteSources("sources", inlFns)
-}
-
-func readFuncLines(file string, start, end uint) (*ssa.FuncLines, error) {
-	f, err := os.Open(os.ExpandEnv(file))
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	var lines []string
-	ln := uint(1)
-	scanner := bufio.NewScanner(f)
-	for scanner.Scan() && ln <= end {
-		if ln >= start {
-			lines = append(lines, scanner.Text())
-		}
-		ln++
-	}
-	return &ssa.FuncLines{Filename: file, StartLineno: start, Lines: lines}, nil
-}
-
-// updateUnsetPredPos propagates the earliest-value position information for b
-// towards all of b's predecessors that need a position, and recurs on that
-// predecessor if its position is updated. B should have a non-empty position.
-func (s *state) updateUnsetPredPos(b *ssa.Block) {
-	if b.Pos == src.NoXPos {
-		s.Fatalf("Block %s should have a position", b)
-	}
-	bestPos := src.NoXPos
-	for _, e := range b.Preds {
-		p := e.Block()
-		if !p.LackingPos() {
-			continue
-		}
-		if bestPos == src.NoXPos {
-			bestPos = b.Pos
-			for _, v := range b.Values {
-				if v.LackingPos() {
-					continue
-				}
-				if v.Pos != src.NoXPos {
-					// Assume values are still in roughly textual order;
-					// TODO: could also seek minimum position?
-					bestPos = v.Pos
-					break
-				}
-			}
-		}
-		p.Pos = bestPos
-		s.updateUnsetPredPos(p) // We do not expect long chains of these, thus recursion is okay.
-	}
-}
-
-// Information about each open-coded defer.
-type openDeferInfo struct {
-	// The ODEFER node representing the function call of the defer
-	n *Node
-	// 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
-	// 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
-	// 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
-}
-
-type state struct {
-	// configuration (arch) information
-	config *ssa.Config
-
-	// function we're building
-	f *ssa.Func
-
-	// Node for function
-	curfn *Node
-
-	// labels and labeled control flow nodes (OFOR, OFORUNTIL, OSWITCH, OSELECT) in f
-	labels       map[string]*ssaLabel
-	labeledNodes map[*Node]*ssaLabel
-
-	// unlabeled break and continue statement tracking
-	breakTo    *ssa.Block // current target for plain break statement
-	continueTo *ssa.Block // current target for plain continue statement
-
-	// current location where we're interpreting the AST
-	curBlock *ssa.Block
-
-	// 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
-
-	// 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
-
-	// all defined variables at the end of each block. Indexed by block ID.
-	defvars []map[*Node]*ssa.Value
-
-	// addresses of PPARAM and PPARAMOUT variables.
-	decladdrs map[*Node]*ssa.Value
-
-	// starting values. Memory, stack pointer, and globals pointer
-	startmem *ssa.Value
-	sp       *ssa.Value
-	sb       *ssa.Value
-	// value representing address of where deferBits autotmp is stored
-	deferBitsAddr *ssa.Value
-	deferBitsTemp *Node
-
-	// line number stack. The current line number is top of stack
-	line []src.XPos
-	// the last line number processed; it may have been popped
-	lastPos src.XPos
-
-	// list of panic calls by function name and line number.
-	// 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
-	hasOpenDefers bool // whether we are doing open-coded defers
-
-	// If doing open-coded defers, list of info about the defer calls in
-	// scanning order. Hence, at exit we should run these defers in reverse
-	// order of this list
-	openDefers []*openDeferInfo
-	// For open-coded defers, this is the beginning and end blocks of the last
-	// defer exit code that we have generated so far. We use these to share
-	// code between exits if the shareDeferExits option (disabled by default)
-	// is on.
-	lastDeferExit       *ssa.Block // Entry block of last defer exit code we generated
-	lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated
-	lastDeferCount      int        // Number of defers encountered at that point
-
-	prevCall *ssa.Value // the previous call; use this to tie results to the call op.
-}
-
-type funcLine struct {
-	f    *obj.LSym
-	base *src.PosBase
-	line uint
-}
-
-type ssaLabel struct {
-	target         *ssa.Block // block identified by this label
-	breakTarget    *ssa.Block // block to break to in control flow node identified by this label
-	continueTarget *ssa.Block // block to continue to in control flow node identified by this label
-}
-
-// label returns the label associated with sym, creating it if necessary.
-func (s *state) label(sym *types.Sym) *ssaLabel {
-	lab := s.labels[sym.Name]
-	if lab == nil {
-		lab = new(ssaLabel)
-		s.labels[sym.Name] = lab
-	}
-	return lab
-}
-
-func (s *state) Logf(msg string, args ...interface{}) { s.f.Logf(msg, args...) }
-func (s *state) Log() bool                            { return s.f.Log() }
-func (s *state) Fatalf(msg string, args ...interface{}) {
-	s.f.Frontend().Fatalf(s.peekPos(), msg, args...)
-}
-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() }
-
-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"}}
-)
-
-// startBlock sets the current block we're generating code in to b.
-func (s *state) startBlock(b *ssa.Block) {
-	if s.curBlock != nil {
-		s.Fatalf("starting block %v when block %v has not ended", b, s.curBlock)
-	}
-	s.curBlock = b
-	s.vars = map[*Node]*ssa.Value{}
-	for n := range s.fwdVars {
-		delete(s.fwdVars, n)
-	}
-}
-
-// endBlock marks the end of generating code for the current block.
-// Returns the (former) current block. Returns nil if there is no current
-// block, i.e. if no code flows to the current execution point.
-func (s *state) endBlock() *ssa.Block {
-	b := s.curBlock
-	if b == nil {
-		return nil
-	}
-	for len(s.defvars) <= int(b.ID) {
-		s.defvars = append(s.defvars, nil)
-	}
-	s.defvars[b.ID] = s.vars
-	s.curBlock = nil
-	s.vars = nil
-	if b.LackingPos() {
-		// Empty plain blocks get the line of their successor (handled after all blocks created),
-		// except for increment blocks in For statements (handled in ssa conversion of OFOR),
-		// and for blocks ending in GOTO/BREAK/CONTINUE.
-		b.Pos = src.NoXPos
-	} else {
-		b.Pos = s.lastPos
-	}
-	return b
-}
-
-// pushLine pushes a line number on the line number stack.
-func (s *state) pushLine(line src.XPos) {
-	if !line.IsKnown() {
-		// 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)")
-		}
-	} else {
-		s.lastPos = line
-	}
-
-	s.line = append(s.line, line)
-}
-
-// popLine pops the top of the line number stack.
-func (s *state) popLine() {
-	s.line = s.line[:len(s.line)-1]
-}
-
-// peekPos peeks the top of the line number stack.
-func (s *state) peekPos() src.XPos {
-	return s.line[len(s.line)-1]
-}
-
-// newValue0 adds a new value with no arguments to the current block.
-func (s *state) newValue0(op ssa.Op, t *types.Type) *ssa.Value {
-	return s.curBlock.NewValue0(s.peekPos(), op, t)
-}
-
-// 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 {
-	return s.curBlock.NewValue0A(s.peekPos(), op, t, aux)
-}
-
-// newValue0I adds a new value with no arguments and an auxint value to the current block.
-func (s *state) newValue0I(op ssa.Op, t *types.Type, auxint int64) *ssa.Value {
-	return s.curBlock.NewValue0I(s.peekPos(), op, t, auxint)
-}
-
-// newValue1 adds a new value with one argument to the current block.
-func (s *state) newValue1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue1(s.peekPos(), op, t, arg)
-}
-
-// 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 {
-	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 {
-	if isStmt {
-		return s.curBlock.NewValue1A(s.peekPos(), op, t, aux, arg)
-	}
-	return s.curBlock.NewValue1A(s.peekPos().WithNotStmt(), op, t, aux, arg)
-}
-
-// newValue1I adds a new value with one argument and an auxint value to the current block.
-func (s *state) newValue1I(op ssa.Op, t *types.Type, aux int64, arg *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue1I(s.peekPos(), op, t, aux, arg)
-}
-
-// newValue2 adds a new value with two arguments to the current block.
-func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1)
-}
-
-// newValue2A adds a new value with two arguments and an aux value to the current block.
-func (s *state) newValue2A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1)
-}
-
-// newValue2Apos adds a new value with two arguments and an aux value to the current block.
-// isStmt determines whether the created values may be a statement or not
-// (i.e., false means never, yes means maybe).
-func (s *state) newValue2Apos(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value, isStmt bool) *ssa.Value {
-	if isStmt {
-		return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1)
-	}
-	return s.curBlock.NewValue2A(s.peekPos().WithNotStmt(), op, t, aux, arg0, arg1)
-}
-
-// newValue2I adds a new value with two arguments and an auxint value to the current block.
-func (s *state) newValue2I(op ssa.Op, t *types.Type, aux int64, arg0, arg1 *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue2I(s.peekPos(), op, t, aux, arg0, arg1)
-}
-
-// newValue3 adds a new value with three arguments to the current block.
-func (s *state) newValue3(op ssa.Op, t *types.Type, arg0, arg1, arg2 *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue3(s.peekPos(), op, t, arg0, arg1, arg2)
-}
-
-// newValue3I adds a new value with three arguments and an auxint value to the current block.
-func (s *state) newValue3I(op ssa.Op, t *types.Type, aux int64, arg0, arg1, arg2 *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue3I(s.peekPos(), op, t, aux, 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 {
-	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 {
-	if isStmt {
-		return s.curBlock.NewValue3A(s.peekPos(), op, t, aux, arg0, arg1, arg2)
-	}
-	return s.curBlock.NewValue3A(s.peekPos().WithNotStmt(), op, t, aux, arg0, arg1, arg2)
-}
-
-// newValue4 adds a new value with four arguments to the current block.
-func (s *state) newValue4(op ssa.Op, t *types.Type, arg0, arg1, arg2, arg3 *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue4(s.peekPos(), op, t, arg0, arg1, arg2, arg3)
-}
-
-// newValue4 adds a new value with four arguments and an auxint value to the current block.
-func (s *state) newValue4I(op ssa.Op, t *types.Type, aux int64, arg0, arg1, arg2, arg3 *ssa.Value) *ssa.Value {
-	return s.curBlock.NewValue4I(s.peekPos(), op, t, aux, arg0, arg1, arg2, arg3)
-}
-
-// entryNewValue0 adds a new value with no arguments to the entry block.
-func (s *state) entryNewValue0(op ssa.Op, t *types.Type) *ssa.Value {
-	return s.f.Entry.NewValue0(src.NoXPos, op, t)
-}
-
-// 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 {
-	return s.f.Entry.NewValue0A(src.NoXPos, op, t, aux)
-}
-
-// entryNewValue1 adds a new value with one argument to the entry block.
-func (s *state) entryNewValue1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
-	return s.f.Entry.NewValue1(src.NoXPos, op, t, arg)
-}
-
-// entryNewValue1 adds a new value with one argument and an auxint value to the entry block.
-func (s *state) entryNewValue1I(op ssa.Op, t *types.Type, auxint int64, arg *ssa.Value) *ssa.Value {
-	return s.f.Entry.NewValue1I(src.NoXPos, op, t, auxint, arg)
-}
-
-// 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 {
-	return s.f.Entry.NewValue1A(src.NoXPos, op, t, aux, arg)
-}
-
-// entryNewValue2 adds a new value with two arguments to the entry block.
-func (s *state) entryNewValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.Value {
-	return s.f.Entry.NewValue2(src.NoXPos, op, t, arg0, arg1)
-}
-
-// 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 {
-	return s.f.Entry.NewValue2A(src.NoXPos, op, t, aux, arg0, arg1)
-}
-
-// const* routines add a new const value to the entry block.
-func (s *state) constSlice(t *types.Type) *ssa.Value {
-	return s.f.ConstSlice(t)
-}
-func (s *state) constInterface(t *types.Type) *ssa.Value {
-	return s.f.ConstInterface(t)
-}
-func (s *state) constNil(t *types.Type) *ssa.Value { return s.f.ConstNil(t) }
-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)
-}
-func (s *state) constInt8(t *types.Type, c int8) *ssa.Value {
-	return s.f.ConstInt8(t, c)
-}
-func (s *state) constInt16(t *types.Type, c int16) *ssa.Value {
-	return s.f.ConstInt16(t, c)
-}
-func (s *state) constInt32(t *types.Type, c int32) *ssa.Value {
-	return s.f.ConstInt32(t, c)
-}
-func (s *state) constInt64(t *types.Type, c int64) *ssa.Value {
-	return s.f.ConstInt64(t, c)
-}
-func (s *state) constFloat32(t *types.Type, c float64) *ssa.Value {
-	return s.f.ConstFloat32(t, c)
-}
-func (s *state) constFloat64(t *types.Type, c float64) *ssa.Value {
-	return s.f.ConstFloat64(t, c)
-}
-func (s *state) constInt(t *types.Type, c int64) *ssa.Value {
-	if s.config.PtrSize == 8 {
-		return s.constInt64(t, c)
-	}
-	if int64(int32(c)) != c {
-		s.Fatalf("integer constant too big %d", c)
-	}
-	return s.constInt32(t, int32(c))
-}
-func (s *state) constOffPtrSP(t *types.Type, c int64) *ssa.Value {
-	return s.f.ConstOffPtrSP(t, c, s.sp)
-}
-
-// newValueOrSfCall* are wrappers around newValue*, which may create a call to a
-// soft-float runtime function instead (when emitting soft-float code).
-func (s *state) newValueOrSfCall1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
-	if s.softFloat {
-		if c, ok := s.sfcall(op, arg); ok {
-			return c
-		}
-	}
-	return s.newValue1(op, t, arg)
-}
-func (s *state) newValueOrSfCall2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.Value {
-	if s.softFloat {
-		if c, ok := s.sfcall(op, arg0, arg1); ok {
-			return c
-		}
-	}
-	return s.newValue2(op, t, arg0, arg1)
-}
-
-type instrumentKind uint8
-
-const (
-	instrumentRead = iota
-	instrumentWrite
-	instrumentMove
-)
-
-func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind) {
-	s.instrument2(t, addr, nil, kind)
-}
-
-// instrumentFields instruments a read/write operation on addr.
-// 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() {
-		s.instrument(t, addr, kind)
-		return
-	}
-	for _, f := range t.Fields().Slice() {
-		if f.Sym.IsBlank() {
-			continue
-		}
-		offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), f.Offset, addr)
-		s.instrumentFields(f.Type, offptr, kind)
-	}
-}
-
-func (s *state) instrumentMove(t *types.Type, dst, src *ssa.Value) {
-	if flag_msan {
-		s.instrument2(t, dst, src, instrumentMove)
-	} else {
-		s.instrument(t, src, instrumentRead)
-		s.instrument(t, dst, instrumentWrite)
-	}
-}
-
-func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrumentKind) {
-	if !s.curfn.Func.InstrumentBody() {
-		return
-	}
-
-	w := t.Size()
-	if w == 0 {
-		return // can't race on zero-sized things
-	}
-
-	if ssa.IsSanitizerSafeAddr(addr) {
-		return
-	}
-
-	var fn *obj.LSym
-	needWidth := false
-
-	if addr2 != nil && kind != instrumentMove {
-		panic("instrument2: non-nil addr2 for non-move instrumentation")
-	}
-
-	if flag_msan {
-		switch kind {
-		case instrumentRead:
-			fn = msanread
-		case instrumentWrite:
-			fn = msanwrite
-		case instrumentMove:
-			fn = msanmove
-		default:
-			panic("unreachable")
-		}
-		needWidth = true
-	} else if 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
-		case instrumentWrite:
-			fn = racewriterange
-		default:
-			panic("unreachable")
-		}
-		needWidth = true
-	} else if 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
-		case instrumentWrite:
-			fn = racewrite
-		default:
-			panic("unreachable")
-		}
-	} else {
-		panic("unreachable")
-	}
-
-	args := []*ssa.Value{addr}
-	if addr2 != nil {
-		args = append(args, addr2)
-	}
-	if needWidth {
-		args = append(args, s.constInt(types.Types[TUINTPTR], w))
-	}
-	s.rtcall(fn, true, nil, args...)
-}
-
-func (s *state) load(t *types.Type, src *ssa.Value) *ssa.Value {
-	s.instrumentFields(t, src, instrumentRead)
-	return s.rawLoad(t, src)
-}
-
-func (s *state) rawLoad(t *types.Type, src *ssa.Value) *ssa.Value {
-	return s.newValue2(ssa.OpLoad, t, src, s.mem())
-}
-
-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())
-}
-
-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
-}
-
-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
-}
-
-// stmtList converts the statement list n to SSA and adds it to s.
-func (s *state) stmtList(l Nodes) {
-	for _, n := range l.Slice() {
-		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) {
-		// 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)
-		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 {
-		return
-	}
-
-	s.stmtList(n.Ninit)
-	switch n.Op {
-
-	case OBLOCK:
-		s.stmtList(n.List)
-
-	// No-ops
-	case OEMPTY, ODCLCONST, ODCLTYPE, OFALL:
-
-	// Expression statements
-	case OCALLFUNC:
-		if isIntrinsicCall(n) {
-			s.intrinsicCall(n)
-			return
-		}
-		fallthrough
-
-	case OCALLMETH, OCALLINTER:
-		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") {
-				m := s.mem()
-				b := s.endBlock()
-				b.Kind = ssa.BlockExit
-				b.SetControl(m)
-				// TODO: never rewrite OPANIC to OCALLFUNC in the
-				// first place. Need to wait until all backends
-				// go through SSA.
-			}
-		}
-	case ODEFER:
-		if Debug_defer > 0 {
-			var defertype string
-			if s.hasOpenDefers {
-				defertype = "open-coded"
-			} else if n.Esc == EscNever {
-				defertype = "stack-allocated"
-			} else {
-				defertype = "heap-allocated"
-			}
-			Warnl(n.Pos, "%s defer", defertype)
-		}
-		if s.hasOpenDefers {
-			s.openDeferRecord(n.Left)
-		} else {
-			d := callDefer
-			if n.Esc == EscNever {
-				d = callDeferStack
-			}
-			s.callResult(n.Left, d)
-		}
-	case OGO:
-		s.callResult(n.Left, callGo)
-
-	case OAS2DOTTYPE:
-		res, resok := s.dottype(n.Right, true)
-		deref := false
-		if !canSSAType(n.Right.Type) {
-			if res.Op != ssa.OpLoad {
-				s.Fatalf("dottype of non-load")
-			}
-			mem := s.mem()
-			if mem.Op == ssa.OpVarKill {
-				mem = mem.Args[0]
-			}
-			if res.Args[1] != mem {
-				s.Fatalf("memory no longer live from 2-result dottype load")
-			}
-			deref = true
-			res = res.Args[0]
-		}
-		s.assign(n.List.First(), res, deref, 0)
-		s.assign(n.List.Second(), resok, false, 0)
-		return
-
-	case 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)
-		return
-
-	case ODCL:
-		if n.Left.Class() == PAUTOHEAP {
-			s.Fatalf("DCL %v", n)
-		}
-
-	case OLABEL:
-		sym := n.Sym
-		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)
-		}
-
-		// Go to that label.
-		// (We pretend "label:" is preceded by "goto label", unless the predecessor is unreachable.)
-		if s.curBlock != nil {
-			b := s.endBlock()
-			b.AddEdgeTo(lab.target)
-		}
-		s.startBlock(lab.target)
-
-	case OGOTO:
-		sym := n.Sym
-
-		lab := s.label(sym)
-		if lab.target == nil {
-			lab.target = s.f.NewBlock(ssa.BlockPlain)
-		}
-
-		b := s.endBlock()
-		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 {
-			// An x=x assignment. No point in doing anything
-			// here. In addition, skipping this assignment
-			// prevents generating:
-			//   VARDEF x
-			//   COPY x -> x
-			// which is bad because x is incorrectly considered
-			// dead before the vardef. See issue #14904.
-			return
-		}
-
-		// Evaluate RHS.
-		rhs := n.Right
-		if rhs != nil {
-			switch rhs.Op {
-			case OSTRUCTLIT, OARRAYLIT, 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) {
-					s.Fatalf("literal with nonzero value in SSA: %v", rhs)
-				}
-				rhs = nil
-			case OAPPEND:
-				// 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 {
-					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)")
-					}
-					break
-				}
-				if Debug_append > 0 {
-					Warnl(n.Pos, "append: len-only update")
-				}
-				s.append(rhs, true)
-				return
-			}
-		}
-
-		if n.Left.isBlank() {
-			// _ = rhs
-			// Just evaluate rhs for side-effects.
-			if rhs != nil {
-				s.expr(rhs)
-			}
-			return
-		}
-
-		var t *types.Type
-		if n.Right != nil {
-			t = n.Right.Type
-		} else {
-			t = n.Left.Type
-		}
-
-		var r *ssa.Value
-		deref := !canSSAType(t)
-		if deref {
-			if rhs == nil {
-				r = nil // Signal assign to use OpZero.
-			} else {
-				r = s.addr(rhs)
-			}
-		} else {
-			if rhs == nil {
-				r = s.zeroVal(t)
-			} else {
-				r = s.expr(rhs)
-			}
-		}
-
-		var skip skipMask
-		if rhs != nil && (rhs.Op == OSLICE || rhs.Op == OSLICE3 || rhs.Op == OSLICESTR) && samesafeexpr(rhs.Left, n.Left) {
-			// 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) {
-				// [0:...] is the same as [:...]
-				i = nil
-			}
-			// TODO: detect defaults for len/cap also.
-			// 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)) {
-			//      j = nil
-			//}
-			//if k != nil && (k.Op == OCAP && samesafeexpr(k.Left, n.Left)) {
-			//      k = nil
-			//}
-			if i == nil {
-				skip |= skipPtr
-				if j == nil {
-					skip |= skipLen
-				}
-				if k == nil {
-					skip |= skipCap
-				}
-			}
-		}
-
-		s.assign(n.Left, r, deref, skip)
-
-	case OIF:
-		if Isconst(n.Left, CTBOOL) {
-			s.stmtList(n.Left.Ninit)
-			if n.Left.BoolVal() {
-				s.stmtList(n.Nbody)
-			} else {
-				s.stmtList(n.Rlist)
-			}
-			break
-		}
-
-		bEnd := s.f.NewBlock(ssa.BlockPlain)
-		var likely int8
-		if n.Likely() {
-			likely = 1
-		}
-		var bThen *ssa.Block
-		if n.Nbody.Len() != 0 {
-			bThen = s.f.NewBlock(ssa.BlockPlain)
-		} else {
-			bThen = bEnd
-		}
-		var bElse *ssa.Block
-		if n.Rlist.Len() != 0 {
-			bElse = s.f.NewBlock(ssa.BlockPlain)
-		} else {
-			bElse = bEnd
-		}
-		s.condBranch(n.Left, bThen, bElse, likely)
-
-		if n.Nbody.Len() != 0 {
-			s.startBlock(bThen)
-			s.stmtList(n.Nbody)
-			if b := s.endBlock(); b != nil {
-				b.AddEdgeTo(bEnd)
-			}
-		}
-		if n.Rlist.Len() != 0 {
-			s.startBlock(bElse)
-			s.stmtList(n.Rlist)
-			if b := s.endBlock(); b != nil {
-				b.AddEdgeTo(bEnd)
-			}
-		}
-		s.startBlock(bEnd)
-
-	case ORETURN:
-		s.stmtList(n.List)
-		b := s.exit()
-		b.Pos = s.lastPos.WithIsStmt()
-
-	case ORETJMP:
-		s.stmtList(n.List)
-		b := s.exit()
-		b.Kind = ssa.BlockRetJmp // override BlockRet
-		b.Aux = n.Sym.Linksym()
-
-	case OCONTINUE, OBREAK:
-		var to *ssa.Block
-		if n.Sym == nil {
-			// plain break/continue
-			switch n.Op {
-			case OCONTINUE:
-				to = s.continueTo
-			case OBREAK:
-				to = s.breakTo
-			}
-		} else {
-			// labeled break/continue; look up the target
-			sym := n.Sym
-			lab := s.label(sym)
-			switch n.Op {
-			case OCONTINUE:
-				to = lab.continueTarget
-			case OBREAK:
-				to = lab.breakTarget
-			}
-		}
-
-		b := s.endBlock()
-		b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block.
-		b.AddEdgeTo(to)
-
-	case OFOR, 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:
-		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
-
-		// first, jump to condition test (OFOR) or body (OFORUNTIL)
-		b := s.endBlock()
-		if n.Op == OFOR {
-			b.AddEdgeTo(bCond)
-			// generate code to test condition
-			s.startBlock(bCond)
-			if n.Left != nil {
-				s.condBranch(n.Left, bBody, bEnd, 1)
-			} else {
-				b := s.endBlock()
-				b.Kind = ssa.BlockPlain
-				b.AddEdgeTo(bBody)
-			}
-
-		} else {
-			b.AddEdgeTo(bBody)
-		}
-
-		// set up for continue/break in body
-		prevContinue := s.continueTo
-		prevBreak := s.breakTo
-		s.continueTo = bIncr
-		s.breakTo = bEnd
-		lab := s.labeledNodes[n]
-		if lab != nil {
-			// labeled for loop
-			lab.continueTarget = bIncr
-			lab.breakTarget = bEnd
-		}
-
-		// generate body
-		s.startBlock(bBody)
-		s.stmtList(n.Nbody)
-
-		// tear down continue/break
-		s.continueTo = prevContinue
-		s.breakTo = prevBreak
-		if lab != nil {
-			lab.continueTarget = nil
-			lab.breakTarget = nil
-		}
-
-		// done with body, goto incr
-		if b := s.endBlock(); b != nil {
-			b.AddEdgeTo(bIncr)
-		}
-
-		// generate incr (and, for OFORUNTIL, condition)
-		s.startBlock(bIncr)
-		if n.Right != nil {
-			s.stmt(n.Right)
-		}
-		if n.Op == 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 {
-					b.Pos = bCond.Pos
-				}
-			}
-		} else {
-			// bCond is unused in OFORUNTIL, so repurpose it.
-			bLateIncr := bCond
-			// test condition
-			s.condBranch(n.Left, bLateIncr, bEnd, 1)
-			// generate late increment
-			s.startBlock(bLateIncr)
-			s.stmtList(n.List)
-			s.endBlock().AddEdgeTo(bBody)
-		}
-
-		s.startBlock(bEnd)
-
-	case OSWITCH, 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 {
-			// labeled
-			lab.breakTarget = bEnd
-		}
-
-		// generate body code
-		s.stmtList(n.Nbody)
-
-		s.breakTo = prevBreak
-		if lab != nil {
-			lab.breakTarget = nil
-		}
-
-		// walk adds explicit OBREAK nodes to the end of all reachable code paths.
-		// If we still have a current block here, then mark it unreachable.
-		if s.curBlock != nil {
-			m := s.mem()
-			b := s.endBlock()
-			b.Kind = ssa.BlockExit
-			b.SetControl(m)
-		}
-		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 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)
-		}
-
-	case 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)
-		}
-		switch n.Left.Class() {
-		case PAUTO, PPARAM, PPARAMOUT:
-		default:
-			s.Fatalf("VARLIVE variable %v must be Auto or Arg", n.Left)
-		}
-		s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, n.Left, s.mem())
-
-	case OCHECKNIL:
-		p := s.expr(n.Left)
-		s.nilCheck(p)
-
-	case OINLMARK:
-		s.newValue1I(ssa.OpInlMark, types.TypeVoid, n.Xoffset, s.mem())
-
-	default:
-		s.Fatalf("unhandled stmt %v", n.Op)
-	}
-}
-
-// If true, share as many open-coded defer exits as possible (with the downside of
-// worse line-number information)
-const shareDeferExits = false
-
-// exit processes any code that needs to be generated just before returning.
-// 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 {
-	if s.hasdefer {
-		if s.hasOpenDefers {
-			if shareDeferExits && s.lastDeferExit != nil && len(s.openDefers) == s.lastDeferCount {
-				if s.curBlock.Kind != ssa.BlockPlain {
-					panic("Block for an exit should be BlockPlain")
-				}
-				s.curBlock.AddEdgeTo(s.lastDeferExit)
-				s.endBlock()
-				return s.lastDeferFinalBlock
-			}
-			s.openDeferExit()
-		} else {
-			s.rtcall(Deferreturn, true, nil)
-		}
-	}
-
-	// Run exit code. Typically, this code copies heap-allocated PPARAMOUT
-	// variables back to the stack.
-	s.stmtList(s.curfn.Func.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.
-	}
-
-	// Do actual return.
-	m := s.mem()
-	b := s.endBlock()
-	b.Kind = ssa.BlockRet
-	b.SetControl(m)
-	if s.hasdefer && s.hasOpenDefers {
-		s.lastDeferFinalBlock = b
-	}
-	return b
-}
-
-type opAndType struct {
-	op    Op
-	etype types.EType
-}
-
-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
-	switch e {
-	default:
-		return e
-	case TINT:
-		if s.config.PtrSize == 8 {
-			return TINT64
-		}
-		return TINT32
-	case TUINT:
-		if s.config.PtrSize == 8 {
-			return TUINT64
-		}
-		return TUINT32
-	case TUINTPTR:
-		if s.config.PtrSize == 8 {
-			return TUINT64
-		}
-		return TUINT32
-	}
-}
-
-func (s *state) ssaOp(op Op, t *types.Type) ssa.Op {
-	etype := s.concreteEtype(t)
-	x, ok := opToSSA[opAndType{op, etype}]
-	if !ok {
-		s.Fatalf("unhandled binary op %v %s", op, etype)
-	}
-	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
-}
-
-type twoTypes struct {
-	etype1 types.EType
-	etype2 types.EType
-}
-
-type twoOpsAndType struct {
-	op1              ssa.Op
-	op2              ssa.Op
-	intermediateType types.EType
-}
-
-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},
-	// 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
-
-	// 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},
-}
-
-// 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},
-}
-
-// 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},
-}
-
-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 {
-	etype1 := s.concreteEtype(t)
-	etype2 := s.concreteEtype(u)
-	x, ok := shiftOpToSSA[opAndTwoTypes{op, etype1, etype2}]
-	if !ok {
-		s.Fatalf("unhandled shift op %v etype=%s/%s", op, etype1, etype2)
-	}
-	return x
-}
-
-// 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) {
-		// ONAMEs and named OLITERALs have the line number
-		// of the decl, not the use. See issue 14742.
-		s.pushLine(n.Pos)
-		defer s.popLine()
-	}
-
-	s.stmtList(n.Ninit)
-	switch n.Op {
-	case OBYTES2STRTMP:
-		slice := s.expr(n.Left)
-		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)
-		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 {
-			// "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)
-		}
-		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() {
-			case 1:
-				return s.constInt8(n.Type, int8(i))
-			case 2:
-				return s.constInt16(n.Type, int16(i))
-			case 4:
-				return s.constInt32(n.Type, int32(i))
-			case 8:
-				return s.constInt64(n.Type, i)
-			default:
-				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 *Mpflt:
-			switch n.Type.Size() {
-			case 4:
-				return s.constFloat32(n.Type, u.Float32())
-			case 8:
-				return s.constFloat64(n.Type, u.Float64())
-			default:
-				s.Fatalf("bad float size %d", n.Type.Size())
-				return nil
-			}
-		case *Mpcplx:
-			r := &u.Real
-			i := &u.Imag
-			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()))
-			case 16:
-				pt := types.Types[TFLOAT64]
-				return s.newValue2(ssa.OpComplexMake, n.Type,
-					s.constFloat64(pt, r.Float64()),
-					s.constFloat64(pt, i.Float64()))
-			default:
-				s.Fatalf("bad float size %d", n.Type.Size())
-				return nil
-			}
-
-		default:
-			s.Fatalf("unhandled OLITERAL %v", n.Val().Ctype())
-			return nil
-		}
-	case OCONVNOP:
-		to := n.Type
-		from := n.Left.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)
-
-		// Special case for not confusing GC and liveness.
-		// We don't want pointers accidentally classified
-		// as not-pointers or vice-versa because of copy
-		// elision.
-		if to.IsPtrShaped() != from.IsPtrShaped() {
-			return s.newValue2(ssa.OpConvert, to, x, s.mem())
-		}
-
-		v := s.newValue1(ssa.OpCopy, to, x) // ensure that v has the right type
-
-		// CONVNOP closure
-		if to.Etype == TFUNC && from.IsPtrShaped() {
-			return v
-		}
-
-		// named <--> unnamed type or typed <--> untyped const
-		if from.Etype == to.Etype {
-			return v
-		}
-
-		// unsafe.Pointer <--> *T
-		if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
-			return v
-		}
-
-		// map <--> *hmap
-		if to.Etype == TMAP && from.IsPtr() &&
-			to.MapType().Hmap == from.Elem() {
-			return v
-		}
-
-		dowidth(from)
-		dowidth(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)
-			return nil
-		}
-
-		if 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 {
-			s.Fatalf("CONVNOP unrecognized non-integer %v -> %v\n", from, to)
-			return nil
-		}
-
-		// 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) {
-			// Bool -> uint8 is generated internally when indexing into runtime.staticbyte.
-			return s.newValue1(ssa.OpCopy, n.Type, x)
-		}
-		if ft.IsInteger() && tt.IsInteger() {
-			var op ssa.Op
-			if tt.Size() == ft.Size() {
-				op = ssa.OpCopy
-			} else if tt.Size() < ft.Size() {
-				// truncation
-				switch 10*ft.Size() + tt.Size() {
-				case 21:
-					op = ssa.OpTrunc16to8
-				case 41:
-					op = ssa.OpTrunc32to8
-				case 42:
-					op = ssa.OpTrunc32to16
-				case 81:
-					op = ssa.OpTrunc64to8
-				case 82:
-					op = ssa.OpTrunc64to16
-				case 84:
-					op = ssa.OpTrunc64to32
-				default:
-					s.Fatalf("weird integer truncation %v -> %v", ft, tt)
-				}
-			} else if ft.IsSigned() {
-				// sign extension
-				switch 10*ft.Size() + tt.Size() {
-				case 12:
-					op = ssa.OpSignExt8to16
-				case 14:
-					op = ssa.OpSignExt8to32
-				case 18:
-					op = ssa.OpSignExt8to64
-				case 24:
-					op = ssa.OpSignExt16to32
-				case 28:
-					op = ssa.OpSignExt16to64
-				case 48:
-					op = ssa.OpSignExt32to64
-				default:
-					s.Fatalf("bad integer sign extension %v -> %v", ft, tt)
-				}
-			} else {
-				// zero extension
-				switch 10*ft.Size() + tt.Size() {
-				case 12:
-					op = ssa.OpZeroExt8to16
-				case 14:
-					op = ssa.OpZeroExt8to32
-				case 18:
-					op = ssa.OpZeroExt8to64
-				case 24:
-					op = ssa.OpZeroExt16to32
-				case 28:
-					op = ssa.OpZeroExt16to64
-				case 48:
-					op = ssa.OpZeroExt32to64
-				default:
-					s.Fatalf("weird integer sign extension %v -> %v", ft, tt)
-				}
-			}
-			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 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 conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
-					conv = conv1
-				}
-			}
-
-			if thearch.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 {
-						return s.uint32Tofloat32(n, x, ft, tt)
-					}
-					if tt.Size() == 8 {
-						return s.uint32Tofloat64(n, x, ft, tt)
-					}
-				} else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() {
-					// ft is float32 or float64, and tt is unsigned integer
-					if ft.Size() == 4 {
-						return s.float32ToUint32(n, x, ft, tt)
-					}
-					if ft.Size() == 8 {
-						return s.float64ToUint32(n, x, ft, tt)
-					}
-				}
-			}
-
-			if !ok {
-				s.Fatalf("weird float conversion %v -> %v", ft, tt)
-			}
-			op1, op2, it := conv.op1, conv.op2, conv.intermediateType
-
-			if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid {
-				// normal case, not tripping over unsigned 64
-				if op1 == ssa.OpCopy {
-					if op2 == ssa.OpCopy {
-						return x
-					}
-					return s.newValueOrSfCall1(op2, n.Type, x)
-				}
-				if op2 == ssa.OpCopy {
-					return s.newValueOrSfCall1(op1, n.Type, x)
-				}
-				return s.newValueOrSfCall1(op2, n.Type, s.newValueOrSfCall1(op1, types.Types[it], x))
-			}
-			// Tricky 64-bit unsigned cases.
-			if ft.IsInteger() {
-				// tt is float32 or float64, and ft is also unsigned
-				if tt.Size() == 4 {
-					return s.uint64Tofloat32(n, x, ft, tt)
-				}
-				if tt.Size() == 8 {
-					return s.uint64Tofloat64(n, x, ft, tt)
-				}
-				s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt)
-			}
-			// ft is float32 or float64, and tt is unsigned integer
-			if ft.Size() == 4 {
-				return s.float32ToUint64(n, x, ft, tt)
-			}
-			if ft.Size() == 8 {
-				return s.float64ToUint64(n, x, ft, tt)
-			}
-			s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt)
-			return nil
-		}
-
-		if ft.IsComplex() && tt.IsComplex() {
-			var op ssa.Op
-			if ft.Size() == tt.Size() {
-				switch ft.Size() {
-				case 8:
-					op = ssa.OpRound32F
-				case 16:
-					op = ssa.OpRound64F
-				default:
-					s.Fatalf("weird complex conversion %v -> %v", ft, tt)
-				}
-			} else if ft.Size() == 8 && tt.Size() == 16 {
-				op = ssa.OpCvt32Fto64F
-			} else if ft.Size() == 16 && tt.Size() == 8 {
-				op = ssa.OpCvt64Fto32F
-			} else {
-				s.Fatalf("weird complex conversion %v -> %v", ft, tt)
-			}
-			ftp := floatForComplex(ft)
-			ttp := 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)
-		return nil
-
-	case ODOTTYPE:
-		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:
-				return c
-			case ONE:
-				return s.newValue1(ssa.OpNot, types.Types[TBOOL], c)
-			default:
-				s.Fatalf("ordered complex compare %v", n.Op)
-			}
-		}
-
-		// Convert OGE and OGT into OLE and OLT.
-		op := n.Op
-		switch op {
-		case OGE:
-			op, a, b = OLE, b, a
-		case OGT:
-			op, a, b = OLT, b, a
-		}
-		if n.Left.Type.IsFloat() {
-			// float comparison
-			return s.newValueOrSfCall2(s.ssaOp(op, n.Left.Type), 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() {
-			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
-
-			areal := s.newValue1(ssa.OpComplexReal, pt, a)
-			breal := s.newValue1(ssa.OpComplexReal, pt, b)
-			aimag := s.newValue1(ssa.OpComplexImag, pt, a)
-			bimag := s.newValue1(ssa.OpComplexImag, pt, b)
-
-			if pt != wt { // Widen for calculation
-				areal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, areal)
-				breal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, breal)
-				aimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, aimag)
-				bimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, bimag)
-			}
-
-			xreal := s.newValueOrSfCall2(subop, wt, s.newValueOrSfCall2(mulop, wt, areal, breal), s.newValueOrSfCall2(mulop, wt, aimag, bimag))
-			ximag := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, areal, bimag), s.newValueOrSfCall2(mulop, wt, aimag, breal))
-
-			if pt != wt { // Narrow to store back
-				xreal = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, xreal)
-				ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, 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)
-		}
-
-		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() {
-			// 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.
-			mulop := ssa.OpMul64F
-			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
-
-			areal := s.newValue1(ssa.OpComplexReal, pt, a)
-			breal := s.newValue1(ssa.OpComplexReal, pt, b)
-			aimag := s.newValue1(ssa.OpComplexImag, pt, a)
-			bimag := s.newValue1(ssa.OpComplexImag, pt, b)
-
-			if pt != wt { // Widen for calculation
-				areal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, areal)
-				breal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, breal)
-				aimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, aimag)
-				bimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, bimag)
-			}
-
-			denom := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, breal, breal), s.newValueOrSfCall2(mulop, wt, bimag, bimag))
-			xreal := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, areal, breal), s.newValueOrSfCall2(mulop, wt, aimag, bimag))
-			ximag := s.newValueOrSfCall2(subop, wt, s.newValueOrSfCall2(mulop, wt, aimag, breal), s.newValueOrSfCall2(mulop, wt, areal, bimag))
-
-			// TODO not sure if this is best done in wide precision or narrow
-			// Double-rounding might be an issue.
-			// Note that the pre-SSA implementation does the entire calculation
-			// in wide format, so wide is compatible.
-			xreal = s.newValueOrSfCall2(divop, wt, xreal, denom)
-			ximag = s.newValueOrSfCall2(divop, wt, ximag, denom)
-
-			if pt != wt { // Narrow to store back
-				xreal = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, xreal)
-				ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, 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)
-		}
-		return s.intDivide(n, a, b)
-	case OMOD:
-		a := s.expr(n.Left)
-		b := s.expr(n.Right)
-		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,
-				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)
-		bt := b.Type
-		if bt.IsSigned() {
-			cmp := s.newValue2(s.ssaOp(OLE, bt), types.Types[TBOOL], s.zeroVal(bt), b)
-			s.check(cmp, panicshift)
-			bt = bt.ToUnsigned()
-		}
-		return s.newValue2(s.ssaShiftOp(n.Op, n.Type, bt), a.Type, a, b)
-	case OANDAND, 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
-		// s.vars table (normally variables are only
-		// associated with ONAME nodes). We convert
-		//     A && B
-		// to
-		//     var = A
-		//     if var {
-		//         var = B
-		//     }
-		// Using var in the subsequent block introduces the
-		// necessary phi variable.
-		el := s.expr(n.Left)
-		s.vars[n] = el
-
-		b := s.endBlock()
-		b.Kind = ssa.BlockIf
-		b.SetControl(el)
-		// In theory, we should set b.Likely here based on context.
-		// However, gc only gives us likeliness hints
-		// in a single place, for plain OIF statements,
-		// and passing around context is finnicky, so don't bother for now.
-
-		bRight := s.f.NewBlock(ssa.BlockPlain)
-		bResult := s.f.NewBlock(ssa.BlockPlain)
-		if n.Op == OANDAND {
-			b.AddEdgeTo(bRight)
-			b.AddEdgeTo(bResult)
-		} else if n.Op == OOROR {
-			b.AddEdgeTo(bResult)
-			b.AddEdgeTo(bRight)
-		}
-
-		s.startBlock(bRight)
-		er := s.expr(n.Right)
-		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)
-
-	// 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,
-				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:
-		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)
-		}
-		which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset)
-		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)
-		}
-		if canSSAType(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)
-		}
-
-	case ODEREF:
-		p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
-		return s.load(n.Type, p)
-
-	case ODOT:
-		if n.Left.Op == 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)
-			}
-			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) {
-			p := s.addr(n)
-			return s.load(n.Type, p)
-		}
-		v := s.expr(n.Left)
-		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 OINDEX:
-		switch {
-		case n.Left.Type.IsString():
-			if n.Bounded() && Isconst(n.Left, CTSTR) && Isconst(n.Right, CTINT) {
-				// 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()])))
-			}
-			a := s.expr(n.Left)
-			i := s.expr(n.Right)
-			len := s.newValue1(ssa.OpStringLen, 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)
-			} else {
-				ptr = s.newValue2(ssa.OpAddPtr, ptrtyp, ptr, i)
-			}
-			return s.load(types.Types[TUINT8], ptr)
-		case n.Left.Type.IsSlice():
-			p := s.addr(n)
-			return s.load(n.Left.Type.Elem(), p)
-		case n.Left.Type.IsArray():
-			if canSSAType(n.Left.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)
-				if bound == 0 {
-					// Bounds check will never succeed.  Might as well
-					// use constants for the bounds check.
-					z := s.constInt(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)
-				}
-				len := s.constInt(types.Types[TINT], bound)
-				s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded()) // checks i == 0
-				return s.newValue1I(ssa.OpArraySelect, n.Type, 0, a)
-			}
-			p := s.addr(n)
-			return s.load(n.Left.Type.Elem(), p)
-		default:
-			s.Fatalf("bad type for index %v", n.Left.Type)
-			return nil
-		}
-
-	case OLEN, OCAP:
-		switch {
-		case n.Left.Type.IsSlice():
-			op := ssa.OpSliceLen
-			if n.Op == 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))
-		default: // array
-			return s.constInt(types.Types[TINT], n.Left.Type.NumElem())
-		}
-
-	case OSPTR:
-		a := s.expr(n.Left)
-		if n.Left.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)
-		var i, j, k *ssa.Value
-		low, high, max := n.SliceBounds()
-		if low != nil {
-			i = s.expr(low)
-		}
-		if high != nil {
-			j = s.expr(high)
-		}
-		if max != nil {
-			k = s.expr(max)
-		}
-		p, l, c := s.slice(v, i, j, k, n.Bounded())
-		return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
-
-	case OSLICESTR:
-		v := s.expr(n.Left)
-		var i, j *ssa.Value
-		low, high, _ := n.SliceBounds()
-		if low != nil {
-			i = s.expr(low)
-		}
-		if high != nil {
-			j = s.expr(high)
-		}
-		p, l, _ := s.slice(v, i, j, nil, n.Bounded())
-		return s.newValue2(ssa.OpStringMake, n.Type, p, l)
-
-	case OCALLFUNC:
-		if isIntrinsicCall(n) {
-			return s.intrinsicCall(n)
-		}
-		fallthrough
-
-	case OCALLINTER, OCALLMETH:
-		return s.callResult(n, callNormal)
-
-	case OGETG:
-		return s.newValue1(ssa.OpGetG, n.Type, s.mem())
-
-	case OAPPEND:
-		return s.append(n, false)
-
-	case OSTRUCTLIT, 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) {
-			s.Fatalf("literal with nonzero value in SSA: %v", n)
-		}
-		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]
-
-	default:
-		s.Fatalf("unhandled expr %v", n.Op)
-		return nil
-	}
-}
-
-// append converts an OAPPEND node to SSA.
-// If inplace is false, it converts the OAPPEND expression n to an ssa.Value,
-// adds it to s, and returns the 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 {
-	// If inplace is false, process as expression "append(s, e1, e2, e3)":
-	//
-	// ptr, len, cap := s
-	// newlen := len + 3
-	// if newlen > cap {
-	//     ptr, len, cap = growslice(s, newlen)
-	//     newlen = len + 3 // recalculate to avoid a spill
-	// }
-	// // with write barriers, if needed:
-	// *(ptr+len) = e1
-	// *(ptr+len+1) = e2
-	// *(ptr+len+2) = e3
-	// return makeslice(ptr, newlen, cap)
-	//
-	//
-	// If inplace is true, process as statement "s = append(s, e1, e2, e3)":
-	//
-	// a := &s
-	// ptr, len, cap := s
-	// newlen := len + 3
-	// if uint(newlen) > uint(cap) {
-	//    newptr, len, newcap = growslice(ptr, len, cap, newlen)
-	//    vardef(a)       // if necessary, advise liveness we are writing a new a
-	//    *a.cap = newcap // write before ptr to avoid a spill
-	//    *a.ptr = newptr // with write barrier
-	// }
-	// newlen = len + 3 // recalculate to avoid a spill
-	// *a.len = newlen
-	// // with write barriers, if needed:
-	// *(ptr+len) = e1
-	// *(ptr+len+1) = e2
-	// *(ptr+len+2) = e3
-
-	et := n.Type.Elem()
-	pt := types.NewPtr(et)
-
-	// Evaluate slice
-	sn := n.List.First() // 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)
-	} else {
-		slice = s.expr(sn)
-	}
-
-	// Allocate new blocks
-	grow := s.f.NewBlock(ssa.BlockPlain)
-	assign := s.f.NewBlock(ssa.BlockPlain)
-
-	// Decide if we need to grow
-	nargs := int64(n.List.Len() - 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))
-
-	cmp := s.newValue2(s.ssaOp(OLT, types.Types[TUINT]), types.Types[TBOOL], c, nl)
-	s.vars[&ptrVar] = p
-
-	if !inplace {
-		s.vars[&newlenVar] = nl
-		s.vars[&capVar] = c
-	} else {
-		s.vars[&lenVar] = l
-	}
-
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.Likely = ssa.BranchUnlikely
-	b.SetControl(cmp)
-	b.AddEdgeTo(grow)
-	b.AddEdgeTo(assign)
-
-	// 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)
-
-	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())
-		}
-		capaddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, sliceCapOffset, addr)
-		s.store(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
-	} 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]
-	}
-
-	b = s.endBlock()
-	b.AddEdgeTo(assign)
-
-	// assign new elements to slots
-	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)
-	}
-
-	// Evaluate args
-	type argRec struct {
-		// if store is true, we're appending the value v.  If false, we're appending the
-		// value at *v.
-		v     *ssa.Value
-		store bool
-	}
-	args := make([]argRec, 0, nargs)
-	for _, n := range n.List.Slice()[1:] {
-		if canSSAType(n.Type) {
-			args = append(args, argRec{v: s.expr(n), store: true})
-		} else {
-			v := s.addr(n)
-			args = append(args, argRec{v: v})
-		}
-	}
-
-	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
-	}
-	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)))
-		if arg.store {
-			s.storeType(et, addr, arg.v, 0, true)
-		} else {
-			s.move(et, addr, arg.v)
-		}
-	}
-
-	delete(s.vars, &ptrVar)
-	if inplace {
-		delete(s.vars, &lenVar)
-		return nil
-	}
-	delete(s.vars, &newlenVar)
-	delete(s.vars, &capVar)
-	// make result
-	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:
-		mid := s.f.NewBlock(ssa.BlockPlain)
-		s.stmtList(cond.Ninit)
-		s.condBranch(cond.Left, mid, no, max8(likely, 0))
-		s.startBlock(mid)
-		s.condBranch(cond.Right, 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
-		// whether the first branch is likely or not. So we pass 0 for
-		// 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:
-		mid := s.f.NewBlock(ssa.BlockPlain)
-		s.stmtList(cond.Ninit)
-		s.condBranch(cond.Left, yes, mid, min8(likely, 0))
-		s.startBlock(mid)
-		s.condBranch(cond.Right, 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)
-		return
-	}
-	c := s.expr(cond)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(c)
-	b.Likely = ssa.BranchPrediction(likely) // gc and ssa both use -1/0/+1 for likeliness
-	b.AddEdgeTo(yes)
-	b.AddEdgeTo(no)
-}
-
-type skipMask uint8
-
-const (
-	skipPtr skipMask = 1 << iota
-	skipLen
-	skipCap
-)
-
-// assign does left = right.
-// Right has already been evaluated to ssa, left has not.
-// 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() {
-		return
-	}
-	t := left.Type
-	dowidth(t)
-	if s.canSSA(left) {
-		if deref {
-			s.Fatalf("can SSA LHS %v but not RHS %s", left, right)
-		}
-		if left.Op == 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.
-			// For instance:
-			//   type T struct {a, b, c int}
-			//   var T x
-			//   x.b = 5
-			// 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
-			nf := t.NumFields()
-			idx := fieldIdx(left)
-
-			// Grab old value of structure.
-			old := s.expr(left.Left)
-
-			// Make new structure.
-			new := s.newValue0(ssa.StructMakeOp(t.NumFields()), t)
-
-			// Add fields as args.
-			for i := 0; i < nf; i++ {
-				if i == idx {
-					new.AddArg(right)
-				} else {
-					new.AddArg(s.newValue1I(ssa.OpStructSelect, t.FieldType(i), int64(i), old))
-				}
-			}
-
-			// Recursively assign the new value we've made to the base of the dot op.
-			s.assign(left.Left, 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)
-			defer s.popLine()
-			// We're assigning to an element of an ssa-able array.
-			// a[i] = v
-			t := left.Left.Type
-			n := t.NumElem()
-
-			i := s.expr(left.Right) // 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)
-				s.boundsCheck(z, z, ssa.BoundsIndex, false)
-				return
-			}
-			if n != 1 {
-				s.Fatalf("assigning to non-1-length array")
-			}
-			// Rewrite to a = [1]{v}
-			len := s.constInt(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)
-			return
-		}
-		// Update variable assignment.
-		s.vars[left] = right
-		s.addNamedValue(left, right)
-		return
-	}
-
-	// 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())
-	}
-
-	// Left is not ssa-able. Compute its address.
-	addr := s.addr(left)
-	if 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]
-	}
-	if deref {
-		// Treat as a mem->mem move.
-		if right == nil {
-			s.zero(t, addr)
-		} else {
-			s.move(t, addr, right)
-		}
-		return
-	}
-	// Treat as a store.
-	s.storeType(t, addr, right, skip, !left.IsAutoTmp())
-}
-
-// zeroVal returns the zero value for type t.
-func (s *state) zeroVal(t *types.Type) *ssa.Value {
-	switch {
-	case t.IsInteger():
-		switch t.Size() {
-		case 1:
-			return s.constInt8(t, 0)
-		case 2:
-			return s.constInt16(t, 0)
-		case 4:
-			return s.constInt32(t, 0)
-		case 8:
-			return s.constInt64(t, 0)
-		default:
-			s.Fatalf("bad sized integer type %v", t)
-		}
-	case t.IsFloat():
-		switch t.Size() {
-		case 4:
-			return s.constFloat32(t, 0)
-		case 8:
-			return s.constFloat64(t, 0)
-		default:
-			s.Fatalf("bad sized float type %v", t)
-		}
-	case t.IsComplex():
-		switch t.Size() {
-		case 8:
-			z := s.constFloat32(types.Types[TFLOAT32], 0)
-			return s.entryNewValue2(ssa.OpComplexMake, t, z, z)
-		case 16:
-			z := s.constFloat64(types.Types[TFLOAT64], 0)
-			return s.entryNewValue2(ssa.OpComplexMake, t, z, z)
-		default:
-			s.Fatalf("bad sized complex type %v", t)
-		}
-
-	case t.IsString():
-		return s.constEmptyString(t)
-	case t.IsPtrShaped():
-		return s.constNil(t)
-	case t.IsBoolean():
-		return s.constBool(false)
-	case t.IsInterface():
-		return s.constInterface(t)
-	case t.IsSlice():
-		return s.constSlice(t)
-	case t.IsStruct():
-		n := t.NumFields()
-		v := s.entryNewValue0(ssa.StructMakeOp(t.NumFields()), t)
-		for i := 0; i < n; i++ {
-			v.AddArg(s.zeroVal(t.FieldType(i)))
-		}
-		return v
-	case t.IsArray():
-		switch t.NumElem() {
-		case 0:
-			return s.entryNewValue0(ssa.OpArrayMake0, t)
-		case 1:
-			return s.entryNewValue1(ssa.OpArrayMake1, t, s.zeroVal(t.Elem()))
-		}
-	}
-	s.Fatalf("zero for type %v not implemented", t)
-	return nil
-}
-
-type callKind int8
-
-const (
-	callNormal callKind = iota
-	callDefer
-	callDeferStack
-	callGo
-)
-
-type sfRtCallDef struct {
-	rtfn  *obj.LSym
-	rtype types.EType
-}
-
-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},
-	}
-}
-
-// TODO: do not emit sfcall if operation can be optimized to constant in later
-// opt phase
-func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) {
-	if callDef, ok := softFloatOps[op]; ok {
-		switch op {
-		case ssa.OpLess32F,
-			ssa.OpLess64F,
-			ssa.OpLeq32F,
-			ssa.OpLeq64F:
-			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])
-		}
-
-		result := s.rtcall(callDef.rtfn, true, []*types.Type{types.Types[callDef.rtype]}, args...)[0]
-		if op == ssa.OpNeq32F || op == ssa.OpNeq64F {
-			result = s.newValue1(ssa.OpNot, result.Type, result)
-		}
-		return result, true
-	}
-	return nil, false
-}
-
-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 intrinsicKey struct {
-	arch *sys.Arch
-	pkg  string
-	fn   string
-}
-
-func init() {
-	intrinsics = map[intrinsicKey]intrinsicBuilder{}
-
-	var all []*sys.Arch
-	var p4 []*sys.Arch
-	var p8 []*sys.Arch
-	var lwatomics []*sys.Arch
-	for _, a := range &sys.Archs {
-		all = append(all, a)
-		if a.PtrSize == 4 {
-			p4 = append(p4, a)
-		} else {
-			p8 = append(p8, a)
-		}
-		if a.Family != sys.PPC64 {
-			lwatomics = append(lwatomics, a)
-		}
-	}
-
-	// add adds the intrinsic b for pkg.fn for the given list of architectures.
-	add := func(pkg, fn string, b intrinsicBuilder, archs ...*sys.Arch) {
-		for _, a := range archs {
-			intrinsics[intrinsicKey{a, pkg, fn}] = b
-		}
-	}
-	// addF does the same as add but operates on architecture families.
-	addF := func(pkg, fn string, b intrinsicBuilder, archFamilies ...sys.ArchFamily) {
-		m := 0
-		for _, f := range archFamilies {
-			if f >= 32 {
-				panic("too many architecture families")
-			}
-			m |= 1 << uint(f)
-		}
-		for _, a := range all {
-			if m>>uint(a.Family)&1 != 0 {
-				intrinsics[intrinsicKey{a, pkg, fn}] = b
-			}
-		}
-	}
-	// alias defines pkg.fn = pkg2.fn2 for all architectures in archs for which pkg2.fn2 exists.
-	alias := func(pkg, fn, pkg2, fn2 string, archs ...*sys.Arch) {
-		aliased := false
-		for _, a := range archs {
-			if b, ok := intrinsics[intrinsicKey{a, pkg2, fn2}]; ok {
-				intrinsics[intrinsicKey{a, pkg, fn}] = b
-				aliased = true
-			}
-		}
-		if !aliased {
-			panic(fmt.Sprintf("attempted to alias undefined intrinsic: %s.%s", pkg, fn))
-		}
-	}
-
-	/******** runtime ********/
-	if !instrumenting {
-		add("runtime", "slicebytetostringtmp",
-			func(s *state, n *Node, 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])
-			},
-			all...)
-	}
-	addF("runtime/internal/math", "MulUintptr",
-		func(s *state, n *Node, 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.OpMul64uover, types.NewTuple(types.Types[TUINT], 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 {
-			data := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, args[0])
-			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 {
-			return s.newValue0(ssa.OpGetClosurePtr, s.f.Config.Types.Uintptr)
-		},
-		all...)
-
-	add("runtime", "getcallerpc",
-		func(s *state, n *Node, 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 {
-			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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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)
-		},
-		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)
-		},
-		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)
-		},
-		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)
-		},
-		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)
-		},
-		sys.PPC64)
-	addF("runtime/internal/atomic", "Loadp",
-		func(s *state, n *Node, 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)
-			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())
-			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())
-			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())
-			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())
-			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())
-			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())
-			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)
-		},
-		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)
-		},
-		sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
-
-	type atomicOpEmitter func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType)
-
-	makeAtomicGuardedIntrinsicARM64 := func(op0, op1 ssa.Op, typ, rtyp types.EType, emit atomicOpEmitter) intrinsicBuilder {
-
-		return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
-			// Target Atomic feature is identified by dynamic detection
-			addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), arm64HasATOMICS, s.sb)
-			v := s.load(types.Types[TBOOL], addr)
-			b := s.endBlock()
-			b.Kind = ssa.BlockIf
-			b.SetControl(v)
-			bTrue := s.f.NewBlock(ssa.BlockPlain)
-			bFalse := s.f.NewBlock(ssa.BlockPlain)
-			bEnd := s.f.NewBlock(ssa.BlockPlain)
-			b.AddEdgeTo(bTrue)
-			b.AddEdgeTo(bFalse)
-			b.Likely = ssa.BranchLikely
-
-			// We have atomic instructions - use it directly.
-			s.startBlock(bTrue)
-			emit(s, n, args, op1, typ)
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Use original instruction sequence.
-			s.startBlock(bFalse)
-			emit(s, n, args, op0, typ)
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Merge results.
-			s.startBlock(bEnd)
-			if rtyp == TNIL {
-				return nil
-			} else {
-				return s.variable(n, types.Types[rtyp])
-			}
-		}
-	}
-
-	atomicXchgXaddEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
-		v := s.newValue3(op, types.NewTuple(types.Types[typ], types.TypeMem), args[0], args[1], s.mem())
-		s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
-		s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
-	}
-	addF("runtime/internal/atomic", "Xchg",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange32, ssa.OpAtomicExchange32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
-		sys.ARM64)
-	addF("runtime/internal/atomic", "Xchg64",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange64, ssa.OpAtomicExchange64Variant, TUINT64, TUINT64, atomicXchgXaddEmitterARM64),
-		sys.ARM64)
-
-	addF("runtime/internal/atomic", "Xadd",
-		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)
-		},
-		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)
-		},
-		sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X)
-
-	addF("runtime/internal/atomic", "Xadd",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, TUINT32, TUINT32, atomicXchgXaddEmitterARM64),
-		sys.ARM64)
-	addF("runtime/internal/atomic", "Xadd64",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, TUINT64, 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)
-		},
-		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)
-		},
-		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)
-		},
-		sys.PPC64)
-
-	atomicCasEmitterARM64 := func(s *state, n *Node, args []*ssa.Value, op ssa.Op, typ types.EType) {
-		v := s.newValue4(op, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
-		s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
-		s.vars[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
-	}
-
-	addF("runtime/internal/atomic", "Cas",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap32, ssa.OpAtomicCompareAndSwap32Variant, TUINT32, TBOOL, atomicCasEmitterARM64),
-		sys.ARM64)
-	addF("runtime/internal/atomic", "Cas64",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap64, ssa.OpAtomicCompareAndSwap64Variant, TUINT64, TBOOL, atomicCasEmitterARM64),
-		sys.ARM64)
-
-	addF("runtime/internal/atomic", "And8",
-		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())
-			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())
-			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())
-			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())
-			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())
-	}
-
-	addF("runtime/internal/atomic", "And8",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd8, ssa.OpAtomicAnd8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
-		sys.ARM64)
-	addF("runtime/internal/atomic", "And",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd32, ssa.OpAtomicAnd32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
-		sys.ARM64)
-	addF("runtime/internal/atomic", "Or8",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr8, ssa.OpAtomicOr8Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
-		sys.ARM64)
-	addF("runtime/internal/atomic", "Or",
-		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr32, ssa.OpAtomicOr32Variant, TNIL, TNIL, atomicAndOrEmitterARM64),
-		sys.ARM64)
-
-	alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...)
-	alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...)
-	alias("runtime/internal/atomic", "Loaduint", "runtime/internal/atomic", "Load", p4...)
-	alias("runtime/internal/atomic", "Loaduint", "runtime/internal/atomic", "Load64", p8...)
-	alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load", p4...)
-	alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load64", p8...)
-	alias("runtime/internal/atomic", "LoadAcq", "runtime/internal/atomic", "Load", lwatomics...)
-	alias("runtime/internal/atomic", "LoadAcq64", "runtime/internal/atomic", "Load64", lwatomics...)
-	alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...)
-	alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...) // linknamed
-	alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...)
-	alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...) // linknamed
-	alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store", p4...)
-	alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store64", p8...)
-	alias("runtime/internal/atomic", "StoreRel", "runtime/internal/atomic", "Store", lwatomics...)
-	alias("runtime/internal/atomic", "StoreRel64", "runtime/internal/atomic", "Store64", lwatomics...)
-	alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...)
-	alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...) // linknamed
-	alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...)
-	alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...) // linknamed
-	alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg", p4...)
-	alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg64", p8...)
-	alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd", p4...)
-	alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd64", p8...)
-	alias("runtime/internal/atomic", "Casuintptr", "runtime/internal/atomic", "Cas", p4...)
-	alias("runtime/internal/atomic", "Casuintptr", "runtime/internal/atomic", "Cas64", p8...)
-	alias("runtime/internal/atomic", "Casp1", "runtime/internal/atomic", "Cas", p4...)
-	alias("runtime/internal/atomic", "Casp1", "runtime/internal/atomic", "Cas64", p8...)
-	alias("runtime/internal/atomic", "CasRel", "runtime/internal/atomic", "Cas", lwatomics...)
-
-	/******** math ********/
-	addF("math", "Sqrt",
-		func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
-			return s.newValue1(ssa.OpSqrt, 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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		sys.ARM64, sys.PPC64, sys.S390X)
-	addF("math", "FMA",
-		func(s *state, n *Node, 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])
-			}
-			v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[TBOOL], x86HasFMA)
-			b := s.endBlock()
-			b.Kind = ssa.BlockIf
-			b.SetControl(v)
-			bTrue := s.f.NewBlock(ssa.BlockPlain)
-			bFalse := s.f.NewBlock(ssa.BlockPlain)
-			bEnd := s.f.NewBlock(ssa.BlockPlain)
-			b.AddEdgeTo(bTrue)
-			b.AddEdgeTo(bFalse)
-			b.Likely = ssa.BranchLikely // >= haswell cpus are common
-
-			// 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.endBlock().AddEdgeTo(bEnd)
-
-			// Call the pure Go version.
-			s.startBlock(bFalse)
-			s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Merge results.
-			s.startBlock(bEnd)
-			return s.variable(n, types.Types[TFLOAT64])
-		},
-		sys.AMD64)
-	addF("math", "FMA",
-		func(s *state, n *Node, 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])
-			}
-			addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb)
-			v := s.load(types.Types[TBOOL], addr)
-			b := s.endBlock()
-			b.Kind = ssa.BlockIf
-			b.SetControl(v)
-			bTrue := s.f.NewBlock(ssa.BlockPlain)
-			bFalse := s.f.NewBlock(ssa.BlockPlain)
-			bEnd := s.f.NewBlock(ssa.BlockPlain)
-			b.AddEdgeTo(bTrue)
-			b.AddEdgeTo(bFalse)
-			b.Likely = ssa.BranchLikely
-
-			// We have 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.endBlock().AddEdgeTo(bEnd)
-
-			// Call the pure Go version.
-			s.startBlock(bFalse)
-			s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Merge results.
-			s.startBlock(bEnd)
-			return s.variable(n, 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)
-			b := s.endBlock()
-			b.Kind = ssa.BlockIf
-			b.SetControl(v)
-			bTrue := s.f.NewBlock(ssa.BlockPlain)
-			bFalse := s.f.NewBlock(ssa.BlockPlain)
-			bEnd := s.f.NewBlock(ssa.BlockPlain)
-			b.AddEdgeTo(bTrue)
-			b.AddEdgeTo(bFalse)
-			b.Likely = ssa.BranchLikely // most machines have sse4.1 nowadays
-
-			// We have the intrinsic - use it directly.
-			s.startBlock(bTrue)
-			s.vars[n] = s.newValue1(op, types.Types[TFLOAT64], args[0])
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Call the pure Go version.
-			s.startBlock(bFalse)
-			s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Merge results.
-			s.startBlock(bEnd)
-			return s.variable(n, types.Types[TFLOAT64])
-		}
-	}
-	addF("math", "RoundToEven",
-		makeRoundAMD64(ssa.OpRoundToEven),
-		sys.AMD64)
-	addF("math", "Floor",
-		makeRoundAMD64(ssa.OpFloor),
-		sys.AMD64)
-	addF("math", "Ceil",
-		makeRoundAMD64(ssa.OpCeil),
-		sys.AMD64)
-	addF("math", "Trunc",
-		makeRoundAMD64(ssa.OpTrunc),
-		sys.AMD64)
-
-	/******** 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])
-		},
-		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])
-		},
-		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)
-		},
-		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])
-		},
-		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)
-		},
-		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)
-		},
-		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])
-		},
-		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)
-		},
-		sys.S390X)
-	alias("math/bits", "ReverseBytes64", "runtime/internal/sys", "Bswap64", all...)
-	alias("math/bits", "ReverseBytes32", "runtime/internal/sys", "Bswap32", all...)
-	// 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])
-		},
-		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])
-		},
-		sys.AMD64, sys.ARM64)
-	addF("math/bits", "Len32",
-		func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
-			if s.config.PtrSize == 4 {
-				return s.newValue1(ssa.OpBitLen32, types.Types[TINT], args[0])
-			}
-			x := s.newValue1(ssa.OpZeroExt32to64, types.Types[TUINT64], args[0])
-			return s.newValue1(ssa.OpBitLen64, 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 {
-			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.OpZeroExt16to64, types.Types[TUINT64], args[0])
-			return s.newValue1(ssa.OpBitLen64, 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])
-		},
-		sys.AMD64)
-	addF("math/bits", "Len8",
-		func(s *state, n *Node, 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.OpZeroExt8to64, types.Types[TUINT64], args[0])
-			return s.newValue1(ssa.OpBitLen64, 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])
-		},
-		sys.AMD64)
-	addF("math/bits", "Len",
-		func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
-			if s.config.PtrSize == 4 {
-				return s.newValue1(ssa.OpBitLen32, types.Types[TINT], args[0])
-			}
-			return s.newValue1(ssa.OpBitLen64, 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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		sys.ARM64)
-	addF("math/bits", "Reverse",
-		func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
-			if s.config.PtrSize == 4 {
-				return s.newValue1(ssa.OpBitRev32, types.Types[TINT], args[0])
-			}
-			return s.newValue1(ssa.OpBitRev64, 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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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)
-			b := s.endBlock()
-			b.Kind = ssa.BlockIf
-			b.SetControl(v)
-			bTrue := s.f.NewBlock(ssa.BlockPlain)
-			bFalse := s.f.NewBlock(ssa.BlockPlain)
-			bEnd := s.f.NewBlock(ssa.BlockPlain)
-			b.AddEdgeTo(bTrue)
-			b.AddEdgeTo(bFalse)
-			b.Likely = ssa.BranchLikely // most machines have popcnt nowadays
-
-			// We have the intrinsic - use it directly.
-			s.startBlock(bTrue)
-			op := op64
-			if s.config.PtrSize == 4 {
-				op = op32
-			}
-			s.vars[n] = s.newValue1(op, types.Types[TINT], args[0])
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Call the pure Go version.
-			s.startBlock(bFalse)
-			s.vars[n] = s.callResult(n, callNormal) // types.Types[TINT]
-			s.endBlock().AddEdgeTo(bEnd)
-
-			// Merge results.
-			s.startBlock(bEnd)
-			return s.variable(n, 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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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])
-		},
-		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 {
-			// 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])
-		},
-		sys.AMD64)
-	alias("math/bits", "Div", "math/bits", "Div64", sys.ArchAMD64)
-
-	alias("runtime/internal/sys", "Ctz8", "math/bits", "TrailingZeros8", all...)
-	alias("runtime/internal/sys", "TrailingZeros8", "math/bits", "TrailingZeros8", all...)
-	alias("runtime/internal/sys", "TrailingZeros64", "math/bits", "TrailingZeros64", all...)
-	alias("runtime/internal/sys", "Len8", "math/bits", "Len8", all...)
-	alias("runtime/internal/sys", "Len64", "math/bits", "Len64", all...)
-	alias("runtime/internal/sys", "OnesCount64", "math/bits", "OnesCount64", all...)
-
-	/******** sync/atomic ********/
-
-	// Note: these are disabled by flag_race in findIntrinsic below.
-	alias("sync/atomic", "LoadInt32", "runtime/internal/atomic", "Load", all...)
-	alias("sync/atomic", "LoadInt64", "runtime/internal/atomic", "Load64", all...)
-	alias("sync/atomic", "LoadPointer", "runtime/internal/atomic", "Loadp", all...)
-	alias("sync/atomic", "LoadUint32", "runtime/internal/atomic", "Load", all...)
-	alias("sync/atomic", "LoadUint64", "runtime/internal/atomic", "Load64", all...)
-	alias("sync/atomic", "LoadUintptr", "runtime/internal/atomic", "Load", p4...)
-	alias("sync/atomic", "LoadUintptr", "runtime/internal/atomic", "Load64", p8...)
-
-	alias("sync/atomic", "StoreInt32", "runtime/internal/atomic", "Store", all...)
-	alias("sync/atomic", "StoreInt64", "runtime/internal/atomic", "Store64", all...)
-	// Note: not StorePointer, that needs a write barrier.  Same below for {CompareAnd}Swap.
-	alias("sync/atomic", "StoreUint32", "runtime/internal/atomic", "Store", all...)
-	alias("sync/atomic", "StoreUint64", "runtime/internal/atomic", "Store64", all...)
-	alias("sync/atomic", "StoreUintptr", "runtime/internal/atomic", "Store", p4...)
-	alias("sync/atomic", "StoreUintptr", "runtime/internal/atomic", "Store64", p8...)
-
-	alias("sync/atomic", "SwapInt32", "runtime/internal/atomic", "Xchg", all...)
-	alias("sync/atomic", "SwapInt64", "runtime/internal/atomic", "Xchg64", all...)
-	alias("sync/atomic", "SwapUint32", "runtime/internal/atomic", "Xchg", all...)
-	alias("sync/atomic", "SwapUint64", "runtime/internal/atomic", "Xchg64", all...)
-	alias("sync/atomic", "SwapUintptr", "runtime/internal/atomic", "Xchg", p4...)
-	alias("sync/atomic", "SwapUintptr", "runtime/internal/atomic", "Xchg64", p8...)
-
-	alias("sync/atomic", "CompareAndSwapInt32", "runtime/internal/atomic", "Cas", all...)
-	alias("sync/atomic", "CompareAndSwapInt64", "runtime/internal/atomic", "Cas64", all...)
-	alias("sync/atomic", "CompareAndSwapUint32", "runtime/internal/atomic", "Cas", all...)
-	alias("sync/atomic", "CompareAndSwapUint64", "runtime/internal/atomic", "Cas64", all...)
-	alias("sync/atomic", "CompareAndSwapUintptr", "runtime/internal/atomic", "Cas", p4...)
-	alias("sync/atomic", "CompareAndSwapUintptr", "runtime/internal/atomic", "Cas64", p8...)
-
-	alias("sync/atomic", "AddInt32", "runtime/internal/atomic", "Xadd", all...)
-	alias("sync/atomic", "AddInt64", "runtime/internal/atomic", "Xadd64", all...)
-	alias("sync/atomic", "AddUint32", "runtime/internal/atomic", "Xadd", all...)
-	alias("sync/atomic", "AddUint64", "runtime/internal/atomic", "Xadd64", all...)
-	alias("sync/atomic", "AddUintptr", "runtime/internal/atomic", "Xadd", p4...)
-	alias("sync/atomic", "AddUintptr", "runtime/internal/atomic", "Xadd64", p8...)
-
-	/******** 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])
-		},
-		sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64LE, sys.ArchPPC64, sys.ArchS390X)
-}
-
-// findIntrinsic returns a function which builds the SSA equivalent of the
-// function identified by the symbol sym.  If sym is not an intrinsic call, returns nil.
-func findIntrinsic(sym *types.Sym) intrinsicBuilder {
-	if sym == nil || sym.Pkg == nil {
-		return nil
-	}
-	pkg := sym.Pkg.Path
-	if sym.Pkg == localpkg {
-		pkg = myimportpath
-	}
-	if 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" {
-		return nil
-	}
-
-	fn := sym.Name
-	if ssa.IntrinsicsDisable {
-		if pkg == "runtime" && (fn == "getcallerpc" || fn == "getcallersp" || fn == "getclosureptr") {
-			// These runtime functions don't have definitions, must be intrinsics.
-		} else {
-			return nil
-		}
-	}
-	return intrinsics[intrinsicKey{thearch.LinkArch.Arch, pkg, fn}]
-}
-
-func isIntrinsicCall(n *Node) bool {
-	if n == nil || n.Left == nil {
-		return false
-	}
-	return findIntrinsic(n.Left.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))
-	if ssa.IntrinsicsDebug > 0 {
-		x := v
-		if x == nil {
-			x = s.mem()
-		}
-		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())
-	}
-	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.
-		args[i] = s.expr(n)
-	}
-	return args
-}
-
-// openDeferRecord adds code to evaluate and store the args for an open-code defer
-// call, and records info about the defer, so we can generate proper code on the
-// exit paths. n is the sub-node of the defer node that is the actual function
-// 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)
-
-	var args []*ssa.Value
-	var argNodes []*Node
-
-	opendefer := &openDeferInfo{
-		n: n,
-	}
-	fn := n.Left
-	if n.Op == 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) {
-			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 fn.Op != ODOTINTER {
-			Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
-		}
-		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)
-	}
-	for _, argn := range n.Rlist.Slice() {
-		var v *ssa.Value
-		if canSSAType(argn.Type) {
-			v = s.openDeferSave(nil, argn.Type, s.expr(argn))
-		} else {
-			v = s.openDeferSave(argn, argn.Type, nil)
-		}
-		args = append(args, v)
-		argNodes = append(argNodes, v.Aux.(*Node))
-	}
-	opendefer.argVals = args
-	opendefer.argNodes = argNodes
-	index := len(s.openDefers)
-	s.openDefers = append(s.openDefers, opendefer)
-
-	// Update deferBits only after evaluation and storage to stack of
-	// args/receiver/interface is successful.
-	bitvalue := s.constInt8(types.Types[TUINT8], 1<<uint(index))
-	newDeferBits := s.newValue2(ssa.OpOr8, types.Types[TUINT8], s.variable(&deferBitsVar, types.Types[TUINT8]), bitvalue)
-	s.vars[&deferBitsVar] = newDeferBits
-	s.store(types.Types[TUINT8], s.deferBitsAddr, newDeferBits)
-}
-
-// openDeferSave generates SSA nodes to store a value (with type t) for an
-// open-coded defer at an explicit autotmp location on the stack, so it can be
-// reloaded and used for the appropriate call on exit. If type t is SSAable, then
-// val must be non-nil (and n should be nil) and val is the value to be stored. If
-// type t is non-SSAable, then n must be non-nil (and val should be nil) and n is
-// evaluated (via s.addr() below) to get the value that is to be stored. The
-// function returns an SSA value representing a pointer to the autotmp location.
-func (s *state) openDeferSave(n *Node, t *types.Type, val *ssa.Value) *ssa.Value {
-	canSSA := canSSAType(t)
-	var pos src.XPos
-	if canSSA {
-		pos = val.Pos
-	} else {
-		pos = n.Pos
-	}
-	argTemp := tempAt(pos.WithNotStmt(), s.curfn, t)
-	argTemp.Name.SetOpenDeferSlot(true)
-	var addrArgTemp *ssa.Value
-	// Use OpVarLive to make sure stack slots for the args, etc. are not
-	// removed by dead-store elimination
-	if s.curBlock.ID != s.f.Entry.ID {
-		// Force the argtmp storing this defer function/receiver/arg to be
-		// declared in the entry block, so that it will be live for the
-		// defer exit code (which will actually access it only if the
-		// associated defer call has been activated).
-		s.defvars[s.f.Entry.ID][&memVar] = s.entryNewValue1A(ssa.OpVarDef, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][&memVar])
-		s.defvars[s.f.Entry.ID][&memVar] = s.entryNewValue1A(ssa.OpVarLive, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][&memVar])
-		addrArgTemp = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(argTemp.Type), argTemp, s.sp, s.defvars[s.f.Entry.ID][&memVar])
-	} else {
-		// Special case if we're still in the entry block. We can't use
-		// the above code, since s.defvars[s.f.Entry.ID] isn't defined
-		// until we end the entry block with s.endBlock().
-		s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, argTemp, s.mem(), false)
-		s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argTemp, s.mem(), false)
-		addrArgTemp = s.newValue2Apos(ssa.OpLocalAddr, types.NewPtr(argTemp.Type), argTemp, s.sp, s.mem(), false)
-	}
-	if t.HasPointers() {
-		// Since we may use this argTemp during exit depending on the
-		// deferBits, we must define it unconditionally on entry.
-		// Therefore, we must make sure it is zeroed out in the entry
-		// block if it contains pointers, else GC may wrongly follow an
-		// uninitialized pointer value.
-		argTemp.Name.SetNeedzero(true)
-	}
-	if !canSSA {
-		a := s.addr(n)
-		s.move(t, addrArgTemp, a)
-		return addrArgTemp
-	}
-	// We are storing to the stack, hence we can avoid the full checks in
-	// storeType() (no write barrier) and do a simple store().
-	s.store(t, addrArgTemp, val)
-	return addrArgTemp
-}
-
-// openDeferExit generates SSA for processing all the open coded defers at exit.
-// The code involves loading deferBits, and checking each of the bits to see if
-// the corresponding defer statement was executed. For each bit that is turned
-// on, the associated defer call is made.
-func (s *state) openDeferExit() {
-	deferExit := s.f.NewBlock(ssa.BlockPlain)
-	s.endBlock().AddEdgeTo(deferExit)
-	s.startBlock(deferExit)
-	s.lastDeferExit = deferExit
-	s.lastDeferCount = len(s.openDefers)
-	zeroval := s.constInt8(types.Types[TUINT8], 0)
-	testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
-	// Test for and run defers in reverse order
-	for i := len(s.openDefers) - 1; i >= 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])
-		// Generate code to check if the bit associated with the current
-		// defer is set.
-		bitval := s.constInt8(types.Types[TUINT8], 1<<uint(i))
-		andval := s.newValue2(ssa.OpAnd8, types.Types[TUINT8], deferBits, bitval)
-		eqVal := s.newValue2(ssa.OpEq8, types.Types[TBOOL], andval, zeroval)
-		b := s.endBlock()
-		b.Kind = ssa.BlockIf
-		b.SetControl(eqVal)
-		b.AddEdgeTo(bEnd)
-		b.AddEdgeTo(bCond)
-		bCond.AddEdgeTo(bEnd)
-		s.startBlock(bCond)
-
-		// Clear this bit in deferBits and force store back to stack, so
-		// we will not try to re-run this defer call if this defer call panics.
-		nbitval := s.newValue1(ssa.OpCom8, types.Types[TUINT8], bitval)
-		maskedval := s.newValue2(ssa.OpAnd8, types.Types[TUINT8], deferBits, nbitval)
-		s.store(types.Types[TUINT8], s.deferBitsAddr, maskedval)
-		// Use this value for following tests, so we keep previous
-		// bits cleared.
-		s.vars[&deferBitsVar] = maskedval
-
-		// Generate code to call the function call of the defer, using the
-		// closure/receiver/args that were stored in argtmps at the point
-		// of the defer statement.
-		argStart := Ctxt.FixedFrameSize()
-		fn := r.n.Left
-		stksize := fn.Type.ArgWidth()
-		var ACArgs []ssa.Param
-		var ACResults []ssa.Param
-		var callArgs []*ssa.Value
-		if r.rcvr != nil {
-			// rcvr in case of OCALLINTER
-			v := s.load(r.rcvr.Type.Elem(), r.rcvr)
-			addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
-			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
-			if testLateExpansion {
-				callArgs = append(callArgs, v)
-			} else {
-				s.store(types.Types[TUINTPTR], addr, v)
-			}
-		}
-		for j, argAddrVal := range r.argVals {
-			f := getParam(r.n, j)
-			pt := types.NewPtr(f.Type)
-			ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + f.Offset)})
-			if testLateExpansion {
-				var a *ssa.Value
-				if !canSSAType(f.Type) {
-					a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem())
-				} else {
-					a = s.load(f.Type, argAddrVal)
-				}
-				callArgs = append(callArgs, a)
-			} else {
-				addr := s.constOffPtrSP(pt, argStart+f.Offset)
-				if !canSSAType(f.Type) {
-					s.move(f.Type, addr, argAddrVal)
-				} else {
-					argVal := s.load(f.Type, argAddrVal)
-					s.storeType(f.Type, addr, argVal, 0, false)
-				}
-			}
-		}
-		var call *ssa.Value
-		if r.closure != nil {
-			v := s.load(r.closure.Type.Elem(), r.closure)
-			s.maybeNilCheckClosure(v, callDefer)
-			codeptr := s.rawLoad(types.Types[TUINTPTR], v)
-			aux := ssa.ClosureAuxCall(ACArgs, ACResults)
-			if testLateExpansion {
-				callArgs = append(callArgs, s.mem())
-				call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
-				call.AddArgs(callArgs...)
-			} else {
-				call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, aux, codeptr, v, s.mem())
-			}
-		} else {
-			aux := ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults)
-			if testLateExpansion {
-				callArgs = append(callArgs, s.mem())
-				call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
-				call.AddArgs(callArgs...)
-			} else {
-				// Do a static call if the original call was a static function or method
-				call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
-			}
-		}
-		call.AuxInt = stksize
-		if testLateExpansion {
-			s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
-		} else {
-			s.vars[&memVar] = call
-		}
-		// Make sure that the stack slots with pointers are kept live
-		// through the call (which is a pre-emption point). Also, we will
-		// use the first call of the last defer exit to compute liveness
-		// for the deferreturn, so we want all stack slots to be live.
-		if r.closureNode != nil {
-			s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.closureNode, s.mem(), false)
-		}
-		if r.rcvrNode != nil {
-			if r.rcvrNode.Type.HasPointers() {
-				s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.rcvrNode, s.mem(), false)
-			}
-		}
-		for _, argNode := range r.argNodes {
-			if argNode.Type.HasPointers() {
-				s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argNode, s.mem(), false)
-			}
-		}
-
-		s.endBlock()
-		s.startBlock(bEnd)
-	}
-}
-
-func (s *state) callResult(n *Node, k callKind) *ssa.Value {
-	return s.call(n, k, false)
-}
-
-func (s *state) callAddr(n *Node, k callKind) *ssa.Value {
-	return s.call(n, k, true)
-}
-
-// Calls the function n using the specified call type.
-// Returns the address of the return value (or nil if none).
-func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
-	s.prevCall = nil
-	var sym *types.Sym     // target symbol (if static)
-	var closure *ssa.Value // ptr to closure to run (if dynamic)
-	var codeptr *ssa.Value // ptr to target code (if dynamic)
-	var rcvr *ssa.Value    // receiver to set
-	fn := n.Left
-	var ACArgs []ssa.Param
-	var ACResults []ssa.Param
-	var callArgs []*ssa.Value
-	res := n.Left.Type.Results()
-	if k == callNormal {
-		nf := res.NumFields()
-		for i := 0; i < nf; i++ {
-			fp := res.Field(i)
-			ACResults = append(ACResults, ssa.Param{Type: fp.Type, Offset: int32(fp.Offset + Ctxt.FixedFrameSize())})
-		}
-	}
-
-	testLateExpansion := false
-
-	switch n.Op {
-	case OCALLFUNC:
-		testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
-		if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC {
-			sym = fn.Sym
-			break
-		}
-		closure = s.expr(fn)
-		if k != callDefer && k != callDeferStack {
-			// Deferred nil function needs to panic when the function is invoked,
-			// not the point of defer statement.
-			s.maybeNilCheckClosure(closure, k)
-		}
-	case OCALLMETH:
-		if fn.Op != ODOTMETH {
-			s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
-		}
-		testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
-		if k == callNormal {
-			sym = fn.Sym
-			break
-		}
-		closure = s.getMethodClosure(fn)
-		// Note: receiver is already present in n.Rlist, so we don't
-		// want to set it here.
-	case OCALLINTER:
-		if fn.Op != ODOTINTER {
-			s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
-		}
-		testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
-		var iclosure *ssa.Value
-		iclosure, rcvr = s.getClosureAndRcvr(fn)
-		if k == callNormal {
-			codeptr = s.load(types.Types[TUINTPTR], iclosure)
-		} else {
-			closure = iclosure
-		}
-	}
-	dowidth(fn.Type)
-	stksize := fn.Type.ArgWidth() // includes receiver, args, and results
-
-	// Run all assignments of temps.
-	// The temps are introduced to avoid overwriting argument
-	// slots when arguments themselves require function calls.
-	s.stmtList(n.List)
-
-	var call *ssa.Value
-	if k == callDeferStack {
-		testLateExpansion = ssa.LateCallExpansionEnabledWithin(s.f)
-		// Make a defer struct d on the stack.
-		t := deferstruct(stksize)
-		d := tempAt(n.Pos, s.curfn, t)
-
-		s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem())
-		addr := s.addr(d)
-
-		// Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer.
-		// 0: siz
-		s.store(types.Types[TUINT32],
-			s.newValue1I(ssa.OpOffPtr, types.Types[TUINT32].PtrTo(), t.FieldOff(0), addr),
-			s.constInt32(types.Types[TUINT32], int32(stksize)))
-		// 1: started, set in deferprocStack
-		// 2: heap, set in deferprocStack
-		// 3: openDefer
-		// 4: sp, set in deferprocStack
-		// 5: pc, set in deferprocStack
-		// 6: fn
-		s.store(closure.Type,
-			s.newValue1I(ssa.OpOffPtr, closure.Type.PtrTo(), t.FieldOff(6), addr),
-			closure)
-		// 7: panic, set in deferprocStack
-		// 8: link, set in deferprocStack
-		// 9: framepc
-		// 10: varp
-		// 11: fd
-
-		// Then, store all the arguments of the defer call.
-		ft := fn.Type
-		off := t.FieldOff(12)
-		args := n.Rlist.Slice()
-
-		// Set receiver (for interface calls). Always a pointer.
-		if rcvr != nil {
-			p := s.newValue1I(ssa.OpOffPtr, ft.Recv().Type.PtrTo(), off, addr)
-			s.store(types.Types[TUINTPTR], p, rcvr)
-		}
-		// Set receiver (for method calls).
-		if n.Op == OCALLMETH {
-			f := ft.Recv()
-			s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
-			args = args[1:]
-		}
-		// Set other args.
-		for _, f := range ft.Params().Fields().Slice() {
-			s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
-			args = args[1:]
-		}
-
-		// Call runtime.deferprocStack with pointer to _defer record.
-		ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())})
-		aux := ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults)
-		if testLateExpansion {
-			callArgs = append(callArgs, addr, s.mem())
-			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
-			call.AddArgs(callArgs...)
-		} else {
-			arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
-			s.store(types.Types[TUINTPTR], arg0, addr)
-			call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
-		}
-		if stksize < int64(Widthptr) {
-			// We need room for both the call to deferprocStack and the call to
-			// the deferred function.
-			// TODO Revisit this if/when we pass args in registers.
-			stksize = int64(Widthptr)
-		}
-		call.AuxInt = stksize
-	} else {
-		// Store arguments to stack, including defer/go arguments and receiver for method calls.
-		// These are written in SP-offset order.
-		argStart := Ctxt.FixedFrameSize()
-		// Defer/go args.
-		if k != callNormal {
-			// Write argsize and closure (args to newproc/deferproc).
-			argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
-			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINT32], Offset: int32(argStart)})
-			if testLateExpansion {
-				callArgs = append(callArgs, argsize)
-			} else {
-				addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
-				s.store(types.Types[TUINT32], addr, argsize)
-			}
-			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart) + int32(Widthptr)})
-			if testLateExpansion {
-				callArgs = append(callArgs, closure)
-			} else {
-				addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
-				s.store(types.Types[TUINTPTR], addr, closure)
-			}
-			stksize += 2 * int64(Widthptr)
-			argStart += 2 * int64(Widthptr)
-		}
-
-		// Set receiver (for interface calls).
-		if rcvr != nil {
-			addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
-			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
-			if testLateExpansion {
-				callArgs = append(callArgs, rcvr)
-			} else {
-				s.store(types.Types[TUINTPTR], addr, rcvr)
-			}
-		}
-
-		// Write args.
-		t := n.Left.Type
-		args := n.Rlist.Slice()
-		if n.Op == OCALLMETH {
-			f := t.Recv()
-			ACArg, arg := s.putArg(args[0], f.Type, argStart+f.Offset, testLateExpansion)
-			ACArgs = append(ACArgs, ACArg)
-			callArgs = append(callArgs, arg)
-			args = args[1:]
-		}
-		for i, n := range args {
-			f := t.Params().Field(i)
-			ACArg, arg := s.putArg(n, f.Type, argStart+f.Offset, testLateExpansion)
-			ACArgs = append(ACArgs, ACArg)
-			callArgs = append(callArgs, arg)
-		}
-
-		callArgs = append(callArgs, s.mem())
-
-		// call target
-		switch {
-		case k == callDefer:
-			aux := ssa.StaticAuxCall(deferproc, ACArgs, ACResults)
-			if testLateExpansion {
-				call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
-				call.AddArgs(callArgs...)
-			} else {
-				call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
-			}
-		case k == callGo:
-			aux := ssa.StaticAuxCall(newproc, ACArgs, ACResults)
-			if testLateExpansion {
-				call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
-				call.AddArgs(callArgs...)
-			} else {
-				call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
-			}
-		case closure != nil:
-			// rawLoad because loading the code pointer from a
-			// closure is always safe, but IsSanitizerSafeAddr
-			// can't always figure that out currently, and it's
-			// critical that we not clobber any arguments already
-			// stored onto the stack.
-			codeptr = s.rawLoad(types.Types[TUINTPTR], closure)
-			if testLateExpansion {
-				aux := ssa.ClosureAuxCall(ACArgs, ACResults)
-				call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure)
-				call.AddArgs(callArgs...)
-			} else {
-				call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, closure, s.mem())
-			}
-		case codeptr != nil:
-			if testLateExpansion {
-				aux := ssa.InterfaceAuxCall(ACArgs, ACResults)
-				call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr)
-				call.AddArgs(callArgs...)
-			} else {
-				call = s.newValue2A(ssa.OpInterCall, types.TypeMem, ssa.InterfaceAuxCall(ACArgs, ACResults), codeptr, s.mem())
-			}
-		case sym != nil:
-			if testLateExpansion {
-				aux := ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults)
-				call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
-				call.AddArgs(callArgs...)
-			} else {
-				call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(sym.Linksym(), ACArgs, ACResults), s.mem())
-			}
-		default:
-			s.Fatalf("bad call type %v %v", n.Op, n)
-		}
-		call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
-	}
-	if testLateExpansion {
-		s.prevCall = call
-		s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
-	} else {
-		s.vars[&memVar] = call
-	}
-	// Insert OVARLIVE nodes
-	s.stmtList(n.Nbody)
-
-	// Finish block for defers
-	if k == callDefer || k == callDeferStack {
-		b := s.endBlock()
-		b.Kind = ssa.BlockDefer
-		b.SetControl(call)
-		bNext := s.f.NewBlock(ssa.BlockPlain)
-		b.AddEdgeTo(bNext)
-		// Add recover edge to exit code.
-		r := s.f.NewBlock(ssa.BlockPlain)
-		s.startBlock(r)
-		s.exit()
-		b.AddEdgeTo(r)
-		b.Likely = ssa.BranchLikely
-		s.startBlock(bNext)
-	}
-
-	if res.NumFields() == 0 || k != callNormal {
-		// call has no return value. Continue with the next statement.
-		return nil
-	}
-	fp := res.Field(0)
-	if returnResultAddr {
-		pt := types.NewPtr(fp.Type)
-		if testLateExpansion {
-			return s.newValue1I(ssa.OpSelectNAddr, pt, 0, call)
-		}
-		return s.constOffPtrSP(pt, fp.Offset+Ctxt.FixedFrameSize())
-	}
-
-	if testLateExpansion {
-		return s.newValue1I(ssa.OpSelectN, fp.Type, 0, call)
-	}
-	return s.load(n.Type, s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize()))
-}
-
-// maybeNilCheckClosure checks if a nil check of a closure is needed in some
-// architecture-dependent situations and, if so, emits the nil check.
-func (s *state) maybeNilCheckClosure(closure *ssa.Value, k callKind) {
-	if thearch.LinkArch.Family == sys.Wasm || objabi.GOOS == "aix" && k != callGo {
-		// On AIX, the closure needs to be verified as fn can be nil, except if it's a call go. This needs to be handled by the runtime to have the "go of nil func value" error.
-		// TODO(neelance): On other architectures this should be eliminated by the optimization steps
-		s.nilCheck(closure)
-	}
-}
-
-// getMethodClosure returns a value representing the closure for a method call
-func (s *state) getMethodClosure(fn *Node) *ssa.Value {
-	// Make a name n2 for the function.
-	// fn.Sym might be sync.(*Mutex).Unlock.
-	// Make a PFUNC node out of that, then evaluate it.
-	// We get back an SSA value representing &sync.(*Mutex).Unlock·f.
-	// We can then pass that to defer or go.
-	n2 := newnamel(fn.Pos, fn.Sym)
-	n2.Name.Curfn = s.curfn
-	n2.SetClass(PFUNC)
-	// n2.Sym already existed, so it's already marked as a function.
-	n2.Pos = fn.Pos
-	n2.Type = types.Types[TUINT8] // dummy type for a static closure. Could use runtime.funcval if we had it.
-	return s.expr(n2)
-}
-
-// getClosureAndRcvr returns values for the appropriate closure and receiver of an
-// interface call
-func (s *state) getClosureAndRcvr(fn *Node) (*ssa.Value, *ssa.Value) {
-	i := s.expr(fn.Left)
-	itab := s.newValue1(ssa.OpITab, types.Types[TUINTPTR], i)
-	s.nilCheck(itab)
-	itabidx := fn.Xoffset + 2*int64(Widthptr) + 8 // offset of fun field in runtime.itab
-	closure := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.UintptrPtr, itabidx, itab)
-	rcvr := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, i)
-	return closure, rcvr
-}
-
-// etypesign returns the signed-ness of e, for integer/pointer etypes.
-// -1 means signed, +1 means unsigned, 0 means non-integer/non-pointer.
-func etypesign(e types.EType) int8 {
-	switch e {
-	case TINT8, TINT16, TINT32, TINT64, TINT:
-		return -1
-	case TUINT8, TUINT16, TUINT32, TUINT64, TUINT, TUINTPTR, TUNSAFEPTR:
-		return +1
-	}
-	return 0
-}
-
-// addr converts the address of the expression n to SSA, adds it to s and returns the SSA result.
-// The value that the returned Value represents is guaranteed to be non-nil.
-func (s *state) addr(n *Node) *ssa.Value {
-	if n.Op != ONAME {
-		s.pushLine(n.Pos)
-		defer s.popLine()
-	}
-
-	t := types.NewPtr(n.Type)
-	switch n.Op {
-	case ONAME:
-		switch n.Class() {
-		case PEXTERN:
-			// global variable
-			v := s.entryNewValue1A(ssa.OpAddr, t, n.Sym.Linksym(), s.sb)
-			// TODO: Make OpAddr use AuxInt as well as Aux.
-			if n.Xoffset != 0 {
-				v = s.entryNewValue1I(ssa.OpOffPtr, v.Type, n.Xoffset, v)
-			}
-			return v
-		case PPARAM:
-			// parameter slot
-			v := s.decladdrs[n]
-			if v != nil {
-				return v
-			}
-			if n == nodfp {
-				// Special arg that points to the frame pointer (Used by ORECOVER).
-				return s.entryNewValue2A(ssa.OpLocalAddr, t, n, s.sp, s.startmem)
-			}
-			s.Fatalf("addr of undeclared ONAME %v. declared: %v", n, s.decladdrs)
-			return nil
-		case PAUTO:
-			return s.newValue2Apos(ssa.OpLocalAddr, t, n, s.sp, s.mem(), !n.IsAutoTmp())
-
-		case PPARAMOUT: // Same as PAUTO -- cannot generate LEA early.
-			// ensure that we reuse symbols for out parameters so
-			// that cse works on their addresses
-			return s.newValue2Apos(ssa.OpLocalAddr, t, n, s.sp, s.mem(), true)
-		default:
-			s.Fatalf("variable address class %v not implemented", n.Class())
-			return nil
-		}
-	case ORESULT:
-		// load return from callee
-		if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall {
-			return s.constOffPtrSP(t, n.Xoffset)
-		}
-		which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Xoffset)
-		if which == -1 {
-			// Do the old thing // TODO: Panic instead.
-			return s.constOffPtrSP(t, n.Xoffset)
-		}
-		x := s.newValue1I(ssa.OpSelectNAddr, t, which, s.prevCall)
-		return x
-
-	case OINDEX:
-		if n.Left.Type.IsSlice() {
-			a := s.expr(n.Left)
-			i := s.expr(n.Right)
-			len := s.newValue1(ssa.OpSliceLen, types.Types[TINT], a)
-			i = s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded())
-			p := s.newValue1(ssa.OpSlicePtr, t, a)
-			return s.newValue2(ssa.OpPtrIndex, t, p, i)
-		} else { // array
-			a := s.addr(n.Left)
-			i := s.expr(n.Right)
-			len := s.constInt(types.Types[TINT], n.Left.Type.NumElem())
-			i = s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded())
-			return s.newValue2(ssa.OpPtrIndex, types.NewPtr(n.Left.Type.Elem()), a, i)
-		}
-	case ODEREF:
-		return s.exprPtr(n.Left, n.Bounded(), n.Pos)
-	case ODOT:
-		p := s.addr(n.Left)
-		return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
-	case ODOTPTR:
-		p := s.exprPtr(n.Left, n.Bounded(), n.Pos)
-		return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p)
-	case OCLOSUREVAR:
-		return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
-			s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr))
-	case OCONVNOP:
-		addr := s.addr(n.Left)
-		return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
-	case OCALLFUNC, OCALLINTER, OCALLMETH:
-		return s.callAddr(n, callNormal)
-	case ODOTTYPE:
-		v, _ := s.dottype(n, false)
-		if v.Op != ssa.OpLoad {
-			s.Fatalf("dottype of non-load")
-		}
-		if v.Args[1] != s.mem() {
-			s.Fatalf("memory no longer live from dottype load")
-		}
-		return v.Args[0]
-	default:
-		s.Fatalf("unhandled addr %v", n.Op)
-		return nil
-	}
-}
-
-// canSSA reports whether n is SSA-able.
-// n must be an ONAME (or an ODOT sequence with an ONAME base).
-func (s *state) canSSA(n *Node) bool {
-	if Debug.N != 0 {
-		return false
-	}
-	for n.Op == ODOT || (n.Op == OINDEX && n.Left.Type.IsArray()) {
-		n = n.Left
-	}
-	if n.Op != ONAME {
-		return false
-	}
-	if n.Name.Addrtaken() {
-		return false
-	}
-	if n.isParamHeapCopy() {
-		return false
-	}
-	if n.Class() == PAUTOHEAP {
-		s.Fatalf("canSSA of PAUTOHEAP %v", n)
-	}
-	switch n.Class() {
-	case PEXTERN:
-		return false
-	case PPARAMOUT:
-		if s.hasdefer {
-			// TODO: handle this case? Named return values must be
-			// in memory so that the deferred function can see them.
-			// Maybe do: if !strings.HasPrefix(n.String(), "~") { return false }
-			// Or maybe not, see issue 18860.  Even unnamed return values
-			// must be written back so if a defer recovers, the caller can see them.
-			return false
-		}
-		if s.cgoUnsafeArgs {
-			// Cgo effectively takes the address of all result args,
-			// but the compiler can't see that.
-			return false
-		}
-	}
-	if n.Class() == PPARAM && n.Sym != nil && n.Sym.Name == ".this" {
-		// wrappers generated by genwrapper need to update
-		// the .this pointer in place.
-		// TODO: treat as a PPARAMOUT?
-		return false
-	}
-	return canSSAType(n.Type)
-	// TODO: try to make more variables SSAable?
-}
-
-// canSSA reports whether variables of type t are SSA-able.
-func canSSAType(t *types.Type) bool {
-	dowidth(t)
-	if t.Width > int64(4*Widthptr) {
-		// 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:
-		// 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 false
-	case TSTRUCT:
-		if t.NumFields() > ssa.MaxStruct {
-			return false
-		}
-		for _, t1 := range t.Fields().Slice() {
-			if !canSSAType(t1.Type) {
-				return false
-			}
-		}
-		return true
-	default:
-		return true
-	}
-}
-
-// exprPtr evaluates n to a pointer and nil-checks it.
-func (s *state) exprPtr(n *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 {
-			s.f.Warnl(lineno, "removed nil check")
-		}
-		return p
-	}
-	s.nilCheck(p)
-	return p
-}
-
-// nilCheck generates nil pointer checking code.
-// 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() {
-		return
-	}
-	s.newValue2(ssa.OpNilCheck, types.TypeVoid, ptr, s.mem())
-}
-
-// boundsCheck generates bounds checking code. Checks if 0 <= idx <[=] len, branches to exit if not.
-// Starts a new block on return.
-// On input, len must be converted to full int width and be nonnegative.
-// Returns idx converted to full int width.
-// If bounded is true then caller guarantees the index is not out of bounds
-// (but boundsCheck will still extend the index to full int width).
-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 or bounds checking is flag-disabled, then no check necessary,
-		// just return the extended index.
-		//
-		// Here, bounded == true if the compiler generated the index itself,
-		// such as in the expansion of a slice initializer. These indexes are
-		// compiler-generated, not Go program variables, so they cannot be
-		// attacker-controlled, so we can omit Spectre masking as well.
-		//
-		// Note that we do not want to omit Spectre masking in code like:
-		//
-		//	if 0 <= i && i < len(x) {
-		//		use(x[i])
-		//	}
-		//
-		// Lucky for us, bounded==false for that code.
-		// In that case (handled below), we emit a bound check (and Spectre mask)
-		// and then the prove pass will remove the bounds check.
-		// In theory the prove pass could potentially remove certain
-		// Spectre masks, but it's very delicate and probably better
-		// to be conservative and leave them all in.
-		return idx
-	}
-
-	bNext := s.f.NewBlock(ssa.BlockPlain)
-	bPanic := s.f.NewBlock(ssa.BlockExit)
-
-	if !idx.Type.IsSigned() {
-		switch kind {
-		case ssa.BoundsIndex:
-			kind = ssa.BoundsIndexU
-		case ssa.BoundsSliceAlen:
-			kind = ssa.BoundsSliceAlenU
-		case ssa.BoundsSliceAcap:
-			kind = ssa.BoundsSliceAcapU
-		case ssa.BoundsSliceB:
-			kind = ssa.BoundsSliceBU
-		case ssa.BoundsSlice3Alen:
-			kind = ssa.BoundsSlice3AlenU
-		case ssa.BoundsSlice3Acap:
-			kind = ssa.BoundsSlice3AcapU
-		case ssa.BoundsSlice3B:
-			kind = ssa.BoundsSlice3BU
-		case ssa.BoundsSlice3C:
-			kind = ssa.BoundsSlice3CU
-		}
-	}
-
-	var cmp *ssa.Value
-	if kind == ssa.BoundsIndex || kind == ssa.BoundsIndexU {
-		cmp = s.newValue2(ssa.OpIsInBounds, types.Types[TBOOL], idx, len)
-	} else {
-		cmp = s.newValue2(ssa.OpIsSliceInBounds, types.Types[TBOOL], idx, len)
-	}
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(cmp)
-	b.Likely = ssa.BranchLikely
-	b.AddEdgeTo(bNext)
-	b.AddEdgeTo(bPanic)
-
-	s.startBlock(bPanic)
-	if thearch.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)
-	} else {
-		mem := s.newValue3I(ssa.OpPanicBounds, types.TypeMem, int64(kind), idx, len, s.mem())
-		s.endBlock().SetControl(mem)
-	}
-	s.startBlock(bNext)
-
-	// In Spectre index mode, apply an appropriate mask to avoid speculative out-of-bounds accesses.
-	if spectreIndex {
-		op := ssa.OpSpectreIndex
-		if kind != ssa.BoundsIndex && kind != ssa.BoundsIndexU {
-			op = ssa.OpSpectreSliceIndex
-		}
-		idx = s.newValue2(op, types.Types[TINT], idx, len)
-	}
-
-	return idx
-}
-
-// If cmp (a bool) is false, panic using the given function.
-func (s *state) check(cmp *ssa.Value, fn *obj.LSym) {
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(cmp)
-	b.Likely = ssa.BranchLikely
-	bNext := s.f.NewBlock(ssa.BlockPlain)
-	line := s.peekPos()
-	pos := Ctxt.PosTable.Pos(line)
-	fl := funcLine{f: fn, base: pos.Base(), line: pos.Line()}
-	bPanic := s.panics[fl]
-	if bPanic == nil {
-		bPanic = s.f.NewBlock(ssa.BlockPlain)
-		s.panics[fl] = bPanic
-		s.startBlock(bPanic)
-		// The panic call takes/returns memory to ensure that the right
-		// memory state is observed if the panic happens.
-		s.rtcall(fn, false, nil)
-	}
-	b.AddEdgeTo(bNext)
-	b.AddEdgeTo(bPanic)
-	s.startBlock(bNext)
-}
-
-func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value {
-	needcheck := true
-	switch b.Op {
-	case ssa.OpConst8, ssa.OpConst16, ssa.OpConst32, ssa.OpConst64:
-		if b.AuxInt != 0 {
-			needcheck = false
-		}
-	}
-	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)
-	}
-	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.
-// Returns a slice of results of the given result types.
-// The call is added to the end of the current block.
-// If returns is false, the block is marked as an exit block.
-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)
-	var ACArgs []ssa.Param
-	var ACResults []ssa.Param
-	var callArgs []*ssa.Value
-
-	for _, arg := range args {
-		t := arg.Type
-		off = 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)
-		}
-		off += size
-	}
-	off = Rnd(off, int64(Widthreg))
-
-	// Accumulate results types and offsets
-	offR := off
-	for _, t := range results {
-		offR = Rnd(offR, t.Alignment())
-		ACResults = append(ACResults, ssa.Param{Type: t, Offset: int32(offR)})
-		offR += t.Size()
-	}
-
-	// Issue call
-	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
-	}
-
-	if !returns {
-		// Finish block
-		b := s.endBlock()
-		b.Kind = ssa.BlockExit
-		b.SetControl(call)
-		call.AuxInt = off - Ctxt.FixedFrameSize()
-		if len(results) > 0 {
-			s.Fatalf("panic call can't have results")
-		}
-		return nil
-	}
-
-	// 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()
-		}
-	}
-	off = Rnd(off, int64(Widthptr))
-
-	// Remember how much callee stack space we needed.
-	call.AuxInt = off
-
-	return res
-}
-
-// do *left = right for type t.
-func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask, leftIsStmt bool) {
-	s.instrument(t, left, instrumentWrite)
-
-	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)
-		return
-	}
-
-	// store scalar fields first, so write barrier stores for
-	// pointer fields can be grouped together, and scalar values
-	// don't need to be live across the write barrier call.
-	// TODO: if the writebarrier pass knows how to reorder stores,
-	// we can do a single store here as long as skip==0.
-	s.storeTypeScalars(t, left, right, skip)
-	if skip&skipPtr == 0 && t.HasPointers() {
-		s.storeTypePtrs(t, left, right)
-	}
-}
-
-// do *left = right for all scalar (non-pointer) parts of t.
-func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip skipMask) {
-	switch {
-	case t.IsBoolean() || t.IsInteger() || t.IsFloat() || t.IsComplex():
-		s.store(t, left, right)
-	case t.IsPtrShaped():
-		if t.IsPtr() && t.Elem().NotInHeap() {
-			s.store(t, left, right) // see issue 42032
-		}
-		// otherwise, no scalar fields.
-	case t.IsString():
-		if skip&skipLen != 0 {
-			return
-		}
-		len := s.newValue1(ssa.OpStringLen, 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)
-	case t.IsSlice():
-		if skip&skipLen == 0 {
-			len := s.newValue1(ssa.OpSliceLen, 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)
-		}
-		if skip&skipCap == 0 {
-			cap := s.newValue1(ssa.OpSliceCap, 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)
-		}
-	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)
-	case t.IsStruct():
-		n := t.NumFields()
-		for i := 0; i < n; i++ {
-			ft := t.FieldType(i)
-			addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
-			val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
-			s.storeTypeScalars(ft, addr, val, 0)
-		}
-	case t.IsArray() && t.NumElem() == 0:
-		// nothing
-	case t.IsArray() && t.NumElem() == 1:
-		s.storeTypeScalars(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right), 0)
-	default:
-		s.Fatalf("bad write barrier type %v", t)
-	}
-}
-
-// do *left = right for all pointer parts of t.
-func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
-	switch {
-	case t.IsPtrShaped():
-		if t.IsPtr() && t.Elem().NotInHeap() {
-			break // see issue 42032
-		}
-		s.store(t, left, right)
-	case t.IsString():
-		ptr := s.newValue1(ssa.OpStringPtr, s.f.Config.Types.BytePtr, right)
-		s.store(s.f.Config.Types.BytePtr, left, ptr)
-	case t.IsSlice():
-		elType := types.NewPtr(t.Elem())
-		ptr := s.newValue1(ssa.OpSlicePtr, elType, right)
-		s.store(elType, left, ptr)
-	case t.IsInterface():
-		// itab field is treated as a scalar.
-		idata := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, right)
-		idataAddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.BytePtrPtr, s.config.PtrSize, left)
-		s.store(s.f.Config.Types.BytePtr, idataAddr, idata)
-	case t.IsStruct():
-		n := t.NumFields()
-		for i := 0; i < n; i++ {
-			ft := t.FieldType(i)
-			if !ft.HasPointers() {
-				continue
-			}
-			addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
-			val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
-			s.storeTypePtrs(ft, addr, val)
-		}
-	case t.IsArray() && t.NumElem() == 0:
-		// nothing
-	case t.IsArray() && t.NumElem() == 1:
-		s.storeTypePtrs(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right))
-	default:
-		s.Fatalf("bad write barrier type %v", t)
-	}
-}
-
-// 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) {
-	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)
-		}
-	} else {
-		s.storeArgWithBase(n, t, s.sp, off)
-	}
-	return ssa.Param{Type: t, Offset: int32(off)}, a
-}
-
-func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) {
-	pt := types.NewPtr(t)
-	var addr *ssa.Value
-	if base == s.sp {
-		// Use special routine that avoids allocation on duplicate offsets.
-		addr = s.constOffPtrSP(pt, off)
-	} else {
-		addr = s.newValue1I(ssa.OpOffPtr, pt, off, base)
-	}
-
-	if !canSSAType(t) {
-		a := s.addr(n)
-		s.move(t, addr, a)
-		return
-	}
-
-	a := s.expr(n)
-	s.storeType(t, addr, a, 0, false)
-}
-
-// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
-// i,j,k may be nil, in which case they are set to their default value.
-// v may be a slice, string or pointer to an array.
-func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value) {
-	t := v.Type
-	var ptr, len, cap *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)
-	case t.IsString():
-		ptr = s.newValue1(ssa.OpStringPtr, types.NewPtr(types.Types[TUINT8]), v)
-		len = s.newValue1(ssa.OpStringLen, types.Types[TINT], v)
-		cap = len
-	case t.IsPtr():
-		if !t.Elem().IsArray() {
-			s.Fatalf("bad ptr to array in slice %v\n", t)
-		}
-		s.nilCheck(v)
-		ptr = s.newValue1(ssa.OpCopy, types.NewPtr(t.Elem().Elem()), v)
-		len = s.constInt(types.Types[TINT], t.Elem().NumElem())
-		cap = len
-	default:
-		s.Fatalf("bad type in slice %v\n", t)
-	}
-
-	// Set default values
-	if i == nil {
-		i = s.constInt(types.Types[TINT], 0)
-	}
-	if j == nil {
-		j = len
-	}
-	three := true
-	if k == nil {
-		three = false
-		k = cap
-	}
-
-	// Panic if slice indices are not in bounds.
-	// Make sure we check these in reverse order so that we're always
-	// comparing against a value known to be nonnegative. See issue 28797.
-	if three {
-		if k != cap {
-			kind := ssa.BoundsSlice3Alen
-			if t.IsSlice() {
-				kind = ssa.BoundsSlice3Acap
-			}
-			k = s.boundsCheck(k, cap, kind, bounded)
-		}
-		if j != k {
-			j = s.boundsCheck(j, k, ssa.BoundsSlice3B, bounded)
-		}
-		i = s.boundsCheck(i, j, ssa.BoundsSlice3C, bounded)
-	} else {
-		if j != k {
-			kind := ssa.BoundsSliceAlen
-			if t.IsSlice() {
-				kind = ssa.BoundsSliceAcap
-			}
-			j = s.boundsCheck(j, k, kind, bounded)
-		}
-		i = s.boundsCheck(i, j, ssa.BoundsSliceB, bounded)
-	}
-
-	// 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])
-
-	// 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)
-	rcap := rlen
-	if j != k && !t.IsString() {
-		rcap = s.newValue2(subOp, types.Types[TINT], k, i)
-	}
-
-	if (i.Op == ssa.OpConst64 || i.Op == ssa.OpConst32) && i.AuxInt == 0 {
-		// No pointer arithmetic necessary.
-		return ptr, rlen, rcap
-	}
-
-	// Calculate the base pointer (rptr) for the new slice.
-	//
-	// Generate the following code assuming that indexes are in bounds.
-	// The masking is to make sure that we don't generate a slice
-	// that points to the next object in memory. We cannot just set
-	// the pointer to nil because then we would create a nil slice or
-	// string.
-	//
-	//     rcap = k - i
-	//     rlen = j - i
-	//     rptr = ptr + (mask(rcap) & (i * stride))
-	//
-	// 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)
-
-	// The delta is the number of bytes to offset ptr by.
-	delta := s.newValue2(mulOp, 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)
-
-	// Compute rptr = ptr + delta.
-	rptr := s.newValue2(ssa.OpAddPtr, ptr.Type, ptr, delta)
-
-	return rptr, rlen, rcap
-}
-
-type u642fcvtTab struct {
-	leq, cvt2F, and, rsh, or, add ssa.Op
-	one                           func(*state, *types.Type, int64) *ssa.Value
-}
-
-var u64_f64 = u642fcvtTab{
-	leq:   ssa.OpLeq64,
-	cvt2F: ssa.OpCvt64to64F,
-	and:   ssa.OpAnd64,
-	rsh:   ssa.OpRsh64Ux64,
-	or:    ssa.OpOr64,
-	add:   ssa.OpAdd64F,
-	one:   (*state).constInt64,
-}
-
-var u64_f32 = u642fcvtTab{
-	leq:   ssa.OpLeq64,
-	cvt2F: ssa.OpCvt64to32F,
-	and:   ssa.OpAnd64,
-	rsh:   ssa.OpRsh64Ux64,
-	or:    ssa.OpOr64,
-	add:   ssa.OpAdd32F,
-	one:   (*state).constInt64,
-}
-
-func (s *state) uint64Tofloat64(n *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 {
-	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 {
-	// if x >= 0 {
-	//    result = (floatY) x
-	// } else {
-	// 	  y = uintX(x) ; y = x & 1
-	// 	  z = uintX(x) ; z = z >> 1
-	// 	  z = z >> 1
-	// 	  z = z | y
-	// 	  result = floatY(z)
-	// 	  result = result + result
-	// }
-	//
-	// Code borrowed from old code generator.
-	// What's going on: large 64-bit "unsigned" looks like
-	// negative number to hardware's integer-to-float
-	// conversion. However, because the mantissa is only
-	// 63 bits, we don't need the LSB, so instead we do an
-	// unsigned right shift (divide by two), convert, and
-	// double. However, before we do that, we need to be
-	// sure that we do not lose a "1" if that made the
-	// difference in the resulting rounding. Therefore, we
-	// preserve it, and OR (not ADD) it back in. The case
-	// that matters is when the eleven discarded bits are
-	// 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)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(cmp)
-	b.Likely = ssa.BranchLikely
-
-	bThen := s.f.NewBlock(ssa.BlockPlain)
-	bElse := s.f.NewBlock(ssa.BlockPlain)
-	bAfter := s.f.NewBlock(ssa.BlockPlain)
-
-	b.AddEdgeTo(bThen)
-	s.startBlock(bThen)
-	a0 := s.newValue1(cvttab.cvt2F, tt, x)
-	s.vars[n] = a0
-	s.endBlock()
-	bThen.AddEdgeTo(bAfter)
-
-	b.AddEdgeTo(bElse)
-	s.startBlock(bElse)
-	one := cvttab.one(s, ft, 1)
-	y := s.newValue2(cvttab.and, ft, x, one)
-	z := s.newValue2(cvttab.rsh, ft, x, one)
-	z = s.newValue2(cvttab.or, ft, z, y)
-	a := s.newValue1(cvttab.cvt2F, tt, z)
-	a1 := s.newValue2(cvttab.add, tt, a, a)
-	s.vars[n] = a1
-	s.endBlock()
-	bElse.AddEdgeTo(bAfter)
-
-	s.startBlock(bAfter)
-	return s.variable(n, n.Type)
-}
-
-type u322fcvtTab struct {
-	cvtI2F, cvtF2F ssa.Op
-}
-
-var u32_f64 = u322fcvtTab{
-	cvtI2F: ssa.OpCvt32to64F,
-	cvtF2F: ssa.OpCopy,
-}
-
-var u32_f32 = u322fcvtTab{
-	cvtI2F: ssa.OpCvt32to32F,
-	cvtF2F: ssa.OpCvt64Fto32F,
-}
-
-func (s *state) uint32Tofloat64(n *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 {
-	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 {
-	// 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)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(cmp)
-	b.Likely = ssa.BranchLikely
-
-	bThen := s.f.NewBlock(ssa.BlockPlain)
-	bElse := s.f.NewBlock(ssa.BlockPlain)
-	bAfter := s.f.NewBlock(ssa.BlockPlain)
-
-	b.AddEdgeTo(bThen)
-	s.startBlock(bThen)
-	a0 := s.newValue1(cvttab.cvtI2F, tt, x)
-	s.vars[n] = a0
-	s.endBlock()
-	bThen.AddEdgeTo(bAfter)
-
-	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)
-	a3 := s.newValue1(cvttab.cvtF2F, tt, a2)
-
-	s.vars[n] = a3
-	s.endBlock()
-	bElse.AddEdgeTo(bAfter)
-
-	s.startBlock(bAfter)
-	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() {
-		s.Fatalf("node must be a map or a channel")
-	}
-	// if n == nil {
-	//   return 0
-	// } else {
-	//   // len
-	//   return *((*int)n)
-	//   // cap
-	//   return *(((*int)n)+1)
-	// }
-	lenType := n.Type
-	nilValue := s.constNil(types.Types[TUINTPTR])
-	cmp := s.newValue2(ssa.OpEqPtr, types.Types[TBOOL], x, nilValue)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(cmp)
-	b.Likely = ssa.BranchUnlikely
-
-	bThen := s.f.NewBlock(ssa.BlockPlain)
-	bElse := s.f.NewBlock(ssa.BlockPlain)
-	bAfter := s.f.NewBlock(ssa.BlockPlain)
-
-	// length/capacity of a nil map/chan is zero
-	b.AddEdgeTo(bThen)
-	s.startBlock(bThen)
-	s.vars[n] = s.zeroVal(lenType)
-	s.endBlock()
-	bThen.AddEdgeTo(bAfter)
-
-	b.AddEdgeTo(bElse)
-	s.startBlock(bElse)
-	switch n.Op {
-	case OLEN:
-		// length is stored in the first word for map/chan
-		s.vars[n] = s.load(lenType, x)
-	case 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)
-	default:
-		s.Fatalf("op must be OLEN or OCAP")
-	}
-	s.endBlock()
-	bElse.AddEdgeTo(bAfter)
-
-	s.startBlock(bAfter)
-	return s.variable(n, lenType)
-}
-
-type f2uCvtTab struct {
-	ltf, cvt2U, subf, or ssa.Op
-	floatValue           func(*state, *types.Type, float64) *ssa.Value
-	intValue             func(*state, *types.Type, int64) *ssa.Value
-	cutoff               uint64
-}
-
-var f32_u64 = f2uCvtTab{
-	ltf:        ssa.OpLess32F,
-	cvt2U:      ssa.OpCvt32Fto64,
-	subf:       ssa.OpSub32F,
-	or:         ssa.OpOr64,
-	floatValue: (*state).constFloat32,
-	intValue:   (*state).constInt64,
-	cutoff:     1 << 63,
-}
-
-var f64_u64 = f2uCvtTab{
-	ltf:        ssa.OpLess64F,
-	cvt2U:      ssa.OpCvt64Fto64,
-	subf:       ssa.OpSub64F,
-	or:         ssa.OpOr64,
-	floatValue: (*state).constFloat64,
-	intValue:   (*state).constInt64,
-	cutoff:     1 << 63,
-}
-
-var f32_u32 = f2uCvtTab{
-	ltf:        ssa.OpLess32F,
-	cvt2U:      ssa.OpCvt32Fto32,
-	subf:       ssa.OpSub32F,
-	or:         ssa.OpOr32,
-	floatValue: (*state).constFloat32,
-	intValue:   func(s *state, t *types.Type, v int64) *ssa.Value { return s.constInt32(t, int32(v)) },
-	cutoff:     1 << 31,
-}
-
-var f64_u32 = f2uCvtTab{
-	ltf:        ssa.OpLess64F,
-	cvt2U:      ssa.OpCvt64Fto32,
-	subf:       ssa.OpSub64F,
-	or:         ssa.OpOr32,
-	floatValue: (*state).constFloat64,
-	intValue:   func(s *state, t *types.Type, v int64) *ssa.Value { return s.constInt32(t, int32(v)) },
-	cutoff:     1 << 31,
-}
-
-func (s *state) float32ToUint64(n *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 {
-	return s.floatToUint(&f64_u64, n, x, ft, tt)
-}
-
-func (s *state) float32ToUint32(n *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 {
-	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 {
-	// cutoff:=1<<(intY_Size-1)
-	// if x < floatX(cutoff) {
-	// 	result = uintY(x)
-	// } else {
-	// 	y = x - floatX(cutoff)
-	// 	z = uintY(y)
-	// 	result = z | -(cutoff)
-	// }
-	cutoff := cvttab.floatValue(s, ft, float64(cvttab.cutoff))
-	cmp := s.newValue2(cvttab.ltf, types.Types[TBOOL], x, cutoff)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(cmp)
-	b.Likely = ssa.BranchLikely
-
-	bThen := s.f.NewBlock(ssa.BlockPlain)
-	bElse := s.f.NewBlock(ssa.BlockPlain)
-	bAfter := s.f.NewBlock(ssa.BlockPlain)
-
-	b.AddEdgeTo(bThen)
-	s.startBlock(bThen)
-	a0 := s.newValue1(cvttab.cvt2U, tt, x)
-	s.vars[n] = a0
-	s.endBlock()
-	bThen.AddEdgeTo(bAfter)
-
-	b.AddEdgeTo(bElse)
-	s.startBlock(bElse)
-	y := s.newValue2(cvttab.subf, ft, x, cutoff)
-	y = s.newValue1(cvttab.cvt2U, tt, y)
-	z := cvttab.intValue(s, tt, int64(-cvttab.cutoff))
-	a1 := s.newValue2(cvttab.or, tt, y, z)
-	s.vars[n] = a1
-	s.endBlock()
-	bElse.AddEdgeTo(bAfter)
-
-	s.startBlock(bAfter)
-	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
-	byteptr := s.f.Config.Types.BytePtr
-
-	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")
-			}
-
-			// 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))
-
-			if n.Left.Type.IsEmptyInterface() && commaok {
-				// Converting empty interface to empty interface with ,ok is just a nil check.
-				return iface, cond
-			}
-
-			// Branch on nilness.
-			b := s.endBlock()
-			b.Kind = ssa.BlockIf
-			b.SetControl(cond)
-			b.Likely = ssa.BranchLikely
-			bOk := s.f.NewBlock(ssa.BlockPlain)
-			bFail := s.f.NewBlock(ssa.BlockPlain)
-			b.AddEdgeTo(bOk)
-			b.AddEdgeTo(bFail)
-
-			if !commaok {
-				// On failure, panic by calling panicnildottype.
-				s.startBlock(bFail)
-				s.rtcall(panicnildottype, false, nil, target)
-
-				// On success, return (perhaps modified) input interface.
-				s.startBlock(bOk)
-				if n.Left.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)
-				typ := s.load(byteptr, off)
-				idata := s.newValue1(ssa.OpIData, byteptr, iface)
-				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)
-			s.endBlock()
-
-			// itab is nil, might as well use that as the nil result.
-			s.startBlock(bFail)
-			s.vars[&typVar] = itab
-			s.endBlock()
-
-			// Merge point.
-			bEnd := s.f.NewBlock(ssa.BlockPlain)
-			bOk.AddEdgeTo(bEnd)
-			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)
-			resok = cond
-			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 n.Left.Type.IsEmptyInterface() {
-			if commaok {
-				call := s.rtcall(assertE2I2, true, []*types.Type{n.Type, types.Types[TBOOL]}, target, iface)
-				return call[0], call[1]
-			}
-			return s.rtcall(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)
-			return call[0], call[1]
-		}
-		return s.rtcall(assertI2I, true, []*types.Type{n.Type}, target, iface)[0], nil
-	}
-
-	if Debug_typeassert > 0 {
-		Warnl(n.Pos, "type assertion inlined")
-	}
-
-	// Converting to a concrete type.
-	direct := isdirectiface(n.Type)
-	itab := s.newValue1(ssa.OpITab, byteptr, iface) // type word of interface
-	if Debug_typeassert > 0 {
-		Warnl(n.Pos, "type assertion inlined")
-	}
-	var targetITab *ssa.Value
-	if n.Left.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())
-	}
-
-	var tmp *Node       // temporary for use with large types
-	var addr *ssa.Value // address of tmp
-	if commaok && !canSSAType(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())
-		addr = s.addr(tmp)
-	}
-
-	cond := s.newValue2(ssa.OpEqPtr, types.Types[TBOOL], itab, targetITab)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.SetControl(cond)
-	b.Likely = ssa.BranchLikely
-
-	bOk := s.f.NewBlock(ssa.BlockPlain)
-	bFail := s.f.NewBlock(ssa.BlockPlain)
-	b.AddEdgeTo(bOk)
-	b.AddEdgeTo(bFail)
-
-	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)
-		} else {
-			s.rtcall(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
-		}
-		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
-	// a control flow merge point.
-	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"}}
-
-	// type assertion succeeded
-	s.startBlock(bOk)
-	if tmp == nil {
-		if direct {
-			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)
-		}
-	} else {
-		p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type), iface)
-		s.move(n.Type, addr, p)
-	}
-	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)
-	} else {
-		s.zero(n.Type, addr)
-	}
-	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)
-		delete(s.vars, valVar)
-	} else {
-		res = s.load(n.Type, addr)
-		s.vars[&memVar] = s.newValue1A(ssa.OpVarKill, types.TypeMem, tmp, s.mem())
-	}
-	resok = s.variable(&okVar, 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]
-	if v != nil {
-		return v
-	}
-	v = s.fwdVars[name]
-	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)
-	}
-	// 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)
-	return v
-}
-
-func (s *state) mem() *ssa.Value {
-	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.).
-		return
-	}
-	if n.IsAutoTmp() {
-		// Don't track temporary variables.
-		return
-	}
-	if n.Class() == 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}
-	values, ok := s.f.NamedValues[loc]
-	if !ok {
-		s.f.Names = append(s.f.Names, loc)
-	}
-	s.f.NamedValues[loc] = append(values, v)
-}
-
-// Generate a disconnected call to a runtime routine and a return.
-func gencallret(pp *Progs, sym *obj.LSym) *obj.Prog {
-	p := pp.Prog(obj.ACALL)
-	p.To.Type = obj.TYPE_MEM
-	p.To.Name = obj.NAME_EXTERN
-	p.To.Sym = sym
-	p = pp.Prog(obj.ARET)
-	return p
-}
-
-// Branch is an unresolved branch.
-type Branch struct {
-	P *obj.Prog  // branch instruction
-	B *ssa.Block // target
-}
-
-// SSAGenState contains state needed during Prog generation.
-type SSAGenState struct {
-	pp *Progs
-
-	// Branches remembers all the branch instructions we've seen
-	// and where they would like to go.
-	Branches []Branch
-
-	// bstart remembers where each block starts (indexed by block ID)
-	bstart []*obj.Prog
-
-	// Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8.
-	ScratchFpMem *Node
-
-	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
-
-	// lineRunStart records the beginning of the current run of instructions
-	// within a single block sharing the same line number
-	// Used to move statement marks to the beginning of such runs.
-	lineRunStart *obj.Prog
-
-	// wasm: The number of values on the WebAssembly stack. This is only used as a safeguard.
-	OnWasmStackSkipped int
-}
-
-// Prog appends a new Prog.
-func (s *SSAGenState) Prog(as obj.As) *obj.Prog {
-	p := s.pp.Prog(as)
-	if ssa.LosesStmtMark(as) {
-		return p
-	}
-	// Float a statement start to the beginning of any same-line run.
-	// lineRunStart is reset at block boundaries, which appears to work well.
-	if s.lineRunStart == nil || s.lineRunStart.Pos.Line() != p.Pos.Line() {
-		s.lineRunStart = p
-	} else if p.Pos.IsStmt() == src.PosIsStmt {
-		s.lineRunStart.Pos = s.lineRunStart.Pos.WithIsStmt()
-		p.Pos = p.Pos.WithNotStmt()
-	}
-	return p
-}
-
-// Pc returns the current Prog.
-func (s *SSAGenState) Pc() *obj.Prog {
-	return s.pp.next
-}
-
-// SetPos sets the current source position.
-func (s *SSAGenState) 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 {
-	p := s.Prog(op)
-	p.To.Type = obj.TYPE_BRANCH
-	s.Branches = append(s.Branches, Branch{P: p, B: target})
-	return p
-}
-
-// DebugFriendlySetPosFrom adjusts Pos.IsStmt subject to heuristics
-// that reduce "jumpy" line number churn when debugging.
-// 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) {
-	switch v.Op {
-	case ssa.OpPhi, ssa.OpCopy, ssa.OpLoadReg, ssa.OpStoreReg:
-		// These are not statements
-		s.SetPos(v.Pos.WithNotStmt())
-	default:
-		p := v.Pos
-		if p != src.NoXPos {
-			// If the position is defined, update the position.
-			// Also convert default IsStmt to NotStmt; only
-			// explicit statement boundaries should appear
-			// 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
-			}
-			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())
-		}
-	}
-}
-
-// genssa appends entries to pp for each instruction in f.
-func genssa(f *ssa.Func, pp *Progs) {
-	var s SSAGenState
-
-	e := f.Frontend().(*ssafn)
-
-	s.livenessMap = liveness(e, f, pp)
-	emitStackObjects(e, pp)
-
-	openDeferInfo := e.curfn.Func.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.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = openDeferInfo
-	}
-
-	// Remember where each block starts.
-	s.bstart = make([]*obj.Prog, f.NumBlocks())
-	s.pp = pp
-	var progToValue map[*obj.Prog]*ssa.Value
-	var progToBlock map[*obj.Prog]*ssa.Block
-	var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point.
-	if f.PrintOrHtmlSSA {
-		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]
-	}
-
-	s.ScratchFpMem = e.scratchFpMem
-
-	if Ctxt.Flag_locationlists {
-		if cap(f.Cache.ValueToProgAfter) < f.NumValues() {
-			f.Cache.ValueToProgAfter = make([]*obj.Prog, f.NumValues())
-		}
-		valueToProgAfter = f.Cache.ValueToProgAfter[:f.NumValues()]
-		for i := range valueToProgAfter {
-			valueToProgAfter[i] = nil
-		}
-	}
-
-	// If the very first instruction is not tagged as a statement,
-	// debuggers may attribute it to previous function in program.
-	firstPos := src.NoXPos
-	for _, v := range f.Entry.Values {
-		if v.Pos.IsStmt() == src.PosIsStmt {
-			firstPos = v.Pos
-			v.Pos = firstPos.WithDefaultStmt()
-			break
-		}
-	}
-
-	// inlMarks has an entry for each Prog that implements an inline mark.
-	// It maps from that Prog to the global inlining id of the inlined body
-	// which should unwind to this Prog's location.
-	var inlMarks map[*obj.Prog]int32
-	var inlMarkList []*obj.Prog
-
-	// inlMarksByPos maps from a (column 1) source position to the set of
-	// Progs that are in the set above and have that source position.
-	var inlMarksByPos map[src.XPos][]*obj.Prog
-
-	// Emit basic blocks
-	for i, b := range f.Blocks {
-		s.bstart[b.ID] = s.pp.next
-		s.lineRunStart = nil
-
-		// Attach a "default" liveness info. Normally this will be
-		// overwritten in the Values loop below for each Value. But
-		// for an empty block this will be used for its control
-		// 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)}
-
-		// Emit values in block
-		thearch.SSAMarkMoves(&s, b)
-		for _, v := range b.Values {
-			x := s.pp.next
-			s.DebugFriendlySetPosFrom(v)
-
-			switch v.Op {
-			case ssa.OpInitMem:
-				// memory arg needs no code
-			case ssa.OpArg:
-				// input args need no code
-			case ssa.OpSP, ssa.OpSB:
-				// nothing to do
-			case ssa.OpSelect0, ssa.OpSelect1:
-				// nothing to do
-			case ssa.OpGetG:
-				// nothing to do when there's a g register,
-				// and checkLower complains if there's not
-			case ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive, ssa.OpVarKill:
-				// nothing to do; already used by liveness
-			case ssa.OpPhi:
-				CheckLoweredPhi(v)
-			case ssa.OpConvert:
-				// nothing to do; no-op conversion for liveness
-				if v.Args[0].Reg() != v.Reg() {
-					v.Fatalf("OpConvert should be a no-op: %s; %s", v.Args[0].LongString(), v.LongString())
-				}
-			case ssa.OpInlMark:
-				p := thearch.Ginsnop(s.pp)
-				if inlMarks == nil {
-					inlMarks = map[*obj.Prog]int32{}
-					inlMarksByPos = map[src.XPos][]*obj.Prog{}
-				}
-				inlMarks[p] = v.AuxInt32()
-				inlMarkList = append(inlMarkList, p)
-				pos := v.Pos.AtColumn1()
-				inlMarksByPos[pos] = append(inlMarksByPos[pos], p)
-
-			default:
-				// Attach this safe point to the next
-				// instruction.
-				s.pp.nextLive = s.livenessMap.Get(v)
-
-				// Special case for first line in function; move it to the start.
-				if firstPos != src.NoXPos {
-					s.SetPos(firstPos)
-					firstPos = src.NoXPos
-				}
-				// let the backend handle it
-				thearch.SSAGenValue(&s, v)
-			}
-
-			if Ctxt.Flag_locationlists {
-				valueToProgAfter[v.ID] = s.pp.next
-			}
-
-			if f.PrintOrHtmlSSA {
-				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)
-			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.
-				if b.Pos == src.NoXPos {
-					b.Pos = pp.Text.Pos // Sometimes p.Pos is empty.  See #35695.
-				}
-			}
-			b.Pos = b.Pos.WithBogusLine() // Debuggers are not good about infinite loops, force a change in line number
-		}
-		// Emit control flow instructions for block
-		var next *ssa.Block
-		if i < len(f.Blocks)-1 && Debug.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
-		s.SetPos(b.Pos)
-		thearch.SSAGenBlock(&s, b, next)
-		if f.PrintOrHtmlSSA {
-			for ; x != s.pp.next; x = x.Link {
-				progToBlock[x] = b
-			}
-		}
-	}
-	if f.Blocks[len(f.Blocks)-1].Kind == ssa.BlockExit {
-		// We need the return address of a panic call to
-		// 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)
-	}
-	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)
-	}
-
-	if inlMarks != nil {
-		// We have some inline marks. Try to find other instructions we're
-		// 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 {
-				// 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.
-				// We must not use anything that even might be zero-sized.)
-				// TODO: are there others?
-				continue
-			}
-			if _, ok := inlMarks[p]; ok {
-				// Don't use inline marks themselves. We don't know
-				// whether they will be zero-sized or not yet.
-				continue
-			}
-			pos := p.Pos.AtColumn1()
-			s := inlMarksByPos[pos]
-			if len(s) == 0 {
-				continue
-			}
-			for _, m := range s {
-				// We found an instruction with the same source position as
-				// 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])
-				// Make the inline mark a real nop, so it doesn't generate any code.
-				m.As = obj.ANOP
-				m.Pos = src.NoXPos
-				m.From = obj.Addr{}
-				m.To = obj.Addr{}
-			}
-			delete(inlMarksByPos, pos)
-		}
-		// 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])
-			}
-		}
-	}
-
-	if Ctxt.Flag_locationlists {
-		e.curfn.Func.DebugInfo = ssa.BuildFuncDebug(Ctxt, f, Debug_locationlist > 1, stackOffset)
-		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 {
-			switch v {
-			case ssa.BlockStart.ID:
-				if b == f.Entry.ID {
-					return 0 // Start at the very beginning, at the assembler-generated prologue.
-					// this should only happen for function args (ssa.OpArg)
-				}
-				return bstart[b].Pc
-			case ssa.BlockEnd.ID:
-				return e.curfn.Func.lsym.Size
-			default:
-				return valueToProgAfter[v].Pc
-			}
-		}
-	}
-
-	// Resolve branches, and relax DefaultStmt into NotStmt
-	for _, br := range s.Branches {
-		br.P.To.SetTarget(s.bstart[br.B.ID])
-		if br.P.Pos.IsStmt() != src.PosIsStmt {
-			br.P.Pos = br.P.Pos.WithNotStmt()
-		} else if v0 := br.B.FirstPossibleStmtValue(); v0 != nil && v0.Pos.Line() == br.P.Pos.Line() && v0.Pos.IsStmt() == src.PosIsStmt {
-			br.P.Pos = br.P.Pos.WithNotStmt()
-		}
-
-	}
-
-	if e.log { // spew to stdout
-		filename := ""
-		for p := pp.Text; p != nil; p = p.Link {
-			if p.Pos.IsKnown() && p.InnermostFilename() != filename {
-				filename = p.InnermostFilename()
-				f.Logf("# %s\n", filename)
-			}
-
-			var s string
-			if v, ok := progToValue[p]; ok {
-				s = v.String()
-			} else if b, ok := progToBlock[p]; ok {
-				s = b.String()
-			} else {
-				s = "   " // most value and branch strings are 2-3 characters long
-			}
-			f.Logf(" %-6s\t%.5d (%s)\t%s\n", s, p.Pc, p.InnermostLineNumber(), p.InstructionString())
-		}
-	}
-	if f.HTMLWriter != nil { // spew to ssa.html
-		var buf bytes.Buffer
-		buf.WriteString("<code>")
-		buf.WriteString("<dl class=\"ssa-gen\">")
-		filename := ""
-		for p := pp.Text; p != nil; p = p.Link {
-			// Don't spam every line with the file name, which is often huge.
-			// Only print changes, and "unknown" is not a change.
-			if p.Pos.IsKnown() && p.InnermostFilename() != filename {
-				filename = p.InnermostFilename()
-				buf.WriteString("<dt class=\"ssa-prog-src\"></dt><dd class=\"ssa-prog\">")
-				buf.WriteString(html.EscapeString("# " + filename))
-				buf.WriteString("</dd>")
-			}
-
-			buf.WriteString("<dt class=\"ssa-prog-src\">")
-			if v, ok := progToValue[p]; ok {
-				buf.WriteString(v.HTML())
-			} else if b, ok := progToBlock[p]; ok {
-				buf.WriteString("<b>" + b.HTML() + "</b>")
-			}
-			buf.WriteString("</dt>")
-			buf.WriteString("<dd class=\"ssa-prog\">")
-			buf.WriteString(fmt.Sprintf("%.5d <span class=\"l%v line-number\">(%s)</span> %s", p.Pc, p.InnermostLineNumber(), p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString())))
-			buf.WriteString("</dd>")
-		}
-		buf.WriteString("</dl>")
-		buf.WriteString("</code>")
-		f.HTMLWriter.WriteColumn("genssa", "genssa", "ssa-prog", buf.String())
-	}
-
-	defframe(&s, e)
-
-	f.HTMLWriter.Close()
-	f.HTMLWriter = nil
-}
-
-func defframe(s *SSAGenState, e *ssafn) {
-	pp := s.pp
-
-	frame := Rnd(s.maxarg+e.stksize, int64(Widthreg))
-	if thearch.PadFrame != nil {
-		frame = thearch.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.Offset = frame
-
-	// Insert code to zero ambiguously live variables so that the
-	// garbage collector only sees initialized values when it
-	// looks for pointers.
-	p := pp.Text
-	var lo, hi int64
-
-	// Opaque state for backend to use. Current backends use it to
-	// keep track of which helper registers have been zeroed.
-	var state uint32
-
-	// Iterate through declarations. They are sorted in decreasing Xoffset order.
-	for _, n := range e.curfn.Func.Dcl {
-		if !n.Name.Needzero() {
-			continue
-		}
-		if n.Class() != 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 lo != hi && n.Xoffset+n.Type.Size() >= lo-int64(2*Widthreg) {
-			// Merge with range we already have.
-			lo = n.Xoffset
-			continue
-		}
-
-		// Zero old range
-		p = thearch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
-
-		// Set new range.
-		lo = n.Xoffset
-		hi = lo + n.Type.Size()
-	}
-
-	// Zero final range.
-	thearch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
-}
-
-// For generating consecutive jump instructions to model a specific branching
-type IndexJump struct {
-	Jump  obj.As
-	Index int
-}
-
-func (s *SSAGenState) 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) {
-	switch next {
-	case b.Succs[0].Block():
-		s.oneJump(b, &jumps[0][0])
-		s.oneJump(b, &jumps[0][1])
-	case b.Succs[1].Block():
-		s.oneJump(b, &jumps[1][0])
-		s.oneJump(b, &jumps[1][1])
-	default:
-		var q *obj.Prog
-		if b.Likely != ssa.BranchUnlikely {
-			s.oneJump(b, &jumps[1][0])
-			s.oneJump(b, &jumps[1][1])
-			q = s.Br(obj.AJMP, b.Succs[1].Block())
-		} else {
-			s.oneJump(b, &jumps[0][0])
-			s.oneJump(b, &jumps[0][1])
-			q = s.Br(obj.AJMP, b.Succs[0].Block())
-		}
-		q.Pos = b.Pos
-	}
-}
-
-// AddAux adds the offset in the aux fields (AuxInt and Aux) of v to a.
-func AddAux(a *obj.Addr, v *ssa.Value) {
-	AddAux2(a, v, v.AuxInt)
-}
-func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
-	if a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR {
-		v.Fatalf("bad AddAux addr %v", a)
-	}
-	// add integer offset
-	a.Offset += offset
-
-	// If no additional symbol offset, we're done.
-	if v.Aux == nil {
-		return
-	}
-	// Add symbol's offset from its base register.
-	switch n := v.Aux.(type) {
-	case *ssa.AuxCall:
-		a.Name = obj.NAME_EXTERN
-		a.Sym = n.Fn
-	case *obj.LSym:
-		a.Name = obj.NAME_EXTERN
-		a.Sym = n
-	case *Node:
-		if n.Class() == PPARAM || n.Class() == PPARAMOUT {
-			a.Name = obj.NAME_PARAM
-			a.Sym = n.Orig.Sym.Linksym()
-			a.Offset += n.Xoffset
-			break
-		}
-		a.Name = obj.NAME_AUTO
-		a.Sym = n.Sym.Linksym()
-		a.Offset += n.Xoffset
-	default:
-		v.Fatalf("aux in %s not implemented %#v", v, v.Aux)
-	}
-}
-
-// extendIndex extends v to a full int width.
-// panic with the given kind if v does not fit in an int (only on 32-bit archs).
-func (s *state) extendIndex(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bool) *ssa.Value {
-	size := idx.Type.Size()
-	if size == s.config.PtrSize {
-		return idx
-	}
-	if size > s.config.PtrSize {
-		// truncate 64-bit indexes on 32-bit pointer archs. Test the
-		// 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)
-		} else {
-			lo = s.newValue1(ssa.OpInt64Lo, types.Types[TUINT], idx)
-		}
-		if bounded || Debug.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))
-		if !idx.Type.IsSigned() {
-			switch kind {
-			case ssa.BoundsIndex:
-				kind = ssa.BoundsIndexU
-			case ssa.BoundsSliceAlen:
-				kind = ssa.BoundsSliceAlenU
-			case ssa.BoundsSliceAcap:
-				kind = ssa.BoundsSliceAcapU
-			case ssa.BoundsSliceB:
-				kind = ssa.BoundsSliceBU
-			case ssa.BoundsSlice3Alen:
-				kind = ssa.BoundsSlice3AlenU
-			case ssa.BoundsSlice3Acap:
-				kind = ssa.BoundsSlice3AcapU
-			case ssa.BoundsSlice3B:
-				kind = ssa.BoundsSlice3BU
-			case ssa.BoundsSlice3C:
-				kind = ssa.BoundsSlice3CU
-			}
-		}
-		b := s.endBlock()
-		b.Kind = ssa.BlockIf
-		b.SetControl(cmp)
-		b.Likely = ssa.BranchLikely
-		b.AddEdgeTo(bNext)
-		b.AddEdgeTo(bPanic)
-
-		s.startBlock(bPanic)
-		mem := s.newValue4I(ssa.OpPanicExtend, types.TypeMem, int64(kind), hi, lo, len, s.mem())
-		s.endBlock().SetControl(mem)
-		s.startBlock(bNext)
-
-		return lo
-	}
-
-	// Extend value to the required size
-	var op ssa.Op
-	if idx.Type.IsSigned() {
-		switch 10*size + s.config.PtrSize {
-		case 14:
-			op = ssa.OpSignExt8to32
-		case 18:
-			op = ssa.OpSignExt8to64
-		case 24:
-			op = ssa.OpSignExt16to32
-		case 28:
-			op = ssa.OpSignExt16to64
-		case 48:
-			op = ssa.OpSignExt32to64
-		default:
-			s.Fatalf("bad signed index extension %s", idx.Type)
-		}
-	} else {
-		switch 10*size + s.config.PtrSize {
-		case 14:
-			op = ssa.OpZeroExt8to32
-		case 18:
-			op = ssa.OpZeroExt8to64
-		case 24:
-			op = ssa.OpZeroExt16to32
-		case 28:
-			op = ssa.OpZeroExt16to64
-		case 48:
-			op = ssa.OpZeroExt32to64
-		default:
-			s.Fatalf("bad unsigned index extension %s", idx.Type)
-		}
-	}
-	return s.newValue1(op, types.Types[TINT], idx)
-}
-
-// CheckLoweredPhi checks that regalloc and stackalloc correctly handled phi values.
-// Called during ssaGenValue.
-func CheckLoweredPhi(v *ssa.Value) {
-	if v.Op != ssa.OpPhi {
-		v.Fatalf("CheckLoweredPhi called with non-phi value: %v", v.LongString())
-	}
-	if v.Type.IsMemory() {
-		return
-	}
-	f := v.Block.Func
-	loc := f.RegAlloc[v.ID]
-	for _, a := range v.Args {
-		if aloc := f.RegAlloc[a.ID]; aloc != loc { // TODO: .Equal() instead?
-			v.Fatalf("phi arg at different location than phi: %v @ %s, but arg %v @ %s\n%s\n", v, loc, a, aloc, v.Block.Func)
-		}
-	}
-}
-
-// CheckLoweredGetClosurePtr checks that v is the first instruction in the function's entry block.
-// The output of LoweredGetClosurePtr is generally hardwired to the correct register.
-// That register contains the closure pointer on closure entry.
-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)
-	}
-}
-
-// 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)
-	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.Name = obj.NAME_PARAM
-	} else {
-		a.Name = obj.NAME_AUTO
-	}
-}
-
-func (s *SSAGenState) 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
-}
-
-// 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
-	s.PrepareCall(v)
-
-	p := s.Prog(obj.ACALL)
-	if pPosIsStmt == src.PosIsStmt {
-		p.Pos = v.Pos.WithIsStmt()
-	} else {
-		p.Pos = v.Pos.WithNotStmt()
-	}
-	if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = sym.Fn
-	} else {
-		// TODO(mdempsky): Can these differences be eliminated?
-		switch thearch.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")
-		}
-		p.To.Reg = v.Args[0].Reg()
-	}
-	return p
-}
-
-// 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) {
-	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())
-		}
-	}
-
-	call, ok := v.Aux.(*ssa.AuxCall)
-
-	if ok && call.Fn == Deferreturn {
-		// Deferred calls will appear to be returning to
-		// the CALL deferreturn(SB) that we are about to emit.
-		// However, the stack trace code will show the line
-		// of the instruction byte before the return PC.
-		// To avoid that being an unrelated instruction,
-		// 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)
-	}
-
-	if ok {
-		// Record call graph information for nowritebarrierrec
-		// analysis.
-		if nowritebarrierrecCheck != nil {
-			nowritebarrierrecCheck.recordCall(s.pp.curfn, call.Fn, v.Pos)
-		}
-	}
-
-	if s.maxarg < v.AuxInt {
-		s.maxarg = v.AuxInt
-	}
-}
-
-// UseArgs records the fact that an instruction needs a certain amount of
-// callee args space for its use.
-func (s *SSAGenState) 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
-	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")
-		}
-		return i
-	}
-	panic(fmt.Sprintf("can't find field in expr %v\n", n))
-
-	// TODO: keep the result of this function somewhere in the ODOT Node
-	// so we don't have to recompute it each time we need it.
-}
-
-// 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
-	strings      map[string]*obj.LSym // map from constant string to data symbols
-	scratchFpMem *Node                // 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
-}
-
-// StringData returns a symbol which
-// is the data component of a global string constant containing s.
-func (e *ssafn) StringData(s string) *obj.LSym {
-	if aux, ok := e.strings[s]; ok {
-		return aux
-	}
-	if e.strings == nil {
-		e.strings = make(map[string]*obj.LSym)
-	}
-	data := 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) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
-	ptrType := types.NewPtr(types.Types[TUINT8])
-	lenType := 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)
-	return p, l
-}
-
-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])
-	// Split this interface up into two separate variables.
-	f := ".itab"
-	if n.Type.IsEmptyInterface() {
-		f = ".type"
-	}
-	c := e.SplitSlot(&name, f, 0, u) // see comment in plive.go:onebitwalktype1.
-	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]
-	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)
-	return p, l, c
-}
-
-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]
-	} else {
-		t = types.Types[TFLOAT32]
-	}
-	r := e.SplitSlot(&name, ".real", 0, t)
-	i := e.SplitSlot(&name, ".imag", t.Size(), t)
-	return r, i
-}
-
-func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
-	var t *types.Type
-	if name.Type.IsSigned() {
-		t = types.Types[TINT32]
-	} else {
-		t = 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])
-	}
-	return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[TUINT32])
-}
-
-func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
-	st := name.Type
-	// Note: the _ field may appear several times.  But
-	// have no fear, identically-named but distinct Autos are
-	// ok, albeit maybe confusing for a debugger.
-	return e.SplitSlot(&name, "."+st.FieldName(i), st.FieldOff(i), st.FieldType(i))
-}
-
-func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
-	n := name.N.(*Node)
-	at := name.Type
-	if at.NumElem() != 1 {
-		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)
-}
-
-// 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)
-
-	if node.Class() != PAUTO || node.Name.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)
-	return ssa.LocalSlot{N: n, Type: t, Off: 0, SplitOf: parent, SplitOffset: offset}
-}
-
-func (e *ssafn) CanSSA(t *types.Type) bool {
-	return canSSAType(t)
-}
-
-func (e *ssafn) Line(pos src.XPos) string {
-	return linestr(pos)
-}
-
-// Log logs a message from the compiler.
-func (e *ssafn) Logf(msg string, args ...interface{}) {
-	if e.log {
-		fmt.Printf(msg, args...)
-	}
-}
-
-func (e *ssafn) Log() bool {
-	return e.log
-}
-
-// 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...)
-}
-
-// 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...)
-}
-
-func (e *ssafn) Debug_checknil() bool {
-	return Debug_checknil != 0
-}
-
-func (e *ssafn) UseWriteBarrier() bool {
-	return use_writebarrier
-}
-
-func (e *ssafn) Syslook(name string) *obj.LSym {
-	switch name {
-	case "goschedguarded":
-		return goschedguarded
-	case "writeBarrier":
-		return writeBarrier
-	case "gcWriteBarrier":
-		return gcWriteBarrier
-	case "typedmemmove":
-		return typedmemmove
-	case "typedmemclr":
-		return typedmemclr
-	}
-	e.Fatalf(src.NoXPos, "unknown Syslook func %v", name)
-	return nil
-}
-
-func (e *ssafn) SetWBPos(pos src.XPos) {
-	e.curfn.Func.setWBPos(pos)
-}
-
-func (e *ssafn) MyImportPath() string {
-	return myimportpath
-}
-
-func (n *Node) Typ() *types.Type {
-	return n.Type
-}
-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
-	}
-}
-
-func clobberBase(n *Node) *Node {
-	if n.Op == ODOT && n.Left.Type.NumFields() == 1 {
-		return clobberBase(n.Left)
-	}
-	if n.Op == OINDEX && n.Left.Type.IsArray() && n.Left.Type.NumElem() == 1 {
-		return clobberBase(n.Left)
-	}
-	return n
-}
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
deleted file mode 100644
index defefd76..0000000
--- 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 &copy
-}
-
-// 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 = &copy
-	return &copy
-}
-
-// 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 = &copy
-	}
-	return &copy
-}
-
-// 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 8d9fbe3..0000000
--- 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 7b4a315..0000000
--- 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 "<nil>"
-	}
-	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 "<nil>"
-	}
-	if n.Op == ONAME {
-		s = n.Sym
-	} else {
-		if n.Func == nil || n.Func.Nname == nil {
-			return "<nil>"
-		}
-		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 = &paramType{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 = &paramType{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 ed4b5a2..c6eb23a 100644
--- a/src/cmd/compile/internal/gc/trace.go
+++ b/src/cmd/compile/internal/gc/trace.go
@@ -9,6 +9,8 @@
 import (
 	"os"
 	tracepkg "runtime/trace"
+
+	"cmd/compile/internal/base"
 )
 
 func init() {
@@ -18,10 +20,10 @@
 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 c0b0503..0000000
--- 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<<k.
-	// TODO(gri) The length argument requirements for (array/slice) make
-	// are the same as for index expressions. Factor the code better;
-	// for instance, indexlit might be called here and incorporate some
-	// of the bounds checks done for make.
-	n = defaultlit(n, types.Types[TINT])
-	*np = n
-
-	return true
-}
-
-func markbreak(n *Node, implicit *Node) {
-	if n == nil {
-		return
-	}
-
-	switch n.Op {
-	case OBREAK:
-		if n.Sym == nil {
-			if implicit != nil {
-				implicit.SetHasBreak(true)
-			}
-		} else {
-			lab := asNode(n.Sym.Label)
-			if lab != nil {
-				lab.SetHasBreak(true)
-			}
-		}
-	case OFOR, OFORUNTIL, OSWITCH, OTYPESW, OSELECT, ORANGE:
-		implicit = n
-		fallthrough
-	default:
-		markbreak(n.Left, implicit)
-		markbreak(n.Right, implicit)
-		markbreaklist(n.Ninit, implicit)
-		markbreaklist(n.Nbody, implicit)
-		markbreaklist(n.List, implicit)
-		markbreaklist(n.Rlist, implicit)
-	}
-}
-
-func markbreaklist(l Nodes, implicit *Node) {
-	s := l.Slice()
-	for i := 0; i < len(s); i++ {
-		n := s[i]
-		if n == nil {
-			continue
-		}
-		if n.Op == OLABEL && i+1 < len(s) && n.Name.Defn == s[i+1] {
-			switch n.Name.Defn.Op {
-			case OFOR, OFORUNTIL, OSWITCH, OTYPESW, OSELECT, ORANGE:
-				n.Sym.Label = asTypesNode(n.Name.Defn)
-				markbreak(n.Name.Defn, n.Name.Defn)
-				n.Sym.Label = nil
-				i++
-				continue
-			}
-		}
-
-		markbreak(n, implicit)
-	}
-}
-
-// isterminating reports whether the Nodes list ends with a terminating statement.
-func (l Nodes) isterminating() bool {
-	s := l.Slice()
-	c := len(s)
-	if c == 0 {
-		return false
-	}
-	return s[c-1].isterminating()
-}
-
-// Isterminating reports whether the node n, the last one in a
-// statement list, is a terminating statement.
-func (n *Node) isterminating() bool {
-	switch n.Op {
-	// NOTE: OLABEL is treated as a separate statement,
-	// not a separate prefix, so skipping to the last statement
-	// in the block handles the labeled statement case by
-	// skipping over the label. No case OLABEL here.
-
-	case OBLOCK:
-		return n.List.isterminating()
-
-	case OGOTO, ORETURN, ORETJMP, OPANIC, OFALL:
-		return true
-
-	case OFOR, OFORUNTIL:
-		if n.Left != nil {
-			return false
-		}
-		if n.HasBreak() {
-			return false
-		}
-		return true
-
-	case OIF:
-		return n.Nbody.isterminating() && n.Rlist.isterminating()
-
-	case OSWITCH, OTYPESW, OSELECT:
-		if n.HasBreak() {
-			return false
-		}
-		def := false
-		for _, n1 := range n.List.Slice() {
-			if !n1.Nbody.isterminating() {
-				return false
-			}
-			if n1.List.Len() == 0 { // default
-				def = true
-			}
-		}
-
-		if n.Op != OSELECT && !def {
-			return false
-		}
-		return true
-	}
-
-	return false
-}
-
-// checkreturn makes sure that fn terminates appropriately.
-func checkreturn(fn *Node) {
-	if fn.Type.NumResults() != 0 && fn.Nbody.Len() != 0 {
-		markbreaklist(fn.Nbody, nil)
-		if !fn.Nbody.isterminating() {
-			yyerrorl(fn.Func.Endlineno, "missing return at end of function")
-		}
-	}
-}
-
-func deadcode(fn *Node) {
-	deadcodeslice(fn.Nbody)
-	deadcodefn(fn)
-}
-
-func deadcodefn(fn *Node) {
-	if fn.Nbody.Len() == 0 {
-		return
-	}
-
-	for _, n := range fn.Nbody.Slice() {
-		if n.Ninit.Len() > 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 748f845..0000000
--- 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 7240f72..0000000
--- 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 ff8cabd..0000000
--- 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 2233961..0000000
--- 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 58be2f8..4baddbc 100644
--- a/src/cmd/compile/internal/gc/util.go
+++ b/src/cmd/compile/internal/gc/util.go
@@ -8,59 +8,35 @@
 	"os"
 	"runtime"
 	"runtime/pprof"
+
+	"cmd/compile/internal/base"
 )
 
-// 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)
-}
-
 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 @@
 			// 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 98ebb23..0000000
--- 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(<type>, 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<<uint(bits) - 1
-				}
-			}
-		}
-		if 0 <= v && v < max {
-			return true
-		}
-
-	case OMOD:
-		if !sign && smallintconst(n.Right) {
-			v := n.Right.Int64Val()
-			if 0 <= v && v <= max {
-				return true
-			}
-		}
-
-	case ODIV:
-		if !sign && smallintconst(n.Right) {
-			v := n.Right.Int64Val()
-			for bits > 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<<uint(bits) <= max {
-		return true
-	}
-
-	return false
-}
-
-// usemethod checks interface method calls for uses of reflect.Type.Method.
-func usemethod(n *Node) {
-	t := n.Left.Type
-
-	// Looking for either of:
-	//	Method(int) reflect.Method
-	//	MethodByName(string) (reflect.Method, bool)
-	//
-	// TODO(crawshaw): improve precision of match by working out
-	//                 how to check the method name.
-	if n := t.NumParams(); n != 1 {
-		return
-	}
-	if n := t.NumResults(); n != 1 && n != 2 {
-		return
-	}
-	p0 := t.Params().Field(0)
-	res0 := t.Results().Field(0)
-	var res1 *types.Field
-	if t.NumResults() == 2 {
-		res1 = t.Results().Field(1)
-	}
-
-	if res1 == nil {
-		if p0.Type.Etype != TINT {
-			return
-		}
-	} else {
-		if !p0.Type.IsString() {
-			return
-		}
-		if !res1.Type.IsBoolean() {
-			return
-		}
-	}
-
-	// Don't mark reflect.(*rtype).Method, etc. themselves in the reflect package.
-	// Those functions may be alive via the itab, which should not cause all methods
-	// alive. We only want to mark their callers.
-	if myimportpath == "reflect" {
-		switch Curfn.Func.Nname.Sym.Name { // TODO: is there a better way than hardcoding the names?
-		case "(*rtype).Method", "(*rtype).MethodByName", "(*interfaceType).Method", "(*interfaceType).MethodByName":
-			return
-		}
-	}
-
-	// Note: Don't rely on res0.Type.String() since its formatting depends on multiple factors
-	//       (including global variables such as numImports - was issue #19028).
-	// Also need to check for reflect package itself (see Issue #38515).
-	if s := res0.Type.Sym; s != nil && s.Name == "Method" && isReflectPkg(s.Pkg) {
-		Curfn.Func.SetReflectMethod(true)
-		// The LSym is initialized at this point. We need to set the attribute on the LSym.
-		Curfn.Func.lsym.Set(obj.AttrReflectMethod, true)
-	}
-}
-
-func usefield(n *Node) {
-	if objabi.Fieldtrack_enabled == 0 {
-		return
-	}
-
-	switch n.Op {
-	default:
-		Fatalf("usefield %v", n.Op)
-
-	case ODOT, ODOTPTR:
-		break
-	}
-	if n.Sym == nil {
-		// No field name.  This DOTPTR was built by the compiler for access
-		// to runtime data structures.  Ignore.
-		return
-	}
-
-	t := n.Left.Type
-	if t.IsPtr() {
-		t = t.Elem()
-	}
-	field := n.Opt().(*types.Field)
-	if field == nil {
-		Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym)
-	}
-	if field.Sym != n.Sym || field.Offset != n.Xoffset {
-		Fatalf("field inconsistency: %v,%v != %v,%v", field.Sym, field.Offset, n.Sym, n.Xoffset)
-	}
-	if !strings.Contains(field.Note, "go:\"track\"") {
-		return
-	}
-
-	outer := n.Left.Type
-	if outer.IsPtr() {
-		outer = outer.Elem()
-	}
-	if outer.Sym == nil {
-		yyerror("tracked field must be in named struct type")
-	}
-	if !types.IsExported(field.Sym.Name) {
-		yyerror("tracked field must be exported (upper case)")
-	}
-
-	sym := tracksym(outer, field)
-	if Curfn.Func.FieldTrack == nil {
-		Curfn.Func.FieldTrack = make(map[*types.Sym]struct{})
-	}
-	Curfn.Func.FieldTrack[sym] = struct{}{}
-}
-
-func candiscardlist(l Nodes) bool {
-	for _, n := range l.Slice() {
-		if !candiscard(n) {
-			return false
-		}
-	}
-	return true
-}
-
-func candiscard(n *Node) bool {
-	if n == nil {
-		return true
-	}
-
-	switch n.Op {
-	default:
-		return false
-
-		// Discardable as long as the subpieces are.
-	case ONAME,
-		ONONAME,
-		OTYPE,
-		OPACK,
-		OLITERAL,
-		OADD,
-		OSUB,
-		OOR,
-		OXOR,
-		OADDSTR,
-		OADDR,
-		OANDAND,
-		OBYTES2STR,
-		ORUNES2STR,
-		OSTR2BYTES,
-		OSTR2RUNES,
-		OCAP,
-		OCOMPLIT,
-		OMAPLIT,
-		OSTRUCTLIT,
-		OARRAYLIT,
-		OSLICELIT,
-		OPTRLIT,
-		OCONV,
-		OCONVIFACE,
-		OCONVNOP,
-		ODOT,
-		OEQ,
-		ONE,
-		OLT,
-		OLE,
-		OGT,
-		OGE,
-		OKEY,
-		OSTRUCTKEY,
-		OLEN,
-		OMUL,
-		OLSH,
-		ORSH,
-		OAND,
-		OANDNOT,
-		ONEW,
-		ONOT,
-		OBITNOT,
-		OPLUS,
-		ONEG,
-		OOROR,
-		OPAREN,
-		ORUNESTR,
-		OREAL,
-		OIMAG,
-		OCOMPLEX:
-		break
-
-		// Discardable as long as we know it's not division by zero.
-	case ODIV, OMOD:
-		if Isconst(n.Right, CTINT) && n.Right.Val().U.(*Mpint).CmpInt64(0) != 0 {
-			break
-		}
-		if Isconst(n.Right, CTFLT) && n.Right.Val().U.(*Mpflt).CmpFloat64(0) != 0 {
-			break
-		}
-		return false
-
-		// Discardable as long as we know it won't fail because of a bad size.
-	case OMAKECHAN, OMAKEMAP:
-		if Isconst(n.Left, CTINT) && n.Left.Val().U.(*Mpint).CmpInt64(0) == 0 {
-			break
-		}
-		return false
-
-		// Difficult to tell what sizes are okay.
-	case OMAKESLICE:
-		return false
-
-	case OMAKESLICECOPY:
-		return false
-	}
-
-	if !candiscard(n.Left) || !candiscard(n.Right) || !candiscardlist(n.Ninit) || !candiscardlist(n.Nbody) || !candiscardlist(n.List) || !candiscardlist(n.Rlist) {
-		return false
-	}
-
-	return true
-}
-
-// 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
-
-// The result of wrapCall MUST be assigned back to n, e.g.
-// 	n.Left = wrapCall(n.Left, init)
-func wrapCall(n *Node, init *Nodes) *Node {
-	if n.Ninit.Len() != 0 {
-		walkstmtlist(n.Ninit.Slice())
-		init.AppendNodes(&n.Ninit)
-	}
-
-	isBuiltinCall := n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER
-
-	// Turn f(a, b, []T{c, d, e}...) back into f(a, b, c, d, e).
-	if !isBuiltinCall && n.IsDDD() {
-		last := n.List.Len() - 1
-		if va := n.List.Index(last); va.Op == OSLICELIT {
-			n.List.Set(append(n.List.Slice()[:last], va.List.Slice()...))
-			n.SetIsDDD(false)
-		}
-	}
-
-	// origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
-	origArgs := make([]*Node, n.List.Len())
-	t := nod(OTFUNC, nil, nil)
-	for i, arg := range n.List.Slice() {
-		s := lookupN("a", i)
-		if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.IsUintptr() && arg.Left.Type.IsUnsafePtr() {
-			origArgs[i] = arg
-			arg = arg.Left
-			n.List.SetIndex(i, arg)
-		}
-		t.List.Append(symfield(s, arg.Type))
-	}
-
-	wrapCall_prgen++
-	sym := lookupN("wrap·", wrapCall_prgen)
-	fn := dclfunc(sym, t)
-
-	args := paramNnames(t.Type)
-	for i, origArg := range origArgs {
-		if origArg == nil {
-			continue
-		}
-		arg := nod(origArg.Op, args[i], nil)
-		arg.Type = origArg.Type
-		args[i] = arg
-	}
-	call := nod(n.Op, nil, nil)
-	if !isBuiltinCall {
-		call.Op = OCALL
-		call.Left = n.Left
-		call.SetIsDDD(n.IsDDD())
-	}
-	call.List.Set(args)
-	fn.Nbody.Set1(call)
-
-	funcbody()
-
-	fn = typecheck(fn, ctxStmt)
-	typecheckslice(fn.Nbody.Slice(), ctxStmt)
-	xtop = append(xtop, fn)
-
-	call = nod(OCALL, nil, nil)
-	call.Left = fn.Func.Nname
-	call.List.Set(n.List.Slice())
-	call = typecheck(call, ctxStmt)
-	call = walkexpr(call, init)
-	return call
-}
-
-// 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 *Node, types_ ...*types.Type) *Node {
-	n := old.copy()
-
-	for _, t := range types_ {
-		dowidth(t)
-	}
-	n.Type = types.SubstAny(n.Type, &types_)
-	if len(types_) > 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 0000000..9f9bb87
--- /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 ed5eea0..0c7bd54 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) 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 0000000..d986ac3
--- /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 a4084a7..13b9bd4 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 0000000..eaa4d5b
--- /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 0000000..7da9b24
--- /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 90%
rename from src/cmd/compile/internal/gc/dump.go
rename to src/cmd/compile/internal/ir/dump.go
index 29eb1c1..fc995ce 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 @@
 // 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 @@
 		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 @@
 		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 @@
 					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 0000000..b32ed71
--- /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 0000000..1a05079
--- /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, "<nil>")
+		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 = <N>" initializing statements, hope they're always
+	// preceded by the DCL which will be re-parsed and typechecked to reproduce
+	// the "v = <N>" 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, "<nil>")
+			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, "<unnamed Func>")
+
+	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, "<struct>")
+
+	case OTINTER:
+		fmt.Fprint(s, "<inter>")
+
+	case OTFUNC:
+		fmt.Fprint(s, "<func>")
+
+	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, ".<nil>")
+			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, "<node %v>", 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, " <nil>")
+		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 0000000..0a9db92
--- /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 "<nil>"
+	}
+	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 "<nil>"
+	}
+	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 0000000..82224ca
--- /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 0000000..a7ff4ac
--- /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 0000000..326f491
--- /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 0000000..fa06396
--- /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 0000000..ffa7daf
--- /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 0000000..fe436867
--- /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 0000000..15c60ba
--- /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 0000000..3896e2b
--- /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 5c7935a..83c6074 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 @@
 // 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 @@
 // 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 @@
 	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
-					}
-				}
-			}
-		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
-				}
-			}
-		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 OCLOSURE:
-			if m := v.visit(n.Func.Closure); m < min {
+	do := func(defn Node) {
+		if defn != nil {
+			if m := v.visit(defn.(*Func)); m < min {
 				min = m
 			}
 		}
-		return true
+	}
+
+	Visit(n, func(n Node) {
+		switch n.Op() {
+		case ONAME:
+			if n := n.(*Name); n.Class == PFUNC {
+				do(n.Defn)
+			}
+		case ODOTMETH, OCALLPART, OMETHEXPR:
+			if fn := MethodExprName(n); fn != nil {
+				do(fn.Defn)
+			}
+		case OCLOSURE:
+			n := n.(*ClosureExpr)
+			do(n.Func)
+		}
 	})
 
-	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 ce4a216..d8c1518 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 @@
 		_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 0000000..c304867
--- /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 0000000..61727fb
--- /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 0000000..a903ea8
--- /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 0000000..ff45f31
--- /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 0000000..c1b3d4e
--- /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 0000000..3431f54
--- /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 a48173e..53ae797 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 @@
 // 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 @@
 	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 @@
 			// 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 @@
 // 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 @@
 	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 @@
 	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 @@
 	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 @@
 		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 @@
 	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 @@
 	// 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 @@
 		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 @@
 	// 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 @@
 // 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 @@
 // 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 @@
 }
 
 // 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 @@
 
 // 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 @@
 	// 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 @@
 				// 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 @@
 
 	{
 		// Reserve an entry for function entry.
-		live := bvalloc(nvars)
+		live := bitvec.New(nvars)
 		lv.livevars = append(lv.livevars, live)
 	}
 
@@ -845,7 +746,7 @@
 				continue
 			}
 
-			live := bvalloc(nvars)
+			live := bitvec.New(nvars)
 			lv.livevars = append(lv.livevars, live)
 		}
 
@@ -878,7 +779,7 @@
 
 		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 @@
 				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 @@
 	}
 
 	// 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 @@
 // 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 @@
 	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 @@
 	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 @@
 		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 @@
 		}
 	}
 
-	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 @@
 		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 @@
 	}
 	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 @@
 // 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 @@
 
 		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 @@
 		}
 
 		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 @@
 				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 @@
 				fmt.Printf("\n")
 			}
 
-			if pcdata.isUnsafePoint {
+			if pcdata.IsUnsafePoint {
 				fmt.Printf("\tunsafe-point\n")
 			}
 		}
@@ -1158,15 +1059,15 @@
 // 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 @@
 	// 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 @@
 	// 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 @@
 
 		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 @@
 	// 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 @@
 	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 @@
 		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 @@
 // 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 @@
 
 	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 e121c1a..7197617 100644
--- a/src/cmd/compile/internal/logopt/logopt_test.go
+++ b/src/cmd/compile/internal/logopt/logopt_test.go
@@ -132,7 +132,7 @@
 	// 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 @@
 			`{"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 be40c16..5991635 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 @@
 	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 5e86772..1a51252 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 @@
 		//	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 9d11c6b..f1cdbd3 100644
--- a/src/cmd/compile/internal/mips/ssa.go
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -7,9 +7,11 @@
 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 @@
 	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 @@
 		}
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 
 	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 @@
 	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 @@
 		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 90c381a..fc0a342 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 @@
 	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 04e7a66..37bb871 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 @@
 		//	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 2727c4d..14cf7af 100644
--- a/src/cmd/compile/internal/mips64/ssa.go
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -7,9 +7,11 @@
 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 @@
 	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 @@
 		}
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 		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 0000000..747c30e
--- /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 == "!<arch>\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 7cce371..cdca9e5 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/noder/lex.go
@@ -2,24 +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 noder
 
 import (
-	"cmd/compile/internal/syntax"
-	"cmd/internal/objabi"
-	"cmd/internal/src"
 	"fmt"
 	"strings"
+
+	"cmd/compile/internal/ir"
+	"cmd/compile/internal/syntax"
+	"cmd/internal/objabi"
 )
 
-// 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))
-}
-
 func isSpace(c rune) bool {
 	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
 }
@@ -28,78 +21,52 @@
 	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
+	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
 
-	// 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
+	typePragmas = ir.NotInHeap
 )
 
-const (
-	FuncPragmas = Nointerface |
-		Noescape |
-		Norace |
-		Nosplit |
-		Noinline |
-		NoCheckPtr |
-		CgoUnsafeArgs |
-		UintptrEscapes |
-		Systemstack |
-		Nowritebarrier |
-		Nowritebarrierrec |
-		Yeswritebarrierrec
-
-	TypePragmas = 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 @@
 		// 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 b2081a1..85a3f06 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 0000000..5b5b09c
--- /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 0000000..a6d3e2d
--- /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 0000000..dfbcf515
--- /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 0000000..b5ac4dd
--- /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 0000000..7cad262
--- /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 e2084fd..97d6962 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 @@
 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 @@
 	// 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 @@
 // 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 @@
 
 	// 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 @@
 // 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 @@
 	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) inspect(n *Node)     { inspect(n, d.visit) }
-func (d *initDeps) inspectList(l Nodes) { inspectList(l, d.visit) }
+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 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 @@
 
 	// 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 @@
 		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 @@
 // 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 @@
 
 // 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 c8ef567..c72d1aa 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 a5a772b..c76962c 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 @@
 	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 @@
 	// 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 3e20c44..c85e110 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 @@
 )
 
 // 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 @@
 	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 @@
 		// 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 @@
 		// 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 @@
 		// 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 @@
 		// 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 @@
 		// 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 @@
 		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 @@
 	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 @@
 		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 @@
 		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 @@
 		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 @@
 				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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 			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 @@
 			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 @@
 			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 @@
 			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 @@
 		// 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 @@
 		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 @@
 
 			// 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 @@
 	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 @@
 
 		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 0000000..fcd824f
--- /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/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
new file mode 100644
index 0000000..3ff14c8
--- /dev/null
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -0,0 +1,1836 @@
+// 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 reflectdata
+
+import (
+	"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 {
+	t, itype *types.Type
+	lsym     *obj.LSym // symbol of the itab itself
+
+	// symbols of each method in
+	// the itab, sorted by byte offset;
+	// filled in by CompileITabs
+	entries []*obj.LSym
+}
+
+type ptabEntry struct {
+	s *types.Sym
+	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
+	signatset   = make(map[*types.Type]struct{})
+	signatslice []*types.Type
+
+	itabs []itabEntry
+	ptabs []*ir.Name
+)
+
+type typeSig struct {
+	name  *types.Sym
+	isym  *obj.LSym
+	tsym  *obj.LSym
+	type_ *types.Type
+	mtype *types.Type
+}
+
+// Builds a type representing a Bucket structure for
+// the given map type. This type is not visible to users -
+// we include only enough information to generate a correct GC
+// program for it.
+// Make sure this stays in sync with runtime/map.go.
+const (
+	BUCKETSIZE  = 8
+	MAXKEYSIZE  = 128
+	MAXELEMSIZE = 128
+)
+
+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 {
+		return 0
+	}
+	return 4 + 2 + 2 + 4 + 4
+}
+
+func makefield(name string, t *types.Type) *types.Field {
+	sym := (*types.Pkg)(nil).Lookup(name)
+	return types.NewField(src.NoXPos, sym, t)
+}
+
+// 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
+	}
+
+	keytype := t.Key()
+	elemtype := t.Elem()
+	types.CalcSize(keytype)
+	types.CalcSize(elemtype)
+	if keytype.Width > MAXKEYSIZE {
+		keytype = types.NewPtr(keytype)
+	}
+	if elemtype.Width > MAXELEMSIZE {
+		elemtype = types.NewPtr(elemtype)
+	}
+
+	field := make([]*types.Field, 0, 5)
+
+	// The first field is: uint8 topbits[BUCKETSIZE].
+	arr := types.NewArray(types.Types[types.TUINT8], BUCKETSIZE)
+	field = append(field, makefield("topbits", arr))
+
+	arr = types.NewArray(keytype, BUCKETSIZE)
+	arr.SetNoalg(true)
+	keys := makefield("keys", arr)
+	field = append(field, keys)
+
+	arr = types.NewArray(elemtype, BUCKETSIZE)
+	arr.SetNoalg(true)
+	elems := makefield("elems", arr)
+	field = append(field, elems)
+
+	// If keys and elems have no pointers, the map implementation
+	// can keep a list of overflow pointers on the side so that
+	// buckets can be marked as having no pointers.
+	// 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.Types[types.TUNSAFEPTR]
+	if !elemtype.HasPointers() && !keytype.HasPointers() {
+		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)
+	types.CalcSize(bucket)
+
+	// Check invariants that map code depends on.
+	if !types.IsComparable(t.Key()) {
+		base.Fatalf("unsupported map key type for %v", t)
+	}
+	if BUCKETSIZE < 8 {
+		base.Fatalf("bucket size too small for proper alignment")
+	}
+	if keytype.Align > BUCKETSIZE {
+		base.Fatalf("key align too big for %v", t)
+	}
+	if elemtype.Align > BUCKETSIZE {
+		base.Fatalf("elem align too big for %v", t)
+	}
+	if keytype.Width > MAXKEYSIZE {
+		base.Fatalf("key size to large for %v", t)
+	}
+	if elemtype.Width > MAXELEMSIZE {
+		base.Fatalf("elem size to large for %v", t)
+	}
+	if t.Key().Width > MAXKEYSIZE && !keytype.IsPtr() {
+		base.Fatalf("key indirect incorrect for %v", t)
+	}
+	if t.Elem().Width > MAXELEMSIZE && !elemtype.IsPtr() {
+		base.Fatalf("elem indirect incorrect for %v", t)
+	}
+	if keytype.Width%int64(keytype.Align) != 0 {
+		base.Fatalf("key size not a multiple of key align for %v", t)
+	}
+	if elemtype.Width%int64(elemtype.Align) != 0 {
+		base.Fatalf("elem size not a multiple of elem align for %v", t)
+	}
+	if bucket.Align%keytype.Align != 0 {
+		base.Fatalf("bucket align not multiple of key align %v", t)
+	}
+	if bucket.Align%elemtype.Align != 0 {
+		base.Fatalf("bucket align not multiple of elem align %v", t)
+	}
+	if keys.Offset%int64(keytype.Align) != 0 {
+		base.Fatalf("bad alignment of keys in bmap for %v", t)
+	}
+	if elems.Offset%int64(elemtype.Align) != 0 {
+		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(types.PtrSize) {
+		base.Fatalf("bad offset of overflow in bmap for %v", t)
+	}
+
+	t.MapType().Bucket = bucket
+
+	bucket.StructType().Map = t
+	return bucket
+}
+
+// MapType builds a type representing a Hmap structure for the given map type.
+// Make sure this stays in sync with runtime/map.go.
+func MapType(t *types.Type) *types.Type {
+	if t.MapType().Hmap != nil {
+		return t.MapType().Hmap
+	}
+
+	bmap := MapBucketType(t)
+
+	// build a struct:
+	// type hmap struct {
+	//    count      int
+	//    flags      uint8
+	//    B          uint8
+	//    noverflow  uint16
+	//    hash0      uint32
+	//    buckets    *bmap
+	//    oldbuckets *bmap
+	//    nevacuate  uintptr
+	//    extra      unsafe.Pointer // *mapextra
+	// }
+	// must match runtime/map.go:hmap.
+	fields := []*types.Field{
+		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[types.TUINTPTR]),
+		makefield("extra", types.Types[types.TUNSAFEPTR]),
+	}
+
+	hmap := types.NewStruct(types.NoPkg, fields)
+	hmap.SetNoalg(true)
+	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*types.PtrSize); hmap.Width != size {
+		base.Fatalf("hmap size not correct: got %d, want %d", hmap.Width, size)
+	}
+
+	t.MapType().Hmap = hmap
+	hmap.StructType().Map = t
+	return hmap
+}
+
+// MapIterType builds a type representing an Hiter structure for the given map type.
+// Make sure this stays in sync with runtime/map.go.
+func MapIterType(t *types.Type) *types.Type {
+	if t.MapType().Hiter != nil {
+		return t.MapType().Hiter
+	}
+
+	hmap := MapType(t)
+	bmap := MapBucketType(t)
+
+	// build a struct:
+	// type hiter struct {
+	//    key         *Key
+	//    elem        *Elem
+	//    t           unsafe.Pointer // *MapType
+	//    h           *hmap
+	//    buckets     *bmap
+	//    bptr        *bmap
+	//    overflow    unsafe.Pointer // *[]*bmap
+	//    oldoverflow unsafe.Pointer // *[]*bmap
+	//    startBucket uintptr
+	//    offset      uint8
+	//    wrapped     bool
+	//    B           uint8
+	//    i           uint8
+	//    bucket      uintptr
+	//    checkBucket uintptr
+	// }
+	// must match runtime/map.go:hiter.
+	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[types.TUNSAFEPTR]),
+		makefield("h", types.NewPtr(hmap)),
+		makefield("buckets", types.NewPtr(bmap)),
+		makefield("bptr", types.NewPtr(bmap)),
+		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.NewStruct(types.NoPkg, fields)
+	hiter.SetNoalg(true)
+	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
+}
+
+// methods returns the methods of the non-interface type t, sorted by name.
+// Generates stub functions as needed.
+func methods(t *types.Type) []*typeSig {
+	// method type
+	mt := types.ReceiverBaseType(t)
+
+	if mt == nil {
+		return nil
+	}
+	typecheck.CalcMethods(mt)
+
+	// type stored in interface word
+	it := t
+
+	if !types.IsDirectIface(it) {
+		it = types.NewPtr(t)
+	}
+
+	// make list of methods for t,
+	// generating code if necessary.
+	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() {
+			base.Fatalf("non-method on %v method %v %v", mt, f.Sym, f)
+		}
+		if f.Type.Recv() == nil {
+			base.Fatalf("receiver with no type on %v method %v %v", mt, f.Sym, f)
+		}
+		if f.Nointerface() {
+			continue
+		}
+
+		// 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 !types.IsMethodApplicable(t, f) {
+			continue
+		}
+
+		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)
+	}
+
+	return ms
+}
+
+// imethods returns the methods of the interface type t, sorted by name.
+func imethods(t *types.Type) []*typeSig {
+	var methods []*typeSig
+	for _, f := range t.Fields().Slice() {
+		if f.Type.Kind() != types.TFUNC || f.Sym == nil {
+			continue
+		}
+		if f.Sym.IsBlank() {
+			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) {
+				base.Fatalf("sigcmp vs sortinter %v %v", last.name, f.Sym)
+			}
+		}
+
+		sig := &typeSig{
+			name:  f.Sym,
+			mtype: f.Type,
+			type_: typecheck.NewMethodType(f.Type, nil),
+		}
+		methods = append(methods, sig)
+
+		// NOTE(rsc): Perhaps an oversight that
+		// IfaceType.Method is not in the reflect data.
+		// Generate the method body, so that compiled
+		// code can refer to it.
+		methodWrapper(t, f)
+	}
+
+	return methods
+}
+
+func dimportpath(p *types.Pkg) {
+	if p.Pathsym != nil {
+		return
+	}
+
+	// If we are compiling the runtime package, there are two runtime packages around
+	// -- localpkg and Pkgs.Runtime. We don't want to produce import path symbols for
+	// both of them, so just produce one for localpkg.
+	if base.Ctxt.Pkgpath == "runtime" && p == ir.Pkgs.Runtime {
+		return
+	}
+
+	str := p.Path
+	if p == types.LocalPkg {
+		// Note: myimportpath != "", or else dgopkgpath won't call dimportpath.
+		str = base.Ctxt.Pkgpath
+	}
+
+	s := base.Ctxt.Lookup("type..importpath." + p.Prefix + ".")
+	ot := dnameData(s, 0, str, "", nil, false)
+	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 objw.Uintptr(s, ot, 0)
+	}
+
+	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 := base.Ctxt.Lookup(`type..importpath."".`)
+		return objw.SymPtr(s, ot, ns, 0)
+	}
+
+	dimportpath(pkg)
+	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 objw.Uint32(s, ot, 0)
+	}
+	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 := base.Ctxt.Lookup(`type..importpath."".`)
+		return objw.SymPtrOff(s, ot, ns)
+	}
+
+	dimportpath(pkg)
+	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 {
+		base.Fatalf("package mismatch for %v", ft.Sym)
+	}
+	nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name))
+	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 {
+		base.Fatalf("name too long: %s", name)
+	}
+	if len(tag) > 1<<16-1 {
+		base.Fatalf("tag too long: %s", tag)
+	}
+
+	// Encode name and tag. See reflect/type.go for details.
+	var bits byte
+	l := 1 + 2 + len(name)
+	if exported {
+		bits |= 1 << 0
+	}
+	if len(tag) > 0 {
+		l += 2 + len(tag)
+		bits |= 1 << 1
+	}
+	if pkg != nil {
+		bits |= 1 << 2
+	}
+	b := make([]byte, l)
+	b[0] = bits
+	b[1] = uint8(len(name) >> 8)
+	b[2] = uint8(len(name))
+	copy(b[3:], name)
+	if len(tag) > 0 {
+		tb := b[3+len(name):]
+		tb[0] = uint8(len(tag) >> 8)
+		tb[1] = uint8(len(tag))
+		copy(tb[2:], tag)
+	}
+
+	ot = int(s.WriteBytes(base.Ctxt, int64(ot), b))
+
+	if pkg != nil {
+		ot = dgopkgpathOff(s, ot, pkg)
+	}
+
+	return ot
+}
+
+var dnameCount int
+
+// dname creates a reflect.name for a struct field or method.
+func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym {
+	// Write out data as "type.." to signal two things to the
+	// linker, first that when dynamically linking, the symbol
+	// should be moved to a relro section, and second that the
+	// contents should not be decoded as a type.
+	sname := "type..namedata."
+	if pkg == nil {
+		// In the common case, share data with other packages.
+		if name == "" {
+			if exported {
+				sname += "-noname-exported." + tag
+			} else {
+				sname += "-noname-unexported." + tag
+			}
+		} else {
+			if exported {
+				sname += name + "." + tag
+			} else {
+				sname += name + "-" + tag
+			}
+		}
+	} else {
+		sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount)
+		dnameCount++
+	}
+	s := base.Ctxt.Lookup(sname)
+	if len(s.P) > 0 {
+		return s
+	}
+	ot := dnameData(s, 0, name, tag, pkg, exported)
+	objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA)
+	s.Set(obj.AttrContentAddressable, true)
+	return s
+}
+
+// dextratype dumps the fields of a runtime.uncommontype.
+// dataAdd is the offset in bytes after the header where the
+// 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 {
+		return ot
+	}
+	noff := int(types.Rnd(int64(ot), int64(types.PtrSize)))
+	if noff != ot {
+		base.Fatalf("unexpected alignment in dextratype for %v", t)
+	}
+
+	for _, a := range m {
+		writeType(a.type_)
+	}
+
+	ot = dgopkgpathOff(lsym, ot, typePkg(t))
+
+	dataAdd += uncommonSize(t)
+	mcount := len(m)
+	if mcount != int(uint16(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)) {
+		base.Fatalf("methods are too far away on %v: %d", t, dataAdd)
+	}
+
+	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()
+	if tsym == nil {
+		switch t.Kind() {
+		case types.TARRAY, types.TSLICE, types.TPTR, types.TCHAN:
+			if t.Elem() != nil {
+				tsym = t.Elem().Sym()
+			}
+		}
+	}
+	if tsym != nil && t != types.Types[t.Kind()] && t != types.ErrorType {
+		return tsym.Pkg
+	}
+	return nil
+}
+
+// dextratypeData dumps the backing array for the []method field of
+// runtime.uncommontype.
+func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int {
+	for _, a := range methods(t) {
+		// ../../../../runtime/type.go:/method
+		exported := types.IsExported(a.name.Name)
+		var pkg *types.Pkg
+		if !exported && a.name.Pkg != typePkg(t) {
+			pkg = a.name.Pkg
+		}
+		nsym := dname(a.name.Name, "", pkg, exported)
+
+		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 {
+	objw.Uint32(s, ot, 0)
+	r := obj.Addrel(s)
+	r.Off = int32(ot)
+	r.Siz = 4
+	r.Sym = x
+	r.Type = objabi.R_METHODOFF
+	return ot + 4
+}
+
+var kinds = []int{
+	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.
+//
+// tflag values must be kept in sync with copies in:
+//	cmd/compile/internal/gc/reflect.go
+//	cmd/link/internal/ld/decodesym.go
+//	reflect/type.go
+//	runtime/type.go
+const (
+	tflagUncommon      = 1 << 0
+	tflagExtraStar     = 1 << 1
+	tflagNamed         = 1 << 2
+	tflagRegularMemory = 1 << 3
+)
+
+var (
+	memhashvarlen  *obj.LSym
+	memequalvarlen *obj.LSym
+)
+
+// dcommontype dumps the contents of a reflect.rtype (runtime._type).
+func dcommontype(lsym *obj.LSym, t *types.Type) int {
+	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 {
+			sptrWeak = false
+		}
+		sptr = writeType(tptr)
+	}
+
+	gcsym, useGCProg, ptrdata := dgcsym(t)
+
+	// ../../../../reflect/type.go:/^type.rtype
+	// actual type structure
+	//	type rtype struct {
+	//		size          uintptr
+	//		ptrdata       uintptr
+	//		hash          uint32
+	//		tflag         tflag
+	//		align         uint8
+	//		fieldAlign    uint8
+	//		kind          uint8
+	//		equal         func(unsafe.Pointer, unsafe.Pointer) bool
+	//		gcdata        *byte
+	//		str           nameOff
+	//		ptrToThis     typeOff
+	//	}
+	ot := 0
+	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 != "" {
+		tflag |= tflagNamed
+	}
+	if isRegularMemory(t) {
+		tflag |= tflagRegularMemory
+	}
+
+	exported := false
+	p := t.LongString()
+	// If we're writing out type T,
+	// we are very likely to write out type *T as well.
+	// Use the string "*T"[1:] for "T", so that the two
+	// share storage. This is a cheap way to reduce the
+	// amount of space taken up by reflect strings.
+	if !strings.HasPrefix(p, "*") {
+		p = "*" + p
+		tflag |= tflagExtraStar
+		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)
+		}
+	}
+
+	ot = objw.Uint8(lsym, ot, tflag)
+
+	// runtime (and common sense) expects alignment to be a power of two.
+	i := int(t.Align)
+
+	if i == 0 {
+		i = 1
+	}
+	if i&(i-1) != 0 {
+		base.Fatalf("invalid alignment %d for %v", t.Align, t)
+	}
+	ot = objw.Uint8(lsym, ot, t.Align) // align
+	ot = objw.Uint8(lsym, ot, t.Align) // fieldAlign
+
+	i = kinds[t.Kind()]
+	if types.IsDirectIface(t) {
+		i |= objabi.KindDirectIface
+	}
+	if useGCProg {
+		i |= objabi.KindGCProg
+	}
+	ot = objw.Uint8(lsym, ot, uint8(i)) // kind
+	if eqfunc != nil {
+		ot = objw.SymPtr(lsym, ot, eqfunc, 0) // equality function
+	} else {
+		ot = objw.Uintptr(lsym, ot, 0) // type we can't do == with
+	}
+	ot = objw.SymPtr(lsym, ot, gcsym, 0) // gcdata
+
+	nsym := dname(p, "", nil, exported)
+	ot = objw.SymPtrOff(lsym, ot, nsym) // str
+	// ptrToThis
+	if sptr == nil {
+		ot = objw.Uint32(lsym, ot, 0)
+	} else if sptrWeak {
+		ot = objw.SymPtrWeakOff(lsym, ot, sptr)
+	} else {
+		ot = objw.SymPtrOff(lsym, ot, sptr)
+	}
+
+	return ot
+}
+
+// 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) *obj.LSym {
+	return base.PkgLinksym("go.track", t.ShortString()+"."+f.Sym.Name, obj.ABI0)
+}
+
+func TypeSymPrefix(prefix string, t *types.Type) *types.Sym {
+	p := prefix + "." + t.ShortString()
+	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()
+	NeedRuntimeType(t)
+	signatmu.Unlock()
+
+	//print("algsym: %s -> %+S\n", p, s);
+
+	return s
+}
+
+func TypeSym(t *types.Type) *types.Sym {
+	if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() {
+		base.Fatalf("TypeSym %v", t)
+	}
+	if t.Kind() == types.TFUNC && t.Recv() != nil {
+		base.Fatalf("misuse of method type: %v", t)
+	}
+	s := types.TypeSym(t)
+	signatmu.Lock()
+	NeedRuntimeType(t)
+	signatmu.Unlock()
+	return s
+}
+
+func TypeLinksymPrefix(prefix string, t *types.Type) *obj.LSym {
+	return TypeSymPrefix(prefix, t).Linksym()
+}
+
+func TypeLinksymLookup(name string) *obj.LSym {
+	return types.TypeSymLookup(name).Linksym()
+}
+
+func TypeLinksym(t *types.Type) *obj.LSym {
+	return TypeSym(t).Linksym()
+}
+
+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)
+}
+
+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.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 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 types.TARRAY:
+		return needkeyupdate(t.Elem())
+
+	case types.TSTRUCT:
+		for _, t1 := range t.Fields().Slice() {
+			if needkeyupdate(t1.Type) {
+				return true
+			}
+		}
+		return false
+
+	default:
+		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.Kind() {
+	case types.TINTER:
+		return true
+
+	case types.TARRAY:
+		return hashMightPanic(t.Elem())
+
+	case types.TSTRUCT:
+		for _, t1 := range t.Fields().Slice() {
+			if hashMightPanic(t1.Type) {
+				return true
+			}
+		}
+		return false
+
+	default:
+		return false
+	}
+}
+
+// formalType replaces byte and rune aliases with real types.
+// 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.Kind()]
+	}
+	return t
+}
+
+func writeType(t *types.Type) *obj.LSym {
+	t = formalType(t)
+	if t.IsUntyped() {
+		base.Fatalf("writeType %v", t)
+	}
+
+	s := types.TypeSym(t)
+	lsym := s.Linksym()
+	if s.Siggen() {
+		return lsym
+	}
+	s.SetSiggen(true)
+
+	// special case (look for runtime below):
+	// when compiling package runtime,
+	// emit the type structures for int, float, etc.
+	tbase := t
+
+	if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
+		tbase = t.Elem()
+	}
+	dupok := 0
+	if tbase.Sym() == nil {
+		dupok = obj.DUPOK
+	}
+
+	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 != 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.Kind() == types.TFORW {
+			return lsym
+		}
+	}
+
+	ot := 0
+	switch t.Kind() {
+	default:
+		ot = dcommontype(lsym, t)
+		ot = dextratype(lsym, ot, t, 0)
+
+	case types.TARRAY:
+		// ../../../../runtime/type.go:/arrayType
+		s1 := writeType(t.Elem())
+		t2 := types.NewSlice(t.Elem())
+		s2 := writeType(t2)
+		ot = dcommontype(lsym, t)
+		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 types.TSLICE:
+		// ../../../../runtime/type.go:/sliceType
+		s1 := writeType(t.Elem())
+		ot = dcommontype(lsym, t)
+		ot = objw.SymPtr(lsym, ot, s1, 0)
+		ot = dextratype(lsym, ot, t, 0)
+
+	case types.TCHAN:
+		// ../../../../runtime/type.go:/chanType
+		s1 := writeType(t.Elem())
+		ot = dcommontype(lsym, t)
+		ot = objw.SymPtr(lsym, ot, s1, 0)
+		ot = objw.Uintptr(lsym, ot, uint64(t.ChanDir()))
+		ot = dextratype(lsym, ot, t, 0)
+
+	case types.TFUNC:
+		for _, t1 := range t.Recvs().Fields().Slice() {
+			writeType(t1.Type)
+		}
+		isddd := false
+		for _, t1 := range t.Params().Fields().Slice() {
+			isddd = t1.IsDDD()
+			writeType(t1.Type)
+		}
+		for _, t1 := range t.Results().Fields().Slice() {
+			writeType(t1.Type)
+		}
+
+		ot = dcommontype(lsym, t)
+		inCount := t.NumRecvs() + t.NumParams()
+		outCount := t.NumResults()
+		if isddd {
+			outCount |= 1 << 15
+		}
+		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()) * types.PtrSize
+		ot = dextratype(lsym, ot, t, dataAdd)
+
+		// Array of rtype pointers follows funcType.
+		for _, t1 := range t.Recvs().Fields().Slice() {
+			ot = objw.SymPtr(lsym, ot, writeType(t1.Type), 0)
+		}
+		for _, t1 := range t.Params().Fields().Slice() {
+			ot = objw.SymPtr(lsym, ot, writeType(t1.Type), 0)
+		}
+		for _, t1 := range t.Results().Fields().Slice() {
+			ot = objw.SymPtr(lsym, ot, writeType(t1.Type), 0)
+		}
+
+	case types.TINTER:
+		m := imethods(t)
+		n := len(m)
+		for _, a := range m {
+			writeType(a.type_)
+		}
+
+		// ../../../../runtime/type.go:/interfaceType
+		ot = dcommontype(lsym, t)
+
+		var tpkg *types.Pkg
+		if t.Sym() != nil && t != types.Types[t.Kind()] && t != types.ErrorType {
+			tpkg = t.Sym().Pkg
+		}
+		ot = dgopkgpath(lsym, ot, tpkg)
+
+		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)
+
+		for _, a := range m {
+			// ../../../../runtime/type.go:/imethod
+			exported := types.IsExported(a.name.Name)
+			var pkg *types.Pkg
+			if !exported && a.name.Pkg != tpkg {
+				pkg = a.name.Pkg
+			}
+			nsym := dname(a.name.Name, "", pkg, exported)
+
+			ot = objw.SymPtrOff(lsym, ot, nsym)
+			ot = objw.SymPtrOff(lsym, ot, writeType(a.type_))
+		}
+
+	// ../../../../runtime/type.go:/mapType
+	case types.TMAP:
+		s1 := writeType(t.Key())
+		s2 := writeType(t.Elem())
+		s3 := writeType(MapBucketType(t))
+		hasher := genhash(t.Key())
+
+		ot = dcommontype(lsym, t)
+		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 = objw.Uint8(lsym, ot, uint8(types.PtrSize))
+			flags |= 1 // indirect key
+		} else {
+			ot = objw.Uint8(lsym, ot, uint8(t.Key().Width))
+		}
+
+		if t.Elem().Width > MAXELEMSIZE {
+			ot = objw.Uint8(lsym, ot, uint8(types.PtrSize))
+			flags |= 2 // indirect value
+		} else {
+			ot = objw.Uint8(lsym, ot, uint8(t.Elem().Width))
+		}
+		ot = objw.Uint16(lsym, ot, uint16(MapBucketType(t).Width))
+		if types.IsReflexive(t.Key()) {
+			flags |= 4 // reflexive key
+		}
+		if needkeyupdate(t.Key()) {
+			flags |= 8 // need key update
+		}
+		if hashMightPanic(t.Key()) {
+			flags |= 16 // hash might panic
+		}
+		ot = objw.Uint32(lsym, ot, flags)
+		ot = dextratype(lsym, ot, t, 0)
+
+	case types.TPTR:
+		if t.Elem().Kind() == types.TANY {
+			// ../../../../runtime/type.go:/UnsafePointerType
+			ot = dcommontype(lsym, t)
+			ot = dextratype(lsym, ot, t, 0)
+
+			break
+		}
+
+		// ../../../../runtime/type.go:/ptrType
+		s1 := writeType(t.Elem())
+
+		ot = dcommontype(lsym, t)
+		ot = objw.SymPtr(lsym, ot, s1, 0)
+		ot = dextratype(lsym, ot, t, 0)
+
+	// ../../../../runtime/type.go:/structType
+	// for security, only the exported fields.
+	case types.TSTRUCT:
+		fields := t.Fields().Slice()
+		for _, t1 := range fields {
+			writeType(t1.Type)
+		}
+
+		// All non-exported struct field names within a struct
+		// type must originate from a single package. By
+		// identifying and recording that package within the
+		// struct type descriptor, we can omit that
+		// information from the field descriptors.
+		var spkg *types.Pkg
+		for _, f := range fields {
+			if !types.IsExported(f.Sym.Name) {
+				spkg = f.Sym.Pkg
+				break
+			}
+		}
+
+		ot = dcommontype(lsym, t)
+		ot = dgopkgpath(lsym, ot, spkg)
+		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)
+
+		for _, f := range fields {
+			// ../../../../runtime/type.go:/structField
+			ot = dnameField(lsym, ot, spkg, f)
+			ot = objw.SymPtr(lsym, ot, writeType(f.Type), 0)
+			offsetAnon := uint64(f.Offset) << 1
+			if offsetAnon>>1 != uint64(f.Offset) {
+				base.Fatalf("%v: bad field offset for %s", t, f.Sym.Name)
+			}
+			if f.Embedded != 0 {
+				offsetAnon |= 1
+			}
+			ot = objw.Uintptr(lsym, ot, offsetAnon)
+		}
+	}
+
+	ot = dextratypeData(lsym, ot, t)
+	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 := 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.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 types.TypeHasNoAlg(t) {
+		keep = false
+	}
+	lsym.Set(obj.AttrMakeTypelink, keep)
+
+	return lsym
+}
+
+// InterfaceMethodOffset returns the offset of the i-th method in the interface
+// type descriptor, ityp.
+func InterfaceMethodOffset(ityp *types.Type, i int64) int64 {
+	// interface type descriptor layout is struct {
+	//   _type        // commonSize
+	//   pkgpath      // 1 word
+	//   []imethod    // 3 words (pointing to [...]imethod below)
+	//   uncommontype // uncommonSize
+	//   [...]imethod
+	// }
+	// The size of imethod is 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 CompileITabs() {
+	for i := range itabs {
+		tab := &itabs[i]
+		methods := genfun(tab.t, tab.itype)
+		if len(methods) == 0 {
+			continue
+		}
+		tab.entries = methods
+	}
+}
+
+// for the given concrete type and interface
+// type, return the (sorted) set of methods
+// on the concrete type that implement the interface
+func genfun(t, it *types.Type) []*obj.LSym {
+	if t == nil || it == nil {
+		return nil
+	}
+	sigs := imethods(it)
+	methods := methods(t)
+	out := make([]*obj.LSym, 0, len(sigs))
+	// TODO(mdempsky): Short circuit before calling methods(t)?
+	// See discussion on CL 105039.
+	if len(sigs) == 0 {
+		return nil
+	}
+
+	// both sigs and methods are sorted by name,
+	// 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)
+			sigs = sigs[1:]
+			if len(sigs) == 0 {
+				break
+			}
+		}
+	}
+
+	if len(sigs) != 0 {
+		base.Fatalf("incomplete itab")
+	}
+
+	return out
+}
+
+// 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 {
+	var syms []*obj.LSym
+	if it == nil {
+		return nil
+	}
+
+	for i := range itabs {
+		e := &itabs[i]
+		if e.lsym == it {
+			syms = e.entries
+			break
+		}
+	}
+	if syms == nil {
+		return nil
+	}
+
+	// keep this arithmetic in sync with *itab layout
+	methodnum := int((offset - 2*int64(types.PtrSize) - 8) / int64(types.PtrSize))
+	if methodnum >= len(syms) {
+		return nil
+	}
+	return syms[methodnum]
+}
+
+// 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 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: types.TypeSymName(t), regular: t.String()})
+			delete(signatset, t)
+		}
+		signatslice = signatslice[:0]
+		sort.Sort(typesByString(signats))
+		for _, ts := range signats {
+			t := ts.t
+			writeType(t)
+			if t.Sym() != nil {
+				writeType(types.NewPtr(t))
+			}
+		}
+	}
+}
+
+func WriteTabs() {
+	// process itabs
+	for _, i := range itabs {
+		// dump empty itab symbol into i.sym
+		// type itab struct {
+		//   inter  *interfacetype
+		//   _type  *_type
+		//   hash   uint32
+		//   _      [4]byte
+		//   fun    [1]uintptr // variable sized
+		// }
+		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 = objw.SymPtr(i.lsym, o, fn, 0) // method pointer for each method
+		}
+		// Nothing writes static itabs, so they are read only.
+		objw.Global(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
+		i.lsym.Set(obj.AttrContentAddressable, true)
+	}
+
+	// process ptabs
+	if types.LocalPkg.Name == "main" && len(ptabs) > 0 {
+		ot := 0
+		s := base.Ctxt.Lookup("go.plugin.tabs")
+		for _, p := range ptabs {
+			// Dump ptab symbol into go.pluginsym package.
+			//
+			// type ptab struct {
+			//	name nameOff
+			//	typ  typeOff // pointer to symbol
+			// }
+			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)
+		}
+		objw.Global(s, int32(ot), int16(obj.RODATA))
+
+		ot = 0
+		s = base.Ctxt.Lookup("go.plugin.exports")
+		for _, p := range ptabs {
+			ot = objw.SymPtr(s, ot, p.Linksym(), 0)
+		}
+		objw.Global(s, int32(ot), int16(obj.RODATA))
+	}
+}
+
+func WriteImportStrings() {
+	// generate import strings for imported packages
+	for _, p := range types.ImportedPkgList() {
+		dimportpath(p)
+	}
+}
+
+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 base.Ctxt.Pkgpath == "runtime" {
+		for i := types.Kind(1); i <= types.TBOOL; i++ {
+			writeType(types.NewPtr(types.Types[i]))
+		}
+		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.
+		writeType(types.NewPtr(types.ErrorType))
+
+		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(ir.Pkgs.Runtime)
+
+		if base.Flag.Race {
+			dimportpath(types.NewPkg("runtime/race", ""))
+		}
+		if base.Flag.MSan {
+			dimportpath(types.NewPkg("runtime/msan", ""))
+		}
+
+		dimportpath(types.NewPkg("main", ""))
+	}
+}
+
+type typeAndStr struct {
+	t       *types.Type
+	short   string
+	regular string
+}
+
+type typesByString []typeAndStr
+
+func (a typesByString) Len() int { return len(a) }
+func (a typesByString) Less(i, j int) bool {
+	if a[i].short != a[j].short {
+		return a[i].short < a[j].short
+	}
+	// When the only difference between the types is whether
+	// they refer to byte or uint8, such as **byte vs **uint8,
+	// the types' ShortStrings can be identical.
+	// To preserve deterministic sort ordering, sort these by String().
+	if a[i].regular != a[j].regular {
+		return a[i].regular < a[j].regular
+	}
+	// Identical anonymous interfaces defined in different locations
+	// 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.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
+}
+func (a typesByString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// maxPtrmaskBytes is the maximum length of a GC ptrmask bitmap,
+// which holds 1-bit entries describing where pointers are in a given type.
+// Above this length, the GC information is recorded as a GC program,
+// which can express repetition compactly. In either form, the
+// information is used by the runtime to initialize the heap bitmap,
+// and for large types (like 128 or more words), they are roughly the
+// same speed. GC programs are never much larger and often more
+// compact. (If large arrays are involved, they can be arbitrarily
+// more compact.)
+//
+// The cutoff must be large enough that any allocation large enough to
+// use a GC program is large enough that it does not share heap bitmap
+// bytes with any other objects, allowing the GC program execution to
+// assume an aligned start and not use atomic operations. In the current
+// runtime, this means all malloc size classes larger than the cutoff must
+// be multiples of four words. On 32-bit systems that's 16 bytes, and
+// all size classes >= 16 bytes are 16-byte aligned, so no real constraint.
+// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed
+// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated
+// is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
+// must be >= 4.
+//
+// We used to use 16 because the GC programs do have some constant overhead
+// to get started, and processing 128 pointers seems to be enough to
+// amortize that overhead well.
+//
+// To make sure that the runtime's chansend can call typeBitsBulkBarrier,
+// we raised the limit to 2048, so that even 32-bit systems are guaranteed to
+// use bitmaps for objects up to 64 kB in size.
+//
+// Also known to reflect/type.go.
+//
+const maxPtrmaskBytes = 2048
+
+// dgcsym emits and returns a data symbol containing GC information for type t,
+// 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 = types.PtrDataSize(t)
+	if ptrdata/int64(types.PtrSize) <= maxPtrmaskBytes*8 {
+		lsym = dgcptrmask(t)
+		return
+	}
+
+	useGCProg = true
+	lsym, ptrdata = dgcprog(t)
+	return
+}
+
+// dgcptrmask emits and returns the symbol containing a pointer mask for type t.
+func dgcptrmask(t *types.Type) *obj.LSym {
+	ptrmask := make([]byte, (types.PtrDataSize(t)/int64(types.PtrSize)+7)/8)
+	fillptrmask(t, ptrmask)
+	p := fmt.Sprintf("gcbits.%x", ptrmask)
+
+	sym := ir.Pkgs.Runtime.Lookup(p)
+	lsym := sym.Linksym()
+	if !sym.Uniq() {
+		sym.SetUniq(true)
+		for i, x := range ptrmask {
+			objw.Uint8(lsym, i, x)
+		}
+		objw.Global(lsym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL)
+		lsym.Set(obj.AttrContentAddressable, true)
+	}
+	return lsym
+}
+
+// fillptrmask fills in ptrmask with 1s corresponding to the
+// word offsets in t that hold pointers.
+// ptrmask is assumed to fit at least typeptrdata(t)/Widthptr bits.
+func fillptrmask(t *types.Type, ptrmask []byte) {
+	for i := range ptrmask {
+		ptrmask[i] = 0
+	}
+	if !t.HasPointers() {
+		return
+	}
+
+	vec := bitvec.New(8 * int32(len(ptrmask)))
+	typebits.Set(t, 0, vec)
+
+	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)
+		}
+	}
+}
+
+// dgcprog emits and returns the symbol containing a GC program for type t
+// along with the size of the data described by the program (in the range [typeptrdata(t), t.Width]).
+// 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) {
+	types.CalcSize(t)
+	if t.Width == types.BADWIDTH {
+		base.Fatalf("dgcprog: %v badwidth", t)
+	}
+	lsym := TypeLinksymPrefix(".gcprog", t)
+	var p gcProg
+	p.init(lsym)
+	p.emit(t, 0)
+	offset := p.w.BitIndex() * int64(types.PtrSize)
+	p.end()
+	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 {
+	lsym   *obj.LSym
+	symoff int
+	w      gcprog.Writer
+}
+
+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 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 = objw.Uint8(p.lsym, p.symoff, x)
+}
+
+func (p *gcProg) end() {
+	p.w.End()
+	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) {
+	types.CalcSize(t)
+	if !t.HasPointers() {
+		return
+	}
+	if t.Width == int64(types.PtrSize) {
+		p.w.Ptr(offset / int64(types.PtrSize))
+		return
+	}
+	switch t.Kind() {
+	default:
+		base.Fatalf("gcProg.emit: unexpected type %v", t)
+
+	case types.TSTRING:
+		p.w.Ptr(offset / int64(types.PtrSize))
+
+	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 types.TSLICE:
+		p.w.Ptr(offset / int64(types.PtrSize))
+
+	case types.TARRAY:
+		if t.NumElem() == 0 {
+			// should have been handled by haspointers check above
+			base.Fatalf("gcProg.emit: empty array")
+		}
+
+		// Flatten array-of-array-of-array to just a big array by multiplying counts.
+		count := t.NumElem()
+		elem := t.Elem()
+		for elem.IsArray() {
+			count *= elem.NumElem()
+			elem = elem.Elem()
+		}
+
+		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)
+			}
+			return
+		}
+		p.emit(elem, offset)
+		p.w.ZeroUntil((offset + elem.Width) / int64(types.PtrSize))
+		p.w.Repeat(elem.Width/int64(types.PtrSize), count-1)
+
+	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
+// size bytes of zeros.
+func ZeroAddr(size int64) ir.Node {
+	if size >= 1<<31 {
+		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 4db0fac..338248a 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 f7c03fe..9df7394 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 @@
 	// 	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 d40bdf7..74bccf8 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 0beb5b4..70c29a4 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 @@
 		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 @@
 	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 @@
 		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 @@
 	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 @@
 
 // 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 @@
 	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 @@
 			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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 		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 cb68fd3..b004a2d 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 5a837d8..488a080 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 @@
 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 @@
 	// 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 @@
 			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 @@
 	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 8037357..d4c7a28 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -7,16 +7,17 @@
 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 @@
 //     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 @@
 //	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 @@
 	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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 		// 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 @@
 		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 @@
 		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 @@
 		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 @@
 
 		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 @@
 
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 
 		// 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 @@
 		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 @@
 		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 @@
 	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 519ac21..937c757 100644
--- a/src/cmd/compile/internal/ssa/block.go
+++ b/src/cmd/compile/internal/ssa/block.go
@@ -52,7 +52,7 @@
 	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 2dade7a..9e4aa6c 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -166,7 +166,7 @@
 					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 63994d1..c267274 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -431,7 +431,6 @@
 	{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 0fe0337..c29bc8f 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 @@
 
 	// 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 @@
 	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 @@
 		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 3b4f2be..f785274 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -275,7 +275,7 @@
 	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 9e76645..8052016 100644
--- a/src/cmd/compile/internal/ssa/cse_test.go
+++ b/src/cmd/compile/internal/ssa/cse_test.go
@@ -14,6 +14,8 @@
 	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 0664013..31d3f62 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 @@
 // 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 @@
 		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 @@
 			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 @@
 			// 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 @@
 		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 @@
 		}
 
 		// 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 @@
 					// 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 @@
 		}
 		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 @@
 			}
 			// 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 @@
 
 	// 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 @@
 	// 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 @@
 				// 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 @@
 				// because dead loads haven't been
 				// eliminated yet.
 				if v.Uses > 0 {
-					seen[n] = true
+					seen.Add(n)
 				}
 			}
 		}
@@ -334,8 +335,8 @@
 
 	// 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 6353f72..68b6ab5 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 @@
 	// 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 @@
 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 @@
 type debugState struct {
 	// See FuncDebug.
 	slots    []LocalSlot
-	vars     []GCNode
+	vars     []*ir.Name
 	varSlots [][]SlotID
 	lists    [][]byte
 
@@ -189,7 +190,7 @@
 	// 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 @@
 	}
 
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 
 	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 bf7f1e8..ea988e4 100644
--- a/src/cmd/compile/internal/ssa/decompose.go
+++ b/src/cmd/compile/internal/ssa/decompose.go
@@ -219,10 +219,6 @@
 	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 679ee8a..579818e 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -24,6 +24,632 @@
 	pt     *types.Type
 }
 
+func isBlockMultiValueExit(b *Block) bool {
+	return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && len(b.Controls) > 0 && b.Controls[0].Op == OpMakeResult
+}
+
+// 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
+}
+
+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
+}
+
+// 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
+}
+
+// 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()
+}
+
+// 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
+	}
+	// 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.
+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
+}
+
+// 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)
+	}
+	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 !x.isAlreadyExpandedAggregateType(selector.Type) {
+			if leafType == selector.Type { // OpIData leads us here, sometimes.
+				leaf.copyOf(selector)
+			} else {
+				x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
+			}
+			if x.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
+		} 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 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 <interface {},mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v8 v1
+		// v21 (142) = SelectN <mem> [1] v20
+		// v22 (142) = SelectN <interface {}> [0] v20
+		// b15: ← b8
+		// v71 (+143) = IData <Nodes> 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 <mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v715
+		// v23 (142) = Load <*uintptr> v19 v20
+		// v823 (142) = IsNonNil <bool> v23
+		// v67 (+143) = Load <*[]*Node> v880 v20
+		// b15: ← b8
+		// v827 (146) = StructSelect <*[]*Node> [0] v67
+		// v846 (146) = Store <mem> {*[]*Node} v769 v827 v20
+		// v73 (+146) = StaticCall <mem> {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 {
+					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.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]
+		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 := x.rewriteSelect(leaf, selector.Args[0], offset)
+		locs = x.splitSlots(ls, ".real", 0, 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 := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize)
+		locs = x.splitSlots(ls, ".len", x.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 := x.rewriteSelect(leaf, selector.Args[0], offset+2*x.ptrSize)
+		locs = x.splitSlots(ls, ".cap", 2*x.ptrSize, leafType)
+
+	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])
+		}
+
+	default:
+		// Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed).
+	}
+
+	return locs
+}
+
+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 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()
+		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
+}
+
+// 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)
+}
+
+// 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)
+}
+
+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)
+}
+
+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)
+}
+
+// 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)
+	}
+
+	switch source.Op {
+	case OpCopy:
+		return x.storeArgOrLoad(pos, b, base, source.Args[0], mem, t, offset)
+
+	case OpLoad:
+		ret := x.decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneLoad, storeTwoLoad)
+		if ret != nil {
+			return ret
+		}
+
+	case OpArg:
+		ret := x.decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneArg, storeTwoArg)
+		if ret != nil {
+			return ret
+		}
+
+	case OpArrayMake0, OpStructMake0:
+		return mem
+
+	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 mem
+
+	case OpArrayMake1:
+		return x.storeArgOrLoad(pos, b, base, source.Args[0], mem, t.Elem(), offset)
+
+	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 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)
+
+	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 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 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)
+	}
+
+	// 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()
+		}
+		return mem
+
+	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 <intVal> v6
+			// v121 (+882) = StaticLECall <floatVal,mem> {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 <Value> {x} (x[Value], x[Value])
+			// is rewritten by decomposeArgs into
+			//   v141 (858) = Arg <uintptr> {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)
+		}
+
+		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()
+		}
+		return mem
+
+	case types.TINT64, types.TUINT64:
+		if t.Width == x.regSize {
+			break
+		}
+		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)
+	}
+
+	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())
+			}
+			// "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
+}
+
 // 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
@@ -38,639 +664,30 @@
 
 	// 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
-
-	if debug {
-		fmt.Printf("\nexpandsCalls(%s)\n", f.Name)
-	}
-
-	canSSAType := f.fe.CanSSA
-	regSize := f.Config.RegSize
 	sp, _ := f.spSb()
-	typ := &f.Config.Types
-	ptrSize := f.Config.PtrSize
+	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.
-	var hiOffset, lowOffset int64
 	if f.Config.BigEndian {
-		lowOffset = 4
+		x.lowOffset = 4
 	} else {
-		hiOffset = 4
+		x.hiOffset = 4
 	}
 
-	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).
-	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()
-	}
-
-	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
-		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
-	}
-
-	// 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.
-	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)
-			}
-		}
-		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
-			} 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])
-			}
-
-		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 <interface {},mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v8 v1
-			// v21 (142) = SelectN <mem> [1] v20
-			// v22 (142) = SelectN <interface {}> [0] v20
-			// b15: ← b8
-			// v71 (+143) = IData <Nodes> 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 <mem> {AuxCall{reflect.Value.Interface([reflect.Value,0])[interface {},24]}} [40] v715
-			// v23 (142) = Load <*uintptr> v19 v20
-			// v823 (142) = IsNonNil <bool> v23
-			// v67 (+143) = Load <*[]*Node> v880 v20
-			// b15: ← b8
-			// v827 (146) = StructSelect <*[]*Node> [0] v67
-			// v846 (146) = Store <mem> {*[]*Node} v769 v827 v20
-			// v73 (+146) = StaticCall <mem> {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())
-			}
-			leaf.copyOf(selector)
-			for _, s := range namedSelects[selector] {
-				locs = append(locs, 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])
-					}
-				} else {
-					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 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 OpComplexReal:
-			ls := rewriteSelect(leaf, selector.Args[0], offset)
-			locs = 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 OpStringLen, OpSliceLen:
-			ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize)
-			locs = splitSlots(ls, ".len", ptrSize, leafType)
-
-		case OpIData:
-			ls := rewriteSelect(leaf, selector.Args[0], offset+ptrSize)
-			locs = splitSlots(ls, ".data", 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])
-			}
-
-		default:
-			// Ignore dead ends. These can occur if this phase is run before decompose builtin (which is not intended, but allowed).
-		}
-
-		return locs
-	}
-
-	// 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
-
-	// 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)
-			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)
-		pos = pos.WithNotStmt()
-		t1Size := t1.Size()
-		return storeOneArg(pos, b, base, source, mem, t2, offArg+t1Size, offStore+t1Size)
-	}
-
-	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)
-	}
-
-	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)
-		}
-
-		switch source.Op {
-		case OpCopy:
-			return storeArgOrLoad(pos, b, base, source.Args[0], mem, t, offset)
-
-		case OpLoad:
-			ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneLoad, storeTwoLoad)
-			if ret != nil {
-				return ret
-			}
-
-		case OpArg:
-			ret := decomposeArgOrLoad(pos, b, base, source, mem, t, offset, storeOneArg, storeTwoArg)
-			if ret != nil {
-				return ret
-			}
-
-		case OpArrayMake0, OpStructMake0:
-			return mem
-
-		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 OpArrayMake1:
-			return storeArgOrLoad(pos, b, base, source.Args[0], mem, t.Elem(), offset)
-
-		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 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)
-			pos = pos.WithNotStmt()
-			return storeArgOrLoad(pos, b, base, source.Args[1], mem, tPart, offset+wPart)
-
-		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 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 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 <intVal> v6
-				// v121 (+882) = StaticLECall <floatVal,mem> {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 <Value> {x} (x[Value], x[Value])
-				// is rewritten by decomposeArgs into
-				//   v141 (858) = Arg <uintptr> {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)
-			}
-
-			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 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 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 types.TSTRING:
-			sel := source.Block.NewValue1(pos, OpStringPtr, typ.BytePtr, source)
-			mem = storeArgOrLoad(pos, b, base, sel, mem, typ.BytePtr, offset)
-			pos = pos.WithNotStmt()
-			sel = source.Block.NewValue1(pos, OpStringLen, typ.Int, source)
-			return storeArgOrLoad(pos, b, base, sel, mem, typ.Int, offset+ptrSize)
-
-		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.TCOMPLEX128:
-			sel := source.Block.NewValue1(pos, OpComplexReal, typ.Float64, source)
-			mem = storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset)
-			pos = pos.WithNotStmt()
-			sel = source.Block.NewValue1(pos, OpComplexImag, typ.Float64, source)
-			return storeArgOrLoad(pos, b, base, sel, mem, typ.Float64, offset+8)
-		}
-
-		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())
-		}
-		return x
-	}
-
-	// 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
-			}
-			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))
-			}
-		}
-		v.resetArgs()
-		return mem
+	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 @@
 		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 @@
 				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 @@
 				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 @@
 			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 @@
 				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 @@
 		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 @@
 		}
 	}
 
-	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 @@
 		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 @@
 		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 @@
 
 	// 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 @@
 		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 @@
 			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 b4c3e5c..32e6d09 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 @@
 		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 @@
 
 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 @@
 		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 e6f899a..de99a8d 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -58,6 +58,11 @@
 	// 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 @@
 }
 
 // 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 @@
 }
 
 // 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 @@
 }
 
 // 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 @@
 }
 
 // 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 @@
 }
 
 // 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 @@
 }
 
 // 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 @@
 }
 
 // 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) 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 (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 @@
 			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 568c643..276c444 100644
--- a/src/cmd/compile/internal/ssa/func_test.go
+++ b/src/cmd/compile/internal/ssa/func_test.go
@@ -232,7 +232,7 @@
 }
 
 // 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 @@
 	op     Op
 	t      *types.Type
 	auxint int64
-	aux    interface{}
+	aux    Aux
 	args   []string
 }
 
@@ -402,12 +402,12 @@
 			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 fbc12fd..df03cb7 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 a866a96..3c75bcf 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.Type> 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.Type> 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.Type> destptr [16]) (MOVOconst [0])
-		(MOVOstore destptr (MOVOconst [0]) mem))
+	(MOVOstorezero (OffPtr <destptr.Type> destptr [16])
+		(MOVOstorezero destptr mem))
 (Zero [48] destptr mem) && config.useSSE =>
-	(MOVOstore (OffPtr <destptr.Type> destptr [32]) (MOVOconst [0])
-		(MOVOstore (OffPtr <destptr.Type> destptr [16]) (MOVOconst [0])
-			(MOVOstore destptr (MOVOconst [0]) mem)))
+	(MOVOstorezero (OffPtr <destptr.Type> destptr [32])
+		(MOVOstorezero (OffPtr <destptr.Type> destptr [16])
+			(MOVOstorezero destptr mem)))
 (Zero [64] destptr mem) && config.useSSE =>
-	(MOVOstore (OffPtr <destptr.Type> destptr [48]) (MOVOconst [0])
-		(MOVOstore (OffPtr <destptr.Type> destptr [32]) (MOVOconst [0])
-			(MOVOstore (OffPtr <destptr.Type> destptr [16]) (MOVOconst [0])
-				(MOVOstore destptr (MOVOconst [0]) mem))))
+	(MOVOstorezero (OffPtr <destptr.Type> destptr [48])
+		(MOVOstorezero (OffPtr <destptr.Type> destptr [32])
+			(MOVOstorezero (OffPtr <destptr.Type> 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 de53726..043162e 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -44,7 +44,7 @@
 	"R11",
 	"R12",
 	"R13",
-	"R14",
+	"g", // a.k.a. R14
 	"R15",
 	"X0",
 	"X1",
@@ -61,7 +61,7 @@
 	"X12",
 	"X13",
 	"X14",
-	"X15",
+	"X15", // constant 0 in ABIInternal
 
 	// If you add registers, update asyncPreempt in runtime
 
@@ -96,11 +96,14 @@
 		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 @@
 		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 @@
 
 		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 @@
 		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 @@
 		// 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 @@
 		{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 @@
 		{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 @@
 		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 11c36b5..de0df36 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 3f4d0c1..a0e2a0d 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 c064046..a762be6 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 384f2e8..c3421da 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 9297ed8..b0f10d0 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 <typ.Int32> {n} [off+4])
     (Arg <typ.UInt32> {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 <typ.UInt32> {n} [off+4])
     (Arg <typ.UInt32> {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 <typ.Int32> {n} [off])
     (Arg <typ.UInt32> {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 <typ.UInt32> {n} [off])
     (Arg <typ.UInt32> {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 1c9a0bb..0000000
--- 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 <typ.BytePtr> {n} [off])
-    (Arg <typ.Int> {n} [off+int32(config.PtrSize)]))
-
-(Arg {n} [off]) && v.Type.IsSlice() =>
-  (SliceMake
-    (Arg <v.Type.Elem().PtrTo()> {n} [off])
-    (Arg <typ.Int> {n} [off+int32(config.PtrSize)])
-    (Arg <typ.Int> {n} [off+2*int32(config.PtrSize)]))
-
-(Arg {n} [off]) && v.Type.IsInterface() =>
-  (IMake
-    (Arg <typ.Uintptr> {n} [off])
-    (Arg <typ.BytePtr> {n} [off+int32(config.PtrSize)]))
-
-(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 16 =>
-  (ComplexMake
-    (Arg <typ.Float64> {n} [off])
-    (Arg <typ.Float64> {n} [off+8]))
-
-(Arg {n} [off]) && v.Type.IsComplex() && v.Type.Size() == 8 =>
-  (ComplexMake
-    (Arg <typ.Float32> {n} [off])
-    (Arg <typ.Float32> {n} [off+4]))
-
-(Arg <t>) && t.IsStruct() && t.NumFields() == 0 && fe.CanSSA(t) =>
-  (StructMake0)
-(Arg <t> {n} [off]) && t.IsStruct() && t.NumFields() == 1 && fe.CanSSA(t) =>
-  (StructMake1
-    (Arg <t.FieldType(0)> {n} [off+int32(t.FieldOff(0))]))
-(Arg <t> {n} [off]) && t.IsStruct() && t.NumFields() == 2 && fe.CanSSA(t) =>
-  (StructMake2
-    (Arg <t.FieldType(0)> {n} [off+int32(t.FieldOff(0))])
-    (Arg <t.FieldType(1)> {n} [off+int32(t.FieldOff(1))]))
-(Arg <t> {n} [off]) && t.IsStruct() && t.NumFields() == 3 && fe.CanSSA(t) =>
-  (StructMake3
-    (Arg <t.FieldType(0)> {n} [off+int32(t.FieldOff(0))])
-    (Arg <t.FieldType(1)> {n} [off+int32(t.FieldOff(1))])
-    (Arg <t.FieldType(2)> {n} [off+int32(t.FieldOff(2))]))
-(Arg <t> {n} [off]) && t.IsStruct() && t.NumFields() == 4 && fe.CanSSA(t) =>
-  (StructMake4
-    (Arg <t.FieldType(0)> {n} [off+int32(t.FieldOff(0))])
-    (Arg <t.FieldType(1)> {n} [off+int32(t.FieldOff(1))])
-    (Arg <t.FieldType(2)> {n} [off+int32(t.FieldOff(2))])
-    (Arg <t.FieldType(3)> {n} [off+int32(t.FieldOff(3))]))
-
-(Arg <t>) && t.IsArray() && t.NumElem() == 0 =>
-  (ArrayMake0)
-(Arg <t> {n} [off]) && t.IsArray() && t.NumElem() == 1 && fe.CanSSA(t) =>
-  (ArrayMake1 (Arg <t.Elem()> {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 b73d9d3..0000000
--- 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 aaf9101..6388aab 100644
--- a/src/cmd/compile/internal/ssa/gen/rulegen.go
+++ b/src/cmd/compile/internal/ssa/gen/rulegen.go
@@ -582,6 +582,7 @@
 			"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 a333982..4cd0ac8 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 @@
 //                           { 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 @@
 	}
 	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 d1bad52..bae5065 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 @@
 				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 16d9461..2e32afe 100644
--- a/src/cmd/compile/internal/ssa/nilcheck_test.go
+++ b/src/cmd/compile/internal/ssa/nilcheck_test.go
@@ -212,7 +212,7 @@
 			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 f4e62b8..2a9c8e4 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 @@
 	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 d167335..f41d014 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 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 @@
 	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 @@
 //  - 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 e590f6b..ccfed93 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -970,6 +970,7 @@
 	OpAMD64MOVQstore
 	OpAMD64MOVOload
 	OpAMD64MOVOstore
+	OpAMD64MOVOstorezero
 	OpAMD64MOVBloadidx1
 	OpAMD64MOVWloadidx1
 	OpAMD64MOVWloadidx2
@@ -998,7 +999,6 @@
 	OpAMD64MOVQstoreconstidx1
 	OpAMD64MOVQstoreconstidx8
 	OpAMD64DUFFZERO
-	OpAMD64MOVOconst
 	OpAMD64REPSTOSQ
 	OpAMD64CALLstatic
 	OpAMD64CALLclosure
@@ -6162,11 +6162,11 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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,37 +13164,25 @@
 		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,
 		faultOnNilArg0: true,
@@ -13201,7 +13202,7 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 				{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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 	{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 @@
 	{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 f5a2b3a..1e04b48 100644
--- a/src/cmd/compile/internal/ssa/poset.go
+++ b/src/cmd/compile/internal/ssa/poset.go
@@ -136,13 +136,13 @@
 // 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<I, A<J, A<K (with no known relation between I,J,K), we create the
 // following DAG:
 //
 //         A
 //        / \
-//       I  dummy
+//       I  extra
 //           /  \
 //          J    K
 //
@@ -223,7 +223,7 @@
 		po.setchr(i1, e2)
 		po.upush(undoSetChr, i1, 0)
 	} else {
-		// If n1 already has two children, add an intermediate dummy
+		// If n1 already has two children, add an intermediate extra
 		// node to record the relation correctly (without relating
 		// n2 to other existing nodes). Use a non-deterministic value
 		// to decide whether to append on the left or the right, to avoid
@@ -231,27 +231,27 @@
 		//
 		//      n1
 		//     /  \
-		//   i1l  dummy
+		//   i1l  extra
 		//        /   \
 		//      i1r   n2
 		//
-		dummy := po.newnode(nil)
+		extra := po.newnode(nil)
 		if (i1^i2)&1 != 0 { // non-deterministic
-			po.setchl(dummy, i1r)
-			po.setchr(dummy, e2)
-			po.setchr(i1, newedge(dummy, false))
+			po.setchl(extra, i1r)
+			po.setchr(extra, e2)
+			po.setchr(i1, newedge(extra, false))
 			po.upush(undoSetChr, i1, i1r)
 		} else {
-			po.setchl(dummy, i1l)
-			po.setchr(dummy, e2)
-			po.setchl(i1, newedge(dummy, false))
+			po.setchl(extra, i1l)
+			po.setchr(extra, e2)
+			po.setchl(i1, newedge(extra, false))
 			po.upush(undoSetChl, i1, i1l)
 		}
 	}
 }
 
 // newnode allocates a new node bound to SSA value n.
-// If n is nil, this is a dummy node (= only used internally).
+// If n is nil, this is an extra node (= only used internally).
 func (po *poset) newnode(n *Value) uint32 {
 	i := po.lastidx + 1
 	po.lastidx++
@@ -380,9 +380,9 @@
 
 	case higherptr != 0:
 		// Higher bound only. To record n < higher, we need
-		// a dummy root:
+		// an extra root:
 		//
-		//        dummy
+		//        extra
 		//        /   \
 		//      root   \
 		//       /      n
@@ -395,11 +395,11 @@
 		if r2 != po.roots[0] { // all constants should be in root #0
 			panic("constant not in root #0")
 		}
-		dummy := po.newnode(nil)
-		po.changeroot(r2, dummy)
-		po.upush(undoChangeRoot, dummy, newedge(r2, false))
-		po.addchild(dummy, r2, false)
-		po.addchild(dummy, i, false)
+		extra := po.newnode(nil)
+		po.changeroot(r2, extra)
+		po.upush(undoChangeRoot, extra, newedge(r2, false))
+		po.addchild(extra, r2, false)
+		po.addchild(extra, i, false)
 		po.addchild(i, i2, true)
 	}
 
@@ -612,7 +612,7 @@
 	panic("findroot didn't find any root")
 }
 
-// mergeroot merges two DAGs into one DAG by creating a new dummy root
+// mergeroot merges two DAGs into one DAG by creating a new extra root
 func (po *poset) mergeroot(r1, r2 uint32) uint32 {
 	// Root #0 is special as it contains all constants. Since mergeroot
 	// discards r2 as root and keeps r1, make sure that r2 is not root #0,
@@ -1004,7 +1004,7 @@
 	case !f1 && f2:
 		// n1 is not in any DAG but n2 is. If n2 is a root, we can put
 		// n1 in its place as a root; otherwise, we need to create a new
-		// dummy root to record the relation.
+		// extra root to record the relation.
 		i1 = po.newnode(n1)
 
 		if po.isroot(i2) {
@@ -1020,17 +1020,17 @@
 
 		// Re-parent as follows:
 		//
-		//                  dummy
+		//                  extra
 		//     r            /   \
 		//      \   ===>   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 0339b07..8c25b1c 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 @@
 		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 @@
 					// 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 f5d1a78..e82aa84 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -521,6 +521,18 @@
 	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 @@
 	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{} {
+func StringToAux(s string) Aux {
+	return stringAux(s)
+}
+func symToAux(s Sym) Aux {
 	return s
 }
-func symToAux(s Sym) interface{} {
+func callToAux(s *AuxCall) Aux {
 	return s
 }
-func callToAux(s *AuxCall) interface{} {
-	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 @@
 
 // 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 @@
 
 // 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 2acdccd..4e7fdb9 100644
--- a/src/cmd/compile/internal/ssa/rewrite386.go
+++ b/src/cmd/compile/internal/ssa/rewrite386.go
@@ -1785,12 +1785,12 @@
 		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 @@
 		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 @@
 		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 75d4ff7..03498c7 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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 	}
 	// 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 @@
 		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 @@
 		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 @@
 	}
 	// match: (Zero [s] destptr mem)
 	// cond: s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE
-	// result: (Zero [s-s%16] (OffPtr <destptr.Type> destptr [s%16]) (MOVOstore destptr (MOVOconst [0]) mem))
+	// result: (Zero [s-s%16] (OffPtr <destptr.Type> destptr [s%16]) (MOVOstorezero destptr mem))
 	for {
 		s := auxIntToInt64(v.AuxInt)
 		destptr := v_0
@@ -34176,10 +34190,8 @@
 		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 @@
 	}
 	// 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 @@
 		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.Type> destptr [16]) (MOVOconst [0]) (MOVOstore destptr (MOVOconst [0]) mem))
+	// result: (MOVOstorezero (OffPtr <destptr.Type> destptr [16]) (MOVOstorezero destptr mem))
 	for {
 		if auxIntToInt64(v.AuxInt) != 32 {
 			break
@@ -34234,20 +34244,18 @@
 		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.Type> destptr [32]) (MOVOconst [0]) (MOVOstore (OffPtr <destptr.Type> destptr [16]) (MOVOconst [0]) (MOVOstore destptr (MOVOconst [0]) mem)))
+	// result: (MOVOstorezero (OffPtr <destptr.Type> destptr [32]) (MOVOstorezero (OffPtr <destptr.Type> destptr [16]) (MOVOstorezero destptr mem)))
 	for {
 		if auxIntToInt64(v.AuxInt) != 48 {
 			break
@@ -34257,25 +34265,23 @@
 		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.Type> destptr [48]) (MOVOconst [0]) (MOVOstore (OffPtr <destptr.Type> destptr [32]) (MOVOconst [0]) (MOVOstore (OffPtr <destptr.Type> destptr [16]) (MOVOconst [0]) (MOVOstore destptr (MOVOconst [0]) mem))))
+	// result: (MOVOstorezero (OffPtr <destptr.Type> destptr [48]) (MOVOstorezero (OffPtr <destptr.Type> destptr [32]) (MOVOstorezero (OffPtr <destptr.Type> destptr [16]) (MOVOstorezero destptr mem))))
 	for {
 		if auxIntToInt64(v.AuxInt) != 64 {
 			break
@@ -34285,30 +34291,28 @@
 		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 @@
 		}
 		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 d9d439f..c958aae 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM.go
@@ -3728,12 +3728,12 @@
 		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 5d5e526..ff1156d 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM64.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -2772,12 +2772,12 @@
 		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 @@
 		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 455f9b1..98f748e 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -4777,12 +4777,12 @@
 		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 @@
 		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 @@
 		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 @@
 		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 a9722b8..b52a1b6 100644
--- a/src/cmd/compile/internal/ssa/rewriteS390X.go
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -6332,12 +6332,12 @@
 		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 @@
 		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 @@
 		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 @@
 		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 c49bc80..60b727f 100644
--- a/src/cmd/compile/internal/ssa/rewritedec64.go
+++ b/src/cmd/compile/internal/ssa/rewritedec64.go
@@ -184,12 +184,12 @@
 	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 <typ.Int32> {n} [off+4]) (Arg <typ.UInt32> {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 @@
 		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 <typ.UInt32> {n} [off+4]) (Arg <typ.UInt32> {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 @@
 		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 <typ.Int32> {n} [off]) (Arg <typ.UInt32> {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 @@
 		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 <typ.UInt32> {n} [off]) (Arg <typ.UInt32> {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 23ff417..0000000
--- 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 <typ.BytePtr> {n} [off]) (Arg <typ.Int> {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 <v.Type.Elem().PtrTo()> {n} [off]) (Arg <typ.Int> {n} [off+int32(config.PtrSize)]) (Arg <typ.Int> {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 <typ.Uintptr> {n} [off]) (Arg <typ.BytePtr> {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 <typ.Float64> {n} [off]) (Arg <typ.Float64> {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 <typ.Float32> {n} [off]) (Arg <typ.Float32> {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 <t>)
-	// 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 <t> {n} [off])
-	// cond: t.IsStruct() && t.NumFields() == 1 && fe.CanSSA(t)
-	// result: (StructMake1 (Arg <t.FieldType(0)> {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 <t> {n} [off])
-	// cond: t.IsStruct() && t.NumFields() == 2 && fe.CanSSA(t)
-	// result: (StructMake2 (Arg <t.FieldType(0)> {n} [off+int32(t.FieldOff(0))]) (Arg <t.FieldType(1)> {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 <t> {n} [off])
-	// cond: t.IsStruct() && t.NumFields() == 3 && fe.CanSSA(t)
-	// result: (StructMake3 (Arg <t.FieldType(0)> {n} [off+int32(t.FieldOff(0))]) (Arg <t.FieldType(1)> {n} [off+int32(t.FieldOff(1))]) (Arg <t.FieldType(2)> {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 <t> {n} [off])
-	// cond: t.IsStruct() && t.NumFields() == 4 && fe.CanSSA(t)
-	// result: (StructMake4 (Arg <t.FieldType(0)> {n} [off+int32(t.FieldOff(0))]) (Arg <t.FieldType(1)> {n} [off+int32(t.FieldOff(1))]) (Arg <t.FieldType(2)> {n} [off+int32(t.FieldOff(2))]) (Arg <t.FieldType(3)> {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 <t>)
-	// 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 <t> {n} [off])
-	// cond: t.IsArray() && t.NumElem() == 1 && fe.CanSSA(t)
-	// result: (ArrayMake1 (Arg <t.Elem()> {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 60ada01..a27002e 100644
--- a/src/cmd/compile/internal/ssa/sizeof_test.go
+++ b/src/cmd/compile/internal/ssa/sizeof_test.go
@@ -22,7 +22,7 @@
 	}{
 		{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 406a3c3..68a6f08 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 @@
 		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 edc43aa..d000b7c 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 @@
 	// 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 @@
 	}
 	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 849c9e8..4378f2d 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 @@
 			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 ec38b7d..e08272c 100644
--- a/src/cmd/compile/internal/ssa/zcse.go
+++ b/src/cmd/compile/internal/ssa/zcse.go
@@ -57,7 +57,7 @@
 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 0000000..7180b38
--- /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 0000000..cc50ab3
--- /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 0000000..a243436
--- /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 0000000..182f840
--- /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 0000000..69ed8ad
--- /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 5218cd0..01ad211 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 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 @@
 }
 
 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 @@
 	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 @@
 	// 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 @@
 			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 @@
 				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 @@
 			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 @@
 
 // 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 @@
 				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 @@
 		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 @@
 }
 
 // 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 @@
 		}
 	}
 	// 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/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
new file mode 100644
index 0000000..6b1ddeb
--- /dev/null
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -0,0 +1,7369 @@
+// 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 (
+	"bufio"
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"go/constant"
+	"html"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"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"
+	"cmd/internal/objabi"
+	"cmd/internal/src"
+	"cmd/internal/sys"
+)
+
+var ssaConfig *ssa.Config
+var ssaCaches []ssa.Cache
+
+var ssaDump string     // early copy of $GOSSAFUNC; the func name to dump output for
+var ssaDir string      // optional destination for ssa dump file
+var ssaDumpStdout bool // whether to dump to stdout
+var ssaDumpCFG string  // generate CFGs for these phases
+const ssaDumpFile = "ssa.html"
+
+// ssaDumpInlined holds all inlined functions when ssaDump contains a function name.
+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 InitConfig() {
+	types_ := ssa.NewTypes()
+
+	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[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(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.
+	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 base.Ctxt.Arch.Name == "amd64" {
+		GCWriteBarrierReg = map[int16]*obj.LSym{
+			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] = 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)
+	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 *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)
+}
+
+// dvarint writes a varint v to the funcdata in symbol x and returns the new offset
+func dvarint(x *obj.LSym, off int, v int64) int {
+	if v < 0 || v > 1e9 {
+		panic(fmt.Sprintf("dvarint: bad offset for funcdata - %v", v))
+	}
+	if v < 1<<7 {
+		return objw.Uint8(x, off, uint8(v))
+	}
+	off = objw.Uint8(x, off, uint8((v&127)|128))
+	if v < 1<<14 {
+		return objw.Uint8(x, off, uint8(v>>7))
+	}
+	off = objw.Uint8(x, off, uint8(((v>>7)&127)|128))
+	if v < 1<<21 {
+		return objw.Uint8(x, off, uint8(v>>14))
+	}
+	off = objw.Uint8(x, off, uint8(((v>>14)&127)|128))
+	if v < 1<<28 {
+		return objw.Uint8(x, off, uint8(v>>21))
+	}
+	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
+// that is using open-coded defers.  This funcdata is used to determine the active
+// defers in a function and execute those defers during panic processing.
+//
+// The funcdata is all encoded in varints (since values will almost always be less than
+// 128, but stack offsets could potentially be up to 2Gbyte). All "locations" (offsets)
+// for stack variables are specified as the number of bytes below varp (pointer to the
+// top of the local variables) for their starting address. The format is:
+//
+//  - Max total argument size among all the defers
+//  - Offset of the deferBits variable
+//  - Number of defers in the function
+//  - Information about each defer call, in reverse order of appearance in the function:
+//    - Total argument size of the call
+//    - Offset of the closure value to call
+//    - Number of arguments (including interface receiver or method receiver as first arg)
+//    - Information about each argument
+//      - Offset of the stored defer argument in this function's frame
+//      - Size of the argument
+//      - Offset of where argument should be placed in the args frame when making call
+func (s *state) emitOpenDeferInfo() {
+	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)
+	// first, so we can output it first to the funcdata
+	var maxargsize int64
+	for i := len(s.openDefers) - 1; i >= 0; i-- {
+		r := s.openDefers[i]
+		argsize := r.n.X.Type().ArgWidth()
+		if argsize > maxargsize {
+			maxargsize = argsize
+		}
+	}
+	off = dvarint(x, off, maxargsize)
+	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.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
+			// arg. (If there is a method receiver, it's already included as
+			// first arg in r.argNodes.)
+			numArgs++
+		}
+		off = dvarint(x, off, int64(numArgs))
+		if r.rcvrNode != nil {
+			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.FrameOffset())
+			off = dvarint(x, off, f.Type.Size())
+			off = dvarint(x, off, f.Offset)
+		}
+	}
+}
+
+// buildssa builds an SSA function for fn.
+// worker indicates which of the backend workers is doing the processing.
+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 || base.Ctxt.Pkgpath+"."+name == ssaDump
+	}
+	var astBuf *bytes.Buffer
+	if printssa {
+		astBuf = &bytes.Buffer{}
+		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())
+		}
+	}
+
+	var s state
+	s.pushLine(fn.Pos())
+	defer s.popLine()
+
+	s.hasdefer = fn.HasDefer()
+	if fn.Pragma&ir.CgoUnsafeArgs != 0 {
+		s.cgoUnsafeArgs = true
+	}
+
+	fe := ssafn{
+		curfn: fn,
+		log:   printssa && ssaDumpStdout,
+	}
+	s.curfn = fn
+
+	s.f = ssa.NewFunc(&fe)
+	s.config = ssaConfig
+	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.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()
+
+	if printssa {
+		ssaDF := ssaDumpFile
+		if ssaDir != "" {
+			ssaDF = filepath.Join(ssaDir, base.Ctxt.Pkgpath+"."+name+".html")
+			ssaD := filepath.Dir(ssaDF)
+			os.MkdirAll(ssaD, 0755)
+		}
+		s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDF, s.f, ssaDumpCFG)
+		// TODO: generate and print a mapping from nodes to values and blocks
+		dumpSourcesColumn(s.f.HTMLWriter, fn)
+		s.f.HTMLWriter.WriteAST("AST", astBuf)
+	}
+
+	// Allocate starting values
+	s.labels = map[string]*ssaLabel{}
+	s.fwdVars = map[ir.Node]*ssa.Value{}
+	s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem)
+
+	s.hasOpenDefers = base.Flag.N == 0 && s.hasdefer && !s.curfn.OpenCodedDeferDisallowed()
+	switch {
+	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 && len(s.curfn.Exit) > 0 {
+		// Skip doing open defers if there is any extra exit code (likely
+		// 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.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).
+		// Open-coded defers are most important for improving performance
+		// for smaller functions (which don't have many returns).
+		s.hasOpenDefers = false
+	}
+
+	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
+	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 := 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[types.TUINT8])
+		s.vars[deferBitsVar] = startDeferBits
+		s.deferBitsAddr = s.addr(deferBitsTemp)
+		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)
+	}
+
+	// Generate addresses of local declarations
+	s.decladdrs = map[*ir.Name]*ssa.Value{}
+	var args []ssa.Param
+	var results []ssa.Param
+	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.
+		default:
+			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.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.Enter)
+	s.zeroResults()
+	s.paramsToHeap()
+	s.stmtList(fn.Body)
+
+	// fallthrough to exit
+	if s.curBlock != nil {
+		s.pushLine(fn.Endlineno)
+		s.exit()
+		s.popLine()
+	}
+
+	for _, b := range s.f.Blocks {
+		if b.Pos != src.NoXPos {
+			s.updateUnsetPredPos(b)
+		}
+	}
+
+	s.f.HTMLWriter.WritePhase("before insert phis", "before insert phis")
+
+	s.insertPhis()
+
+	// Main call to ssa package to compile function
+	ssa.Compile(s.f)
+
+	if s.hasOpenDefers {
+		s.emitOpenDeferInfo()
+	}
+
+	return s.f
+}
+
+// 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 := 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)
+	}
+
+	// Read sources of inlined functions.
+	var inlFns []*ssa.FuncLines
+	for _, fi := range ssaDumpInlined {
+		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
+		}
+		inlFns = append(inlFns, fnLines)
+	}
+
+	sort.Sort(ssa.ByTopo(inlFns))
+	if targetFn != nil {
+		inlFns = append([]*ssa.FuncLines{targetFn}, inlFns...)
+	}
+
+	writer.WriteSources("sources", inlFns)
+}
+
+func readFuncLines(file string, start, end uint) (*ssa.FuncLines, error) {
+	f, err := os.Open(os.ExpandEnv(file))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	var lines []string
+	ln := uint(1)
+	scanner := bufio.NewScanner(f)
+	for scanner.Scan() && ln <= end {
+		if ln >= start {
+			lines = append(lines, scanner.Text())
+		}
+		ln++
+	}
+	return &ssa.FuncLines{Filename: file, StartLineno: start, Lines: lines}, nil
+}
+
+// updateUnsetPredPos propagates the earliest-value position information for b
+// towards all of b's predecessors that need a position, and recurs on that
+// predecessor if its position is updated. B should have a non-empty position.
+func (s *state) updateUnsetPredPos(b *ssa.Block) {
+	if b.Pos == src.NoXPos {
+		s.Fatalf("Block %s should have a position", b)
+	}
+	bestPos := src.NoXPos
+	for _, e := range b.Preds {
+		p := e.Block()
+		if !p.LackingPos() {
+			continue
+		}
+		if bestPos == src.NoXPos {
+			bestPos = b.Pos
+			for _, v := range b.Values {
+				if v.LackingPos() {
+					continue
+				}
+				if v.Pos != src.NoXPos {
+					// Assume values are still in roughly textual order;
+					// TODO: could also seek minimum position?
+					bestPos = v.Pos
+					break
+				}
+			}
+		}
+		p.Pos = bestPos
+		s.updateUnsetPredPos(p) // We do not expect long chains of these, thus recursion is okay.
+	}
+}
+
+// Information about each open-coded defer.
+type openDeferInfo struct {
+	// 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 *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 *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 []*ir.Name
+}
+
+type state struct {
+	// configuration (arch) information
+	config *ssa.Config
+
+	// function we're building
+	f *ssa.Func
+
+	// Node for function
+	curfn *ir.Func
+
+	// labels in f
+	labels map[string]*ssaLabel
+
+	// unlabeled break and continue statement tracking
+	breakTo    *ssa.Block // current target for plain break statement
+	continueTo *ssa.Block // current target for plain continue statement
+
+	// current location where we're interpreting the AST
+	curBlock *ssa.Block
+
+	// 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[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[ir.Node]*ssa.Value
+
+	// all defined variables at the end of each block. Indexed by block ID.
+	defvars []map[ir.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
+	sp       *ssa.Value
+	sb       *ssa.Value
+	// value representing address of where deferBits autotmp is stored
+	deferBitsAddr *ssa.Value
+	deferBitsTemp *ir.Name
+
+	// line number stack. The current line number is top of stack
+	line []src.XPos
+	// the last line number processed; it may have been popped
+	lastPos src.XPos
+
+	// list of panic calls by function name and line number.
+	// Used to deduplicate panic calls.
+	panics map[funcLine]*ssa.Block
+
+	cgoUnsafeArgs bool
+	hasdefer      bool // whether the function contains a defer statement
+	softFloat     bool
+	hasOpenDefers bool // whether we are doing open-coded defers
+
+	// If doing open-coded defers, list of info about the defer calls in
+	// scanning order. Hence, at exit we should run these defers in reverse
+	// order of this list
+	openDefers []*openDeferInfo
+	// For open-coded defers, this is the beginning and end blocks of the last
+	// defer exit code that we have generated so far. We use these to share
+	// code between exits if the shareDeferExits option (disabled by default)
+	// is on.
+	lastDeferExit       *ssa.Block // Entry block of last defer exit code we generated
+	lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated
+	lastDeferCount      int        // Number of defers encountered at that point
+
+	prevCall *ssa.Value // the previous call; use this to tie results to the call op.
+}
+
+type funcLine struct {
+	f    *obj.LSym
+	base *src.PosBase
+	line uint
+}
+
+type ssaLabel struct {
+	target         *ssa.Block // block identified by this label
+	breakTarget    *ssa.Block // block to break to in control flow node identified by this label
+	continueTarget *ssa.Block // block to continue to in control flow node identified by this label
+}
+
+// label returns the label associated with sym, creating it if necessary.
+func (s *state) label(sym *types.Sym) *ssaLabel {
+	lab := s.labels[sym.Name]
+	if lab == nil {
+		lab = new(ssaLabel)
+		s.labels[sym.Name] = lab
+	}
+	return lab
+}
+
+func (s *state) Logf(msg string, args ...interface{}) { s.f.Logf(msg, args...) }
+func (s *state) Log() bool                            { return s.f.Log() }
+func (s *state) Fatalf(msg string, args ...interface{}) {
+	s.f.Frontend().Fatalf(s.peekPos(), msg, args...)
+}
+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 (
+	// 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.
+func (s *state) startBlock(b *ssa.Block) {
+	if s.curBlock != nil {
+		s.Fatalf("starting block %v when block %v has not ended", b, s.curBlock)
+	}
+	s.curBlock = b
+	s.vars = map[ir.Node]*ssa.Value{}
+	for n := range s.fwdVars {
+		delete(s.fwdVars, n)
+	}
+}
+
+// endBlock marks the end of generating code for the current block.
+// Returns the (former) current block. Returns nil if there is no current
+// block, i.e. if no code flows to the current execution point.
+func (s *state) endBlock() *ssa.Block {
+	b := s.curBlock
+	if b == nil {
+		return nil
+	}
+	for len(s.defvars) <= int(b.ID) {
+		s.defvars = append(s.defvars, nil)
+	}
+	s.defvars[b.ID] = s.vars
+	s.curBlock = nil
+	s.vars = nil
+	if b.LackingPos() {
+		// Empty plain blocks get the line of their successor (handled after all blocks created),
+		// except for increment blocks in For statements (handled in ssa conversion of OFOR),
+		// and for blocks ending in GOTO/BREAK/CONTINUE.
+		b.Pos = src.NoXPos
+	} else {
+		b.Pos = s.lastPos
+	}
+	return b
+}
+
+// pushLine pushes a line number on the line number stack.
+func (s *state) pushLine(line src.XPos) {
+	if !line.IsKnown() {
+		// the frontend may emit node with line number missing,
+		// use the parent line number in this case.
+		line = s.peekPos()
+		if base.Flag.K != 0 {
+			base.Warn("buildssa: unknown position (line 0)")
+		}
+	} else {
+		s.lastPos = line
+	}
+
+	s.line = append(s.line, line)
+}
+
+// popLine pops the top of the line number stack.
+func (s *state) popLine() {
+	s.line = s.line[:len(s.line)-1]
+}
+
+// peekPos peeks the top of the line number stack.
+func (s *state) peekPos() src.XPos {
+	return s.line[len(s.line)-1]
+}
+
+// newValue0 adds a new value with no arguments to the current block.
+func (s *state) newValue0(op ssa.Op, t *types.Type) *ssa.Value {
+	return s.curBlock.NewValue0(s.peekPos(), op, t)
+}
+
+// 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 ssa.Aux) *ssa.Value {
+	return s.curBlock.NewValue0A(s.peekPos(), op, t, aux)
+}
+
+// newValue0I adds a new value with no arguments and an auxint value to the current block.
+func (s *state) newValue0I(op ssa.Op, t *types.Type, auxint int64) *ssa.Value {
+	return s.curBlock.NewValue0I(s.peekPos(), op, t, auxint)
+}
+
+// newValue1 adds a new value with one argument to the current block.
+func (s *state) newValue1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue1(s.peekPos(), op, t, arg)
+}
+
+// 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 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 ssa.Aux, arg *ssa.Value, isStmt bool) *ssa.Value {
+	if isStmt {
+		return s.curBlock.NewValue1A(s.peekPos(), op, t, aux, arg)
+	}
+	return s.curBlock.NewValue1A(s.peekPos().WithNotStmt(), op, t, aux, arg)
+}
+
+// newValue1I adds a new value with one argument and an auxint value to the current block.
+func (s *state) newValue1I(op ssa.Op, t *types.Type, aux int64, arg *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue1I(s.peekPos(), op, t, aux, arg)
+}
+
+// newValue2 adds a new value with two arguments to the current block.
+func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1)
+}
+
+// newValue2A adds a new value with two arguments and an aux value to the current block.
+func (s *state) newValue2A(op ssa.Op, t *types.Type, aux 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 ssa.Aux, arg0, arg1 *ssa.Value, isStmt bool) *ssa.Value {
+	if isStmt {
+		return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1)
+	}
+	return s.curBlock.NewValue2A(s.peekPos().WithNotStmt(), op, t, aux, arg0, arg1)
+}
+
+// newValue2I adds a new value with two arguments and an auxint value to the current block.
+func (s *state) newValue2I(op ssa.Op, t *types.Type, aux int64, arg0, arg1 *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue2I(s.peekPos(), op, t, aux, arg0, arg1)
+}
+
+// newValue3 adds a new value with three arguments to the current block.
+func (s *state) newValue3(op ssa.Op, t *types.Type, arg0, arg1, arg2 *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue3(s.peekPos(), op, t, arg0, arg1, arg2)
+}
+
+// newValue3I adds a new value with three arguments and an auxint value to the current block.
+func (s *state) newValue3I(op ssa.Op, t *types.Type, aux int64, arg0, arg1, arg2 *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue3I(s.peekPos(), op, t, aux, 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 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 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)
+	}
+	return s.curBlock.NewValue3A(s.peekPos().WithNotStmt(), op, t, aux, arg0, arg1, arg2)
+}
+
+// newValue4 adds a new value with four arguments to the current block.
+func (s *state) newValue4(op ssa.Op, t *types.Type, arg0, arg1, arg2, arg3 *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue4(s.peekPos(), op, t, arg0, arg1, arg2, arg3)
+}
+
+// newValue4 adds a new value with four arguments and an auxint value to the current block.
+func (s *state) newValue4I(op ssa.Op, t *types.Type, aux int64, arg0, arg1, arg2, arg3 *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue4I(s.peekPos(), op, t, aux, arg0, arg1, arg2, arg3)
+}
+
+// entryNewValue0 adds a new value with no arguments to the entry block.
+func (s *state) entryNewValue0(op ssa.Op, t *types.Type) *ssa.Value {
+	return s.f.Entry.NewValue0(src.NoXPos, op, t)
+}
+
+// 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 ssa.Aux) *ssa.Value {
+	return s.f.Entry.NewValue0A(src.NoXPos, op, t, aux)
+}
+
+// entryNewValue1 adds a new value with one argument to the entry block.
+func (s *state) entryNewValue1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
+	return s.f.Entry.NewValue1(src.NoXPos, op, t, arg)
+}
+
+// entryNewValue1 adds a new value with one argument and an auxint value to the entry block.
+func (s *state) entryNewValue1I(op ssa.Op, t *types.Type, auxint int64, arg *ssa.Value) *ssa.Value {
+	return s.f.Entry.NewValue1I(src.NoXPos, op, t, auxint, arg)
+}
+
+// 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 ssa.Aux, arg *ssa.Value) *ssa.Value {
+	return s.f.Entry.NewValue1A(src.NoXPos, op, t, aux, arg)
+}
+
+// entryNewValue2 adds a new value with two arguments to the entry block.
+func (s *state) entryNewValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.Value {
+	return s.f.Entry.NewValue2(src.NoXPos, op, t, arg0, arg1)
+}
+
+// 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 ssa.Aux, arg0, arg1 *ssa.Value) *ssa.Value {
+	return s.f.Entry.NewValue2A(src.NoXPos, op, t, aux, arg0, arg1)
+}
+
+// const* routines add a new const value to the entry block.
+func (s *state) constSlice(t *types.Type) *ssa.Value {
+	return s.f.ConstSlice(t)
+}
+func (s *state) constInterface(t *types.Type) *ssa.Value {
+	return s.f.ConstInterface(t)
+}
+func (s *state) constNil(t *types.Type) *ssa.Value { return s.f.ConstNil(t) }
+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[types.TBOOL], c)
+}
+func (s *state) constInt8(t *types.Type, c int8) *ssa.Value {
+	return s.f.ConstInt8(t, c)
+}
+func (s *state) constInt16(t *types.Type, c int16) *ssa.Value {
+	return s.f.ConstInt16(t, c)
+}
+func (s *state) constInt32(t *types.Type, c int32) *ssa.Value {
+	return s.f.ConstInt32(t, c)
+}
+func (s *state) constInt64(t *types.Type, c int64) *ssa.Value {
+	return s.f.ConstInt64(t, c)
+}
+func (s *state) constFloat32(t *types.Type, c float64) *ssa.Value {
+	return s.f.ConstFloat32(t, c)
+}
+func (s *state) constFloat64(t *types.Type, c float64) *ssa.Value {
+	return s.f.ConstFloat64(t, c)
+}
+func (s *state) constInt(t *types.Type, c int64) *ssa.Value {
+	if s.config.PtrSize == 8 {
+		return s.constInt64(t, c)
+	}
+	if int64(int32(c)) != c {
+		s.Fatalf("integer constant too big %d", c)
+	}
+	return s.constInt32(t, int32(c))
+}
+func (s *state) constOffPtrSP(t *types.Type, c int64) *ssa.Value {
+	return s.f.ConstOffPtrSP(t, c, s.sp)
+}
+
+// newValueOrSfCall* are wrappers around newValue*, which may create a call to a
+// soft-float runtime function instead (when emitting soft-float code).
+func (s *state) newValueOrSfCall1(op ssa.Op, t *types.Type, arg *ssa.Value) *ssa.Value {
+	if s.softFloat {
+		if c, ok := s.sfcall(op, arg); ok {
+			return c
+		}
+	}
+	return s.newValue1(op, t, arg)
+}
+func (s *state) newValueOrSfCall2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.Value {
+	if s.softFloat {
+		if c, ok := s.sfcall(op, arg0, arg1); ok {
+			return c
+		}
+	}
+	return s.newValue2(op, t, arg0, arg1)
+}
+
+type instrumentKind uint8
+
+const (
+	instrumentRead = iota
+	instrumentWrite
+	instrumentMove
+)
+
+func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind) {
+	s.instrument2(t, addr, nil, kind)
+}
+
+// instrumentFields instruments a read/write operation on addr.
+// 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 !base.Flag.MSan || !t.IsStruct() {
+		s.instrument(t, addr, kind)
+		return
+	}
+	for _, f := range t.Fields().Slice() {
+		if f.Sym.IsBlank() {
+			continue
+		}
+		offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), f.Offset, addr)
+		s.instrumentFields(f.Type, offptr, kind)
+	}
+}
+
+func (s *state) instrumentMove(t *types.Type, dst, src *ssa.Value) {
+	if base.Flag.MSan {
+		s.instrument2(t, dst, src, instrumentMove)
+	} else {
+		s.instrument(t, src, instrumentRead)
+		s.instrument(t, dst, instrumentWrite)
+	}
+}
+
+func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrumentKind) {
+	if !s.curfn.InstrumentBody() {
+		return
+	}
+
+	w := t.Size()
+	if w == 0 {
+		return // can't race on zero-sized things
+	}
+
+	if ssa.IsSanitizerSafeAddr(addr) {
+		return
+	}
+
+	var fn *obj.LSym
+	needWidth := false
+
+	if addr2 != nil && kind != instrumentMove {
+		panic("instrument2: non-nil addr2 for non-move instrumentation")
+	}
+
+	if base.Flag.MSan {
+		switch kind {
+		case instrumentRead:
+			fn = ir.Syms.Msanread
+		case instrumentWrite:
+			fn = ir.Syms.Msanwrite
+		case instrumentMove:
+			fn = ir.Syms.Msanmove
+		default:
+			panic("unreachable")
+		}
+		needWidth = true
+	} 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 = ir.Syms.Racereadrange
+		case instrumentWrite:
+			fn = ir.Syms.Racewriterange
+		default:
+			panic("unreachable")
+		}
+		needWidth = true
+	} 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 = ir.Syms.Raceread
+		case instrumentWrite:
+			fn = ir.Syms.Racewrite
+		default:
+			panic("unreachable")
+		}
+	} else {
+		panic("unreachable")
+	}
+
+	args := []*ssa.Value{addr}
+	if addr2 != nil {
+		args = append(args, addr2)
+	}
+	if needWidth {
+		args = append(args, s.constInt(types.Types[types.TUINTPTR], w))
+	}
+	s.rtcall(fn, true, nil, args...)
+}
+
+func (s *state) load(t *types.Type, src *ssa.Value) *ssa.Value {
+	s.instrumentFields(t, src, instrumentRead)
+	return s.rawLoad(t, src)
+}
+
+func (s *state) rawLoad(t *types.Type, src *ssa.Value) *ssa.Value {
+	return s.newValue2(ssa.OpLoad, t, src, s.mem())
+}
+
+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())
+}
+
+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
+}
+
+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
+}
+
+// stmtList converts the statement list n to SSA and adds it to s.
+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 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())
+		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() != ir.OLABEL {
+		return
+	}
+
+	s.stmtList(n.Init())
+	switch n.Op() {
+
+	case ir.OBLOCK:
+		n := n.(*ir.BlockStmt)
+		s.stmtList(n.List)
+
+	// No-ops
+	case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL:
+
+	// Expression statements
+	case ir.OCALLFUNC:
+		n := n.(*ir.CallExpr)
+		if ir.IsIntrinsicCall(n) {
+			s.intrinsicCall(n)
+			return
+		}
+		fallthrough
+
+	case ir.OCALLINTER:
+		n := n.(*ir.CallExpr)
+		s.callResult(n, callNormal)
+		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
+				b.SetControl(m)
+				// TODO: never rewrite OPANIC to OCALLFUNC in the
+				// first place. Need to wait until all backends
+				// go through SSA.
+			}
+		}
+	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() == ir.EscNever {
+				defertype = "stack-allocated"
+			} else {
+				defertype = "heap-allocated"
+			}
+			base.WarnfAt(n.Pos(), "%s defer", defertype)
+		}
+		if s.hasOpenDefers {
+			s.openDeferRecord(n.Call.(*ir.CallExpr))
+		} else {
+			d := callDefer
+			if n.Esc() == ir.EscNever {
+				d = callDeferStack
+			}
+			s.callResult(n.Call.(*ir.CallExpr), d)
+		}
+	case ir.OGO:
+		n := n.(*ir.GoDeferStmt)
+		s.callResult(n.Call.(*ir.CallExpr), callGo)
+
+	case ir.OAS2DOTTYPE:
+		n := n.(*ir.AssignListStmt)
+		res, resok := s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true)
+		deref := false
+		if !TypeOK(n.Rhs[0].Type()) {
+			if res.Op != ssa.OpLoad {
+				s.Fatalf("dottype of non-load")
+			}
+			mem := s.mem()
+			if mem.Op == ssa.OpVarKill {
+				mem = mem.Args[0]
+			}
+			if res.Args[1] != mem {
+				s.Fatalf("memory no longer live from 2-result dottype load")
+			}
+			deref = true
+			res = res.Args[0]
+		}
+		s.assign(n.Lhs[0], res, deref, 0)
+		s.assign(n.Lhs[1], resok, false, 0)
+		return
+
+	case ir.OAS2FUNC:
+		// We come here only when it is an intrinsic call returning two values.
+		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 ir.ODCL:
+		n := n.(*ir.Decl)
+		if v := n.X; v.Esc() == ir.EscHeap {
+			s.newHeapaddr(v)
+		}
+
+	case ir.OLABEL:
+		n := n.(*ir.LabelStmt)
+		sym := n.Label
+		lab := s.label(sym)
+
+		// The label might already have a target block via a goto.
+		if lab.target == nil {
+			lab.target = s.f.NewBlock(ssa.BlockPlain)
+		}
+
+		// Go to that label.
+		// (We pretend "label:" is preceded by "goto label", unless the predecessor is unreachable.)
+		if s.curBlock != nil {
+			b := s.endBlock()
+			b.AddEdgeTo(lab.target)
+		}
+		s.startBlock(lab.target)
+
+	case ir.OGOTO:
+		n := n.(*ir.BranchStmt)
+		sym := n.Label
+
+		lab := s.label(sym)
+		if lab.target == nil {
+			lab.target = s.f.NewBlock(ssa.BlockPlain)
+		}
+
+		b := s.endBlock()
+		b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block.
+		b.AddEdgeTo(lab.target)
+
+	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:
+			//   VARDEF x
+			//   COPY x -> x
+			// which is bad because x is incorrectly considered
+			// dead before the vardef. See issue #14904.
+			return
+		}
+
+		// Evaluate RHS.
+		rhs := n.Y
+		if rhs != nil {
+			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 !ir.IsZero(rhs) {
+					s.Fatalf("literal with nonzero value in SSA: %v", rhs)
+				}
+				rhs = nil
+			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 !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.X) {
+					if base.Debug.Append > 0 { // replicating old diagnostic message
+						base.WarnfAt(n.Pos(), "append: len-only update (in local slice)")
+					}
+					break
+				}
+				if base.Debug.Append > 0 {
+					base.WarnfAt(n.Pos(), "append: len-only update")
+				}
+				s.append(rhs, true)
+				return
+			}
+		}
+
+		if ir.IsBlank(n.X) {
+			// _ = rhs
+			// Just evaluate rhs for side-effects.
+			if rhs != nil {
+				s.expr(rhs)
+			}
+			return
+		}
+
+		var t *types.Type
+		if n.Y != nil {
+			t = n.Y.Type()
+		} else {
+			t = n.X.Type()
+		}
+
+		var r *ssa.Value
+		deref := !TypeOK(t)
+		if deref {
+			if rhs == nil {
+				r = nil // Signal assign to use OpZero.
+			} else {
+				r = s.addr(rhs)
+			}
+		} else {
+			if rhs == nil {
+				r = s.zeroVal(t)
+			} else {
+				r = s.expr(rhs)
+			}
+		}
+
+		var skip skipMask
+		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.
+			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
+			}
+			// TODO: detect defaults for len/cap also.
+			// 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)) {
+			//      j = nil
+			//}
+			//if k != nil && (k.Op == OCAP && SameSafeExpr(k.Left, n.Left)) {
+			//      k = nil
+			//}
+			if i == nil {
+				skip |= skipPtr
+				if j == nil {
+					skip |= skipLen
+				}
+				if k == nil {
+					skip |= skipCap
+				}
+			}
+		}
+
+		s.assign(n.X, r, deref, skip)
+
+	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.Else)
+			}
+			break
+		}
+
+		bEnd := s.f.NewBlock(ssa.BlockPlain)
+		var likely int8
+		if n.Likely {
+			likely = 1
+		}
+		var bThen *ssa.Block
+		if len(n.Body) != 0 {
+			bThen = s.f.NewBlock(ssa.BlockPlain)
+		} else {
+			bThen = bEnd
+		}
+		var bElse *ssa.Block
+		if len(n.Else) != 0 {
+			bElse = s.f.NewBlock(ssa.BlockPlain)
+		} else {
+			bElse = bEnd
+		}
+		s.condBranch(n.Cond, bThen, bElse, likely)
+
+		if len(n.Body) != 0 {
+			s.startBlock(bThen)
+			s.stmtList(n.Body)
+			if b := s.endBlock(); b != nil {
+				b.AddEdgeTo(bEnd)
+			}
+		}
+		if len(n.Else) != 0 {
+			s.startBlock(bElse)
+			s.stmtList(n.Else)
+			if b := s.endBlock(); b != nil {
+				b.AddEdgeTo(bEnd)
+			}
+		}
+		s.startBlock(bEnd)
+
+	case ir.ORETURN:
+		n := n.(*ir.ReturnStmt)
+		s.stmtList(n.Results)
+		b := s.exit()
+		b.Pos = s.lastPos.WithIsStmt()
+
+	case ir.OTAILCALL:
+		n := n.(*ir.TailCallStmt)
+		b := s.exit()
+		b.Kind = ssa.BlockRetJmp // override BlockRet
+		b.Aux = callTargetLSym(n.Target, s.curfn.LSym)
+
+	case ir.OCONTINUE, ir.OBREAK:
+		n := n.(*ir.BranchStmt)
+		var to *ssa.Block
+		if n.Label == nil {
+			// plain break/continue
+			switch n.Op() {
+			case ir.OCONTINUE:
+				to = s.continueTo
+			case ir.OBREAK:
+				to = s.breakTo
+			}
+		} else {
+			// labeled break/continue; look up the target
+			sym := n.Label
+			lab := s.label(sym)
+			switch n.Op() {
+			case ir.OCONTINUE:
+				to = lab.continueTarget
+			case ir.OBREAK:
+				to = lab.breakTarget
+			}
+		}
+
+		b := s.endBlock()
+		b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block.
+		b.AddEdgeTo(to)
+
+	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()
+
+		// first, jump to condition test (OFOR) or body (OFORUNTIL)
+		b := s.endBlock()
+		if n.Op() == ir.OFOR {
+			b.AddEdgeTo(bCond)
+			// generate code to test condition
+			s.startBlock(bCond)
+			if n.Cond != nil {
+				s.condBranch(n.Cond, bBody, bEnd, 1)
+			} else {
+				b := s.endBlock()
+				b.Kind = ssa.BlockPlain
+				b.AddEdgeTo(bBody)
+			}
+
+		} else {
+			b.AddEdgeTo(bBody)
+		}
+
+		// set up for continue/break in body
+		prevContinue := s.continueTo
+		prevBreak := s.breakTo
+		s.continueTo = bIncr
+		s.breakTo = bEnd
+		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.Body)
+
+		// tear down continue/break
+		s.continueTo = prevContinue
+		s.breakTo = prevBreak
+		if lab != nil {
+			lab.continueTarget = nil
+			lab.breakTarget = nil
+		}
+
+		// done with body, goto incr
+		if b := s.endBlock(); b != nil {
+			b.AddEdgeTo(bIncr)
+		}
+
+		// generate incr (and, for OFORUNTIL, condition)
+		s.startBlock(bIncr)
+		if n.Post != nil {
+			s.stmt(n.Post)
+		}
+		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() != ir.OFORUNTIL && b.Pos == src.NoXPos {
+					b.Pos = bCond.Pos
+				}
+			}
+		} else {
+			// bCond is unused in OFORUNTIL, so repurpose it.
+			bLateIncr := bCond
+			// test condition
+			s.condBranch(n.Cond, bLateIncr, bEnd, 1)
+			// generate late increment
+			s.startBlock(bLateIncr)
+			s.stmtList(n.Late)
+			s.endBlock().AddEdgeTo(bBody)
+		}
+
+		s.startBlock(bEnd)
+
+	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
+		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(body)
+
+		s.breakTo = prevBreak
+		if lab != nil {
+			lab.breakTarget = nil
+		}
+
+		// walk adds explicit OBREAK nodes to the end of all reachable code paths.
+		// If we still have a current block here, then mark it unreachable.
+		if s.curBlock != nil {
+			m := s.mem()
+			b := s.endBlock()
+			b.Kind = ssa.BlockExit
+			b.SetControl(m)
+		}
+		s.startBlock(bEnd)
+
+	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 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.
+		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 ir.OVARLIVE:
+		// Insert a varlive op to record that a variable is still live.
+		n := n.(*ir.UnaryExpr)
+		v := n.X.(*ir.Name)
+		if !v.Addrtaken() {
+			s.Fatalf("VARLIVE variable %v must have Addrtaken set", v)
+		}
+		switch v.Class {
+		case ir.PAUTO, ir.PPARAM, ir.PPARAMOUT:
+		default:
+			s.Fatalf("VARLIVE variable %v must be Auto or Arg", v)
+		}
+		s.vars[memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, v, s.mem())
+
+	case ir.OCHECKNIL:
+		n := n.(*ir.UnaryExpr)
+		p := s.expr(n.X)
+		s.nilCheck(p)
+
+	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())
+	}
+}
+
+// If true, share as many open-coded defer exits as possible (with the downside of
+// worse line-number information)
+const shareDeferExits = false
+
+// exit processes any code that needs to be generated just before returning.
+// 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 {
+				if s.curBlock.Kind != ssa.BlockPlain {
+					panic("Block for an exit should be BlockPlain")
+				}
+				s.curBlock.AddEdgeTo(s.lastDeferExit)
+				s.endBlock()
+				return s.lastDeferFinalBlock
+			}
+			s.openDeferExit()
+		} else {
+			s.rtcall(ir.Syms.Deferreturn, true, nil)
+		}
+	}
+
+	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)
+
+		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.
+
+			// 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 {
+		s.lastDeferFinalBlock = b
+	}
+	return b
+}
+
+type opAndType struct {
+	op    ir.Op
+	etype types.Kind
+}
+
+var opToSSA = map[opAndType]ssa.Op{
+	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 types.TINT:
+		if s.config.PtrSize == 8 {
+			return types.TINT64
+		}
+		return types.TINT32
+	case types.TUINT:
+		if s.config.PtrSize == 8 {
+			return types.TUINT64
+		}
+		return types.TUINT32
+	case types.TUINTPTR:
+		if s.config.PtrSize == 8 {
+			return types.TUINT64
+		}
+		return types.TUINT32
+	}
+}
+
+func (s *state) ssaOp(op ir.Op, t *types.Type) ssa.Op {
+	etype := s.concreteEtype(t)
+	x, ok := opToSSA[opAndType{op, etype}]
+	if !ok {
+		s.Fatalf("unhandled binary op %v %s", op, etype)
+	}
+	return x
+}
+
+type opAndTwoTypes struct {
+	op     ir.Op
+	etype1 types.Kind
+	etype2 types.Kind
+}
+
+type twoTypes struct {
+	etype1 types.Kind
+	etype2 types.Kind
+}
+
+type twoOpsAndType struct {
+	op1              ssa.Op
+	op2              ssa.Op
+	intermediateType types.Kind
+}
+
+var fpConvOpToSSA = map[twoTypes]twoOpsAndType{
+
+	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{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{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{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{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{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}]
+	if !ok {
+		s.Fatalf("unhandled shift op %v etype=%s/%s", op, etype1, etype2)
+	}
+	return x
+}
+
+// expr converts the expression n to ssa, adds it to s and returns the ssa result.
+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())
+		defer s.popLine()
+	}
+
+	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[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[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 := 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())
+		}
+		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))
+			case 2:
+				return s.constInt16(n.Type(), int16(i))
+			case 4:
+				return s.constInt32(n.Type(), int32(i))
+			case 8:
+				return s.constInt64(n.Type(), i)
+			default:
+				s.Fatalf("bad integer size %d", n.Type().Size())
+				return nil
+			}
+		case constant.String:
+			i := constant.StringVal(u)
+			if i == "" {
+				return s.constEmptyString(n.Type())
+			}
+			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(), f)
+			case 8:
+				return s.constFloat64(n.Type(), f)
+			default:
+				s.Fatalf("bad float size %d", n.Type().Size())
+				return nil
+			}
+		case constant.Complex:
+			re, _ := constant.Float64Val(constant.Real(u))
+			im, _ := constant.Float64Val(constant.Imag(u))
+			switch n.Type().Size() {
+			case 8:
+				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[types.TFLOAT64]
+				return s.newValue2(ssa.OpComplexMake, n.Type(),
+					s.constFloat64(pt, re),
+					s.constFloat64(pt, im))
+			default:
+				s.Fatalf("bad complex size %d", n.Type().Size())
+				return nil
+			}
+		default:
+			s.Fatalf("unhandled OLITERAL %v", u.Kind())
+			return nil
+		}
+	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.X)
+		if to == from {
+			return x
+		}
+
+		// Special case for not confusing GC and liveness.
+		// We don't want pointers accidentally classified
+		// as not-pointers or vice-versa because of copy
+		// elision.
+		if to.IsPtrShaped() != from.IsPtrShaped() {
+			return s.newValue2(ssa.OpConvert, to, x, s.mem())
+		}
+
+		v := s.newValue1(ssa.OpCopy, to, x) // ensure that v has the right type
+
+		// CONVNOP closure
+		if to.Kind() == types.TFUNC && from.IsPtrShaped() {
+			return v
+		}
+
+		// named <--> unnamed type or typed <--> untyped const
+		if from.Kind() == to.Kind() {
+			return v
+		}
+
+		// unsafe.Pointer <--> *T
+		if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
+			return v
+		}
+
+		// map <--> *hmap
+		if to.Kind() == types.TMAP && from.IsPtr() &&
+			to.MapType().Hmap == from.Elem() {
+			return v
+		}
+
+		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.Kind()) != etypesign(to.Kind()) {
+			s.Fatalf("CONVNOP sign mismatch %v (%s) -> %v (%s)\n", from, from.Kind(), to, to.Kind())
+			return nil
+		}
+
+		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.Kind()) == 0 {
+			s.Fatalf("CONVNOP unrecognized non-integer %v -> %v\n", from, to)
+			return nil
+		}
+
+		// integer, same width, same sign
+		return v
+
+	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)
+		}
+		if ft.IsInteger() && tt.IsInteger() {
+			var op ssa.Op
+			if tt.Size() == ft.Size() {
+				op = ssa.OpCopy
+			} else if tt.Size() < ft.Size() {
+				// truncation
+				switch 10*ft.Size() + tt.Size() {
+				case 21:
+					op = ssa.OpTrunc16to8
+				case 41:
+					op = ssa.OpTrunc32to8
+				case 42:
+					op = ssa.OpTrunc32to16
+				case 81:
+					op = ssa.OpTrunc64to8
+				case 82:
+					op = ssa.OpTrunc64to16
+				case 84:
+					op = ssa.OpTrunc64to32
+				default:
+					s.Fatalf("weird integer truncation %v -> %v", ft, tt)
+				}
+			} else if ft.IsSigned() {
+				// sign extension
+				switch 10*ft.Size() + tt.Size() {
+				case 12:
+					op = ssa.OpSignExt8to16
+				case 14:
+					op = ssa.OpSignExt8to32
+				case 18:
+					op = ssa.OpSignExt8to64
+				case 24:
+					op = ssa.OpSignExt16to32
+				case 28:
+					op = ssa.OpSignExt16to64
+				case 48:
+					op = ssa.OpSignExt32to64
+				default:
+					s.Fatalf("bad integer sign extension %v -> %v", ft, tt)
+				}
+			} else {
+				// zero extension
+				switch 10*ft.Size() + tt.Size() {
+				case 12:
+					op = ssa.OpZeroExt8to16
+				case 14:
+					op = ssa.OpZeroExt8to32
+				case 18:
+					op = ssa.OpZeroExt8to64
+				case 24:
+					op = ssa.OpZeroExt16to32
+				case 28:
+					op = ssa.OpZeroExt16to64
+				case 48:
+					op = ssa.OpZeroExt32to64
+				default:
+					s.Fatalf("weird integer sign extension %v -> %v", ft, tt)
+				}
+			}
+			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 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat {
+				if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
+					conv = conv1
+				}
+			}
+			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 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 {
+						return s.uint32Tofloat32(n, x, ft, tt)
+					}
+					if tt.Size() == 8 {
+						return s.uint32Tofloat64(n, x, ft, tt)
+					}
+				} else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() {
+					// ft is float32 or float64, and tt is unsigned integer
+					if ft.Size() == 4 {
+						return s.float32ToUint32(n, x, ft, tt)
+					}
+					if ft.Size() == 8 {
+						return s.float64ToUint32(n, x, ft, tt)
+					}
+				}
+			}
+
+			if !ok {
+				s.Fatalf("weird float conversion %v -> %v", ft, tt)
+			}
+			op1, op2, it := conv.op1, conv.op2, conv.intermediateType
+
+			if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid {
+				// normal case, not tripping over unsigned 64
+				if op1 == ssa.OpCopy {
+					if op2 == ssa.OpCopy {
+						return x
+					}
+					return s.newValueOrSfCall1(op2, n.Type(), x)
+				}
+				if op2 == ssa.OpCopy {
+					return s.newValueOrSfCall1(op1, n.Type(), x)
+				}
+				return s.newValueOrSfCall1(op2, n.Type(), s.newValueOrSfCall1(op1, types.Types[it], x))
+			}
+			// Tricky 64-bit unsigned cases.
+			if ft.IsInteger() {
+				// tt is float32 or float64, and ft is also unsigned
+				if tt.Size() == 4 {
+					return s.uint64Tofloat32(n, x, ft, tt)
+				}
+				if tt.Size() == 8 {
+					return s.uint64Tofloat64(n, x, ft, tt)
+				}
+				s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt)
+			}
+			// ft is float32 or float64, and tt is unsigned integer
+			if ft.Size() == 4 {
+				return s.float32ToUint64(n, x, ft, tt)
+			}
+			if ft.Size() == 8 {
+				return s.float64ToUint64(n, x, ft, tt)
+			}
+			s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt)
+			return nil
+		}
+
+		if ft.IsComplex() && tt.IsComplex() {
+			var op ssa.Op
+			if ft.Size() == tt.Size() {
+				switch ft.Size() {
+				case 8:
+					op = ssa.OpRound32F
+				case 16:
+					op = ssa.OpRound64F
+				default:
+					s.Fatalf("weird complex conversion %v -> %v", ft, tt)
+				}
+			} else if ft.Size() == 8 && tt.Size() == 16 {
+				op = ssa.OpCvt32Fto64F
+			} else if ft.Size() == 16 && tt.Size() == 8 {
+				op = ssa.OpCvt64Fto32F
+			} else {
+				s.Fatalf("weird complex conversion %v -> %v", ft, 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.X.Type().Kind(), n.Type().Kind())
+		return nil
+
+	case ir.ODOTTYPE:
+		n := n.(*ir.TypeAssertExpr)
+		res, _ := s.dottype(n, false)
+		return res
+
+	// binary ops
+	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 ir.ONE:
+				return s.newValue1(ssa.OpNot, types.Types[types.TBOOL], c)
+			default:
+				s.Fatalf("ordered complex compare %v", n.Op())
+			}
+		}
+
+		// Convert OGE and OGT into OLE and OLT.
+		op := n.Op()
+		switch op {
+		case ir.OGE:
+			op, a, b = ir.OLE, b, a
+		case ir.OGT:
+			op, a, b = ir.OLT, b, a
+		}
+		if n.X.Type().IsFloat() {
+			// float comparison
+			return s.newValueOrSfCall2(s.ssaOp(op, n.X.Type()), types.Types[types.TBOOL], a, b)
+		}
+		// integer comparison
+		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 := 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)
+			aimag := s.newValue1(ssa.OpComplexImag, pt, a)
+			bimag := s.newValue1(ssa.OpComplexImag, pt, b)
+
+			if pt != wt { // Widen for calculation
+				areal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, areal)
+				breal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, breal)
+				aimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, aimag)
+				bimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, bimag)
+			}
+
+			xreal := s.newValueOrSfCall2(subop, wt, s.newValueOrSfCall2(mulop, wt, areal, breal), s.newValueOrSfCall2(mulop, wt, aimag, bimag))
+			ximag := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, areal, bimag), s.newValueOrSfCall2(mulop, wt, aimag, breal))
+
+			if pt != wt { // Narrow to store back
+				xreal = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, xreal)
+				ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, 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)
+		}
+
+		return s.newValue2(s.ssaOp(n.Op(), n.Type()), a.Type, a, b)
+
+	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.
+			mulop := ssa.OpMul64F
+			addop := ssa.OpAdd64F
+			subop := ssa.OpSub64F
+			divop := ssa.OpDiv64F
+			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)
+			aimag := s.newValue1(ssa.OpComplexImag, pt, a)
+			bimag := s.newValue1(ssa.OpComplexImag, pt, b)
+
+			if pt != wt { // Widen for calculation
+				areal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, areal)
+				breal = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, breal)
+				aimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, aimag)
+				bimag = s.newValueOrSfCall1(ssa.OpCvt32Fto64F, wt, bimag)
+			}
+
+			denom := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, breal, breal), s.newValueOrSfCall2(mulop, wt, bimag, bimag))
+			xreal := s.newValueOrSfCall2(addop, wt, s.newValueOrSfCall2(mulop, wt, areal, breal), s.newValueOrSfCall2(mulop, wt, aimag, bimag))
+			ximag := s.newValueOrSfCall2(subop, wt, s.newValueOrSfCall2(mulop, wt, aimag, breal), s.newValueOrSfCall2(mulop, wt, areal, bimag))
+
+			// TODO not sure if this is best done in wide precision or narrow
+			// Double-rounding might be an issue.
+			// Note that the pre-SSA implementation does the entire calculation
+			// in wide format, so wide is compatible.
+			xreal = s.newValueOrSfCall2(divop, wt, xreal, denom)
+			ximag = s.newValueOrSfCall2(divop, wt, ximag, denom)
+
+			if pt != wt { // Narrow to store back
+				xreal = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, xreal)
+				ximag = s.newValueOrSfCall1(ssa.OpCvt64Fto32F, pt, 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)
+		}
+		return s.intDivide(n, a, b)
+	case ir.OMOD:
+		n := n.(*ir.BinaryExpr)
+		a := s.expr(n.X)
+		b := s.expr(n.Y)
+		return s.intDivide(n, a, b)
+	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 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(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 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
+		// s.vars table (normally variables are only
+		// associated with ONAME nodes). We convert
+		//     A && B
+		// to
+		//     var = A
+		//     if var {
+		//         var = B
+		//     }
+		// Using var in the subsequent block introduces the
+		// necessary phi variable.
+		n := n.(*ir.LogicalExpr)
+		el := s.expr(n.X)
+		s.vars[n] = el
+
+		b := s.endBlock()
+		b.Kind = ssa.BlockIf
+		b.SetControl(el)
+		// In theory, we should set b.Likely here based on context.
+		// However, gc only gives us likeliness hints
+		// in a single place, for plain OIF statements,
+		// and passing around context is finnicky, so don't bother for now.
+
+		bRight := s.f.NewBlock(ssa.BlockPlain)
+		bResult := s.f.NewBlock(ssa.BlockPlain)
+		if n.Op() == ir.OANDAND {
+			b.AddEdgeTo(bRight)
+			b.AddEdgeTo(bResult)
+		} else if n.Op() == ir.OOROR {
+			b.AddEdgeTo(bResult)
+			b.AddEdgeTo(bRight)
+		}
+
+		s.startBlock(bRight)
+		er := s.expr(n.Y)
+		s.vars[n] = er
+
+		b = s.endBlock()
+		b.AddEdgeTo(bResult)
+
+		s.startBlock(bResult)
+		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 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 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.Offset)
+			return s.rawLoad(n.Type(), addr)
+		}
+		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.Offset)
+			return s.rawLoad(n.Type(), addr)
+		}
+		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)
+		}
+
+	case ir.ODEREF:
+		n := n.(*ir.StarExpr)
+		p := s.exprPtr(n.X, n.Bounded(), n.Pos())
+		return s.load(n.Type(), p)
+
+	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 !ir.IsZero(n.X) {
+				s.Fatalf("literal with nonzero value in SSA: %v", n.X)
+			}
+			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 ir.IsAddressable(n) && !s.canSSA(n) {
+			p := s.addr(n)
+			return s.load(n.Type(), p)
+		}
+		v := s.expr(n.X)
+		return s.newValue1I(ssa.OpStructSelect, n.Type(), int64(fieldIdx(n)), v)
+
+	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 ir.OINDEX:
+		n := n.(*ir.IndexExpr)
+		switch {
+		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[types.TUINT8], int64(int8(ir.StringVal(n.X)[ir.Int64Val(n.Index)])))
+			}
+			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 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[types.TUINT8], ptr)
+		case n.X.Type().IsSlice():
+			p := s.addr(n)
+			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.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[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())
+				}
+				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)
+			}
+			p := s.addr(n)
+			return s.load(n.X.Type().Elem(), p)
+		default:
+			s.Fatalf("bad type for index %v", n.X.Type())
+			return nil
+		}
+
+	case ir.OLEN, ir.OCAP:
+		n := n.(*ir.UnaryExpr)
+		switch {
+		case n.X.Type().IsSlice():
+			op := ssa.OpSliceLen
+			if n.Op() == ir.OCAP {
+				op = ssa.OpSliceCap
+			}
+			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[types.TINT], n.X.Type().NumElem())
+		}
+
+	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 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
+		if n.Low != nil {
+			i = s.expr(n.Low)
+		}
+		if n.High != nil {
+			j = s.expr(n.High)
+		}
+		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)
+
+	case ir.OSLICESTR:
+		n := n.(*ir.SliceExpr)
+		v := s.expr(n.X)
+		var i, j *ssa.Value
+		if n.Low != nil {
+			i = s.expr(n.Low)
+		}
+		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)
+
+	case ir.OCALLFUNC:
+		n := n.(*ir.CallExpr)
+		if ir.IsIntrinsicCall(n) {
+			return s.intrinsicCall(n)
+		}
+		fallthrough
+
+	case ir.OCALLINTER, ir.OCALLMETH:
+		n := n.(*ir.CallExpr)
+		return s.callResult(n, callNormal)
+
+	case ir.OGETG:
+		n := n.(*ir.CallExpr)
+		return s.newValue1(ssa.OpGetG, n.Type(), s.mem())
+
+	case ir.OAPPEND:
+		return s.append(n.(*ir.CallExpr), false)
+
+	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.
+		n := n.(*ir.CompLitExpr)
+		if !ir.IsZero(n) {
+			s.Fatalf("literal with nonzero value in SSA: %v", n)
+		}
+		return s.zeroVal(n.Type())
+
+	case ir.ONEW:
+		n := n.(*ir.UnaryExpr)
+		return s.newObject(n.Type().Elem())
+
+	default:
+		s.Fatalf("unhandled expr %v", n.Op())
+		return nil
+	}
+}
+
+// append converts an OAPPEND node to SSA.
+// If inplace is false, it converts the OAPPEND expression n to an ssa.Value,
+// adds it to s, and returns the 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 *ir.CallExpr, inplace bool) *ssa.Value {
+	// If inplace is false, process as expression "append(s, e1, e2, e3)":
+	//
+	// ptr, len, cap := s
+	// newlen := len + 3
+	// if newlen > cap {
+	//     ptr, len, cap = growslice(s, newlen)
+	//     newlen = len + 3 // recalculate to avoid a spill
+	// }
+	// // with write barriers, if needed:
+	// *(ptr+len) = e1
+	// *(ptr+len+1) = e2
+	// *(ptr+len+2) = e3
+	// return makeslice(ptr, newlen, cap)
+	//
+	//
+	// If inplace is true, process as statement "s = append(s, e1, e2, e3)":
+	//
+	// a := &s
+	// ptr, len, cap := s
+	// newlen := len + 3
+	// if uint(newlen) > uint(cap) {
+	//    newptr, len, newcap = growslice(ptr, len, cap, newlen)
+	//    vardef(a)       // if necessary, advise liveness we are writing a new a
+	//    *a.cap = newcap // write before ptr to avoid a spill
+	//    *a.ptr = newptr // with write barrier
+	// }
+	// newlen = len + 3 // recalculate to avoid a spill
+	// *a.len = newlen
+	// // with write barriers, if needed:
+	// *(ptr+len) = e1
+	// *(ptr+len+1) = e2
+	// *(ptr+len+2) = e3
+
+	et := n.Type().Elem()
+	pt := types.NewPtr(et)
+
+	// Evaluate slice
+	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)
+	} else {
+		slice = s.expr(sn)
+	}
+
+	// Allocate new blocks
+	grow := s.f.NewBlock(ssa.BlockPlain)
+	assign := s.f.NewBlock(ssa.BlockPlain)
+
+	// Decide if we need to grow
+	nargs := int64(len(n.Args) - 1)
+	p := s.newValue1(ssa.OpSlicePtr, pt, slice)
+	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(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
+	} else {
+		s.vars[lenVar] = l
+	}
+
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.Likely = ssa.BranchUnlikely
+	b.SetControl(cmp)
+	b.AddEdgeTo(grow)
+	b.AddEdgeTo(assign)
+
+	// Call growslice
+	s.startBlock(grow)
+	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() == 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, 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
+	} else {
+		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()
+	b.AddEdgeTo(assign)
+
+	// assign new elements to slots
+	s.startBlock(assign)
+
+	if inplace {
+		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
+	type argRec struct {
+		// if store is true, we're appending the value v.  If false, we're appending the
+		// value at *v.
+		v     *ssa.Value
+		store bool
+	}
+	args := make([]argRec, 0, nargs)
+	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)
+			args = append(args, argRec{v: v})
+		}
+	}
+
+	p = s.variable(ptrVar, pt) // generates phi for ptr
+	if !inplace {
+		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[types.TINT], int64(i)))
+		if arg.store {
+			s.storeType(et, addr, arg.v, 0, true)
+		} else {
+			s.move(et, addr, arg.v)
+		}
+	}
+
+	delete(s.vars, ptrVar)
+	if inplace {
+		delete(s.vars, lenVar)
+		return nil
+	}
+	delete(s.vars, newlenVar)
+	delete(s.vars, capVar)
+	// make result
+	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 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.Init())
+		s.condBranch(cond.X, mid, no, max8(likely, 0))
+		s.startBlock(mid)
+		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
+		// whether the first branch is likely or not. So we pass 0 for
+		// 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 ir.OOROR:
+		cond := cond.(*ir.LogicalExpr)
+		mid := s.f.NewBlock(ssa.BlockPlain)
+		s.stmtList(cond.Init())
+		s.condBranch(cond.X, yes, mid, min8(likely, 0))
+		s.startBlock(mid)
+		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 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)
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(c)
+	b.Likely = ssa.BranchPrediction(likely) // gc and ssa both use -1/0/+1 for likeliness
+	b.AddEdgeTo(yes)
+	b.AddEdgeTo(no)
+}
+
+type skipMask uint8
+
+const (
+	skipPtr skipMask = 1 << iota
+	skipLen
+	skipCap
+)
+
+// assign does left = right.
+// Right has already been evaluated to ssa, left has not.
+// 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 ir.Node, right *ssa.Value, deref bool, skip skipMask) {
+	if left.Op() == ir.ONAME && ir.IsBlank(left) {
+		return
+	}
+	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() == 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.
+			// For instance:
+			//   type T struct {a, b, c int}
+			//   var T x
+			//   x.b = 5
+			// For the x.b = 5 assignment we want to generate x = T{x.a, 5, x.c}
+
+			// Grab information about the structure type.
+			left := left.(*ir.SelectorExpr)
+			t := left.X.Type()
+			nf := t.NumFields()
+			idx := fieldIdx(left)
+
+			// Grab old value of structure.
+			old := s.expr(left.X)
+
+			// Make new structure.
+			new := s.newValue0(ssa.StructMakeOp(t.NumFields()), t)
+
+			// Add fields as args.
+			for i := 0; i < nf; i++ {
+				if i == idx {
+					new.AddArg(right)
+				} else {
+					new.AddArg(s.newValue1I(ssa.OpStructSelect, t.FieldType(i), int64(i), old))
+				}
+			}
+
+			// Recursively assign the new value we've made to the base of the dot op.
+			s.assign(left.X, new, false, 0)
+			// TODO: do we need to update named values here?
+			return
+		}
+		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.X.Type()
+			n := t.NumElem()
+
+			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[types.TINT], 0)
+				s.boundsCheck(z, z, ssa.BoundsIndex, false)
+				return
+			}
+			if n != 1 {
+				s.Fatalf("assigning to non-1-length array")
+			}
+			// Rewrite to a = [1]{v}
+			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.X, v, false, 0)
+			return
+		}
+		left := left.(*ir.Name)
+		// Update variable assignment.
+		s.vars[left] = right
+		s.addNamedValue(left, right)
+		return
+	}
+
+	// If this assignment clobbers an entire local variable, then emit
+	// OpVarDef so liveness analysis knows the variable is redefined.
+	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 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[types.TUNSAFEPTR]
+	}
+	if deref {
+		// Treat as a mem->mem move.
+		if right == nil {
+			s.zero(t, addr)
+		} else {
+			s.move(t, addr, right)
+		}
+		return
+	}
+	// Treat as a store.
+	s.storeType(t, addr, right, skip, !ir.IsAutoTmp(left))
+}
+
+// zeroVal returns the zero value for type t.
+func (s *state) zeroVal(t *types.Type) *ssa.Value {
+	switch {
+	case t.IsInteger():
+		switch t.Size() {
+		case 1:
+			return s.constInt8(t, 0)
+		case 2:
+			return s.constInt16(t, 0)
+		case 4:
+			return s.constInt32(t, 0)
+		case 8:
+			return s.constInt64(t, 0)
+		default:
+			s.Fatalf("bad sized integer type %v", t)
+		}
+	case t.IsFloat():
+		switch t.Size() {
+		case 4:
+			return s.constFloat32(t, 0)
+		case 8:
+			return s.constFloat64(t, 0)
+		default:
+			s.Fatalf("bad sized float type %v", t)
+		}
+	case t.IsComplex():
+		switch t.Size() {
+		case 8:
+			z := s.constFloat32(types.Types[types.TFLOAT32], 0)
+			return s.entryNewValue2(ssa.OpComplexMake, t, z, z)
+		case 16:
+			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)
+		}
+
+	case t.IsString():
+		return s.constEmptyString(t)
+	case t.IsPtrShaped():
+		return s.constNil(t)
+	case t.IsBoolean():
+		return s.constBool(false)
+	case t.IsInterface():
+		return s.constInterface(t)
+	case t.IsSlice():
+		return s.constSlice(t)
+	case t.IsStruct():
+		n := t.NumFields()
+		v := s.entryNewValue0(ssa.StructMakeOp(t.NumFields()), t)
+		for i := 0; i < n; i++ {
+			v.AddArg(s.zeroVal(t.FieldType(i)))
+		}
+		return v
+	case t.IsArray():
+		switch t.NumElem() {
+		case 0:
+			return s.entryNewValue0(ssa.OpArrayMake0, t)
+		case 1:
+			return s.entryNewValue1(ssa.OpArrayMake1, t, s.zeroVal(t.Elem()))
+		}
+	}
+	s.Fatalf("zero for type %v not implemented", t)
+	return nil
+}
+
+type callKind int8
+
+const (
+	callNormal callKind = iota
+	callDefer
+	callDeferStack
+	callGo
+)
+
+type sfRtCallDef struct {
+	rtfn  *obj.LSym
+	rtype types.Kind
+}
+
+var softFloatOps map[ssa.Op]sfRtCallDef
+
+func softfloatInit() {
+	// Some of these operations get transformed by sfcall.
+	softFloatOps = map[ssa.Op]sfRtCallDef{
+		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},
+	}
+}
+
+// TODO: do not emit sfcall if operation can be optimized to constant in later
+// opt phase
+func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) {
+	if callDef, ok := softFloatOps[op]; ok {
+		switch op {
+		case ssa.OpLess32F,
+			ssa.OpLess64F,
+			ssa.OpLeq32F,
+			ssa.OpLeq64F:
+			args[0], args[1] = args[1], args[0]
+		case ssa.OpSub32F,
+			ssa.OpSub64F:
+			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]
+		if op == ssa.OpNeq32F || op == ssa.OpNeq64F {
+			result = s.newValue1(ssa.OpNot, result.Type, result)
+		}
+		return result, true
+	}
+	return nil, false
+}
+
+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 *ir.CallExpr, args []*ssa.Value) *ssa.Value
+
+type intrinsicKey struct {
+	arch *sys.Arch
+	pkg  string
+	fn   string
+}
+
+func InitTables() {
+	intrinsics = map[intrinsicKey]intrinsicBuilder{}
+
+	var all []*sys.Arch
+	var p4 []*sys.Arch
+	var p8 []*sys.Arch
+	var lwatomics []*sys.Arch
+	for _, a := range &sys.Archs {
+		all = append(all, a)
+		if a.PtrSize == 4 {
+			p4 = append(p4, a)
+		} else {
+			p8 = append(p8, a)
+		}
+		if a.Family != sys.PPC64 {
+			lwatomics = append(lwatomics, a)
+		}
+	}
+
+	// add adds the intrinsic b for pkg.fn for the given list of architectures.
+	add := func(pkg, fn string, b intrinsicBuilder, archs ...*sys.Arch) {
+		for _, a := range archs {
+			intrinsics[intrinsicKey{a, pkg, fn}] = b
+		}
+	}
+	// addF does the same as add but operates on architecture families.
+	addF := func(pkg, fn string, b intrinsicBuilder, archFamilies ...sys.ArchFamily) {
+		m := 0
+		for _, f := range archFamilies {
+			if f >= 32 {
+				panic("too many architecture families")
+			}
+			m |= 1 << uint(f)
+		}
+		for _, a := range all {
+			if m>>uint(a.Family)&1 != 0 {
+				intrinsics[intrinsicKey{a, pkg, fn}] = b
+			}
+		}
+	}
+	// alias defines pkg.fn = pkg2.fn2 for all architectures in archs for which pkg2.fn2 exists.
+	alias := func(pkg, fn, pkg2, fn2 string, archs ...*sys.Arch) {
+		aliased := false
+		for _, a := range archs {
+			if b, ok := intrinsics[intrinsicKey{a, pkg2, fn2}]; ok {
+				intrinsics[intrinsicKey{a, pkg, fn}] = b
+				aliased = true
+			}
+		}
+		if !aliased {
+			panic(fmt.Sprintf("attempted to alias undefined intrinsic: %s.%s", pkg, fn))
+		}
+	}
+
+	/******** runtime ********/
+	if !base.Flag.Cfg.Instrumenting {
+		add("runtime", "slicebytetostringtmp",
+			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])
+			},
+			all...)
+	}
+	addF("runtime/internal/math", "MulUintptr",
+		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[types.TUINT], types.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 *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())
+			return nil
+		},
+		all...)
+	add("runtime", "getclosureptr",
+		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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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)
+			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 *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 *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 *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 *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 *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 *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 *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 *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 *ir.CallExpr, args []*ssa.Value, op ssa.Op, typ types.Kind)
+
+	makeAtomicGuardedIntrinsicARM64 := func(op0, op1 ssa.Op, typ, rtyp types.Kind, emit atomicOpEmitter) intrinsicBuilder {
+
+		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[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)
+			bTrue := s.f.NewBlock(ssa.BlockPlain)
+			bFalse := s.f.NewBlock(ssa.BlockPlain)
+			bEnd := s.f.NewBlock(ssa.BlockPlain)
+			b.AddEdgeTo(bTrue)
+			b.AddEdgeTo(bFalse)
+			b.Likely = ssa.BranchLikely
+
+			// We have atomic instructions - use it directly.
+			s.startBlock(bTrue)
+			emit(s, n, args, op1, typ)
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Use original instruction sequence.
+			s.startBlock(bFalse)
+			emit(s, n, args, op0, typ)
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Merge results.
+			s.startBlock(bEnd)
+			if rtyp == types.TNIL {
+				return nil
+			} else {
+				return s.variable(n, types.Types[rtyp])
+			}
+		}
+	}
+
+	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[n] = s.newValue1(ssa.OpSelect0, types.Types[typ], v)
+	}
+	addF("runtime/internal/atomic", "Xchg",
+		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange32, ssa.OpAtomicExchange32Variant, types.TUINT32, types.TUINT32, atomicXchgXaddEmitterARM64),
+		sys.ARM64)
+	addF("runtime/internal/atomic", "Xchg64",
+		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicExchange64, ssa.OpAtomicExchange64Variant, types.TUINT64, types.TUINT64, atomicXchgXaddEmitterARM64),
+		sys.ARM64)
+
+	addF("runtime/internal/atomic", "Xadd",
+		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 *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, types.TUINT32, types.TUINT32, atomicXchgXaddEmitterARM64),
+		sys.ARM64)
+	addF("runtime/internal/atomic", "Xadd64",
+		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd64, ssa.OpAtomicAdd64Variant, types.TUINT64, types.TUINT64, atomicXchgXaddEmitterARM64),
+		sys.ARM64)
+
+	addF("runtime/internal/atomic", "Cas",
+		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 *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 *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 *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, types.TUINT32, types.TBOOL, atomicCasEmitterARM64),
+		sys.ARM64)
+	addF("runtime/internal/atomic", "Cas64",
+		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicCompareAndSwap64, ssa.OpAtomicCompareAndSwap64Variant, types.TUINT64, types.TBOOL, atomicCasEmitterARM64),
+		sys.ARM64)
+
+	addF("runtime/internal/atomic", "And8",
+		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 *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 *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 *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 *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, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
+		sys.ARM64)
+	addF("runtime/internal/atomic", "And",
+		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAnd32, ssa.OpAtomicAnd32Variant, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
+		sys.ARM64)
+	addF("runtime/internal/atomic", "Or8",
+		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr8, ssa.OpAtomicOr8Variant, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
+		sys.ARM64)
+	addF("runtime/internal/atomic", "Or",
+		makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicOr32, ssa.OpAtomicOr32Variant, types.TNIL, types.TNIL, atomicAndOrEmitterARM64),
+		sys.ARM64)
+
+	alias("runtime/internal/atomic", "Loadint64", "runtime/internal/atomic", "Load64", all...)
+	alias("runtime/internal/atomic", "Xaddint64", "runtime/internal/atomic", "Xadd64", all...)
+	alias("runtime/internal/atomic", "Loaduint", "runtime/internal/atomic", "Load", p4...)
+	alias("runtime/internal/atomic", "Loaduint", "runtime/internal/atomic", "Load64", p8...)
+	alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load", p4...)
+	alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load64", p8...)
+	alias("runtime/internal/atomic", "LoadAcq", "runtime/internal/atomic", "Load", lwatomics...)
+	alias("runtime/internal/atomic", "LoadAcq64", "runtime/internal/atomic", "Load64", lwatomics...)
+	alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...)
+	alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq", p4...) // linknamed
+	alias("runtime/internal/atomic", "LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...)
+	alias("sync", "runtime_LoadAcquintptr", "runtime/internal/atomic", "LoadAcq64", p8...) // linknamed
+	alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store", p4...)
+	alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store64", p8...)
+	alias("runtime/internal/atomic", "StoreRel", "runtime/internal/atomic", "Store", lwatomics...)
+	alias("runtime/internal/atomic", "StoreRel64", "runtime/internal/atomic", "Store64", lwatomics...)
+	alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...)
+	alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel", p4...) // linknamed
+	alias("runtime/internal/atomic", "StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...)
+	alias("sync", "runtime_StoreReluintptr", "runtime/internal/atomic", "StoreRel64", p8...) // linknamed
+	alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg", p4...)
+	alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg64", p8...)
+	alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd", p4...)
+	alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd64", p8...)
+	alias("runtime/internal/atomic", "Casuintptr", "runtime/internal/atomic", "Cas", p4...)
+	alias("runtime/internal/atomic", "Casuintptr", "runtime/internal/atomic", "Cas64", p8...)
+	alias("runtime/internal/atomic", "Casp1", "runtime/internal/atomic", "Cas", p4...)
+	alias("runtime/internal/atomic", "Casp1", "runtime/internal/atomic", "Cas64", p8...)
+	alias("runtime/internal/atomic", "CasRel", "runtime/internal/atomic", "Cas", lwatomics...)
+
+	/******** math ********/
+	addF("math", "Sqrt",
+		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 *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 *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 *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 *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 *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 *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 *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 *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 *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[types.TFLOAT64])
+			}
+			v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasFMA)
+			b := s.endBlock()
+			b.Kind = ssa.BlockIf
+			b.SetControl(v)
+			bTrue := s.f.NewBlock(ssa.BlockPlain)
+			bFalse := s.f.NewBlock(ssa.BlockPlain)
+			bEnd := s.f.NewBlock(ssa.BlockPlain)
+			b.AddEdgeTo(bTrue)
+			b.AddEdgeTo(bFalse)
+			b.Likely = ssa.BranchLikely // >= haswell cpus are common
+
+			// We have the intrinsic - use it directly.
+			s.startBlock(bTrue)
+			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.
+			s.startBlock(bFalse)
+			s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Merge results.
+			s.startBlock(bEnd)
+			return s.variable(n, types.Types[types.TFLOAT64])
+		},
+		sys.AMD64)
+	addF("math", "FMA",
+		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[types.TFLOAT64])
+			}
+			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)
+			bTrue := s.f.NewBlock(ssa.BlockPlain)
+			bFalse := s.f.NewBlock(ssa.BlockPlain)
+			bEnd := s.f.NewBlock(ssa.BlockPlain)
+			b.AddEdgeTo(bTrue)
+			b.AddEdgeTo(bFalse)
+			b.Likely = ssa.BranchLikely
+
+			// We have the intrinsic - use it directly.
+			s.startBlock(bTrue)
+			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.
+			s.startBlock(bFalse)
+			s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Merge results.
+			s.startBlock(bEnd)
+			return s.variable(n, types.Types[types.TFLOAT64])
+		},
+		sys.ARM)
+
+	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)
+			bTrue := s.f.NewBlock(ssa.BlockPlain)
+			bFalse := s.f.NewBlock(ssa.BlockPlain)
+			bEnd := s.f.NewBlock(ssa.BlockPlain)
+			b.AddEdgeTo(bTrue)
+			b.AddEdgeTo(bFalse)
+			b.Likely = ssa.BranchLikely // most machines have sse4.1 nowadays
+
+			// We have the intrinsic - use it directly.
+			s.startBlock(bTrue)
+			s.vars[n] = s.newValue1(op, types.Types[types.TFLOAT64], args[0])
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Call the pure Go version.
+			s.startBlock(bFalse)
+			s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Merge results.
+			s.startBlock(bEnd)
+			return s.variable(n, types.Types[types.TFLOAT64])
+		}
+	}
+	addF("math", "RoundToEven",
+		makeRoundAMD64(ssa.OpRoundToEven),
+		sys.AMD64)
+	addF("math", "Floor",
+		makeRoundAMD64(ssa.OpFloor),
+		sys.AMD64)
+	addF("math", "Ceil",
+		makeRoundAMD64(ssa.OpCeil),
+		sys.AMD64)
+	addF("math", "Trunc",
+		makeRoundAMD64(ssa.OpTrunc),
+		sys.AMD64)
+
+	/******** math/bits ********/
+	addF("math/bits", "TrailingZeros64",
+		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 *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 *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 *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 *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 *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 *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 *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...)
+	alias("math/bits", "ReverseBytes32", "runtime/internal/sys", "Bswap32", all...)
+	// 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 *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 *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 *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+			if s.config.PtrSize == 4 {
+				return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], args[0])
+			}
+			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 *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+			if s.config.PtrSize == 4 {
+				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[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 *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 *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+			if s.config.PtrSize == 4 {
+				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[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 *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 *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+			if s.config.PtrSize == 4 {
+				return s.newValue1(ssa.OpBitLen32, types.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 *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 *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 *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 *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 *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+			if s.config.PtrSize == 4 {
+				return s.newValue1(ssa.OpBitRev32, types.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 *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 *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 *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 *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 *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)
+			bTrue := s.f.NewBlock(ssa.BlockPlain)
+			bFalse := s.f.NewBlock(ssa.BlockPlain)
+			bEnd := s.f.NewBlock(ssa.BlockPlain)
+			b.AddEdgeTo(bTrue)
+			b.AddEdgeTo(bFalse)
+			b.Likely = ssa.BranchLikely // most machines have popcnt nowadays
+
+			// We have the intrinsic - use it directly.
+			s.startBlock(bTrue)
+			op := op64
+			if s.config.PtrSize == 4 {
+				op = op32
+			}
+			s.vars[n] = s.newValue1(op, types.Types[types.TINT], args[0])
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Call the pure Go version.
+			s.startBlock(bFalse)
+			s.vars[n] = s.callResult(n, callNormal) // types.Types[TINT]
+			s.endBlock().AddEdgeTo(bEnd)
+
+			// Merge results.
+			s.startBlock(bEnd)
+			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 *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 *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 *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 *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 *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 *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 *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 *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+			// check for divide-by-zero/overflow and panic with appropriate message
+			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)
+
+	alias("runtime/internal/sys", "Ctz8", "math/bits", "TrailingZeros8", all...)
+	alias("runtime/internal/sys", "TrailingZeros8", "math/bits", "TrailingZeros8", all...)
+	alias("runtime/internal/sys", "TrailingZeros64", "math/bits", "TrailingZeros64", all...)
+	alias("runtime/internal/sys", "Len8", "math/bits", "Len8", all...)
+	alias("runtime/internal/sys", "Len64", "math/bits", "Len64", all...)
+	alias("runtime/internal/sys", "OnesCount64", "math/bits", "OnesCount64", all...)
+
+	/******** sync/atomic ********/
+
+	// Note: these are disabled by flag_race in findIntrinsic below.
+	alias("sync/atomic", "LoadInt32", "runtime/internal/atomic", "Load", all...)
+	alias("sync/atomic", "LoadInt64", "runtime/internal/atomic", "Load64", all...)
+	alias("sync/atomic", "LoadPointer", "runtime/internal/atomic", "Loadp", all...)
+	alias("sync/atomic", "LoadUint32", "runtime/internal/atomic", "Load", all...)
+	alias("sync/atomic", "LoadUint64", "runtime/internal/atomic", "Load64", all...)
+	alias("sync/atomic", "LoadUintptr", "runtime/internal/atomic", "Load", p4...)
+	alias("sync/atomic", "LoadUintptr", "runtime/internal/atomic", "Load64", p8...)
+
+	alias("sync/atomic", "StoreInt32", "runtime/internal/atomic", "Store", all...)
+	alias("sync/atomic", "StoreInt64", "runtime/internal/atomic", "Store64", all...)
+	// Note: not StorePointer, that needs a write barrier.  Same below for {CompareAnd}Swap.
+	alias("sync/atomic", "StoreUint32", "runtime/internal/atomic", "Store", all...)
+	alias("sync/atomic", "StoreUint64", "runtime/internal/atomic", "Store64", all...)
+	alias("sync/atomic", "StoreUintptr", "runtime/internal/atomic", "Store", p4...)
+	alias("sync/atomic", "StoreUintptr", "runtime/internal/atomic", "Store64", p8...)
+
+	alias("sync/atomic", "SwapInt32", "runtime/internal/atomic", "Xchg", all...)
+	alias("sync/atomic", "SwapInt64", "runtime/internal/atomic", "Xchg64", all...)
+	alias("sync/atomic", "SwapUint32", "runtime/internal/atomic", "Xchg", all...)
+	alias("sync/atomic", "SwapUint64", "runtime/internal/atomic", "Xchg64", all...)
+	alias("sync/atomic", "SwapUintptr", "runtime/internal/atomic", "Xchg", p4...)
+	alias("sync/atomic", "SwapUintptr", "runtime/internal/atomic", "Xchg64", p8...)
+
+	alias("sync/atomic", "CompareAndSwapInt32", "runtime/internal/atomic", "Cas", all...)
+	alias("sync/atomic", "CompareAndSwapInt64", "runtime/internal/atomic", "Cas64", all...)
+	alias("sync/atomic", "CompareAndSwapUint32", "runtime/internal/atomic", "Cas", all...)
+	alias("sync/atomic", "CompareAndSwapUint64", "runtime/internal/atomic", "Cas64", all...)
+	alias("sync/atomic", "CompareAndSwapUintptr", "runtime/internal/atomic", "Cas", p4...)
+	alias("sync/atomic", "CompareAndSwapUintptr", "runtime/internal/atomic", "Cas64", p8...)
+
+	alias("sync/atomic", "AddInt32", "runtime/internal/atomic", "Xadd", all...)
+	alias("sync/atomic", "AddInt64", "runtime/internal/atomic", "Xadd64", all...)
+	alias("sync/atomic", "AddUint32", "runtime/internal/atomic", "Xadd", all...)
+	alias("sync/atomic", "AddUint64", "runtime/internal/atomic", "Xadd64", all...)
+	alias("sync/atomic", "AddUintptr", "runtime/internal/atomic", "Xadd", p4...)
+	alias("sync/atomic", "AddUintptr", "runtime/internal/atomic", "Xadd64", p8...)
+
+	/******** math/big ********/
+	add("math/big", "mulWW",
+		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)
+}
+
+// findIntrinsic returns a function which builds the SSA equivalent of the
+// function identified by the symbol sym.  If sym is not an intrinsic call, returns nil.
+func findIntrinsic(sym *types.Sym) intrinsicBuilder {
+	if sym == nil || sym.Pkg == nil {
+		return nil
+	}
+	pkg := sym.Pkg.Path
+	if sym.Pkg == types.LocalPkg {
+		pkg = base.Ctxt.Pkgpath
+	}
+	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 Arch.SoftFloat && pkg == "math" {
+		return nil
+	}
+
+	fn := sym.Name
+	if ssa.IntrinsicsDisable {
+		if pkg == "runtime" && (fn == "getcallerpc" || fn == "getcallersp" || fn == "getclosureptr") {
+			// These runtime functions don't have definitions, must be intrinsics.
+		} else {
+			return nil
+		}
+	}
+	return intrinsics[intrinsicKey{Arch.LinkArch.Arch, pkg, fn}]
+}
+
+func IsIntrinsicCall(n *ir.CallExpr) bool {
+	if n == nil {
+		return false
+	}
+	name, ok := n.X.(*ir.Name)
+	if !ok {
+		return false
+	}
+	return findIntrinsic(name.Sym()) != nil
+}
+
+// intrinsicCall converts a call to a recognized intrinsic function into the intrinsic SSA operation.
+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 {
+			x = s.mem()
+		}
+		if x.Op == ssa.OpSelect0 || x.Op == ssa.OpSelect1 {
+			x = x.Args[0]
+		}
+		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 *ir.CallExpr) []*ssa.Value {
+	args := make([]*ssa.Value, len(n.Args))
+	for i, n := range n.Args {
+		args[i] = s.expr(n)
+	}
+	return args
+}
+
+// openDeferRecord adds code to evaluate and store the args for an open-code defer
+// call, and records info about the defer, so we can generate proper code on the
+// exit paths. n is the sub-node of the defer node that is the actual function
+// 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 *ir.CallExpr) {
+	var args []*ssa.Value
+	var argNodes []*ir.Name
+
+	opendefer := &openDeferInfo{
+		n: n,
+	}
+	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.(*ir.Name)
+		if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) {
+			opendefer.closure = closure
+		}
+	} else if n.Op() == ir.OCALLMETH {
+		base.Fatalf("OCALLMETH missed by walkCall")
+	} else {
+		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.(*ir.Name)
+		opendefer.rcvrNode = opendefer.rcvr.Aux.(*ir.Name)
+	}
+	for _, argn := range n.Args {
+		var v *ssa.Value
+		if TypeOK(argn.Type()) {
+			v = s.openDeferSave(nil, argn.Type(), s.expr(argn))
+		} else {
+			v = s.openDeferSave(argn, argn.Type(), nil)
+		}
+		args = append(args, v)
+		argNodes = append(argNodes, v.Aux.(*ir.Name))
+	}
+	opendefer.argVals = args
+	opendefer.argNodes = argNodes
+	index := len(s.openDefers)
+	s.openDefers = append(s.openDefers, opendefer)
+
+	// Update deferBits only after evaluation and storage to stack of
+	// args/receiver/interface is successful.
+	bitvalue := s.constInt8(types.Types[types.TUINT8], 1<<uint(index))
+	newDeferBits := s.newValue2(ssa.OpOr8, types.Types[types.TUINT8], s.variable(deferBitsVar, types.Types[types.TUINT8]), bitvalue)
+	s.vars[deferBitsVar] = newDeferBits
+	s.store(types.Types[types.TUINT8], s.deferBitsAddr, newDeferBits)
+}
+
+// openDeferSave generates SSA nodes to store a value (with type t) for an
+// open-coded defer at an explicit autotmp location on the stack, so it can be
+// reloaded and used for the appropriate call on exit. If type t is SSAable, then
+// val must be non-nil (and n should be nil) and val is the value to be stored. If
+// type t is non-SSAable, then n must be non-nil (and val should be nil) and n is
+// evaluated (via s.addr() below) to get the value that is to be stored. The
+// function returns an SSA value representing a pointer to the autotmp location.
+func (s *state) openDeferSave(n ir.Node, t *types.Type, val *ssa.Value) *ssa.Value {
+	canSSA := TypeOK(t)
+	var pos src.XPos
+	if canSSA {
+		pos = val.Pos
+	} else {
+		pos = n.Pos()
+	}
+	argTemp := typecheck.TempAt(pos.WithNotStmt(), s.curfn, t)
+	argTemp.SetOpenDeferSlot(true)
+	var addrArgTemp *ssa.Value
+	// Use OpVarLive to make sure stack slots for the args, etc. are not
+	// removed by dead-store elimination
+	if s.curBlock.ID != s.f.Entry.ID {
+		// Force the argtmp storing this defer function/receiver/arg to be
+		// declared in the entry block, so that it will be live for the
+		// defer exit code (which will actually access it only if the
+		// associated defer call has been activated).
+		s.defvars[s.f.Entry.ID][memVar] = s.entryNewValue1A(ssa.OpVarDef, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][memVar])
+		s.defvars[s.f.Entry.ID][memVar] = s.entryNewValue1A(ssa.OpVarLive, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][memVar])
+		addrArgTemp = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(argTemp.Type()), argTemp, s.sp, s.defvars[s.f.Entry.ID][memVar])
+	} else {
+		// Special case if we're still in the entry block. We can't use
+		// the above code, since s.defvars[s.f.Entry.ID] isn't defined
+		// until we end the entry block with s.endBlock().
+		s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, argTemp, s.mem(), false)
+		s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argTemp, s.mem(), false)
+		addrArgTemp = s.newValue2Apos(ssa.OpLocalAddr, types.NewPtr(argTemp.Type()), argTemp, s.sp, s.mem(), false)
+	}
+	if t.HasPointers() {
+		// Since we may use this argTemp during exit depending on the
+		// deferBits, we must define it unconditionally on entry.
+		// Therefore, we must make sure it is zeroed out in the entry
+		// block if it contains pointers, else GC may wrongly follow an
+		// uninitialized pointer value.
+		argTemp.SetNeedzero(true)
+	}
+	if !canSSA {
+		a := s.addr(n)
+		s.move(t, addrArgTemp, a)
+		return addrArgTemp
+	}
+	// We are storing to the stack, hence we can avoid the full checks in
+	// storeType() (no write barrier) and do a simple store().
+	s.store(t, addrArgTemp, val)
+	return addrArgTemp
+}
+
+// openDeferExit generates SSA for processing all the open coded defers at exit.
+// The code involves loading deferBits, and checking each of the bits to see if
+// the corresponding defer statement was executed. For each bit that is turned
+// on, the associated defer call is made.
+func (s *state) openDeferExit() {
+	deferExit := s.f.NewBlock(ssa.BlockPlain)
+	s.endBlock().AddEdgeTo(deferExit)
+	s.startBlock(deferExit)
+	s.lastDeferExit = deferExit
+	s.lastDeferCount = len(s.openDefers)
+	zeroval := s.constInt8(types.Types[types.TUINT8], 0)
+	// Test for and run defers in reverse order
+	for i := len(s.openDefers) - 1; i >= 0; i-- {
+		r := s.openDefers[i]
+		bCond := s.f.NewBlock(ssa.BlockPlain)
+		bEnd := s.f.NewBlock(ssa.BlockPlain)
+
+		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[types.TUINT8], 1<<uint(i))
+		andval := s.newValue2(ssa.OpAnd8, types.Types[types.TUINT8], deferBits, bitval)
+		eqVal := s.newValue2(ssa.OpEq8, types.Types[types.TBOOL], andval, zeroval)
+		b := s.endBlock()
+		b.Kind = ssa.BlockIf
+		b.SetControl(eqVal)
+		b.AddEdgeTo(bEnd)
+		b.AddEdgeTo(bCond)
+		bCond.AddEdgeTo(bEnd)
+		s.startBlock(bCond)
+
+		// Clear this bit in deferBits and force store back to stack, so
+		// we will not try to re-run this defer call if this defer call panics.
+		nbitval := s.newValue1(ssa.OpCom8, types.Types[types.TUINT8], bitval)
+		maskedval := s.newValue2(ssa.OpAnd8, types.Types[types.TUINT8], deferBits, nbitval)
+		s.store(types.Types[types.TUINT8], s.deferBitsAddr, maskedval)
+		// Use this value for following tests, so we keep previous
+		// bits cleared.
+		s.vars[deferBitsVar] = maskedval
+
+		// Generate code to call the function call of the defer, using the
+		// closure/receiver/args that were stored in argtmps at the point
+		// of the defer statement.
+		argStart := base.Ctxt.FixedFrameSize()
+		fn := r.n.X
+		stksize := fn.Type().ArgWidth()
+		var ACArgs []ssa.Param
+		var ACResults []ssa.Param
+		var callArgs []*ssa.Value
+		if r.rcvr != nil {
+			// rcvr in case of OCALLINTER
+			v := s.load(r.rcvr.Type.Elem(), r.rcvr)
+			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINTPTR], Offset: int32(argStart)})
+			callArgs = append(callArgs, v)
+		}
+		for j, argAddrVal := range r.argVals {
+			f := getParam(r.n, j)
+			ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + f.Offset)})
+			var a *ssa.Value
+			if !TypeOK(f.Type) {
+				a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem())
+			} else {
+				a = s.load(f.Type, argAddrVal)
+			}
+			callArgs = append(callArgs, a)
+		}
+		var call *ssa.Value
+		if r.closure != nil {
+			v := s.load(r.closure.Type.Elem(), r.closure)
+			s.maybeNilCheckClosure(v, callDefer)
+			codeptr := s.rawLoad(types.Types[types.TUINTPTR], v)
+			aux := ssa.ClosureAuxCall(ACArgs, ACResults)
+			call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
+		} else {
+			aux := ssa.StaticAuxCall(fn.(*ir.Name).Linksym(), ACArgs, ACResults)
+			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+		}
+		callArgs = append(callArgs, s.mem())
+		call.AddArgs(callArgs...)
+		call.AuxInt = stksize
+		s.vars[memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
+		// Make sure that the stack slots with pointers are kept live
+		// through the call (which is a pre-emption point). Also, we will
+		// use the first call of the last defer exit to compute liveness
+		// for the deferreturn, so we want all stack slots to be live.
+		if r.closureNode != nil {
+			s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.closureNode, s.mem(), false)
+		}
+		if r.rcvrNode != nil {
+			if r.rcvrNode.Type().HasPointers() {
+				s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.rcvrNode, s.mem(), false)
+			}
+		}
+		for _, argNode := range r.argNodes {
+			if argNode.Type().HasPointers() {
+				s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argNode, s.mem(), false)
+			}
+		}
+
+		s.endBlock()
+		s.startBlock(bEnd)
+	}
+}
+
+func (s *state) callResult(n *ir.CallExpr, k callKind) *ssa.Value {
+	return s.call(n, k, false)
+}
+
+func (s *state) callAddr(n *ir.CallExpr, k callKind) *ssa.Value {
+	return s.call(n, k, true)
+}
+
+// Calls the function n using the specified call type.
+// Returns the address of the return value (or nil if none).
+func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Value {
+	s.prevCall = nil
+	var callee *ir.Name    // target function (if static)
+	var closure *ssa.Value // ptr to closure to run (if dynamic)
+	var codeptr *ssa.Value // ptr to target code (if dynamic)
+	var rcvr *ssa.Value    // receiver to set
+	fn := n.X
+	var ACArgs []ssa.Param
+	var ACResults []ssa.Param
+	var callArgs []*ssa.Value
+	res := n.X.Type().Results()
+	if k == callNormal {
+		nf := res.NumFields()
+		for i := 0; i < nf; i++ {
+			fp := res.Field(i)
+			ACResults = append(ACResults, ssa.Param{Type: fp.Type, Offset: int32(fp.Offset + base.Ctxt.FixedFrameSize())})
+		}
+	}
+
+	inRegisters := false
+
+	switch n.Op() {
+	case ir.OCALLFUNC:
+		if k == callNormal && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC {
+			fn := fn.(*ir.Name)
+			callee = fn
+			// TODO remove after register abi is working
+			inRegistersImported := fn.Pragma()&ir.RegisterParams != 0
+			inRegistersSamePackage := fn.Func != nil && fn.Func.Pragma&ir.RegisterParams != 0
+			inRegisters = inRegistersImported || inRegistersSamePackage
+			if inRegisters {
+				s.f.Warnl(n.Pos(), "called function %v has register params", callee)
+			}
+			break
+		}
+		closure = s.expr(fn)
+		if k != callDefer && k != callDeferStack {
+			// Deferred nil function needs to panic when the function is invoked,
+			// not the point of defer statement.
+			s.maybeNilCheckClosure(closure, k)
+		}
+	case ir.OCALLMETH:
+		base.Fatalf("OCALLMETH missed by walkCall")
+	case ir.OCALLINTER:
+		if fn.Op() != ir.ODOTINTER {
+			s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op())
+		}
+		fn := fn.(*ir.SelectorExpr)
+		var iclosure *ssa.Value
+		iclosure, rcvr = s.getClosureAndRcvr(fn)
+		if k == callNormal {
+			codeptr = s.load(types.Types[types.TUINTPTR], iclosure)
+		} else {
+			closure = iclosure
+		}
+	}
+	types.CalcSize(fn.Type())
+	stksize := fn.Type().ArgWidth() // includes receiver, args, and results
+
+	var call *ssa.Value
+	if k == callDeferStack {
+		// Make a defer struct d on the stack.
+		t := deferstruct(stksize)
+		d := typecheck.TempAt(n.Pos(), s.curfn, t)
+
+		s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem())
+		addr := s.addr(d)
+
+		// Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer.
+		// 0: siz
+		s.store(types.Types[types.TUINT32],
+			s.newValue1I(ssa.OpOffPtr, types.Types[types.TUINT32].PtrTo(), t.FieldOff(0), addr),
+			s.constInt32(types.Types[types.TUINT32], int32(stksize)))
+		// 1: started, set in deferprocStack
+		// 2: heap, set in deferprocStack
+		// 3: openDefer
+		// 4: sp, set in deferprocStack
+		// 5: pc, set in deferprocStack
+		// 6: fn
+		s.store(closure.Type,
+			s.newValue1I(ssa.OpOffPtr, closure.Type.PtrTo(), t.FieldOff(6), addr),
+			closure)
+		// 7: panic, set in deferprocStack
+		// 8: link, set in deferprocStack
+		// 9: framepc
+		// 10: varp
+		// 11: fd
+
+		// Then, store all the arguments of the defer call.
+		ft := fn.Type()
+		off := t.FieldOff(12)
+		args := n.Args
+
+		// Set receiver (for interface calls). Always a pointer.
+		if rcvr != nil {
+			p := s.newValue1I(ssa.OpOffPtr, ft.Recv().Type.PtrTo(), off, addr)
+			s.store(types.Types[types.TUINTPTR], p, rcvr)
+		}
+		// Set receiver (for method calls).
+		if n.Op() == ir.OCALLMETH {
+			base.Fatalf("OCALLMETH missed by walkCall")
+		}
+		// Set other args.
+		for _, f := range ft.Params().Fields().Slice() {
+			s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
+			args = args[1:]
+		}
+
+		// Call runtime.deferprocStack with pointer to _defer record.
+		ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINTPTR], Offset: int32(base.Ctxt.FixedFrameSize())})
+		aux := ssa.StaticAuxCall(ir.Syms.DeferprocStack, ACArgs, ACResults)
+		callArgs = append(callArgs, addr, s.mem())
+		call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+		call.AddArgs(callArgs...)
+		if stksize < int64(types.PtrSize) {
+			// We need room for both the call to deferprocStack and the call to
+			// the deferred function.
+			// TODO Revisit this if/when we pass args in registers.
+			stksize = int64(types.PtrSize)
+		}
+		call.AuxInt = stksize
+	} else {
+		// Store arguments to stack, including defer/go arguments and receiver for method calls.
+		// These are written in SP-offset order.
+		argStart := base.Ctxt.FixedFrameSize()
+		// Defer/go args.
+		if k != callNormal {
+			// Write argsize and closure (args to newproc/deferproc).
+			argsize := s.constInt32(types.Types[types.TUINT32], int32(stksize))
+			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINT32], Offset: int32(argStart)})
+			callArgs = append(callArgs, argsize)
+			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINTPTR], Offset: int32(argStart) + int32(types.PtrSize)})
+			callArgs = append(callArgs, closure)
+			stksize += 2 * int64(types.PtrSize)
+			argStart += 2 * int64(types.PtrSize)
+		}
+
+		// Set receiver (for interface calls).
+		if rcvr != nil {
+			ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINTPTR], Offset: int32(argStart)})
+			callArgs = append(callArgs, rcvr)
+		}
+
+		// Write args.
+		t := n.X.Type()
+		args := n.Args
+		if n.Op() == ir.OCALLMETH {
+			base.Fatalf("OCALLMETH missed by walkCall")
+		}
+		for i, n := range args {
+			f := t.Params().Field(i)
+			ACArg, arg := s.putArg(n, f.Type, argStart+f.Offset)
+			ACArgs = append(ACArgs, ACArg)
+			callArgs = append(callArgs, arg)
+		}
+
+		callArgs = append(callArgs, s.mem())
+
+		// call target
+		switch {
+		case k == callDefer:
+			aux := ssa.StaticAuxCall(ir.Syms.Deferproc, ACArgs, ACResults)
+			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+		case k == callGo:
+			aux := ssa.StaticAuxCall(ir.Syms.Newproc, ACArgs, ACResults)
+			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+		case closure != nil:
+			// rawLoad because loading the code pointer from a
+			// closure is always safe, but IsSanitizerSafeAddr
+			// can't always figure that out currently, and it's
+			// critical that we not clobber any arguments already
+			// stored onto the stack.
+			codeptr = s.rawLoad(types.Types[types.TUINTPTR], closure)
+			aux := ssa.ClosureAuxCall(ACArgs, ACResults)
+			call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure)
+		case codeptr != nil:
+			aux := ssa.InterfaceAuxCall(ACArgs, ACResults)
+			call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr)
+		case callee != nil:
+			aux := ssa.StaticAuxCall(callTargetLSym(callee, s.curfn.LSym), ACArgs, ACResults)
+			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+		default:
+			s.Fatalf("bad call type %v %v", n.Op(), n)
+		}
+		call.AddArgs(callArgs...)
+		call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
+	}
+	s.prevCall = call
+	s.vars[memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
+	// Insert OVARLIVE nodes
+	for _, name := range n.KeepAlive {
+		s.stmt(ir.NewUnaryExpr(n.Pos(), ir.OVARLIVE, name))
+	}
+
+	// Finish block for defers
+	if k == callDefer || k == callDeferStack {
+		b := s.endBlock()
+		b.Kind = ssa.BlockDefer
+		b.SetControl(call)
+		bNext := s.f.NewBlock(ssa.BlockPlain)
+		b.AddEdgeTo(bNext)
+		// Add recover edge to exit code.
+		r := s.f.NewBlock(ssa.BlockPlain)
+		s.startBlock(r)
+		s.exit()
+		b.AddEdgeTo(r)
+		b.Likely = ssa.BranchLikely
+		s.startBlock(bNext)
+	}
+
+	if res.NumFields() == 0 || k != callNormal {
+		// call has no return value. Continue with the next statement.
+		return nil
+	}
+	fp := res.Field(0)
+	if returnResultAddr {
+		pt := types.NewPtr(fp.Type)
+		return s.newValue1I(ssa.OpSelectNAddr, pt, 0, call)
+	}
+
+	return s.newValue1I(ssa.OpSelectN, fp.Type, 0, call)
+}
+
+// maybeNilCheckClosure checks if a nil check of a closure is needed in some
+// architecture-dependent situations and, if so, emits the nil check.
+func (s *state) maybeNilCheckClosure(closure *ssa.Value, k callKind) {
+	if Arch.LinkArch.Family == sys.Wasm || objabi.GOOS == "aix" && k != callGo {
+		// On AIX, the closure needs to be verified as fn can be nil, except if it's a call go. This needs to be handled by the runtime to have the "go of nil func value" error.
+		// TODO(neelance): On other architectures this should be eliminated by the optimization steps
+		s.nilCheck(closure)
+	}
+}
+
+// getClosureAndRcvr returns values for the appropriate closure and receiver of an
+// interface call
+func (s *state) getClosureAndRcvr(fn *ir.SelectorExpr) (*ssa.Value, *ssa.Value) {
+	i := s.expr(fn.X)
+	itab := s.newValue1(ssa.OpITab, types.Types[types.TUINTPTR], i)
+	s.nilCheck(itab)
+	itabidx := fn.Offset() + 2*int64(types.PtrSize) + 8 // offset of fun field in runtime.itab
+	closure := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.UintptrPtr, itabidx, itab)
+	rcvr := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, i)
+	return closure, rcvr
+}
+
+// etypesign returns the signed-ness of e, for integer/pointer etypes.
+// -1 means signed, +1 means unsigned, 0 means non-integer/non-pointer.
+func etypesign(e types.Kind) int8 {
+	switch e {
+	case types.TINT8, types.TINT16, types.TINT32, types.TINT64, types.TINT:
+		return -1
+	case types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, types.TUINT, types.TUINTPTR, types.TUNSAFEPTR:
+		return +1
+	}
+	return 0
+}
+
+// addr converts the address of the expression n to SSA, adds it to s and returns the SSA result.
+// The value that the returned Value represents is guaranteed to be non-nil.
+func (s *state) addr(n ir.Node) *ssa.Value {
+	if n.Op() != ir.ONAME {
+		s.pushLine(n.Pos())
+		defer s.popLine()
+	}
+
+	if s.canSSA(n) {
+		s.Fatalf("addr of canSSA expression: %+v", n)
+	}
+
+	t := types.NewPtr(n.Type())
+	linksymOffset := func(lsym *obj.LSym, offset int64) *ssa.Value {
+		v := s.entryNewValue1A(ssa.OpAddr, t, lsym, s.sb)
+		// TODO: Make OpAddr use AuxInt as well as Aux.
+		if offset != 0 {
+			v = s.entryNewValue1I(ssa.OpOffPtr, v.Type, offset, v)
+		}
+		return v
+	}
+	switch n.Op() {
+	case ir.OLINKSYMOFFSET:
+		no := n.(*ir.LinksymOffsetExpr)
+		return linksymOffset(no.Linksym, no.Offset_)
+	case ir.ONAME:
+		n := n.(*ir.Name)
+		if n.Heapaddr != nil {
+			return s.expr(n.Heapaddr)
+		}
+		switch n.Class {
+		case ir.PEXTERN:
+			// global variable
+			return linksymOffset(n.Linksym(), 0)
+		case ir.PPARAM:
+			// parameter slot
+			v := s.decladdrs[n]
+			if v != nil {
+				return v
+			}
+			if n == ir.RegFP {
+				// Special arg that points to the frame pointer (Used by ORECOVER).
+				return s.entryNewValue2A(ssa.OpLocalAddr, t, n, s.sp, s.startmem)
+			}
+			s.Fatalf("addr of undeclared ONAME %v. declared: %v", n, s.decladdrs)
+			return nil
+		case ir.PAUTO:
+			return s.newValue2Apos(ssa.OpLocalAddr, t, n, s.sp, s.mem(), !ir.IsAutoTmp(n))
+
+		case ir.PPARAMOUT: // Same as PAUTO -- cannot generate LEA early.
+			// ensure that we reuse symbols for out parameters so
+			// that cse works on their addresses
+			return s.newValue2Apos(ssa.OpLocalAddr, t, n, s.sp, s.mem(), true)
+		default:
+			s.Fatalf("variable address class %v not implemented", n.Class)
+			return nil
+		}
+	case ir.ORESULT:
+		// load return from callee
+		n := n.(*ir.ResultExpr)
+		if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall {
+			return s.constOffPtrSP(t, n.Offset)
+		}
+		which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffset(n.Offset)
+		if which == -1 {
+			// Do the old thing // TODO: Panic instead.
+			return s.constOffPtrSP(t, n.Offset)
+		}
+		x := s.newValue1I(ssa.OpSelectNAddr, t, which, s.prevCall)
+		return x
+
+	case ir.OINDEX:
+		n := n.(*ir.IndexExpr)
+		if n.X.Type().IsSlice() {
+			a := s.expr(n.X)
+			i := s.expr(n.Index)
+			len := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], a)
+			i = s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded())
+			p := s.newValue1(ssa.OpSlicePtr, t, a)
+			return s.newValue2(ssa.OpPtrIndex, t, p, i)
+		} else { // array
+			a := s.addr(n.X)
+			i := s.expr(n.Index)
+			len := s.constInt(types.Types[types.TINT], n.X.Type().NumElem())
+			i = s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded())
+			return s.newValue2(ssa.OpPtrIndex, types.NewPtr(n.X.Type().Elem()), a, i)
+		}
+	case ir.ODEREF:
+		n := n.(*ir.StarExpr)
+		return s.exprPtr(n.X, n.Bounded(), n.Pos())
+	case ir.ODOT:
+		n := n.(*ir.SelectorExpr)
+		p := s.addr(n.X)
+		return s.newValue1I(ssa.OpOffPtr, t, n.Offset(), p)
+	case ir.ODOTPTR:
+		n := n.(*ir.SelectorExpr)
+		p := s.exprPtr(n.X, n.Bounded(), n.Pos())
+		return s.newValue1I(ssa.OpOffPtr, t, n.Offset(), p)
+	case ir.OCONVNOP:
+		n := n.(*ir.ConvExpr)
+		if n.Type() == n.X.Type() {
+			return s.addr(n.X)
+		}
+		addr := s.addr(n.X)
+		return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
+	case ir.OCALLFUNC, ir.OCALLINTER:
+		n := n.(*ir.CallExpr)
+		return s.callAddr(n, callNormal)
+	case ir.ODOTTYPE:
+		n := n.(*ir.TypeAssertExpr)
+		v, _ := s.dottype(n, false)
+		if v.Op != ssa.OpLoad {
+			s.Fatalf("dottype of non-load")
+		}
+		if v.Args[1] != s.mem() {
+			s.Fatalf("memory no longer live from dottype load")
+		}
+		return v.Args[0]
+	default:
+		s.Fatalf("unhandled addr %v", n.Op())
+		return nil
+	}
+}
+
+// canSSA reports whether n is SSA-able.
+// n must be an ONAME (or an ODOT sequence with an ONAME base).
+func (s *state) canSSA(n ir.Node) bool {
+	if base.Flag.N != 0 {
+		return false
+	}
+	for {
+		nn := n
+		if nn.Op() == ir.ODOT {
+			nn := nn.(*ir.SelectorExpr)
+			n = nn.X
+			continue
+		}
+		if nn.Op() == ir.OINDEX {
+			nn := nn.(*ir.IndexExpr)
+			if nn.X.Type().IsArray() {
+				n = nn.X
+				continue
+			}
+		}
+		break
+	}
+	if n.Op() != ir.ONAME {
+		return false
+	}
+	return s.canSSAName(n.(*ir.Name)) && TypeOK(n.Type())
+}
+
+func (s *state) canSSAName(name *ir.Name) bool {
+	if name.Addrtaken() || !name.OnStack() {
+		return false
+	}
+	switch name.Class {
+	case ir.PPARAMOUT:
+		if s.hasdefer {
+			// TODO: handle this case? Named return values must be
+			// in memory so that the deferred function can see them.
+			// Maybe do: if !strings.HasPrefix(n.String(), "~") { return false }
+			// Or maybe not, see issue 18860.  Even unnamed return values
+			// must be written back so if a defer recovers, the caller can see them.
+			return false
+		}
+		if s.cgoUnsafeArgs {
+			// Cgo effectively takes the address of all result args,
+			// but the compiler can't see that.
+			return false
+		}
+	}
+	if name.Class == ir.PPARAM && name.Sym() != nil && name.Sym().Name == ".this" {
+		// wrappers generated by genwrapper need to update
+		// the .this pointer in place.
+		// TODO: treat as a PPARAMOUT?
+		return false
+	}
+	return true
+	// TODO: try to make more variables SSAable?
+}
+
+// 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.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 TypeOK(t.Elem())
+		}
+		return false
+	case types.TSTRUCT:
+		if t.NumFields() > ssa.MaxStruct {
+			return false
+		}
+		for _, t1 := range t.Fields().Slice() {
+			if !TypeOK(t1.Type) {
+				return false
+			}
+		}
+		return true
+	default:
+		return true
+	}
+}
+
+// exprPtr evaluates n to a pointer and nil-checks it.
+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 {
+			s.f.Warnl(lineno, "removed nil check")
+		}
+		return p
+	}
+	s.nilCheck(p)
+	return p
+}
+
+// nilCheck generates nil pointer checking code.
+// Used only for automatically inserted nil checks,
+// not for user code like 'x != nil'.
+func (s *state) nilCheck(ptr *ssa.Value) {
+	if base.Debug.DisableNil != 0 || s.curfn.NilCheckDisabled() {
+		return
+	}
+	s.newValue2(ssa.OpNilCheck, types.TypeVoid, ptr, s.mem())
+}
+
+// boundsCheck generates bounds checking code. Checks if 0 <= idx <[=] len, branches to exit if not.
+// Starts a new block on return.
+// On input, len must be converted to full int width and be nonnegative.
+// Returns idx converted to full int width.
+// If bounded is true then caller guarantees the index is not out of bounds
+// (but boundsCheck will still extend the index to full int width).
+func (s *state) boundsCheck(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bool) *ssa.Value {
+	idx = s.extendIndex(idx, len, kind, bounded)
+
+	if bounded || base.Flag.B != 0 {
+		// If bounded or bounds checking is flag-disabled, then no check necessary,
+		// just return the extended index.
+		//
+		// Here, bounded == true if the compiler generated the index itself,
+		// such as in the expansion of a slice initializer. These indexes are
+		// compiler-generated, not Go program variables, so they cannot be
+		// attacker-controlled, so we can omit Spectre masking as well.
+		//
+		// Note that we do not want to omit Spectre masking in code like:
+		//
+		//	if 0 <= i && i < len(x) {
+		//		use(x[i])
+		//	}
+		//
+		// Lucky for us, bounded==false for that code.
+		// In that case (handled below), we emit a bound check (and Spectre mask)
+		// and then the prove pass will remove the bounds check.
+		// In theory the prove pass could potentially remove certain
+		// Spectre masks, but it's very delicate and probably better
+		// to be conservative and leave them all in.
+		return idx
+	}
+
+	bNext := s.f.NewBlock(ssa.BlockPlain)
+	bPanic := s.f.NewBlock(ssa.BlockExit)
+
+	if !idx.Type.IsSigned() {
+		switch kind {
+		case ssa.BoundsIndex:
+			kind = ssa.BoundsIndexU
+		case ssa.BoundsSliceAlen:
+			kind = ssa.BoundsSliceAlenU
+		case ssa.BoundsSliceAcap:
+			kind = ssa.BoundsSliceAcapU
+		case ssa.BoundsSliceB:
+			kind = ssa.BoundsSliceBU
+		case ssa.BoundsSlice3Alen:
+			kind = ssa.BoundsSlice3AlenU
+		case ssa.BoundsSlice3Acap:
+			kind = ssa.BoundsSlice3AcapU
+		case ssa.BoundsSlice3B:
+			kind = ssa.BoundsSlice3BU
+		case ssa.BoundsSlice3C:
+			kind = ssa.BoundsSlice3CU
+		}
+	}
+
+	var cmp *ssa.Value
+	if kind == ssa.BoundsIndex || kind == ssa.BoundsIndexU {
+		cmp = s.newValue2(ssa.OpIsInBounds, types.Types[types.TBOOL], idx, len)
+	} else {
+		cmp = s.newValue2(ssa.OpIsSliceInBounds, types.Types[types.TBOOL], idx, len)
+	}
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(cmp)
+	b.Likely = ssa.BranchLikely
+	b.AddEdgeTo(bNext)
+	b.AddEdgeTo(bPanic)
+
+	s.startBlock(bPanic)
+	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)
+	} else {
+		mem := s.newValue3I(ssa.OpPanicBounds, types.TypeMem, int64(kind), idx, len, s.mem())
+		s.endBlock().SetControl(mem)
+	}
+	s.startBlock(bNext)
+
+	// In Spectre index mode, apply an appropriate mask to avoid speculative out-of-bounds accesses.
+	if base.Flag.Cfg.SpectreIndex {
+		op := ssa.OpSpectreIndex
+		if kind != ssa.BoundsIndex && kind != ssa.BoundsIndexU {
+			op = ssa.OpSpectreSliceIndex
+		}
+		idx = s.newValue2(op, types.Types[types.TINT], idx, len)
+	}
+
+	return idx
+}
+
+// If cmp (a bool) is false, panic using the given function.
+func (s *state) check(cmp *ssa.Value, fn *obj.LSym) {
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(cmp)
+	b.Likely = ssa.BranchLikely
+	bNext := s.f.NewBlock(ssa.BlockPlain)
+	line := s.peekPos()
+	pos := base.Ctxt.PosTable.Pos(line)
+	fl := funcLine{f: fn, base: pos.Base(), line: pos.Line()}
+	bPanic := s.panics[fl]
+	if bPanic == nil {
+		bPanic = s.f.NewBlock(ssa.BlockPlain)
+		s.panics[fl] = bPanic
+		s.startBlock(bPanic)
+		// The panic call takes/returns memory to ensure that the right
+		// memory state is observed if the panic happens.
+		s.rtcall(fn, false, nil)
+	}
+	b.AddEdgeTo(bNext)
+	b.AddEdgeTo(bPanic)
+	s.startBlock(bNext)
+}
+
+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:
+		if b.AuxInt != 0 {
+			needcheck = false
+		}
+	}
+	if needcheck {
+		// do a size-appropriate check for zero
+		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)
+}
+
+// rtcall issues a call to the given runtime function fn with the listed args.
+// Returns a slice of results of the given result types.
+// The call is added to the end of the current block.
+// If returns is false, the block is marked as an exit block.
+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 := base.Ctxt.FixedFrameSize()
+	var ACArgs []ssa.Param
+	var ACResults []ssa.Param
+	var callArgs []*ssa.Value
+
+	for _, arg := range args {
+		t := arg.Type
+		off = types.Rnd(off, t.Alignment())
+		size := t.Size()
+		ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
+		callArgs = append(callArgs, arg)
+		off += size
+	}
+	off = types.Rnd(off, int64(types.RegSize))
+
+	// Accumulate results types and offsets
+	offR := off
+	for _, t := range results {
+		offR = types.Rnd(offR, t.Alignment())
+		ACResults = append(ACResults, ssa.Param{Type: t, Offset: int32(offR)})
+		offR += t.Size()
+	}
+
+	// Issue call
+	var call *ssa.Value
+	aux := ssa.StaticAuxCall(fn, ACArgs, ACResults)
+	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 - base.Ctxt.FixedFrameSize()
+		if len(results) > 0 {
+			s.Fatalf("panic call can't have results")
+		}
+		return nil
+	}
+
+	// Load results
+	res := make([]*ssa.Value, len(results))
+	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 = types.Rnd(off, int64(types.PtrSize))
+
+	// Remember how much callee stack space we needed.
+	call.AuxInt = off
+
+	return res
+}
+
+// do *left = right for type t.
+func (s *state) storeType(t *types.Type, left, right *ssa.Value, skip skipMask, leftIsStmt bool) {
+	s.instrument(t, left, instrumentWrite)
+
+	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)
+		return
+	}
+
+	// store scalar fields first, so write barrier stores for
+	// pointer fields can be grouped together, and scalar values
+	// don't need to be live across the write barrier call.
+	// TODO: if the writebarrier pass knows how to reorder stores,
+	// we can do a single store here as long as skip==0.
+	s.storeTypeScalars(t, left, right, skip)
+	if skip&skipPtr == 0 && t.HasPointers() {
+		s.storeTypePtrs(t, left, right)
+	}
+}
+
+// do *left = right for all scalar (non-pointer) parts of t.
+func (s *state) storeTypeScalars(t *types.Type, left, right *ssa.Value, skip skipMask) {
+	switch {
+	case t.IsBoolean() || t.IsInteger() || t.IsFloat() || t.IsComplex():
+		s.store(t, left, right)
+	case t.IsPtrShaped():
+		if t.IsPtr() && t.Elem().NotInHeap() {
+			s.store(t, left, right) // see issue 42032
+		}
+		// otherwise, no scalar fields.
+	case t.IsString():
+		if skip&skipLen != 0 {
+			return
+		}
+		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[types.TINT], lenAddr, len)
+	case t.IsSlice():
+		if skip&skipLen == 0 {
+			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[types.TINT], lenAddr, len)
+		}
+		if skip&skipCap == 0 {
+			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[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[types.TUINTPTR], left, itab)
+	case t.IsStruct():
+		n := t.NumFields()
+		for i := 0; i < n; i++ {
+			ft := t.FieldType(i)
+			addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
+			val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
+			s.storeTypeScalars(ft, addr, val, 0)
+		}
+	case t.IsArray() && t.NumElem() == 0:
+		// nothing
+	case t.IsArray() && t.NumElem() == 1:
+		s.storeTypeScalars(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right), 0)
+	default:
+		s.Fatalf("bad write barrier type %v", t)
+	}
+}
+
+// do *left = right for all pointer parts of t.
+func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
+	switch {
+	case t.IsPtrShaped():
+		if t.IsPtr() && t.Elem().NotInHeap() {
+			break // see issue 42032
+		}
+		s.store(t, left, right)
+	case t.IsString():
+		ptr := s.newValue1(ssa.OpStringPtr, s.f.Config.Types.BytePtr, right)
+		s.store(s.f.Config.Types.BytePtr, left, ptr)
+	case t.IsSlice():
+		elType := types.NewPtr(t.Elem())
+		ptr := s.newValue1(ssa.OpSlicePtr, elType, right)
+		s.store(elType, left, ptr)
+	case t.IsInterface():
+		// itab field is treated as a scalar.
+		idata := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, right)
+		idataAddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.BytePtrPtr, s.config.PtrSize, left)
+		s.store(s.f.Config.Types.BytePtr, idataAddr, idata)
+	case t.IsStruct():
+		n := t.NumFields()
+		for i := 0; i < n; i++ {
+			ft := t.FieldType(i)
+			if !ft.HasPointers() {
+				continue
+			}
+			addr := s.newValue1I(ssa.OpOffPtr, ft.PtrTo(), t.FieldOff(i), left)
+			val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
+			s.storeTypePtrs(ft, addr, val)
+		}
+	case t.IsArray() && t.NumElem() == 0:
+		// nothing
+	case t.IsArray() && t.NumElem() == 1:
+		s.storeTypePtrs(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right))
+	default:
+		s.Fatalf("bad write barrier type %v", t)
+	}
+}
+
+// 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 !TypeOK(t) {
+		a = s.newValue2(ssa.OpDereference, t, s.addr(n), s.mem())
+	} else {
+		a = s.expr(n)
+	}
+	return ssa.Param{Type: t, Offset: int32(off)}, a
+}
+
+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 {
+		// Use special routine that avoids allocation on duplicate offsets.
+		addr = s.constOffPtrSP(pt, off)
+	} else {
+		addr = s.newValue1I(ssa.OpOffPtr, pt, off, base)
+	}
+
+	if !TypeOK(t) {
+		a := s.addr(n)
+		s.move(t, addr, a)
+		return
+	}
+
+	a := s.expr(n)
+	s.storeType(t, addr, a, 0, false)
+}
+
+// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
+// i,j,k may be nil, in which case they are set to their default value.
+// v may be a slice, string or pointer to an array.
+func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value) {
+	t := v.Type
+	var ptr, len, cap *ssa.Value
+	switch {
+	case t.IsSlice():
+		ptr = s.newValue1(ssa.OpSlicePtr, types.NewPtr(t.Elem()), 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[types.TUINT8]), v)
+		len = s.newValue1(ssa.OpStringLen, types.Types[types.TINT], v)
+		cap = len
+	case t.IsPtr():
+		if !t.Elem().IsArray() {
+			s.Fatalf("bad ptr to array in slice %v\n", t)
+		}
+		s.nilCheck(v)
+		ptr = s.newValue1(ssa.OpCopy, types.NewPtr(t.Elem().Elem()), v)
+		len = s.constInt(types.Types[types.TINT], t.Elem().NumElem())
+		cap = len
+	default:
+		s.Fatalf("bad type in slice %v\n", t)
+	}
+
+	// Set default values
+	if i == nil {
+		i = s.constInt(types.Types[types.TINT], 0)
+	}
+	if j == nil {
+		j = len
+	}
+	three := true
+	if k == nil {
+		three = false
+		k = cap
+	}
+
+	// Panic if slice indices are not in bounds.
+	// Make sure we check these in reverse order so that we're always
+	// comparing against a value known to be nonnegative. See issue 28797.
+	if three {
+		if k != cap {
+			kind := ssa.BoundsSlice3Alen
+			if t.IsSlice() {
+				kind = ssa.BoundsSlice3Acap
+			}
+			k = s.boundsCheck(k, cap, kind, bounded)
+		}
+		if j != k {
+			j = s.boundsCheck(j, k, ssa.BoundsSlice3B, bounded)
+		}
+		i = s.boundsCheck(i, j, ssa.BoundsSlice3C, bounded)
+	} else {
+		if j != k {
+			kind := ssa.BoundsSliceAlen
+			if t.IsSlice() {
+				kind = ssa.BoundsSliceAcap
+			}
+			j = s.boundsCheck(j, k, kind, bounded)
+		}
+		i = s.boundsCheck(i, j, ssa.BoundsSliceB, bounded)
+	}
+
+	// Word-sized integer operations.
+	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[types.TINT], j, i)
+	rcap := rlen
+	if j != k && !t.IsString() {
+		rcap = s.newValue2(subOp, types.Types[types.TINT], k, i)
+	}
+
+	if (i.Op == ssa.OpConst64 || i.Op == ssa.OpConst32) && i.AuxInt == 0 {
+		// No pointer arithmetic necessary.
+		return ptr, rlen, rcap
+	}
+
+	// Calculate the base pointer (rptr) for the new slice.
+	//
+	// Generate the following code assuming that indexes are in bounds.
+	// The masking is to make sure that we don't generate a slice
+	// that points to the next object in memory. We cannot just set
+	// the pointer to nil because then we would create a nil slice or
+	// string.
+	//
+	//     rcap = k - i
+	//     rlen = j - i
+	//     rptr = ptr + (mask(rcap) & (i * stride))
+	//
+	// 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[types.TINT], ptr.Type.Elem().Width)
+
+	// The delta is the number of bytes to offset ptr by.
+	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[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)
+
+	return rptr, rlen, rcap
+}
+
+type u642fcvtTab struct {
+	leq, cvt2F, and, rsh, or, add ssa.Op
+	one                           func(*state, *types.Type, int64) *ssa.Value
+}
+
+var u64_f64 = u642fcvtTab{
+	leq:   ssa.OpLeq64,
+	cvt2F: ssa.OpCvt64to64F,
+	and:   ssa.OpAnd64,
+	rsh:   ssa.OpRsh64Ux64,
+	or:    ssa.OpOr64,
+	add:   ssa.OpAdd64F,
+	one:   (*state).constInt64,
+}
+
+var u64_f32 = u642fcvtTab{
+	leq:   ssa.OpLeq64,
+	cvt2F: ssa.OpCvt64to32F,
+	and:   ssa.OpAnd64,
+	rsh:   ssa.OpRsh64Ux64,
+	or:    ssa.OpOr64,
+	add:   ssa.OpAdd32F,
+	one:   (*state).constInt64,
+}
+
+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 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 ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+	// if x >= 0 {
+	//    result = (floatY) x
+	// } else {
+	// 	  y = uintX(x) ; y = x & 1
+	// 	  z = uintX(x) ; z = z >> 1
+	// 	  z = z >> 1
+	// 	  z = z | y
+	// 	  result = floatY(z)
+	// 	  result = result + result
+	// }
+	//
+	// Code borrowed from old code generator.
+	// What's going on: large 64-bit "unsigned" looks like
+	// negative number to hardware's integer-to-float
+	// conversion. However, because the mantissa is only
+	// 63 bits, we don't need the LSB, so instead we do an
+	// unsigned right shift (divide by two), convert, and
+	// double. However, before we do that, we need to be
+	// sure that we do not lose a "1" if that made the
+	// difference in the resulting rounding. Therefore, we
+	// preserve it, and OR (not ADD) it back in. The case
+	// that matters is when the eleven discarded bits are
+	// 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[types.TBOOL], s.zeroVal(ft), x)
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(cmp)
+	b.Likely = ssa.BranchLikely
+
+	bThen := s.f.NewBlock(ssa.BlockPlain)
+	bElse := s.f.NewBlock(ssa.BlockPlain)
+	bAfter := s.f.NewBlock(ssa.BlockPlain)
+
+	b.AddEdgeTo(bThen)
+	s.startBlock(bThen)
+	a0 := s.newValue1(cvttab.cvt2F, tt, x)
+	s.vars[n] = a0
+	s.endBlock()
+	bThen.AddEdgeTo(bAfter)
+
+	b.AddEdgeTo(bElse)
+	s.startBlock(bElse)
+	one := cvttab.one(s, ft, 1)
+	y := s.newValue2(cvttab.and, ft, x, one)
+	z := s.newValue2(cvttab.rsh, ft, x, one)
+	z = s.newValue2(cvttab.or, ft, z, y)
+	a := s.newValue1(cvttab.cvt2F, tt, z)
+	a1 := s.newValue2(cvttab.add, tt, a, a)
+	s.vars[n] = a1
+	s.endBlock()
+	bElse.AddEdgeTo(bAfter)
+
+	s.startBlock(bAfter)
+	return s.variable(n, n.Type())
+}
+
+type u322fcvtTab struct {
+	cvtI2F, cvtF2F ssa.Op
+}
+
+var u32_f64 = u322fcvtTab{
+	cvtI2F: ssa.OpCvt32to64F,
+	cvtF2F: ssa.OpCopy,
+}
+
+var u32_f32 = u322fcvtTab{
+	cvtI2F: ssa.OpCvt32to32F,
+	cvtF2F: ssa.OpCvt64Fto32F,
+}
+
+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 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 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[types.TBOOL], s.zeroVal(ft), x)
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(cmp)
+	b.Likely = ssa.BranchLikely
+
+	bThen := s.f.NewBlock(ssa.BlockPlain)
+	bElse := s.f.NewBlock(ssa.BlockPlain)
+	bAfter := s.f.NewBlock(ssa.BlockPlain)
+
+	b.AddEdgeTo(bThen)
+	s.startBlock(bThen)
+	a0 := s.newValue1(cvttab.cvtI2F, tt, x)
+	s.vars[n] = a0
+	s.endBlock()
+	bThen.AddEdgeTo(bAfter)
+
+	b.AddEdgeTo(bElse)
+	s.startBlock(bElse)
+	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
+	s.endBlock()
+	bElse.AddEdgeTo(bAfter)
+
+	s.startBlock(bAfter)
+	return s.variable(n, n.Type())
+}
+
+// referenceTypeBuiltin generates code for the len/cap builtins for maps and channels.
+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 {
+	//   return 0
+	// } else {
+	//   // len
+	//   return *((*int)n)
+	//   // cap
+	//   return *(((*int)n)+1)
+	// }
+	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)
+	b.Likely = ssa.BranchUnlikely
+
+	bThen := s.f.NewBlock(ssa.BlockPlain)
+	bElse := s.f.NewBlock(ssa.BlockPlain)
+	bAfter := s.f.NewBlock(ssa.BlockPlain)
+
+	// length/capacity of a nil map/chan is zero
+	b.AddEdgeTo(bThen)
+	s.startBlock(bThen)
+	s.vars[n] = s.zeroVal(lenType)
+	s.endBlock()
+	bThen.AddEdgeTo(bAfter)
+
+	b.AddEdgeTo(bElse)
+	s.startBlock(bElse)
+	switch n.Op() {
+	case ir.OLEN:
+		// length is stored in the first word for map/chan
+		s.vars[n] = s.load(lenType, x)
+	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)
+	default:
+		s.Fatalf("op must be OLEN or OCAP")
+	}
+	s.endBlock()
+	bElse.AddEdgeTo(bAfter)
+
+	s.startBlock(bAfter)
+	return s.variable(n, lenType)
+}
+
+type f2uCvtTab struct {
+	ltf, cvt2U, subf, or ssa.Op
+	floatValue           func(*state, *types.Type, float64) *ssa.Value
+	intValue             func(*state, *types.Type, int64) *ssa.Value
+	cutoff               uint64
+}
+
+var f32_u64 = f2uCvtTab{
+	ltf:        ssa.OpLess32F,
+	cvt2U:      ssa.OpCvt32Fto64,
+	subf:       ssa.OpSub32F,
+	or:         ssa.OpOr64,
+	floatValue: (*state).constFloat32,
+	intValue:   (*state).constInt64,
+	cutoff:     1 << 63,
+}
+
+var f64_u64 = f2uCvtTab{
+	ltf:        ssa.OpLess64F,
+	cvt2U:      ssa.OpCvt64Fto64,
+	subf:       ssa.OpSub64F,
+	or:         ssa.OpOr64,
+	floatValue: (*state).constFloat64,
+	intValue:   (*state).constInt64,
+	cutoff:     1 << 63,
+}
+
+var f32_u32 = f2uCvtTab{
+	ltf:        ssa.OpLess32F,
+	cvt2U:      ssa.OpCvt32Fto32,
+	subf:       ssa.OpSub32F,
+	or:         ssa.OpOr32,
+	floatValue: (*state).constFloat32,
+	intValue:   func(s *state, t *types.Type, v int64) *ssa.Value { return s.constInt32(t, int32(v)) },
+	cutoff:     1 << 31,
+}
+
+var f64_u32 = f2uCvtTab{
+	ltf:        ssa.OpLess64F,
+	cvt2U:      ssa.OpCvt64Fto32,
+	subf:       ssa.OpSub64F,
+	or:         ssa.OpOr32,
+	floatValue: (*state).constFloat64,
+	intValue:   func(s *state, t *types.Type, v int64) *ssa.Value { return s.constInt32(t, int32(v)) },
+	cutoff:     1 << 31,
+}
+
+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 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 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 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 ir.Node, x *ssa.Value, ft, tt *types.Type) *ssa.Value {
+	// cutoff:=1<<(intY_Size-1)
+	// if x < floatX(cutoff) {
+	// 	result = uintY(x)
+	// } else {
+	// 	y = x - floatX(cutoff)
+	// 	z = uintY(y)
+	// 	result = z | -(cutoff)
+	// }
+	cutoff := cvttab.floatValue(s, ft, float64(cvttab.cutoff))
+	cmp := s.newValue2(cvttab.ltf, types.Types[types.TBOOL], x, cutoff)
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(cmp)
+	b.Likely = ssa.BranchLikely
+
+	bThen := s.f.NewBlock(ssa.BlockPlain)
+	bElse := s.f.NewBlock(ssa.BlockPlain)
+	bAfter := s.f.NewBlock(ssa.BlockPlain)
+
+	b.AddEdgeTo(bThen)
+	s.startBlock(bThen)
+	a0 := s.newValue1(cvttab.cvt2U, tt, x)
+	s.vars[n] = a0
+	s.endBlock()
+	bThen.AddEdgeTo(bAfter)
+
+	b.AddEdgeTo(bElse)
+	s.startBlock(bElse)
+	y := s.newValue2(cvttab.subf, ft, x, cutoff)
+	y = s.newValue1(cvttab.cvt2U, tt, y)
+	z := cvttab.intValue(s, tt, int64(-cvttab.cutoff))
+	a1 := s.newValue2(cvttab.or, tt, y, z)
+	s.vars[n] = a1
+	s.endBlock()
+	bElse.AddEdgeTo(bAfter)
+
+	s.startBlock(bAfter)
+	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 *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() {
+			// Converting to an empty interface.
+			// Input could be an empty or nonempty interface.
+			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[types.TBOOL], itab, s.constNil(byteptr))
+
+			if n.X.Type().IsEmptyInterface() && commaok {
+				// Converting empty interface to empty interface with ,ok is just a nil check.
+				return iface, cond
+			}
+
+			// Branch on nilness.
+			b := s.endBlock()
+			b.Kind = ssa.BlockIf
+			b.SetControl(cond)
+			b.Likely = ssa.BranchLikely
+			bOk := s.f.NewBlock(ssa.BlockPlain)
+			bFail := s.f.NewBlock(ssa.BlockPlain)
+			b.AddEdgeTo(bOk)
+			b.AddEdgeTo(bFail)
+
+			if !commaok {
+				// On failure, panic by calling panicnildottype.
+				s.startBlock(bFail)
+				s.rtcall(ir.Syms.Panicnildottype, false, nil, target)
+
+				// On success, return (perhaps modified) input interface.
+				s.startBlock(bOk)
+				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(types.PtrSize), itab)
+				typ := s.load(byteptr, off)
+				idata := s.newValue1(ssa.OpIData, byteptr, iface)
+				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(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.endBlock()
+
+			// Merge point.
+			bEnd := s.f.NewBlock(ssa.BlockPlain)
+			bOk.AddEdgeTo(bEnd)
+			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)
+			resok = cond
+			delete(s.vars, typVar)
+			return
+		}
+		// converting to a nonempty interface needs a runtime call.
+		if base.Debug.TypeAssert > 0 {
+			base.WarnfAt(n.Pos(), "type assertion not inlined")
+		}
+		if n.X.Type().IsEmptyInterface() {
+			if commaok {
+				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(ir.Syms.AssertE2I, true, []*types.Type{n.Type()}, target, iface)[0], nil
+		}
+		if commaok {
+			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(ir.Syms.AssertI2I, true, []*types.Type{n.Type()}, target, iface)[0], nil
+	}
+
+	if base.Debug.TypeAssert > 0 {
+		base.WarnfAt(n.Pos(), "type assertion inlined")
+	}
+
+	// Converting to a concrete type.
+	direct := types.IsDirectIface(n.Type())
+	itab := s.newValue1(ssa.OpITab, byteptr, iface) // type word of interface
+	if base.Debug.TypeAssert > 0 {
+		base.WarnfAt(n.Pos(), "type assertion inlined")
+	}
+	var targetITab *ssa.Value
+	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.Itab)
+	}
+
+	var tmp ir.Node     // temporary for use with large types
+	var addr *ssa.Value // address of tmp
+	if commaok && !TypeOK(n.Type()) {
+		// unSSAable type, use temporary.
+		// TODO: get rid of some of these temporaries.
+		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[types.TBOOL], itab, targetITab)
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(cond)
+	b.Likely = ssa.BranchLikely
+
+	bOk := s.f.NewBlock(ssa.BlockPlain)
+	bFail := s.f.NewBlock(ssa.BlockPlain)
+	b.AddEdgeTo(bOk)
+	b.AddEdgeTo(bFail)
+
+	if !commaok {
+		// on failure, panic by calling panicdottype
+		s.startBlock(bFail)
+		taddr := s.reflectType(n.X.Type())
+		if n.X.Type().IsEmptyInterface() {
+			s.rtcall(ir.Syms.PanicdottypeE, false, nil, itab, target, taddr)
+		} else {
+			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
+		}
+		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
+	// a control flow merge point.
+	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 := ssaMarker("val")
+
+	// type assertion succeeded
+	s.startBlock(bOk)
+	if tmp == nil {
+		if direct {
+			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)
+		}
+	} else {
+		p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface)
+		s.move(n.Type(), addr, p)
+	}
+	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())
+	} else {
+		s.zero(n.Type(), addr)
+	}
+	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())
+		delete(s.vars, valVar)
+	} else {
+		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[types.TBOOL])
+	delete(s.vars, okVar)
+	return res, resok
+}
+
+// variable returns the value of a variable at the current location.
+func (s *state) variable(n ir.Node, t *types.Type) *ssa.Value {
+	v := s.vars[n]
+	if v != nil {
+		return v
+	}
+	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, 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, 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)
+}
+
+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 ir.IsAutoTmp(n) {
+		// Don't track temporary variables.
+		return
+	}
+	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
+	}
+	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)
+	}
+	s.f.NamedValues[loc] = append(values, v)
+}
+
+// Generate a disconnected call to a runtime routine and a return.
+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
+	p.To.Sym = sym
+	p = pp.Prog(obj.ARET)
+	return p
+}
+
+// Branch is an unresolved branch.
+type Branch struct {
+	P *obj.Prog  // branch instruction
+	B *ssa.Block // target
+}
+
+// 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.
+	Branches []Branch
+
+	// bstart remembers where each block starts (indexed by block ID)
+	bstart []*obj.Prog
+
+	// Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8.
+	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 liveness.Map
+
+	// lineRunStart records the beginning of the current run of instructions
+	// within a single block sharing the same line number
+	// Used to move statement marks to the beginning of such runs.
+	lineRunStart *obj.Prog
+
+	// wasm: The number of values on the WebAssembly stack. This is only used as a safeguard.
+	OnWasmStackSkipped int
+}
+
+// Prog appends a new Prog.
+func (s *State) Prog(as obj.As) *obj.Prog {
+	p := s.pp.Prog(as)
+	if objw.LosesStmtMark(as) {
+		return p
+	}
+	// Float a statement start to the beginning of any same-line run.
+	// lineRunStart is reset at block boundaries, which appears to work well.
+	if s.lineRunStart == nil || s.lineRunStart.Pos.Line() != p.Pos.Line() {
+		s.lineRunStart = p
+	} else if p.Pos.IsStmt() == src.PosIsStmt {
+		s.lineRunStart.Pos = s.lineRunStart.Pos.WithIsStmt()
+		p.Pos = p.Pos.WithNotStmt()
+	}
+	return p
+}
+
+// Pc returns the current Prog.
+func (s *State) Pc() *obj.Prog {
+	return s.pp.Next
+}
+
+// SetPos sets the current source position.
+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 *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})
+	return p
+}
+
+// DebugFriendlySetPosFrom adjusts Pos.IsStmt subject to heuristics
+// that reduce "jumpy" line number churn when debugging.
+// 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 *State) DebugFriendlySetPosFrom(v *ssa.Value) {
+	switch v.Op {
+	case ssa.OpPhi, ssa.OpCopy, ssa.OpLoadReg, ssa.OpStoreReg:
+		// These are not statements
+		s.SetPos(v.Pos.WithNotStmt())
+	default:
+		p := v.Pos
+		if p != src.NoXPos {
+			// If the position is defined, update the position.
+			// Also convert default IsStmt to NotStmt; only
+			// explicit statement boundaries should appear
+			// 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 State
+			}
+			s.SetPos(p)
+		} else {
+			s.SetPos(s.pp.Pos.WithNotStmt())
+		}
+	}
+}
+
+// genssa appends entries to pp for each instruction in f.
+func genssa(f *ssa.Func, pp *objw.Progs) {
+	var s State
+	s.ABI = f.OwnAux.Fn.ABI()
+
+	e := f.Frontend().(*ssafn)
+
+	s.livenessMap = liveness.Compute(e.curfn, f, e.stkptrsize, pp)
+
+	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)
+		p.From.SetConst(objabi.FUNCDATA_OpenCodedDeferInfo)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = openDeferInfo
+	}
+
+	// Remember where each block starts.
+	s.bstart = make([]*obj.Prog, f.NumBlocks())
+	s.pp = pp
+	var progToValue map[*obj.Prog]*ssa.Value
+	var progToBlock map[*obj.Prog]*ssa.Block
+	var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point.
+	if f.PrintOrHtmlSSA {
+		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]
+	}
+
+	s.ScratchFpMem = e.scratchFpMem
+
+	if base.Ctxt.Flag_locationlists {
+		if cap(f.Cache.ValueToProgAfter) < f.NumValues() {
+			f.Cache.ValueToProgAfter = make([]*obj.Prog, f.NumValues())
+		}
+		valueToProgAfter = f.Cache.ValueToProgAfter[:f.NumValues()]
+		for i := range valueToProgAfter {
+			valueToProgAfter[i] = nil
+		}
+	}
+
+	// If the very first instruction is not tagged as a statement,
+	// debuggers may attribute it to previous function in program.
+	firstPos := src.NoXPos
+	for _, v := range f.Entry.Values {
+		if v.Pos.IsStmt() == src.PosIsStmt {
+			firstPos = v.Pos
+			v.Pos = firstPos.WithDefaultStmt()
+			break
+		}
+	}
+
+	// inlMarks has an entry for each Prog that implements an inline mark.
+	// It maps from that Prog to the global inlining id of the inlined body
+	// which should unwind to this Prog's location.
+	var inlMarks map[*obj.Prog]int32
+	var inlMarkList []*obj.Prog
+
+	// inlMarksByPos maps from a (column 1) source position to the set of
+	// Progs that are in the set above and have that source position.
+	var inlMarksByPos map[src.XPos][]*obj.Prog
+
+	// Emit basic blocks
+	for i, b := range f.Blocks {
+		s.bstart[b.ID] = s.pp.Next
+		s.lineRunStart = nil
+
+		// Attach a "default" liveness info. Normally this will be
+		// overwritten in the Values loop below for each Value. But
+		// for an empty block this will be used for its control
+		// 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 = objw.LivenessIndex{StackMapIndex: -1, IsUnsafePoint: liveness.IsUnsafe(f)}
+
+		// Emit values in block
+		Arch.SSAMarkMoves(&s, b)
+		for _, v := range b.Values {
+			x := s.pp.Next
+			s.DebugFriendlySetPosFrom(v)
+
+			switch v.Op {
+			case ssa.OpInitMem:
+				// memory arg needs no code
+			case ssa.OpArg:
+				// input args need no code
+			case ssa.OpSP, ssa.OpSB:
+				// nothing to do
+			case ssa.OpSelect0, ssa.OpSelect1:
+				// nothing to do
+			case ssa.OpGetG:
+				// nothing to do when there's a g register,
+				// and checkLower complains if there's not
+			case ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive, ssa.OpVarKill:
+				// nothing to do; already used by liveness
+			case ssa.OpPhi:
+				CheckLoweredPhi(v)
+			case ssa.OpConvert:
+				// nothing to do; no-op conversion for liveness
+				if v.Args[0].Reg() != v.Reg() {
+					v.Fatalf("OpConvert should be a no-op: %s; %s", v.Args[0].LongString(), v.LongString())
+				}
+			case ssa.OpInlMark:
+				p := Arch.Ginsnop(s.pp)
+				if inlMarks == nil {
+					inlMarks = map[*obj.Prog]int32{}
+					inlMarksByPos = map[src.XPos][]*obj.Prog{}
+				}
+				inlMarks[p] = v.AuxInt32()
+				inlMarkList = append(inlMarkList, p)
+				pos := v.Pos.AtColumn1()
+				inlMarksByPos[pos] = append(inlMarksByPos[pos], p)
+
+			default:
+				// Attach this safe point to the next
+				// instruction.
+				s.pp.NextLive = s.livenessMap.Get(v)
+
+				// Special case for first line in function; move it to the start.
+				if firstPos != src.NoXPos {
+					s.SetPos(firstPos)
+					firstPos = src.NoXPos
+				}
+				// let the backend handle it
+				Arch.SSAGenValue(&s, v)
+			}
+
+			if base.Ctxt.Flag_locationlists {
+				valueToProgAfter[v.ID] = s.pp.Next
+			}
+
+			if f.PrintOrHtmlSSA {
+				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 := 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.
+				if b.Pos == src.NoXPos {
+					b.Pos = pp.Text.Pos // Sometimes p.Pos is empty.  See #35695.
+				}
+			}
+			b.Pos = b.Pos.WithBogusLine() // Debuggers are not good about infinite loops, force a change in line number
+		}
+		// Emit control flow instructions for block
+		var next *ssa.Block
+		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
+		s.SetPos(b.Pos)
+		Arch.SSAGenBlock(&s, b, next)
+		if f.PrintOrHtmlSSA {
+			for ; x != s.pp.Next; x = x.Link {
+				progToBlock[x] = b
+			}
+		}
+	}
+	if f.Blocks[len(f.Blocks)-1].Kind == ssa.BlockExit {
+		// We need the return address of a panic call to
+		// 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.
+		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, ir.Syms.Deferreturn)
+	}
+
+	if inlMarks != nil {
+		// We have some inline marks. Try to find other instructions we're
+		// 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 || 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.
+				// We must not use anything that even might be zero-sized.)
+				// TODO: are there others?
+				continue
+			}
+			if _, ok := inlMarks[p]; ok {
+				// Don't use inline marks themselves. We don't know
+				// whether they will be zero-sized or not yet.
+				continue
+			}
+			pos := p.Pos.AtColumn1()
+			s := inlMarksByPos[pos]
+			if len(s) == 0 {
+				continue
+			}
+			for _, m := range s {
+				// We found an instruction with the same source position as
+				// some of the inline marks.
+				// Use this instruction instead.
+				p.Pos = p.Pos.WithIsStmt() // promote position to a statement
+				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
+				m.From = obj.Addr{}
+				m.To = obj.Addr{}
+			}
+			delete(inlMarksByPos, pos)
+		}
+		// 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.CurFunc.LSym.Func().AddInlMark(p, inlMarks[p])
+			}
+		}
+	}
+
+	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.
+		debugInfo.GetPC = func(b, v ssa.ID) int64 {
+			switch v {
+			case ssa.BlockStart.ID:
+				if b == f.Entry.ID {
+					return 0 // Start at the very beginning, at the assembler-generated prologue.
+					// this should only happen for function args (ssa.OpArg)
+				}
+				return bstart[b].Pc
+			case ssa.BlockEnd.ID:
+				return e.curfn.LSym.Size
+			default:
+				return valueToProgAfter[v].Pc
+			}
+		}
+	}
+
+	// Resolve branches, and relax DefaultStmt into NotStmt
+	for _, br := range s.Branches {
+		br.P.To.SetTarget(s.bstart[br.B.ID])
+		if br.P.Pos.IsStmt() != src.PosIsStmt {
+			br.P.Pos = br.P.Pos.WithNotStmt()
+		} else if v0 := br.B.FirstPossibleStmtValue(); v0 != nil && v0.Pos.Line() == br.P.Pos.Line() && v0.Pos.IsStmt() == src.PosIsStmt {
+			br.P.Pos = br.P.Pos.WithNotStmt()
+		}
+
+	}
+
+	if e.log { // spew to stdout
+		filename := ""
+		for p := pp.Text; p != nil; p = p.Link {
+			if p.Pos.IsKnown() && p.InnermostFilename() != filename {
+				filename = p.InnermostFilename()
+				f.Logf("# %s\n", filename)
+			}
+
+			var s string
+			if v, ok := progToValue[p]; ok {
+				s = v.String()
+			} else if b, ok := progToBlock[p]; ok {
+				s = b.String()
+			} else {
+				s = "   " // most value and branch strings are 2-3 characters long
+			}
+			f.Logf(" %-6s\t%.5d (%s)\t%s\n", s, p.Pc, p.InnermostLineNumber(), p.InstructionString())
+		}
+	}
+	if f.HTMLWriter != nil { // spew to ssa.html
+		var buf bytes.Buffer
+		buf.WriteString("<code>")
+		buf.WriteString("<dl class=\"ssa-gen\">")
+		filename := ""
+		for p := pp.Text; p != nil; p = p.Link {
+			// Don't spam every line with the file name, which is often huge.
+			// Only print changes, and "unknown" is not a change.
+			if p.Pos.IsKnown() && p.InnermostFilename() != filename {
+				filename = p.InnermostFilename()
+				buf.WriteString("<dt class=\"ssa-prog-src\"></dt><dd class=\"ssa-prog\">")
+				buf.WriteString(html.EscapeString("# " + filename))
+				buf.WriteString("</dd>")
+			}
+
+			buf.WriteString("<dt class=\"ssa-prog-src\">")
+			if v, ok := progToValue[p]; ok {
+				buf.WriteString(v.HTML())
+			} else if b, ok := progToBlock[p]; ok {
+				buf.WriteString("<b>" + b.HTML() + "</b>")
+			}
+			buf.WriteString("</dt>")
+			buf.WriteString("<dd class=\"ssa-prog\">")
+			buf.WriteString(fmt.Sprintf("%.5d <span class=\"l%v line-number\">(%s)</span> %s", p.Pc, p.InnermostLineNumber(), p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString())))
+			buf.WriteString("</dd>")
+		}
+		buf.WriteString("</dl>")
+		buf.WriteString("</code>")
+		f.HTMLWriter.WriteColumn("genssa", "genssa", "ssa-prog", buf.String())
+	}
+
+	defframe(&s, e)
+
+	f.HTMLWriter.Close()
+	f.HTMLWriter = nil
+}
+
+func defframe(s *State, e *ssafn) {
+	pp := s.pp
+
+	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(types.Rnd(e.curfn.Type().ArgWidth(), int64(types.RegSize)))
+	pp.Text.To.Offset = frame
+
+	// Insert code to zero ambiguously live variables so that the
+	// garbage collector only sees initialized values when it
+	// looks for pointers.
+	p := pp.Text
+	var lo, hi int64
+
+	// Opaque state for backend to use. Current backends use it to
+	// keep track of which helper registers have been zeroed.
+	var state uint32
+
+	// Iterate through declarations. They are sorted in decreasing Xoffset order.
+	for _, n := range e.curfn.Dcl {
+		if !n.Needzero() {
+			continue
+		}
+		if n.Class != ir.PAUTO {
+			e.Fatalf(n.Pos(), "needzero class %d", n.Class)
+		}
+		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.FrameOffset()+n.Type().Size() >= lo-int64(2*types.RegSize) {
+			// Merge with range we already have.
+			lo = n.FrameOffset()
+			continue
+		}
+
+		// Zero old range
+		p = Arch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
+
+		// Set new range.
+		lo = n.FrameOffset()
+		hi = lo + n.Type().Size()
+	}
+
+	// Zero final range.
+	Arch.ZeroRange(pp, p, frame+lo, hi-lo, &state)
+}
+
+// For generating consecutive jump instructions to model a specific branching
+type IndexJump struct {
+	Jump  obj.As
+	Index int
+}
+
+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 *State) CombJump(b, next *ssa.Block, jumps *[2][2]IndexJump) {
+	switch next {
+	case b.Succs[0].Block():
+		s.oneJump(b, &jumps[0][0])
+		s.oneJump(b, &jumps[0][1])
+	case b.Succs[1].Block():
+		s.oneJump(b, &jumps[1][0])
+		s.oneJump(b, &jumps[1][1])
+	default:
+		var q *obj.Prog
+		if b.Likely != ssa.BranchUnlikely {
+			s.oneJump(b, &jumps[1][0])
+			s.oneJump(b, &jumps[1][1])
+			q = s.Br(obj.AJMP, b.Succs[1].Block())
+		} else {
+			s.oneJump(b, &jumps[0][0])
+			s.oneJump(b, &jumps[0][1])
+			q = s.Br(obj.AJMP, b.Succs[0].Block())
+		}
+		q.Pos = b.Pos
+	}
+}
+
+// AddAux adds the offset in the aux fields (AuxInt and Aux) of v to a.
+func AddAux(a *obj.Addr, v *ssa.Value) {
+	AddAux2(a, v, v.AuxInt)
+}
+func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
+	if a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR {
+		v.Fatalf("bad AddAux addr %v", a)
+	}
+	// add integer offset
+	a.Offset += offset
+
+	// If no additional symbol offset, we're done.
+	if v.Aux == nil {
+		return
+	}
+	// Add symbol's offset from its base register.
+	switch n := v.Aux.(type) {
+	case *ssa.AuxCall:
+		a.Name = obj.NAME_EXTERN
+		a.Sym = n.Fn
+	case *obj.LSym:
+		a.Name = obj.NAME_EXTERN
+		a.Sym = n
+	case *ir.Name:
+		if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+			a.Name = obj.NAME_PARAM
+			a.Sym = ir.Orig(n).(*ir.Name).Linksym()
+			a.Offset += n.FrameOffset()
+			break
+		}
+		a.Name = obj.NAME_AUTO
+		a.Sym = n.Linksym()
+		a.Offset += n.FrameOffset()
+	default:
+		v.Fatalf("aux in %s not implemented %#v", v, v.Aux)
+	}
+}
+
+// extendIndex extends v to a full int width.
+// panic with the given kind if v does not fit in an int (only on 32-bit archs).
+func (s *state) extendIndex(idx, len *ssa.Value, kind ssa.BoundsKind, bounded bool) *ssa.Value {
+	size := idx.Type.Size()
+	if size == s.config.PtrSize {
+		return idx
+	}
+	if size > s.config.PtrSize {
+		// truncate 64-bit indexes on 32-bit pointer archs. Test the
+		// 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[types.TINT], idx)
+		} else {
+			lo = s.newValue1(ssa.OpInt64Lo, types.Types[types.TUINT], idx)
+		}
+		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[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:
+				kind = ssa.BoundsIndexU
+			case ssa.BoundsSliceAlen:
+				kind = ssa.BoundsSliceAlenU
+			case ssa.BoundsSliceAcap:
+				kind = ssa.BoundsSliceAcapU
+			case ssa.BoundsSliceB:
+				kind = ssa.BoundsSliceBU
+			case ssa.BoundsSlice3Alen:
+				kind = ssa.BoundsSlice3AlenU
+			case ssa.BoundsSlice3Acap:
+				kind = ssa.BoundsSlice3AcapU
+			case ssa.BoundsSlice3B:
+				kind = ssa.BoundsSlice3BU
+			case ssa.BoundsSlice3C:
+				kind = ssa.BoundsSlice3CU
+			}
+		}
+		b := s.endBlock()
+		b.Kind = ssa.BlockIf
+		b.SetControl(cmp)
+		b.Likely = ssa.BranchLikely
+		b.AddEdgeTo(bNext)
+		b.AddEdgeTo(bPanic)
+
+		s.startBlock(bPanic)
+		mem := s.newValue4I(ssa.OpPanicExtend, types.TypeMem, int64(kind), hi, lo, len, s.mem())
+		s.endBlock().SetControl(mem)
+		s.startBlock(bNext)
+
+		return lo
+	}
+
+	// Extend value to the required size
+	var op ssa.Op
+	if idx.Type.IsSigned() {
+		switch 10*size + s.config.PtrSize {
+		case 14:
+			op = ssa.OpSignExt8to32
+		case 18:
+			op = ssa.OpSignExt8to64
+		case 24:
+			op = ssa.OpSignExt16to32
+		case 28:
+			op = ssa.OpSignExt16to64
+		case 48:
+			op = ssa.OpSignExt32to64
+		default:
+			s.Fatalf("bad signed index extension %s", idx.Type)
+		}
+	} else {
+		switch 10*size + s.config.PtrSize {
+		case 14:
+			op = ssa.OpZeroExt8to32
+		case 18:
+			op = ssa.OpZeroExt8to64
+		case 24:
+			op = ssa.OpZeroExt16to32
+		case 28:
+			op = ssa.OpZeroExt16to64
+		case 48:
+			op = ssa.OpZeroExt32to64
+		default:
+			s.Fatalf("bad unsigned index extension %s", idx.Type)
+		}
+	}
+	return s.newValue1(op, types.Types[types.TINT], idx)
+}
+
+// CheckLoweredPhi checks that regalloc and stackalloc correctly handled phi values.
+// Called during ssaGenValue.
+func CheckLoweredPhi(v *ssa.Value) {
+	if v.Op != ssa.OpPhi {
+		v.Fatalf("CheckLoweredPhi called with non-phi value: %v", v.LongString())
+	}
+	if v.Type.IsMemory() {
+		return
+	}
+	f := v.Block.Func
+	loc := f.RegAlloc[v.ID]
+	for _, a := range v.Args {
+		if aloc := f.RegAlloc[a.ID]; aloc != loc { // TODO: .Equal() instead?
+			v.Fatalf("phi arg at different location than phi: %v @ %s, but arg %v @ %s\n%s\n", v, loc, a, aloc, v.Block.Func)
+		}
+	}
+}
+
+// CheckLoweredGetClosurePtr checks that v is the first instruction in the function's entry block.
+// The output of LoweredGetClosurePtr is generally hardwired to the correct register.
+// That register contains the closure pointer on closure entry.
+func CheckLoweredGetClosurePtr(v *ssa.Value) {
+	entry := v.Block.Func.Entry
+	if entry != v.Block || entry.Values[0] != v {
+		base.Fatalf("in %s, badly placed LoweredGetClosurePtr: %v %v", v.Block.Func.Name, v.Block, v)
+	}
+}
+
+func AddrAuto(a *obj.Addr, v *ssa.Value) {
+	n, off := ssa.AutoVar(v)
+	a.Type = obj.TYPE_MEM
+	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 *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.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 *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)
+	if pPosIsStmt == src.PosIsStmt {
+		p.Pos = v.Pos.WithIsStmt()
+	} else {
+		p.Pos = v.Pos.WithNotStmt()
+	}
+	if sym, ok := v.Aux.(*ssa.AuxCall); ok && sym.Fn != nil {
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = sym.Fn
+	} else {
+		// TODO(mdempsky): Can these differences be eliminated?
+		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:
+			base.Fatalf("unknown indirect call family")
+		}
+		p.To.Reg = v.Args[0].Reg()
+	}
+	return p
+}
+
+// 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 *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 == 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 == 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
+		// of the instruction byte before the return PC.
+		// To avoid that being an unrelated instruction,
+		// 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.
+		Arch.Ginsnopdefer(s.pp)
+	}
+
+	if ok {
+		// Record call graph information for nowritebarrierrec
+		// analysis.
+		if nowritebarrierrecCheck != nil {
+			nowritebarrierrecCheck.recordCall(s.pp.CurFunc, call.Fn, v.Pos)
+		}
+	}
+
+	if s.maxarg < v.AuxInt {
+		s.maxarg = v.AuxInt
+	}
+}
+
+// UseArgs records the fact that an instruction needs a certain amount of
+// callee args space for its use.
+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 *ir.SelectorExpr) int {
+	t := n.X.Type()
+	if !t.IsStruct() {
+		panic("ODOT's LHS is not a struct")
+	}
+
+	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
+		}
+	}
+	panic(fmt.Sprintf("can't find field in expr %v\n", n))
+
+	// TODO: keep the result of this function somewhere in the ODOT Node
+	// so we don't have to recompute it each time we need it.
+}
+
+// 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        *ir.Func
+	strings      map[string]*obj.LSym // map from constant string to data symbols
+	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
+}
+
+// StringData returns a symbol which
+// is the data component of a global string constant containing s.
+func (e *ssafn) StringData(s string) *obj.LSym {
+	if aux, ok := e.strings[s]; ok {
+		return aux
+	}
+	if e.strings == nil {
+		e.strings = make(map[string]*obj.LSym)
+	}
+	data := staticdata.StringSym(e.curfn.Pos(), s)
+	e.strings[s] = data
+	return data
+}
+
+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[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)
+	return p, l
+}
+
+func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
+	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() {
+		f = ".type"
+	}
+	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[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)
+	return p, l, c
+}
+
+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[types.TFLOAT64]
+	} else {
+		t = types.Types[types.TFLOAT32]
+	}
+	r := e.SplitSlot(&name, ".real", 0, t)
+	i := e.SplitSlot(&name, ".imag", t.Size(), t)
+	return r, i
+}
+
+func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
+	var t *types.Type
+	if name.Type.IsSigned() {
+		t = types.Types[types.TINT32]
+	} else {
+		t = types.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[types.TUINT32])
+}
+
+func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
+	st := name.Type
+	// Note: the _ field may appear several times.  But
+	// have no fear, identically-named but distinct Autos are
+	// ok, albeit maybe confusing for a debugger.
+	return e.SplitSlot(&name, "."+st.FieldName(i), st.FieldOff(i), st.FieldType(i))
+}
+
+func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
+	n := name.N
+	at := name.Type
+	if at.NumElem() != 1 {
+		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 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
+
+	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: 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 TypeOK(t)
+}
+
+func (e *ssafn) Line(pos src.XPos) string {
+	return base.FmtPos(pos)
+}
+
+// Log logs a message from the compiler.
+func (e *ssafn) Logf(msg string, args ...interface{}) {
+	if e.log {
+		fmt.Printf(msg, args...)
+	}
+}
+
+func (e *ssafn) Log() bool {
+	return e.log
+}
+
+// Fatal reports a compiler error and exits.
+func (e *ssafn) Fatalf(pos src.XPos, msg string, args ...interface{}) {
+	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{}) {
+	base.WarnfAt(pos, fmt_, args...)
+}
+
+func (e *ssafn) Debug_checknil() bool {
+	return base.Debug.Nil != 0
+}
+
+func (e *ssafn) UseWriteBarrier() bool {
+	return base.Flag.WB
+}
+
+func (e *ssafn) Syslook(name string) *obj.LSym {
+	switch name {
+	case "goschedguarded":
+		return ir.Syms.Goschedguarded
+	case "writeBarrier":
+		return ir.Syms.WriteBarrier
+	case "gcWriteBarrier":
+		return ir.Syms.GCWriteBarrier
+	case "typedmemmove":
+		return ir.Syms.Typedmemmove
+	case "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.SetWBPos(pos)
+}
+
+func (e *ssafn) MyImportPath() string {
+	return base.Ctxt.Pkgpath
+}
+
+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
+}
+
+// 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 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
+}
+
+// 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 0000000..b06fd7a
--- /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 0000000..8936c4f
--- /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 0000000..f3ad82e
--- /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 f84bd2d..22680dc 100644
--- a/src/cmd/compile/internal/syntax/dumper_test.go
+++ b/src/cmd/compile/internal/syntax/dumper_test.go
@@ -13,7 +13,7 @@
 		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 815630f..487cab1 100644
--- a/src/cmd/compile/internal/syntax/nodes.go
+++ b/src/cmd/compile/internal/syntax/nodes.go
@@ -114,7 +114,7 @@
 
 // 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 3c759b2..a7cd40f 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 c3b9aca..fe72e7a 100644
--- a/src/cmd/compile/internal/syntax/printer_test.go
+++ b/src/cmd/compile/internal/syntax/printer_test.go
@@ -18,7 +18,7 @@
 		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 3cf5473..ef295eb 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 3b97cb6..2936b65 100644
--- a/src/cmd/compile/internal/syntax/tokens.go
+++ b/src/cmd/compile/internal/syntax/tokens.go
@@ -6,7 +6,7 @@
 
 type token uint
 
-//go:generate stringer -type token -linecomment
+//go:generate stringer -type token -linecomment tokens.go
 
 const (
 	_    token = iota
@@ -105,7 +105,7 @@
 
 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 0000000..decc296
--- /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 0000000..19dd3a5
--- /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 8c42881..3fffe57 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 59f905d..7159f0e 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 91%
rename from src/cmd/compile/internal/gc/dep_test.go
rename to src/cmd/compile/internal/test/dep_test.go
index c1dac93..26122e6 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 @@
 	}
 	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 8ac4436..e7e2f7e 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 c619d25..884a983 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 edad6d0..5f5f7d6 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 21c6587..ebc4f89 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 02735e5..9d31975 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 98%
rename from src/cmd/compile/internal/gc/lang_test.go
rename to src/cmd/compile/internal/test/lang_test.go
index 72e7f07..67c1551 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 78d2dd2..1d7043f 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 8101e44..4d84f9c 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 ce2eedf..ea88f0a 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 7f7c946..2f3e24c 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 db5ca7d..b87daed 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 @@
 		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 98%
rename from src/cmd/compile/internal/gc/truncconst_test.go
rename to src/cmd/compile/internal/test/truncconst_test.go
index d153818..7705042 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 89f4cb9..cb1a6e0 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 0000000..1c1b077
--- /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 0000000..4a84bb1
--- /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 0000000..0dee852
--- /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 96%
rename from src/cmd/compile/internal/gc/builtin_test.go
rename to src/cmd/compile/internal/typecheck/builtin_test.go
index 57f24b2..fb9d3e3 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 0000000..1a8e583
--- /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 0000000..eab0bb0
--- /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 0000000..63d0a1e
--- /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 0000000..339fb00
--- /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 0000000..7ab5f68
--- /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/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
new file mode 100644
index 0000000..6fab74e
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -0,0 +1,1673 @@
+// 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 export.
+//
+// The indexed export data format is an evolution of the previous
+// binary export data format. Its chief contribution is introducing an
+// index table, which allows efficient random access of individual
+// declarations and inline function bodies. In turn, this allows
+// avoiding unnecessary work for compilation units that import large
+// packages.
+//
+//
+// The top-level data format is structured as:
+//
+//     Header struct {
+//         Tag        byte   // 'i'
+//         Version    uvarint
+//         StringSize uvarint
+//         DataSize   uvarint
+//     }
+//
+//     Strings [StringSize]byte
+//     Data    [DataSize]byte
+//
+//     MainIndex []struct{
+//         PkgPath   stringOff
+//         PkgName   stringOff
+//         PkgHeight uvarint
+//
+//         Decls []struct{
+//             Name   stringOff
+//             Offset declOff
+//         }
+//     }
+//
+//     Fingerprint [8]byte
+//
+// uvarint means a uint64 written out using uvarint encoding.
+//
+// []T means a uvarint followed by that many T objects. In other
+// words:
+//
+//     Len   uvarint
+//     Elems [Len]T
+//
+// stringOff means a uvarint that indicates an offset within the
+// Strings section. At that offset is another uvarint, followed by
+// that many bytes, which form the string value.
+//
+// declOff means a uvarint that indicates an offset within the Data
+// section where the associated declaration can be found.
+//
+//
+// There are five kinds of declarations, distinguished by their first
+// byte:
+//
+//     type Var struct {
+//         Tag  byte // 'V'
+//         Pos  Pos
+//         Type typeOff
+//     }
+//
+//     type Func struct {
+//         Tag       byte // 'F'
+//         Pos       Pos
+//         Signature Signature
+//     }
+//
+//     type Const struct {
+//         Tag   byte // 'C'
+//         Pos   Pos
+//         Value Value
+//     }
+//
+//     type Type struct {
+//         Tag        byte // 'T'
+//         Pos        Pos
+//         Underlying typeOff
+//
+//         Methods []struct{  // omitted if Underlying is an interface type
+//             Pos       Pos
+//             Name      stringOff
+//             Recv      Param
+//             Signature Signature
+//         }
+//     }
+//
+//     type Alias struct {
+//         Tag  byte // 'A'
+//         Pos  Pos
+//         Type typeOff
+//     }
+//
+//
+// typeOff means a uvarint that either indicates a predeclared type,
+// or an offset into the Data section. If the uvarint is less than
+// predeclReserved, then it indicates the index into the predeclared
+// types list (see predeclared in bexport.go for order). Otherwise,
+// subtracting predeclReserved yields the offset of a type descriptor.
+//
+// Value means a type and type-specific value. See
+// (*exportWriter).value for details.
+//
+//
+// There are nine kinds of type descriptors, distinguished by an itag:
+//
+//     type DefinedType struct {
+//         Tag     itag // definedType
+//         Name    stringOff
+//         PkgPath stringOff
+//     }
+//
+//     type PointerType struct {
+//         Tag  itag // pointerType
+//         Elem typeOff
+//     }
+//
+//     type SliceType struct {
+//         Tag  itag // sliceType
+//         Elem typeOff
+//     }
+//
+//     type ArrayType struct {
+//         Tag  itag // arrayType
+//         Len  uint64
+//         Elem typeOff
+//     }
+//
+//     type ChanType struct {
+//         Tag  itag   // chanType
+//         Dir  uint64 // 1 RecvOnly; 2 SendOnly; 3 SendRecv
+//         Elem typeOff
+//     }
+//
+//     type MapType struct {
+//         Tag  itag // mapType
+//         Key  typeOff
+//         Elem typeOff
+//     }
+//
+//     type FuncType struct {
+//         Tag       itag // signatureType
+//         PkgPath   stringOff
+//         Signature Signature
+//     }
+//
+//     type StructType struct {
+//         Tag     itag // structType
+//         PkgPath stringOff
+//         Fields []struct {
+//             Pos      Pos
+//             Name     stringOff
+//             Type     typeOff
+//             Embedded bool
+//             Note     stringOff
+//         }
+//     }
+//
+//     type InterfaceType struct {
+//         Tag     itag // interfaceType
+//         PkgPath stringOff
+//         Embeddeds []struct {
+//             Pos  Pos
+//             Type typeOff
+//         }
+//         Methods []struct {
+//             Pos       Pos
+//             Name      stringOff
+//             Signature Signature
+//         }
+//     }
+//
+//
+//     type Signature struct {
+//         Params   []Param
+//         Results  []Param
+//         Variadic bool  // omitted if Results is empty
+//     }
+//
+//     type Param struct {
+//         Pos  Pos
+//         Name stringOff
+//         Type typOff
+//     }
+//
+//
+// Pos encodes a file:line:column triple, incorporating a simple delta
+// encoding scheme within a data object. See exportWriter.pos for
+// details.
+//
+//
+// Compiler-specific details.
+//
+// cmd/compile writes out a second index for inline bodies and also
+// appends additional compiler-specific details after declarations.
+// Third-party tools are not expected to depend on these details and
+// they're expected to change much more rapidly, so they're omitted
+// here. See exportWriter's varExt/funcExt/etc methods for details.
+
+package typecheck
+
+import (
+	"bufio"
+	"bytes"
+	"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.
+// 1: added column details to Pos
+// 0: Go1.11 encoding
+const iexportVersion = 1
+
+// predeclReserved is the number of type offsets reserved for types
+// implicitly declared in the universe block.
+const predeclReserved = 32
+
+// An itag distinguishes the kind of type that was written into the
+// indexed export format.
+type itag uint64
+
+const (
+	// Types
+	definedType itag = iota
+	pointerType
+	sliceType
+	arrayType
+	chanType
+	mapType
+	signatureType
+	structType
+	interfaceType
+)
+
+func WriteExports(out *bufio.Writer) {
+	p := iexporter{
+		allPkgs:     map[*types.Pkg]bool{},
+		stringIndex: map[string]uint64{},
+		declIndex:   map[*types.Sym]uint64{},
+		inlineIndex: map[*types.Sym]uint64{},
+		typIndex:    map[*types.Type]uint64{},
+	}
+
+	for i, pt := range predeclared() {
+		p.typIndex[pt] = uint64(i)
+	}
+	if 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 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())
+	}
+
+	// Append indices to data0 section.
+	dataLen := uint64(p.data0.Len())
+	w := p.newWriter()
+	w.writeIndex(p.declIndex, true)
+	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')
+	hdr.uint64(iexportVersion)
+	hdr.uint64(uint64(p.strings.Len()))
+	hdr.uint64(dataLen)
+
+	// Flush output.
+	h := md5.New()
+	wr := io.MultiWriter(out, h)
+	io.Copy(wr, &hdr)
+	io.Copy(wr, &p.strings)
+	io.Copy(wr, &p.data0)
+
+	// Add fingerprint (used by linker object file).
+	// Attach this to the end, so tools (e.g. gcimporter) don't care.
+	copy(base.Ctxt.Fingerprint[:], h.Sum(nil)[:])
+	out.Write(base.Ctxt.Fingerprint[:])
+}
+
+// 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[*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 {
+		pkgSyms[types.LocalPkg] = nil
+		for pkg := range w.p.allPkgs {
+			pkgSyms[pkg] = nil
+		}
+	}
+
+	// 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 := range pkgSyms {
+		pkgs = append(pkgs, pkg)
+	}
+	sort.Slice(pkgs, func(i, j int) bool {
+		return pkgs[i].Path < pkgs[j].Path
+	})
+
+	w.uint64(uint64(len(pkgs)))
+	for _, pkg := range pkgs {
+		w.string(pkg.Path)
+		if mainIndex {
+			w.string(pkg.Name)
+			w.uint64(uint64(pkg.Height))
+		}
+
+		// 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])
+		}
+	}
+}
+
+type iexporter struct {
+	// allPkgs tracks all packages that have been referenced by
+	// the export data, so we can ensure to include them in the
+	// main index.
+	allPkgs map[*types.Pkg]bool
+
+	declTodo ir.NameQueue
+
+	strings     intWriter
+	stringIndex map[string]uint64
+
+	data0       intWriter
+	declIndex   map[*types.Sym]uint64
+	inlineIndex map[*types.Sym]uint64
+	typIndex    map[*types.Type]uint64
+}
+
+// stringOff returns the offset of s within the string section.
+// If not already present, it's added to the end.
+func (p *iexporter) stringOff(s string) uint64 {
+	off, ok := p.stringIndex[s]
+	if !ok {
+		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)
+	}
+	return off
+}
+
+// pushDecl adds n to the declaration work queue, if not already present.
+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 == types.BuiltinPkg || n.Sym().Pkg == ir.Pkgs.Unsafe {
+		return
+	}
+
+	if _, ok := p.declIndex[n.Sym()]; ok {
+		return
+	}
+
+	p.declIndex[n.Sym()] = ^uint64(0) // mark n present in work queue
+	p.declTodo.PushRight(n)
+}
+
+// exportWriter handles writing out individual data section chunks.
+type exportWriter struct {
+	p *iexporter
+
+	data       intWriter
+	currPkg    *types.Pkg
+	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 *ir.Name) {
+	w := p.newWriter()
+	w.setPkg(n.Sym().Pkg, false)
+
+	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.varExt(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.funcExt(n)
+
+		default:
+			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)
+		}
+
+		// Constant.
+		w.tag('C')
+		w.pos(n.Pos())
+		w.value(n.Type(), n.Val())
+		w.constExt(n)
+
+	case ir.OTYPE:
+		if types.IsDotAlias(n.Sym()) {
+			// Alias.
+			w.tag('A')
+			w.pos(n.Pos())
+			w.typ(n.Type())
+			break
+		}
+
+		// Defined type.
+		w.tag('T')
+		w.pos(n.Pos())
+
+		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
+		}
+		w.typ(underlying)
+
+		t := n.Type()
+		if t.IsInterface() {
+			w.typeExt(t)
+			break
+		}
+
+		ms := t.Methods()
+		w.uint64(uint64(ms.Len()))
+		for _, m := range ms.Slice() {
+			w.pos(m.Pos)
+			w.selector(m.Sym)
+			w.param(m.Type.Recv())
+			w.signature(m.Type)
+		}
+
+		w.typeExt(t)
+		for _, m := range ms.Slice() {
+			w.methExt(m)
+		}
+
+	default:
+		base.Fatalf("unexpected node: %v", n)
+	}
+
+	w.finish("dcl", p.declIndex, n.Sym())
+}
+
+func (w *exportWriter) tag(tag byte) {
+	w.data.WriteByte(tag)
+}
+
+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.dclIndex = make(map[*ir.Name]int, len(f.Func.Inl.Dcl))
+	w.funcBody(f.Func)
+
+	w.finish("inl", p.inlineIndex, f.Sym())
+}
+
+func (w *exportWriter) pos(pos src.XPos) {
+	p := base.Ctxt.PosTable.Pos(pos)
+	file := p.Base().AbsFilename()
+	line := int64(p.RelLine())
+	column := int64(p.RelCol())
+
+	// Encode position relative to the last position: column
+	// delta, then line delta, then file name. We reserve the
+	// bottom bit of the column and line deltas to encode whether
+	// the remaining fields are present.
+	//
+	// Note: Because data objects may be read out of order (or not
+	// at all), we can only apply delta encoding within a single
+	// object. This is handled implicitly by tracking prevFile,
+	// prevLine, and prevColumn as fields of exportWriter.
+
+	deltaColumn := (column - w.prevColumn) << 1
+	deltaLine := (line - w.prevLine) << 1
+
+	if file != w.prevFile {
+		deltaLine |= 1
+	}
+	if deltaLine != 0 {
+		deltaColumn |= 1
+	}
+
+	w.int64(deltaColumn)
+	if deltaColumn&1 != 0 {
+		w.int64(deltaLine)
+		if deltaLine&1 != 0 {
+			w.string(file)
+		}
+	}
+
+	w.prevFile = file
+	w.prevLine = line
+	w.prevColumn = column
+}
+
+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 ir.Node) {
+	// Ensure any referenced declarations are written out too.
+	w.p.pushDecl(n.Name())
+
+	s := n.Sym()
+	w.string(s.Name)
+	w.pkg(s.Pkg)
+}
+
+func (w *exportWriter) selector(s *types.Sym) {
+	if w.currPkg == nil {
+		base.Fatalf("missing currPkg")
+	}
+
+	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(s.Name)
+}
+
+func (w *exportWriter) typ(t *types.Type) {
+	w.data.uint64(w.p.typOff(t))
+}
+
+func (p *iexporter) newWriter() *exportWriter {
+	return &exportWriter{p: p}
+}
+
+func (w *exportWriter) flush() uint64 {
+	off := uint64(w.p.data0.Len())
+	io.Copy(&w.p.data0, &w.data)
+	return off
+}
+
+func (p *iexporter) typOff(t *types.Type) uint64 {
+	off, ok := p.typIndex[t]
+	if !ok {
+		w := p.newWriter()
+		w.doTyp(t)
+		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
+}
+
+func (w *exportWriter) startType(k itag) {
+	w.data.uint64(uint64(k))
+}
+
+func (w *exportWriter) doTyp(t *types.Type) {
+	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(t.Obj().(*ir.Name))
+		return
+	}
+
+	switch t.Kind() {
+	case types.TPTR:
+		w.startType(pointerType)
+		w.typ(t.Elem())
+
+	case types.TSLICE:
+		w.startType(sliceType)
+		w.typ(t.Elem())
+
+	case types.TARRAY:
+		w.startType(arrayType)
+		w.uint64(uint64(t.NumElem()))
+		w.typ(t.Elem())
+
+	case types.TCHAN:
+		w.startType(chanType)
+		w.uint64(uint64(t.ChanDir()))
+		w.typ(t.Elem())
+
+	case types.TMAP:
+		w.startType(mapType)
+		w.typ(t.Key())
+		w.typ(t.Elem())
+
+	case types.TFUNC:
+		w.startType(signatureType)
+		w.setPkg(t.Pkg(), true)
+		w.signature(t)
+
+	case types.TSTRUCT:
+		w.startType(structType)
+		w.setPkg(t.Pkg(), true)
+
+		w.uint64(uint64(t.NumFields()))
+		for _, f := range t.FieldSlice() {
+			w.pos(f.Pos)
+			w.selector(f.Sym)
+			w.typ(f.Type)
+			w.bool(f.Embedded != 0)
+			w.string(f.Note)
+		}
+
+	case types.TINTER:
+		var embeddeds, methods []*types.Field
+		for _, m := range t.Methods().Slice() {
+			if m.Sym != nil {
+				methods = append(methods, m)
+			} else {
+				embeddeds = append(embeddeds, m)
+			}
+		}
+
+		w.startType(interfaceType)
+		w.setPkg(t.Pkg(), true)
+
+		w.uint64(uint64(len(embeddeds)))
+		for _, f := range embeddeds {
+			w.pos(f.Pos)
+			w.typ(f.Type)
+		}
+
+		w.uint64(uint64(len(methods)))
+		for _, f := range methods {
+			w.pos(f.Pos)
+			w.selector(f.Sym)
+			w.signature(f.Type)
+		}
+
+	default:
+		base.Fatalf("unexpected type: %v", t)
+	}
+}
+
+func (w *exportWriter) setPkg(pkg *types.Pkg, write bool) {
+	if pkg == types.NoPkg {
+		base.Fatalf("missing pkg")
+	}
+
+	if write {
+		w.pkg(pkg)
+	}
+
+	w.currPkg = pkg
+}
+
+func (w *exportWriter) signature(t *types.Type) {
+	w.paramList(t.Params().FieldSlice())
+	w.paramList(t.Results().FieldSlice())
+	if n := t.Params().NumFields(); n > 0 {
+		w.bool(t.Params().Field(n - 1).IsDDD())
+	}
+}
+
+func (w *exportWriter) paramList(fs []*types.Field) {
+	w.uint64(uint64(len(fs)))
+	for _, f := range fs {
+		w.param(f)
+	}
+}
+
+func (w *exportWriter) param(f *types.Field) {
+	w.pos(f.Pos)
+	w.localIdent(types.OrigSym(f.Sym))
+	w.typ(f.Type)
+}
+
+func constTypeOf(typ *types.Type) constant.Kind {
+	switch typ {
+	case types.UntypedInt, types.UntypedRune:
+		return constant.Int
+	case types.UntypedFloat:
+		return constant.Float
+	case types.UntypedComplex:
+		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 constant.Value) {
+	ir.AssertValidTypeForConst(typ, v)
+	w.typ(typ)
+
+	// Each type has only one admissible constant representation,
+	// so we could type switch directly on v.U here. However,
+	// switching on the type increases symmetry with import logic
+	// and provides a useful consistency check.
+
+	switch constTypeOf(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, ir.ConstPrec / 8
+	}
+
+	switch typ.Kind() {
+	case types.TFLOAT32, types.TCOMPLEX64:
+		return true, 3
+	case types.TFLOAT64, types.TCOMPLEX128:
+		return true, 7
+	}
+
+	signed = typ.IsSigned()
+	maxBytes = uint(typ.Size())
+
+	// The go/types API doesn't expose sizes to importers, so they
+	// don't know how big these types are.
+	switch typ.Kind() {
+	case types.TINT, types.TUINT, types.TUINTPTR:
+		maxBytes = 8
+	}
+
+	return
+}
+
+// mpint exports a multi-precision integer.
+//
+// For unsigned types, small values are written out as a single
+// byte. Larger values are written out as a length-prefixed big-endian
+// byte string, where the length prefix is encoded as its complement.
+// For example, bytes 0, 1, and 2 directly represent the integer
+// values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-,
+// 2-, and 3-byte big-endian string follow.
+//
+// Encoding for signed types use the same general approach as for
+// unsigned types, except small values use zig-zag encoding and the
+// bottom bit of length prefix byte for large values is reserved as a
+// sign bit.
+//
+// The exact boundary between small and large encodings varies
+// 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.
+func (w *exportWriter) mpint(x constant.Value, typ *types.Type) {
+	signed, maxBytes := intSize(typ)
+
+	negative := constant.Sign(x) < 0
+	if !signed && negative {
+		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]
+	}
+
+	if len(b) > 0 && b[0] == 0 {
+		base.Fatalf("leading zeros")
+	}
+	if uint(len(b)) > maxBytes {
+		base.Fatalf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)
+	}
+
+	maxSmall := 256 - maxBytes
+	if signed {
+		maxSmall = 256 - 2*maxBytes
+	}
+	if maxBytes == 1 {
+		maxSmall = 256
+	}
+
+	// Check if x can use small value encoding.
+	if len(b) <= 1 {
+		var ux uint
+		if len(b) == 1 {
+			ux = uint(b[0])
+		}
+		if signed {
+			ux <<= 1
+			if negative {
+				ux--
+			}
+		}
+		if ux < maxSmall {
+			w.data.WriteByte(byte(ux))
+			return
+		}
+	}
+
+	n := 256 - uint(len(b))
+	if signed {
+		n = 256 - 2*uint(len(b))
+		if negative {
+			n |= 1
+		}
+	}
+	if n < maxSmall || n >= 256 {
+		base.Fatalf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)
+	}
+
+	w.data.WriteByte(byte(n))
+	w.data.Write(b)
+}
+
+// mpfloat exports a multi-precision floating point number.
+//
+// The number's value is decomposed into mantissa × 2**exponent, where
+// 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(v constant.Value, typ *types.Type) {
+	f := ir.BigFloat(v)
+	if f.IsInf() {
+		base.Fatalf("infinite constant")
+	}
+
+	// Break into f = mant × 2**exp, with 0.5 <= mant < 1.
+	var mant big.Float
+	exp := int64(f.MantExp(&mant))
+
+	// Scale so that mant is an integer.
+	prec := mant.MinPrec()
+	mant.SetMantExp(&mant, int(prec))
+	exp -= int64(prec)
+
+	manti, acc := mant.Int(nil)
+	if acc != big.Exact {
+		base.Fatalf("mantissa scaling failed for %f (%s)", f, acc)
+	}
+	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 {
+		x = 1
+	}
+	w.uint64(x)
+	return b
+}
+
+func (w *exportWriter) int64(x int64)   { w.data.int64(x) }
+func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) }
+func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) }
+
+// Compiler-specific extensions.
+
+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 *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() {
+			w.string(f.Note)
+		}
+	}
+
+	// Inline body.
+	if n.Func.Inl != nil {
+		w.uint64(1 + uint64(n.Func.Inl.Cost))
+		if n.Func.ExportInline() {
+			w.p.doInline(n)
+		}
+
+		// Endlineno for inlined function.
+		w.pos(n.Func.Endlineno)
+	} else {
+		w.uint64(0)
+	}
+}
+
+func (w *exportWriter) methExt(m *types.Field) {
+	w.bool(m.Nointerface())
+	w.funcExt(m.Nname.(*ir.Name))
+}
+
+func (w *exportWriter) linkname(s *types.Sym) {
+	w.string(s.Linkname)
+}
+
+func (w *exportWriter) symIdx(s *types.Sym) {
+	lsym := s.Linksym()
+	if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
+		// Don't export index for non-package symbols, linkname'd symbols,
+		// and symbols without an index. They can only be referenced by
+		// name.
+		w.int64(-1)
+	} else {
+		// For a defined symbol, export its index.
+		// For re-exporting an imported symbol, pass its index through.
+		w.int64(int64(lsym.SymIdx))
+	}
+}
+
+func (w *exportWriter) typeExt(t *types.Type) {
+	// Export whether this type is marked notinheap.
+	w.bool(t.NotInHeap())
+	// For type T, export the index of type descriptor symbols of T and *T.
+	if i, ok := typeSymIdx[t]; ok {
+		w.int64(i[0])
+		w.int64(i[1])
+		return
+	}
+	w.symIdx(types.TypeSym(t))
+	w.symIdx(types.TypeSym(t.PtrTo()))
+}
+
+// Inline bodies.
+
+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(ir.OEND)
+}
+
+func (w *exportWriter) node(n ir.Node) {
+	if ir.OpPrec[n.Op()] < 0 {
+		w.stmt(n)
+	} else {
+		w.expr(n)
+	}
+}
+
+// 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 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.Init() {
+			w.stmt(n)
+		}
+	}
+
+	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 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 ir.OAS:
+		// Don't export "v = <N>" initializing statements, hope they're always
+		// preceded by the DCL which will be re-parsed and typecheck to reproduce
+		// the "v = <N>" again.
+		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 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 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 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 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 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:
+		base.Fatalf("exporter: CANNOT EXPORT: %v\nPlease notify gri@\n", n.Op())
+	}
+}
+
+func isNamedTypeSwitch(x ir.Node) bool {
+	guard, ok := x.(*ir.TypeSwitchGuard)
+	return ok && guard.Tag != nil
+}
+
+func (w *exportWriter) caseList(cases []*ir.CaseClause, namedTypeSwitch bool) {
+	w.uint64(uint64(len(cases)))
+	for _, cas := range cases {
+		w.pos(cas.Pos())
+		w.stmtList(cas.List)
+		if namedTypeSwitch {
+			w.localName(cas.Var)
+		}
+		w.stmtList(cas.Body)
+	}
+}
+
+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)
+	}
+}
+
+func (w *exportWriter) exprList(list ir.Nodes) {
+	for _, n := range list {
+		w.expr(n)
+	}
+	w.op(ir.OEND)
+}
+
+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
+}
+
+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 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.
+		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(ir.ONAME)
+		w.localName(n)
+
+	// case OPACK, ONONAME:
+	// 	should have been resolved by typechecking - handled by default case
+
+	case ir.OTYPE:
+		w.op(ir.OTYPE)
+		w.typ(n.Type())
+
+	case ir.OTYPESW:
+		n := n.(*ir.TypeSwitchGuard)
+		w.op(ir.OTYPESW)
+		w.pos(n.Pos())
+		var s *types.Sym
+		if n.Tag != nil {
+			if n.Tag.Op() != ir.ONONAME {
+				base.Fatalf("expected ONONAME, got %v", n.Tag)
+			}
+			s = n.Tag.Sym()
+		}
+		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 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 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 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 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)
+		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 n.Op() == ir.OAPPEND {
+			w.bool(n.IsDDD)
+		} else if n.IsDDD {
+			base.Fatalf("exporter: unexpected '...' with %v call", n.Op())
+		}
+
+	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(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 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 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 ir.ODCLCONST:
+		// if exporting, DCLCONST should just be removed as its usage
+		// has already been replaced with literals
+
+	default:
+		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 ir.Op) {
+	w.uint64(uint64(op))
+}
+
+func (w *exportWriter) exprsOrNil(a, b ir.Node) {
+	ab := 0
+	if a != nil {
+		ab |= 1
+	}
+	if b != nil {
+		ab |= 2
+	}
+	w.uint64(uint64(ab))
+	if ab&1 != 0 {
+		w.expr(a)
+	}
+	if ab&2 != 0 {
+		w.node(b)
+	}
+}
+
+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 *ir.Name) {
+	if ir.IsBlank(n) {
+		w.int64(-1)
+		return
+	}
+
+	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) {
+	if w.currPkg == nil {
+		base.Fatalf("missing currPkg")
+	}
+
+	// Anonymous parameters.
+	if s == nil {
+		w.string("")
+		return
+	}
+
+	name := s.Name
+	if name == "_" {
+		w.string("_")
+		return
+	}
+
+	// TODO(mdempsky): Fix autotmp hack.
+	if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, ".autotmp_") {
+		base.Fatalf("unexpected dot in identifier: %v", name)
+	}
+
+	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)
+}
+
+type intWriter struct {
+	bytes.Buffer
+}
+
+func (w *intWriter) int64(x int64) {
+	var buf [binary.MaxVarintLen64]byte
+	n := binary.PutVarint(buf[:], x)
+	w.Write(buf[:n])
+}
+
+func (w *intWriter) uint64(x uint64) {
+	var buf [binary.MaxVarintLen64]byte
+	n := binary.PutUvarint(buf[:], x)
+	w.Write(buf[:n])
+}
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
new file mode 100644
index 0000000..b73ef51
--- /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 9483688..2f3aa16 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 c6f68ed..4059f26 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 63d2a12..07f4b76 100644
--- a/src/cmd/compile/internal/gc/mkbuiltin.go
+++ b/src/cmd/compile/internal/typecheck/mkbuiltin.go
@@ -33,9 +33,12 @@
 	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 @@
 	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 @@
 		}
 		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 @@
 	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 0000000..14ed175
--- /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 0000000..b88a9f2
--- /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 0000000..202a932
--- /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 0000000..018614d
--- /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 0000000..6fdafef
--- /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 0000000..cb43457
--- /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<<k.
+	// TODO(gri) The length argument requirements for (array/slice) make
+	// are the same as for index expressions. Factor the code better;
+	// for instance, indexlit might be called here and incorporate some
+	// of the bounds checks done for make.
+	n = DefaultLit(n, types.Types[types.TINT])
+	*np = n
+
+	return true
+}
+
+// markBreak marks control statements containing break statements with SetHasBreak(true).
+func markBreak(fn *ir.Func) {
+	var labels map[*types.Sym]ir.Node
+	var implicit ir.Node
+
+	var mark func(ir.Node) bool
+	mark = func(n ir.Node) bool {
+		switch n.Op() {
+		default:
+			ir.DoChildren(n, mark)
+
+		case ir.OBREAK:
+			n := n.(*ir.BranchStmt)
+			if n.Label == nil {
+				setHasBreak(implicit)
+			} else {
+				setHasBreak(labels[n.Label])
+			}
+
+		case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OSELECT, ir.ORANGE:
+			old := implicit
+			implicit = n
+			var sym *types.Sym
+			switch n := n.(type) {
+			case *ir.ForStmt:
+				sym = n.Label
+			case *ir.RangeStmt:
+				sym = n.Label
+			case *ir.SelectStmt:
+				sym = n.Label
+			case *ir.SwitchStmt:
+				sym = n.Label
+			}
+			if sym != nil {
+				if labels == nil {
+					// Map creation delayed until we need it - most functions don't.
+					labels = make(map[*types.Sym]ir.Node)
+				}
+				labels[sym] = n
+			}
+			ir.DoChildren(n, mark)
+			if sym != nil {
+				delete(labels, sym)
+			}
+			implicit = old
+		}
+		return false
+	}
+
+	mark(fn)
+}
+
+func controlLabel(n ir.Node) *types.Sym {
+	switch n := n.(type) {
+	default:
+		base.Fatalf("controlLabel %+v", n.Op())
+		return nil
+	case *ir.ForStmt:
+		return n.Label
+	case *ir.RangeStmt:
+		return n.Label
+	case *ir.SelectStmt:
+		return n.Label
+	case *ir.SwitchStmt:
+		return n.Label
+	}
+}
+
+func setHasBreak(n ir.Node) {
+	switch n := n.(type) {
+	default:
+		base.Fatalf("setHasBreak %+v", n.Op())
+	case nil:
+		// ignore
+	case *ir.ForStmt:
+		n.HasBreak = true
+	case *ir.RangeStmt:
+		n.HasBreak = true
+	case *ir.SelectStmt:
+		n.HasBreak = true
+	case *ir.SwitchStmt:
+		n.HasBreak = true
+	}
+}
+
+// isTermNodes reports whether the Nodes list ends with a terminating statement.
+func isTermNodes(l ir.Nodes) bool {
+	s := l
+	c := len(s)
+	if c == 0 {
+		return false
+	}
+	return isTermNode(s[c-1])
+}
+
+// isTermNode reports whether the node n, the last one in a
+// statement list, is a terminating statement.
+func isTermNode(n ir.Node) bool {
+	switch n.Op() {
+	// NOTE: OLABEL is treated as a separate statement,
+	// not a separate prefix, so skipping to the last statement
+	// in the block handles the labeled statement case by
+	// skipping over the label. No case OLABEL here.
+
+	case ir.OBLOCK:
+		n := n.(*ir.BlockStmt)
+		return isTermNodes(n.List)
+
+	case ir.OGOTO, ir.ORETURN, ir.OTAILCALL, ir.OPANIC, ir.OFALL:
+		return true
+
+	case ir.OFOR, ir.OFORUNTIL:
+		n := n.(*ir.ForStmt)
+		if n.Cond != nil {
+			return false
+		}
+		if n.HasBreak {
+			return false
+		}
+		return true
+
+	case ir.OIF:
+		n := n.(*ir.IfStmt)
+		return isTermNodes(n.Body) && isTermNodes(n.Else)
+
+	case ir.OSWITCH:
+		n := n.(*ir.SwitchStmt)
+		if n.HasBreak {
+			return false
+		}
+		def := false
+		for _, cas := range n.Cases {
+			if !isTermNodes(cas.Body) {
+				return false
+			}
+			if len(cas.List) == 0 { // default
+				def = true
+			}
+		}
+		return def
+
+	case ir.OSELECT:
+		n := n.(*ir.SelectStmt)
+		if n.HasBreak {
+			return false
+		}
+		for _, cas := range n.Cases {
+			if !isTermNodes(cas.Body) {
+				return false
+			}
+		}
+		return true
+	}
+
+	return false
+}
+
+// CheckUnused checks for any declared variables that weren't used.
+func CheckUnused(fn *ir.Func) {
+	// Only report unused variables if we haven't seen any type-checking
+	// errors yet.
+	if base.Errors() != 0 {
+		return
+	}
+
+	// Propagate the used flag for typeswitch variables up to the NONAME in its definition.
+	for _, ln := range fn.Dcl {
+		if ln.Op() == ir.ONAME && ln.Class == ir.PAUTO && ln.Used() {
+			if guard, ok := ln.Defn.(*ir.TypeSwitchGuard); ok {
+				guard.Used = true
+			}
+		}
+	}
+
+	for _, ln := range fn.Dcl {
+		if ln.Op() != ir.ONAME || ln.Class != ir.PAUTO || ln.Used() {
+			continue
+		}
+		if defn, ok := ln.Defn.(*ir.TypeSwitchGuard); ok {
+			if defn.Used {
+				continue
+			}
+			base.ErrorfAt(defn.Tag.Pos(), "%v declared but not used", ln.Sym())
+			defn.Used = true // suppress repeats
+		} else {
+			base.ErrorfAt(ln.Pos(), "%v declared but not used", ln.Sym())
+		}
+	}
+}
+
+// CheckReturn makes sure that fn terminates appropriately.
+func CheckReturn(fn *ir.Func) {
+	if fn.Type().NumResults() != 0 && len(fn.Body) != 0 {
+		markBreak(fn)
+		if !isTermNodes(fn.Body) {
+			base.ErrorfAt(fn.Endlineno, "missing return at end of function")
+		}
+	}
+}
+
+// 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() == 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 0000000..402b8de
--- /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 0000000..2c2700f
--- /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 52b5399..a1b518e 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/etype_string.go b/src/cmd/compile/internal/types/etype_string.go
deleted file mode 100644
index 14fd5b7..0000000
--- a/src/cmd/compile/internal/types/etype_string.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Code generated by "stringer -type EType -trimprefix T"; DO NOT EDIT.
-
-package types
-
-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[Txxx-0]
-	_ = x[TINT8-1]
-	_ = x[TUINT8-2]
-	_ = x[TINT16-3]
-	_ = x[TUINT16-4]
-	_ = x[TINT32-5]
-	_ = x[TUINT32-6]
-	_ = x[TINT64-7]
-	_ = x[TUINT64-8]
-	_ = x[TINT-9]
-	_ = x[TUINT-10]
-	_ = x[TUINTPTR-11]
-	_ = x[TCOMPLEX64-12]
-	_ = x[TCOMPLEX128-13]
-	_ = x[TFLOAT32-14]
-	_ = x[TFLOAT64-15]
-	_ = x[TBOOL-16]
-	_ = x[TPTR-17]
-	_ = x[TFUNC-18]
-	_ = x[TSLICE-19]
-	_ = x[TARRAY-20]
-	_ = x[TSTRUCT-21]
-	_ = x[TCHAN-22]
-	_ = x[TMAP-23]
-	_ = x[TINTER-24]
-	_ = x[TFORW-25]
-	_ = x[TANY-26]
-	_ = x[TSTRING-27]
-	_ = x[TUNSAFEPTR-28]
-	_ = x[TIDEAL-29]
-	_ = x[TNIL-30]
-	_ = x[TBLANK-31]
-	_ = x[TFUNCARGS-32]
-	_ = x[TCHANARGS-33]
-	_ = x[TSSA-34]
-	_ = x[TTUPLE-35]
-	_ = x[TRESULTS-36]
-	_ = x[NTYPE-37]
-}
-
-const _EType_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
-
-var _EType_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 158, 161, 166, 174, 182, 185, 190, 197, 202}
-
-func (i EType) String() string {
-	if i >= EType(len(_EType_index)-1) {
-		return "EType(" + strconv.FormatInt(int64(i), 10) + ")"
-	}
-	return _EType_name[_EType_index[i]:_EType_index[i+1]]
-}
diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go
new file mode 100644
index 0000000..da224d4
--- /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 "<S>"
+	}
+
+	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("<S>")
+		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("<T>")
+		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("<T>")
+		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 0000000..1a324aa
--- /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 a77f514..9bc636d 100644
--- a/src/cmd/compile/internal/types/identity.go
+++ b/src/cmd/compile/internal/types/identity.go
@@ -25,17 +25,17 @@
 	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 @@
 	}
 	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/kind_string.go b/src/cmd/compile/internal/types/kind_string.go
new file mode 100644
index 0000000..1e1e846
--- /dev/null
+++ b/src/cmd/compile/internal/types/kind_string.go
@@ -0,0 +1,60 @@
+// Code generated by "stringer -type Kind -trimprefix T type.go"; DO NOT EDIT.
+
+package types
+
+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[Txxx-0]
+	_ = x[TINT8-1]
+	_ = x[TUINT8-2]
+	_ = x[TINT16-3]
+	_ = x[TUINT16-4]
+	_ = x[TINT32-5]
+	_ = x[TUINT32-6]
+	_ = x[TINT64-7]
+	_ = x[TUINT64-8]
+	_ = x[TINT-9]
+	_ = x[TUINT-10]
+	_ = x[TUINTPTR-11]
+	_ = x[TCOMPLEX64-12]
+	_ = x[TCOMPLEX128-13]
+	_ = x[TFLOAT32-14]
+	_ = x[TFLOAT64-15]
+	_ = x[TBOOL-16]
+	_ = x[TPTR-17]
+	_ = x[TFUNC-18]
+	_ = x[TSLICE-19]
+	_ = x[TARRAY-20]
+	_ = x[TSTRUCT-21]
+	_ = x[TCHAN-22]
+	_ = x[TMAP-23]
+	_ = x[TINTER-24]
+	_ = x[TFORW-25]
+	_ = x[TANY-26]
+	_ = x[TSTRING-27]
+	_ = x[TUNSAFEPTR-28]
+	_ = x[TIDEAL-29]
+	_ = x[TNIL-30]
+	_ = x[TBLANK-31]
+	_ = x[TFUNCARGS-32]
+	_ = x[TCHANARGS-33]
+	_ = x[TSSA-34]
+	_ = x[TTUPLE-35]
+	_ = x[TRESULTS-36]
+	_ = x[NTYPE-37]
+}
+
+const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
+
+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 Kind) String() string {
+	if i >= Kind(len(_Kind_index)-1) {
+		return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	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 bcc6789..a6d2e20 100644
--- a/src/cmd/compile/internal/types/pkg.go
+++ b/src/cmd/compile/internal/types/pkg.go
@@ -31,8 +31,7 @@
 	// 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 @@
 	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 @@
 		Name: name,
 		Pkg:  pkg,
 	}
-	if name == ".inittask" {
-		InitSyms = append(InitSyms, s)
-	}
 	pkg.Syms[name] = s
 	return s, false
 }
@@ -144,3 +137,7 @@
 	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 40d3d86..d7c454f 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 @@
 		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 @@
 	Block = blockgen
 }
 
-func IsDclstackValid() bool {
+func isDclstackValid() bool {
 	for _, d := range dclstack {
 		if d.sym == nil {
 			return false
@@ -79,19 +82,20 @@
 }
 
 // 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 @@
 	// function scope.
 	return &s.Def
 }
+
+func CheckDclstack() {
+	if !isDclstackValid() {
+		base.Fatalf("mark left on the dclstack")
+	}
+}
diff --git a/src/cmd/compile/internal/types/size.go b/src/cmd/compile/internal/types/size.go
new file mode 100644
index 0000000..98540ee
--- /dev/null
+++ b/src/cmd/compile/internal/types/size.go
@@ -0,0 +1,633 @@
+// 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"
+	"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
+)
+
+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).
+var defercalc int
+
+func Rnd(o int64, r int64) int64 {
+	if r < 1 || r > 8 || r&(r-1) != 0 {
+		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 *Type) {
+	seen := make(map[*Sym]*Field)
+	var methods []*Field
+
+	addMethod := func(m *Field, explicit bool) {
+		switch prev := seen[m.Sym]; {
+		case prev == nil:
+			seen[m.Sym] = m
+		case AllowsGoVersion(t.Pkg(), 1, 14) && !explicit && Identical(m.Type, prev.Type):
+			return
+		default:
+			base.ErrorfAt(m.Pos, "duplicate method %s", m.Sym.Name)
+		}
+		methods = append(methods, m)
+	}
+
+	for _, m := range t.Methods().Slice() {
+		if m.Sym == nil {
+			continue
+		}
+
+		CheckSize(m.Type)
+		addMethod(m, true)
+	}
+
+	for _, m := range t.Methods().Slice() {
+		if m.Sym != nil {
+			continue
+		}
+
+		if !m.Type.IsInterface() {
+			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
+			// include the broken embedded type when
+			// printing t.
+			// TODO(mdempsky): Revisit this.
+			methods = append(methods, m)
+			continue
+		}
+
+		// Embedded interface: duplicate all methods
+		// (including broken ones, if any) and add to t's
+		// method set.
+		for _, t1 := range m.Type.Fields().Slice() {
+			// Use m.Pos rather than t1.Pos to preserve embedding position.
+			f := NewField(m.Pos, t1.Sym, t1.Type)
+			addMethod(f, false)
+		}
+	}
+
+	sort.Sort(MethodsByName(methods))
+
+	if int64(len(methods)) >= MaxWidth/int64(PtrSize) {
+		base.ErrorfAt(typePos(t), "interface too large")
+	}
+	for i, m := range methods {
+		m.Offset = int64(i) * int64(PtrSize)
+	}
+
+	// Access fields directly to avoid recursively calling CalcSize
+	// within Type.Fields().
+	t.Extra.(*Interface).Fields.Set(methods)
+}
+
+func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
+	starto := o
+	maxalign := int32(flag)
+	if maxalign < 1 {
+		maxalign = 1
+	}
+	lastzero := int64(0)
+	for _, f := range t.Fields().Slice() {
+		if f.Type == nil {
+			// broken field, just skip it so that other valid fields
+			// get a width.
+			continue
+		}
+
+		CalcSize(f.Type)
+		if int32(f.Type.Align) > maxalign {
+			maxalign = int32(f.Type.Align)
+		}
+		if f.Type.Align > 0 {
+			o = Rnd(o, int64(f.Type.Align))
+		}
+		f.Offset = o
+		if f.Nname != nil {
+			// addrescapes has similar code to update these offsets.
+			// 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.
+			f.Nname.(VarObject).RecordFrameOffset(o)
+		}
+
+		w := f.Type.Width
+		if w < 0 {
+			base.Fatalf("invalid width %d", f.Type.Width)
+		}
+		if w == 0 {
+			lastzero = o
+		}
+		o += w
+		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 {
+			base.ErrorfAt(typePos(errtype), "type %L too large", errtype)
+			o = 8 // small but nonzero
+		}
+	}
+
+	// For nonzero-sized structs which end in a zero-sized thing, we add
+	// an extra byte of padding to the type. This padding ensures that
+	// taking the address of the zero-sized thing can't manufacture a
+	// pointer to the next object in the heap. See issue 9401.
+	if flag == 1 && o > starto && o == lastzero {
+		o++
+	}
+
+	// final width is rounded
+	if flag != 0 {
+		o = Rnd(o, int64(maxalign))
+	}
+	t.Align = uint8(maxalign)
+
+	// type width only includes back to first field's offset
+	t.Width = o - starto
+
+	return o
+}
+
+// findTypeLoop searches for an invalid type declaration loop involving
+// type t and reports whether one is found. If so, path contains the
+// loop.
+//
+// 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 *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 {
+		// 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:]
+				return true
+			}
+		}
+
+		*path = append(*path, t)
+		if findTypeLoop(t.Obj().(TypeObject).TypeDefn(), path) {
+			return true
+		}
+		*path = (*path)[:len(*path)-1]
+	} else {
+		// Anonymous type. Recurse on contained types.
+
+		switch t.Kind() {
+		case TARRAY:
+			if findTypeLoop(t.Elem(), path) {
+				return true
+			}
+		case TSTRUCT:
+			for _, f := range t.Fields().Slice() {
+				if findTypeLoop(f.Type, path) {
+					return true
+				}
+			}
+		case TINTER:
+			for _, m := range t.Methods().Slice() {
+				if m.Type.IsInterface() { // embedded interface
+					if findTypeLoop(m.Type, path) {
+						return true
+					}
+				}
+			}
+		}
+	}
+
+	return false
+}
+
+func reportTypeLoop(t *Type) {
+	if t.Broke() {
+		return
+	}
+
+	var l []*Type
+	if !findTypeLoop(t, &l) {
+		base.Fatalf("failed to find type loop for: %v", t)
+	}
+
+	// Rotate loop so that the earliest type declaration is first.
+	i := 0
+	for j, t := range l[1:] {
+		if typePos(t).Before(typePos(l[i])) {
+			i = j + 1
+		}
+	}
+	l = append(l[i:], l[:i]...)
+
+	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", base.FmtPos(typePos(t)), t)
+		t.SetBroke(true)
+	}
+	fmt.Fprintf(&msg, "\t%v: %v", base.FmtPos(typePos(l[0])), l[0])
+	base.ErrorfAt(typePos(l[0]), msg.String())
+}
+
+// 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 CalcSize(t *Type) {
+	// Calling CalcSize when typecheck tracing enabled is not safe.
+	// See issue #33658.
+	if base.EnableTrace && SkipSizeForTracing {
+		return
+	}
+	if PtrSize == 0 {
+		// Assume this is a test.
+		return
+	}
+
+	if t == nil {
+		return
+	}
+
+	if t.Width == -2 {
+		reportTypeLoop(t)
+		t.Width = 0
+		t.Align = 1
+		return
+	}
+
+	if t.WidthCalculated() {
+		return
+	}
+
+	if CalcSizeDisabled {
+		if t.Broke() {
+			// break infinite recursion from Fatal call below
+			return
+		}
+		t.SetBroke(true)
+		base.Fatalf("width not calculated: %v", t)
+	}
+
+	// break infinite recursion if the broken recursive type
+	// is referenced again
+	if t.Broke() && t.Width == 0 {
+		return
+	}
+
+	// defer CheckSize calls until after we're done
+	DeferCheckSize()
+
+	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.Kind()
+	switch et {
+	case TFUNC, TCHAN, TMAP, TSTRING:
+		break
+
+	// SimType == 0 during bootstrap
+	default:
+		if SimType[t.Kind()] != 0 {
+			et = SimType[t.Kind()]
+		}
+	}
+
+	var w int64
+	switch et {
+	default:
+		base.Fatalf("CalcSize: unknown type: %v", t)
+
+	// compiler-specific stuff
+	case TINT8, TUINT8, TBOOL:
+		// bool is int8
+		w = 1
+
+	case TINT16, TUINT16:
+		w = 2
+
+	case TINT32, TUINT32, TFLOAT32:
+		w = 4
+
+	case TINT64, TUINT64, TFLOAT64:
+		w = 8
+		t.Align = uint8(RegSize)
+
+	case TCOMPLEX64:
+		w = 8
+		t.Align = 4
+
+	case TCOMPLEX128:
+		w = 16
+		t.Align = uint8(RegSize)
+
+	case TPTR:
+		w = int64(PtrSize)
+		CheckSize(t.Elem())
+
+	case TUNSAFEPTR:
+		w = int64(PtrSize)
+
+	case TINTER: // implemented as 2 pointers
+		w = 2 * int64(PtrSize)
+		t.Align = uint8(PtrSize)
+		expandiface(t)
+
+	case TCHAN: // implemented as pointer
+		w = int64(PtrSize)
+
+		CheckSize(t.Elem())
+
+		// make fake type to check later to
+		// trigger channel argument check.
+		t1 := NewChanArgs(t)
+		CheckSize(t1)
+
+	case TCHANARGS:
+		t1 := t.ChanArgs()
+		CalcSize(t1) // just in case
+		if t1.Elem().Width >= 1<<16 {
+			base.ErrorfAt(typePos(t1), "channel element type too large (>64kB)")
+		}
+		w = 1 // anything will do
+
+	case TMAP: // implemented as pointer
+		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:
+		// not a real type; should be replaced before use.
+		base.Fatalf("CalcSize any")
+
+	case TSTRING:
+		if StringSize == 0 {
+			base.Fatalf("early CalcSize string")
+		}
+		w = StringSize
+		t.Align = uint8(PtrSize)
+
+	case TARRAY:
+		if t.Elem() == nil {
+			break
+		}
+
+		CalcSize(t.Elem())
+		if t.Elem().Width != 0 {
+			cap := (uint64(MaxWidth) - 1) / uint64(t.Elem().Width)
+			if uint64(t.NumElem()) > cap {
+				base.ErrorfAt(typePos(t), "type %L larger than address space", t)
+			}
+		}
+		w = t.NumElem() * t.Elem().Width
+		t.Align = t.Elem().Align
+
+	case TSLICE:
+		if t.Elem() == nil {
+			break
+		}
+		w = SliceSize
+		CheckSize(t.Elem())
+		t.Align = uint8(PtrSize)
+
+	case TSTRUCT:
+		if t.IsFuncArgStruct() {
+			base.Fatalf("CalcSize fn struct %v", t)
+		}
+		w = calcStructOffset(t, t, 0, 1)
+
+	// make fake type to check later to
+	// trigger function argument computation.
+	case TFUNC:
+		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 = 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 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 {
+			base.Fatalf("invalid alignment for %v", t)
+		}
+		t.Align = uint8(w)
+	}
+
+	base.Pos = lno
+
+	ResumeCheckSize()
+}
+
+// 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 CheckSize
+// to compute it.  during a declaration like
+//
+//	type T *struct { next T }
+//
+// it is necessary to defer the calculation of the struct width
+// 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
+// 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 []*Type
+
+func CheckSize(t *Type) {
+	if t == nil {
+		return
+	}
+
+	// function arg structs should not be checked
+	// outside of the enclosing function.
+	if t.IsFuncArgStruct() {
+		base.Fatalf("CheckSize %v", t)
+	}
+
+	if defercalc == 0 {
+		CalcSize(t)
+		return
+	}
+
+	// if type has not yet been pushed on deferredTypeStack yet, do it now
+	if !t.Deferwidth() {
+		t.SetDeferwidth(true)
+		deferredTypeStack = append(deferredTypeStack, t)
+	}
+}
+
+func DeferCheckSize() {
+	defercalc++
+}
+
+func ResumeCheckSize() {
+	if defercalc == 1 {
+		for len(deferredTypeStack) > 0 {
+			t := deferredTypeStack[len(deferredTypeStack)-1]
+			deferredTypeStack = deferredTypeStack[:len(deferredTypeStack)-1]
+			t.SetDeferwidth(false)
+			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 ea947d8..675739f 100644
--- a/src/cmd/compile/internal/types/sizeof_test.go
+++ b/src/cmd/compile/internal/types/sizeof_test.go
@@ -20,11 +20,11 @@
 		_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 0000000..dc59b06
--- /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 07bce4d..0e66ed3 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 @@
 // 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 @@
 	return sym != nil && sym.Name == "_"
 }
 
-func (sym *Sym) LinksymName() string {
-	if sym.IsBlank() {
-		return "_"
+// 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
 	}
-	if sym.Linkname != "" {
-		return sym.Linkname
-	}
-	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.Linkname != "" {
+		return base.Linkname(sym.Linkname, abi)
 	}
-	if sym.Func() {
-		// This is a function symbol. Mark it as "internal ABI".
-		return Ctxt.LookupABIInit(sym.LinksymName(), obj.ABIInternal, initPkg)
-	}
-	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 023ab9a..0dfbef8 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
+}
 
-//go:generate stringer -type EType -trimprefix T
+// A TypeObject is an Object representing a named type.
+type TypeObject interface {
+	Object
+	TypeDefn() *Type // for "type T Defn", returns Defn
+}
 
-// EType describes a kind of type.
-type EType uint8
+// 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
+
+// Kind describes a kind of type.
+type Kind uint8
 
 const (
-	Txxx EType = iota
+	Txxx Kind = iota
 
 	TINT8
 	TUINT8
@@ -90,7 +107,7 @@
 // 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 (
 	// 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 @@
 	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) 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 @@
 // 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 @@
 	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 @@
 	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 @@
 
 	// 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 @@
 
 // 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 @@
 }
 
 // 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 @@
 // 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 @@
 
 // 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 @@
 // 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 @@
 	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 @@
 		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 @@
 	}
 	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 @@
 		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 @@
 	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 @@
 // 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 @@
 	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 @@
 	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) 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 @@
 	// 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) 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 @@
 		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 @@
 	}
 	// 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 @@
 }
 
 // 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 @@
 // 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 @@
 // 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 @@
 	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 @@
 	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 @@
 			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 @@
 // (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 @@
 // 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) 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) 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 @@
 	if t == UntypedString || t == UntypedBool {
 		return true
 	}
-	switch t.Etype {
+	switch t.kind {
 	case TNIL, TIDEAL:
 		return true
 	}
@@ -1449,7 +1492,7 @@
 // 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 @@
 	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 @@
 	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 e8b1073..f9f629c 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 0000000..230b544
--- /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 0000000..97f9de9
--- /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(<type>, 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 0000000..1d1cbc2
--- /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 0000000..7c385c0
--- /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 0000000..73442dc
--- /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 0000000..fa8e2c0
--- /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 0000000..7b65db5
--- /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<<uint(bits) - 1
+				}
+			}
+		}
+		if 0 <= v && v < max {
+			return true
+		}
+
+	case ir.OMOD:
+		n := n.(*ir.BinaryExpr)
+		if !sign && ir.IsSmallIntConst(n.Y) {
+			v := ir.Int64Val(n.Y)
+			if 0 <= v && v <= max {
+				return true
+			}
+		}
+
+	case ir.ODIV:
+		n := n.(*ir.BinaryExpr)
+		if !sign && ir.IsSmallIntConst(n.Y) {
+			v := ir.Int64Val(n.Y)
+			for bits > 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<<uint(bits) <= max {
+		return true
+	}
+
+	return false
+}
+
+// usemethod checks interface method calls for uses of reflect.Type.Method.
+func usemethod(n *ir.CallExpr) {
+	t := n.X.Type()
+
+	// Looking for either of:
+	//	Method(int) reflect.Method
+	//	MethodByName(string) (reflect.Method, bool)
+	//
+	// TODO(crawshaw): improve precision of match by working out
+	//                 how to check the method name.
+	if n := t.NumParams(); n != 1 {
+		return
+	}
+	if n := t.NumResults(); n != 1 && n != 2 {
+		return
+	}
+	p0 := t.Params().Field(0)
+	res0 := t.Results().Field(0)
+	var res1 *types.Field
+	if t.NumResults() == 2 {
+		res1 = t.Results().Field(1)
+	}
+
+	if res1 == nil {
+		if p0.Type.Kind() != types.TINT {
+			return
+		}
+	} else {
+		if !p0.Type.IsString() {
+			return
+		}
+		if !res1.Type.IsBoolean() {
+			return
+		}
+	}
+
+	// Don't mark reflect.(*rtype).Method, etc. themselves in the reflect package.
+	// Those functions may be alive via the itab, which should not cause all methods
+	// alive. We only want to mark their callers.
+	if base.Ctxt.Pkgpath == "reflect" {
+		switch ir.CurFunc.Nname.Sym().Name { // TODO: is there a better way than hardcoding the names?
+		case "(*rtype).Method", "(*rtype).MethodByName", "(*interfaceType).Method", "(*interfaceType).MethodByName":
+			return
+		}
+	}
+
+	// Note: Don't rely on res0.Type.String() since its formatting depends on multiple factors
+	//       (including global variables such as numImports - was issue #19028).
+	// Also need to check for reflect package itself (see Issue #38515).
+	if s := res0.Type.Sym(); s != nil && s.Name == "Method" && types.IsReflectPkg(s.Pkg) {
+		ir.CurFunc.SetReflectMethod(true)
+		// The LSym is initialized at this point. We need to set the attribute on the LSym.
+		ir.CurFunc.LSym.Set(obj.AttrReflectMethod, true)
+	}
+}
+
+func usefield(n *ir.SelectorExpr) {
+	if objabi.Fieldtrack_enabled == 0 {
+		return
+	}
+
+	switch n.Op() {
+	default:
+		base.Fatalf("usefield %v", n.Op())
+
+	case ir.ODOT, ir.ODOTPTR:
+		break
+	}
+
+	field := n.Selection
+	if field == nil {
+		base.Fatalf("usefield %v %v without paramfld", n.X.Type(), n.Sel)
+	}
+	if field.Sym != n.Sel {
+		base.Fatalf("field inconsistency: %v != %v", field.Sym, n.Sel)
+	}
+	if !strings.Contains(field.Note, "go:\"track\"") {
+		return
+	}
+
+	outer := n.X.Type()
+	if outer.IsPtr() {
+		outer = outer.Elem()
+	}
+	if outer.Sym() == nil {
+		base.Errorf("tracked field must be in named struct type")
+	}
+	if !types.IsExported(field.Sym.Name) {
+		base.Errorf("tracked field must be exported (upper case)")
+	}
+
+	sym := reflectdata.TrackSym(outer, field)
+	if ir.CurFunc.FieldTrack == nil {
+		ir.CurFunc.FieldTrack = make(map[*obj.LSym]struct{})
+	}
+	ir.CurFunc.FieldTrack[sym] = struct{}{}
+}
diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go
new file mode 100644
index 0000000..fe0b6a0
--- /dev/null
+++ b/src/cmd/compile/internal/walk/order.go
@@ -0,0 +1,1437 @@
+// 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 (
+	"fmt"
+
+	"cmd/compile/internal/base"
+	"cmd/compile/internal/escape"
+	"cmd/compile/internal/ir"
+	"cmd/compile/internal/reflectdata"
+	"cmd/compile/internal/staticinit"
+	"cmd/compile/internal/typecheck"
+	"cmd/compile/internal/types"
+	"cmd/internal/src"
+)
+
+// 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.
+
+// orderState holds state during the ordering process.
+type orderState struct {
+	out  []ir.Node             // list of generated statements
+	temp []*ir.Name            // stack of temporary variables
+	free map[string][]*ir.Name // free list of unused temporaries, by type.LongString().
+	edit func(ir.Node) ir.Node // cached closure of o.exprNoLHS
+}
+
+// Order rewrites fn.Nbody to apply the ordering constraints
+// described in the comment at the top of the file.
+func order(fn *ir.Func) {
+	if base.Flag.W > 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 0000000..47cd2fd
--- /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 0000000..5ab24b2
--- /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 0000000..873be289
--- /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 0000000..46a621c
--- /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 0000000..162de01
--- /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 0000000..9879a6c
--- /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 0000000..b47d96d
--- /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 9c9f6ed..e4ef9d7 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 @@
 	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 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 @@
 		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 @@
 		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 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 @@
 		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 @@
 		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 @@
 				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 @@
 				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 @@
 
 	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 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 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 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 e137daa..fc806f9 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 @@
 		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 a33ddc8..3ca4797 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 fbf76d0..00dfa07 100644
--- a/src/cmd/compile/internal/x86/ssa.go
+++ b/src/cmd/compile/internal/x86/ssa.go
@@ -8,16 +8,18 @@
 	"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 @@
 //     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 @@
 	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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		}
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 		}
 		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 @@
 		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 @@
 			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 @@
 		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 @@
 	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 @@
 			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 @@
 		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 @@
 		// 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 @@
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 		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 3aa64a5..cb2f4e8 100644
--- a/src/cmd/compile/main.go
+++ b/src/cmd/compile/main.go
@@ -8,12 +8,14 @@
 	"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 @@
 	"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 @@
 	}
 
 	gc.Main(archInit)
-	gc.Exit(0)
+	base.Exit(0)
 }
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index c8c3212..332f2fa 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -1765,6 +1765,8 @@
 		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 cf85f2a..7520b0e 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -23,80 +23,41 @@
 // 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 @@
 	"math/big",
 	"math/bits",
 	"sort",
+	"strconv",
 }
 
 // File prefixes that are ignored by go/build anyway, and cause
@@ -111,6 +73,7 @@
 var ignorePrefixes = []string{
 	".",
 	"_",
+	"#",
 }
 
 // File suffixes that use build tags introduced since Go 1.4.
@@ -124,6 +87,7 @@
 	"_wasm.s",
 	"_wasm.go",
 	"_test.s",
+	"_test.go",
 }
 
 func bootstrapBuildTools() {
@@ -149,31 +113,47 @@
 	// 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 e7c63f0..db3f281 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 3ce3238..d14b232 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -811,6 +811,7 @@
 	// 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 c48904e..3222479 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -28,18 +28,18 @@
 	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 780d639..0e7af6d 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -71,7 +71,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/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index cc4e2b2..2087855 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -129,7 +129,11 @@
 		}
 	}
 
-	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 @@
 	//   - 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 c1661d7..e9b25fe 100644
--- a/src/cmd/internal/archive/archive.go
+++ b/src/cmd/internal/archive/archive.go
@@ -118,9 +118,9 @@
 
 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 @@
 		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 @@
 
 // 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 @@
 	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 07c3406..22608e7 100644
--- a/src/cmd/internal/goobj/mkbuiltin.go
+++ b/src/cmd/internal/goobj/mkbuiltin.go
@@ -118,8 +118,8 @@
 
 // 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 8c8ff58..8206902 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -39,6 +39,7 @@
 	"cmd/internal/sys"
 	"fmt"
 	"sync"
+	"sync/atomic"
 )
 
 // An Addr is an argument to an instruction.
@@ -250,6 +251,12 @@
 	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 @@
 	// 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 @@
 	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 @@
 	{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 @@
 }
 
 // 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 @@
 	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 @@
 	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 @@
 	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 2b09699..679ce7e 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -80,6 +80,11 @@
 		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 @@
 	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 764fc5b..f498fd6 100644
--- a/src/cmd/internal/obj/s390x/condition_code.go
+++ b/src/cmd/internal/obj/s390x/condition_code.go
@@ -124,3 +124,5 @@
 	// 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 388bd40f4..5407c8d 100644
--- a/src/cmd/internal/obj/s390x/rotate.go
+++ b/src/cmd/internal/obj/s390x/rotate.go
@@ -113,3 +113,5 @@
 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 d2cec73..2f55793 100644
--- a/src/cmd/internal/obj/textflag.go
+++ b/src/cmd/internal/obj/textflag.go
@@ -33,7 +33,7 @@
 	// 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 @@
 	// 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 30c1a6a..3be4b59 100644
--- a/src/cmd/internal/obj/x86/a.out.go
+++ b/src/cmd/internal/obj/x86/a.out.go
@@ -263,6 +263,7 @@
 	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 184fb43..84de58a 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -637,13 +637,19 @@
 		}
 	}
 
-	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 @@
 		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 @@
 		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 @@
 
 // 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 @@
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 		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 @@
 	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 fd1c998..1a0784c 100644
--- a/src/cmd/internal/objabi/path.go
+++ b/src/cmd/internal/objabi/path.go
@@ -56,6 +56,8 @@
 		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 52035e9..92d38bb 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -55,6 +55,7 @@
 	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 17d5040..3148968 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -489,10 +489,16 @@
 	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 @@
 		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 @@
 		// 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 @@
 			}
 		}
 
+		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 @@
 		// 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 5a096f1..cbd8118 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -92,11 +92,10 @@
 	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 c98e4de..85a8ff4 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -102,6 +102,41 @@
 		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 @@
 		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 971cc43..98c2131 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -322,6 +322,7 @@
 const (
 	// Loader.flags
 	FlagStrictDups = 1 << iota
+	FlagUseABIAlias
 )
 
 func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorReporter) *Loader {
@@ -2270,6 +2271,9 @@
 // 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 9b776d4..0572c6c 100644
--- a/src/crypto/x509/internal/macos/corefoundation.go
+++ b/src/crypto/x509/internal/macos/corefoundation.go
@@ -39,7 +39,6 @@
 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 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 @@
 
 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 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 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 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 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 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 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 a4495d6..1ce39fa 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<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFArrayGetCount(SB)
-TEXT ·x509_CFArrayGetValueAtIndex_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFArrayGetValueAtIndex_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFArrayGetValueAtIndex(SB)
-TEXT ·x509_CFDataGetBytePtr_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFDataGetBytePtr_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFDataGetBytePtr(SB)
-TEXT ·x509_CFDataGetLength_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFDataGetLength_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFDataGetLength(SB)
-TEXT ·x509_CFStringCreateWithBytes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFStringCreateWithBytes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFStringCreateWithBytes(SB)
-TEXT ·x509_CFRelease_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFRelease_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFRelease(SB)
-TEXT ·x509_CFDictionaryGetValueIfPresent_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFDictionaryGetValueIfPresent_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFDictionaryGetValueIfPresent(SB)
-TEXT ·x509_CFNumberGetValue_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFNumberGetValue_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_CFNumberGetValue(SB)
-TEXT ·x509_CFEqual_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_CFEqual_trampoline<ABIInternal>(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 5e39e93..3163e3a 100644
--- a/src/crypto/x509/internal/macos/security.go
+++ b/src/crypto/x509/internal/macos/security.go
@@ -63,7 +63,6 @@
 
 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 @@
 
 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 @@
 
 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 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 bd446db..bea265a 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<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_SecTrustSettingsCopyCertificates(SB)
-TEXT ·x509_SecItemExport_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_SecItemExport_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_SecItemExport(SB)
-TEXT ·x509_SecTrustSettingsCopyTrustSettings_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_SecTrustSettingsCopyTrustSettings_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_SecTrustSettingsCopyTrustSettings(SB)
-TEXT ·x509_SecPolicyCopyProperties_trampoline(SB),NOSPLIT,$0-0
+TEXT ·x509_SecPolicyCopyProperties_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	x509_SecPolicyCopyProperties(SB)
diff --git a/src/embed/embed.go b/src/embed/embed.go
index 98da870..7f2719d 100644
--- a/src/embed/embed.go
+++ b/src/embed/embed.go
@@ -143,7 +143,7 @@
 // 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 @@
 // 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 c97c668..e5c849e 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -76,8 +76,12 @@
 	  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 @@
 	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 4641442..223c363 100644
--- a/src/go/constant/value.go
+++ b/src/go/constant/value.go
@@ -17,6 +17,7 @@
 	"go/token"
 	"math"
 	"math/big"
+	"math/bits"
 	"strconv"
 	"strings"
 	"sync"
@@ -607,7 +608,11 @@
 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 @@
 	}
 	// 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 2866774..91ad0b0 100644
--- a/src/go/constant/value_test.go
+++ b/src/go/constant/value_test.go
@@ -706,3 +706,24 @@
 		})
 	}
 }
+
+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 c59dd16..a3184e7 100644
--- a/src/go/internal/gcimporter/iimport.go
+++ b/src/go/internal/gcimporter/iimport.go
@@ -15,6 +15,7 @@
 	"go/token"
 	"go/types"
 	"io"
+	"math/big"
 	"sort"
 )
 
@@ -320,7 +321,9 @@
 		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 @@
 	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 @@
 				v = ^v
 			}
 		}
-		return constant.MakeInt64(v)
+		x.SetInt64(v)
+		return
 	}
 
 	v := -n
@@ -395,39 +399,23 @@
 	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
+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()))
 	}
-
-	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)
-	}
-	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 d625959..b5bbb2d 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -101,6 +101,13 @@
 // 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 75cebc9..dde451e 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -42,7 +42,7 @@
 	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 @@
 		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 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 @@
 		{`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 @@
 		},
 
 		// 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 616564b..d6f18c9 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -120,6 +120,7 @@
 		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 fd35f78..078ed44 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -353,8 +353,8 @@
 			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 280792e..3bc8ee0 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -8,6 +8,7 @@
 
 import (
 	"errors"
+	"fmt"
 	"go/ast"
 	"go/constant"
 	"go/token"
@@ -69,6 +70,12 @@
 	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 @@
 	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 @@
 	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 @@
 		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 (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 @@
 	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 ce31dab..ca7d926 100644
--- a/src/go/types/check_test.go
+++ b/src/go/types/check_test.go
@@ -27,12 +27,14 @@
 
 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 @@
 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 @@
 	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 @@
 // 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 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 := "<no package>"
 	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 @@
 
 	// 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 @@
 
 	// 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 @@
 	}
 }
 
+// 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
 	}
-
-	// Otherwise, run all the tests.
-	for _, files := range tests {
-		checkFiles(t, files)
-	}
+	testenv.MustHaveGoBuild(t)
+	DefPredeclaredTestFuncs()
+	testPkg(t, strings.Split(*testFiles, ","), *goVersion)
 }
 
-func TestFixedBugs(t *testing.T) { testDir(t, "fixedbugs") }
+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 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 1cab1cc..c634d27 100644
--- a/src/go/types/conversions.go
+++ b/src/go/types/conversions.go
@@ -55,8 +55,8 @@
 		// - 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 1f0bc35..b861cde 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -189,7 +189,7 @@
 		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 @@
 			// 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) 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 (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 @@
 
 	}
 
+	// 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 @@
 		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 @@
 	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 @@
 			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 c01a12c..ac28c3b 100644
--- a/src/go/types/errorcodes.go
+++ b/src/go/types/errorcodes.go
@@ -386,8 +386,8 @@
 	// _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 @@
 	//
 	// 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 @@
 	//  	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 d940bf0..3a97ac0 100644
--- a/src/go/types/eval_test.go
+++ b/src/go/types/eval_test.go
@@ -76,7 +76,7 @@
 		`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 eb20561..aec3172 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -78,13 +78,77 @@
 	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 @@
 		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)
+		if x.val.Kind() == constant.Unknown {
+			// nothing to do (and don't cause an error below in the overflow check)
+			return
+		}
 		var prec uint
-		if isUnsigned(typ) {
-			prec = uint(check.conf.sizeof(typ) * 8)
+		if isUnsigned(x.typ) {
+			prec = uint(check.conf.sizeof(x.typ) * 8)
 		}
-		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)
-		}
+		x.val = constant.UnaryOp(e.Op, x.val, prec)
+		x.expr = e
+		check.overflow(x, e.Op, x.Pos())
 		return
 	}
 
@@ -579,6 +640,8 @@
 			if !hasNil(target) {
 				return nil
 			}
+			// Preserve the type of nil as UntypedNil: see #13061.
+			return Typ[UntypedNil]
 		default:
 			return nil
 		}
@@ -665,15 +728,16 @@
 	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 @@
 
 	// 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 @@
 
 	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 @@
 			}
 			// 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 @@
 	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 @@
 	}
 
 	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 @@
 		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 @@
 		}
 
 	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 148edbf..954a7ca 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 @@
 					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 b637f8b..e441159 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -23,7 +23,7 @@
 	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 @@
 					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 @@
 						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 @@
 							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 @@
 				}
 			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 @@
 		// 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 @@
 	// 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 @@
 	// 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:]
 	}
-	return path
+	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)
+	}
 }
 
 // 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 5ca4493..29f7113 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -106,6 +106,7 @@
 		// 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 @@
 				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 @@
 		// 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 @@
 		"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 @@
 	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 98830eb..6ee28f1 100644
--- a/src/go/types/testdata/builtins.src
+++ b/src/go/types/testdata/builtins.src
@@ -283,7 +283,7 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	_ = 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 @@
 	_ = 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 adbbf28..5608b15 100644
--- a/src/go/types/testdata/const0.src
+++ b/src/go/types/testdata/const0.src
@@ -348,3 +348,16 @@
 	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<<prec - 1
+
+const _ = maxInt + /* ERROR constant addition overflow */ 1
+const _ = -maxInt - /* ERROR constant subtraction overflow */ 1
+const _ = maxInt ^ /* ERROR constant bitwise XOR overflow */ -1
+const _ = maxInt * /* ERROR constant multiplication overflow */ 2
+const _ = maxInt << /* ERROR constant shift overflow */ 2
+const _ = 1 << /* ERROR constant shift overflow */ prec
+
+const _ = ^ /* ERROR constant bitwise complement overflow */ maxInt
diff --git a/src/go/types/testdata/const1.src b/src/go/types/testdata/const1.src
index d827704..56b6bd4 100644
--- a/src/go/types/testdata/const1.src
+++ b/src/go/types/testdata/const1.src
@@ -43,7 +43,12 @@
 
 const (
 	smallestFloat32 = 1.0 / (1<<(127 - 1 + 23))
-	smallestFloat64 = 1.0 / (1<<(1023 - 1 + 52))
+	// TODO(gri) The compiler limits integers to 512 bit and thus
+	//           we cannot compute the value (1<<(1023 - 1 + 52))
+	//           without overflow. For now we match the compiler.
+	//           See also issue #44057.
+	// smallestFloat64 = 1.0 / (1<<(1023 - 1 + 52))
+	smallestFloat64 = 4.940656458412465441765687928682213723651e-324
 )
 
 const (
@@ -53,7 +58,12 @@
 
 const (
 	maxFloat32 = 1<<127 * (1<<24 - 1) / (1.0<<23)
-	maxFloat64 = 1<<1023 * (1<<53 - 1) / (1.0<<52)
+	// TODO(gri) The compiler limits integers to 512 bit and thus
+	//           we cannot compute the value 1<<1023
+	//           without overflow. For now we match the compiler.
+	//           See also issue #44057.
+	// maxFloat64 = 1<<1023 * (1<<53 - 1) / (1.0<<52)
+	maxFloat64 = 1.797693134862315708145274237317043567981e+308
 )
 
 const (
@@ -271,7 +281,9 @@
 	_ = assert(float64(smallestFloat32) == smallestFloat32)
 	_ = assert(float64(smallestFloat32/2) == smallestFloat32/2)
 	_ = assert(float64(smallestFloat64) == smallestFloat64)
-	_ = assert(float64(smallestFloat64/2) == 0)
+	// TODO(gri) With the change to the declaration of smallestFloat64
+	//           this now fails to be true. See issue #44058.
+	// _ = assert(float64(smallestFloat64/2) == 0)
 )
 
 const (
diff --git a/src/go/types/testdata/decls2a.src b/src/go/types/testdata/decls2/decls2a.src
similarity index 100%
rename from src/go/types/testdata/decls2a.src
rename to src/go/types/testdata/decls2/decls2a.src
diff --git a/src/go/types/testdata/decls2b.src b/src/go/types/testdata/decls2/decls2b.src
similarity index 100%
rename from src/go/types/testdata/decls2b.src
rename to src/go/types/testdata/decls2/decls2b.src
diff --git a/src/go/types/testdata/go1_12.src b/src/go/types/testdata/go1_12.src
new file mode 100644
index 0000000..1e529f1
--- /dev/null
+++ b/src/go/types/testdata/go1_12.src
@@ -0,0 +1,35 @@
+// 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_12 // go1.12
+
+// numeric literals
+const (
+	_ = 1_000 // ERROR "underscores in numeric literals requires go1.13 or later"
+	_ = 0b111 // ERROR "binary literals requires go1.13 or later"
+	_ = 0o567 // ERROR "0o/0O-style octal literals requires go1.13 or later"
+	_ = 0xabc // ok
+	_ = 0x0p1 // ERROR "hexadecimal floating-point literals requires go1.13 or later"
+
+	_ = 0B111 // ERROR "binary"
+	_ = 0O567 // ERROR "octal"
+	_ = 0Xabc // ok
+	_ = 0X0P1 // ERROR "hexadecimal floating-point"
+
+	_ = 1_000i // ERROR "underscores"
+	_ = 0b111i // ERROR "binary"
+	_ = 0o567i // ERROR "octal"
+	_ = 0xabci // ERROR "hexadecimal floating-point"
+	_ = 0x0p1i // ERROR "hexadecimal floating-point"
+)
+
+// signed shift counts
+var (
+	s int
+	_ = 1 << s // ERROR "invalid operation: signed shift count s \(variable of type int\) requires go1.13 or later"
+	_ = 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 0000000..6aa1364
--- /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 0000000..3ead1e9
--- /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 e96fca3..5ceb96e 100644
--- a/src/go/types/testdata/importdecl0a.src
+++ b/src/go/types/testdata/importdecl0/importdecl0a.src
@@ -10,7 +10,7 @@
 	// 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 6844e70..5569042 100644
--- a/src/go/types/testdata/importdecl0b.src
+++ b/src/go/types/testdata/importdecl0/importdecl0b.src
@@ -8,7 +8,7 @@
 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 ee70bbd..43a7bcd 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 0000000..f892938
--- /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 c9a38ae..4d3c59a 100644
--- a/src/go/types/testdata/shifts.src
+++ b/src/go/types/testdata/shifts.src
@@ -20,7 +20,7 @@
 		// 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 @@
 		_ 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 1377729..de8f936 100644
--- a/src/go/types/testdata/stmt0.src
+++ b/src/go/types/testdata/stmt0.src
@@ -90,7 +90,7 @@
 
 	// 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 54f5ef1..6e2d1b5 100644
--- a/src/go/types/testdata/vardecl.src
+++ b/src/go/types/testdata/vardecl.src
@@ -158,6 +158,18 @@
 	}
 }
 
+
+// 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 @@
 	_, _, _ = 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 087cda4..66e194e 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 @@
 	}
 
 	// 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 @@
 	}
 
 	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 2b39801..b924949 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -51,12 +51,12 @@
 	}
 	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 @@
 	}
 
 	// 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 @@
 			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 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 @@
 	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 0000000..1546941
--- /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 0000000..6700fac
--- /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 0000000..70e2ed1
--- /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 0000000..5ef9883
--- /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 0000000..88af212
--- /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)<<i) != 0 {
+			kind = abiStepPointer
+		}
+		a.steps = append(a.steps, abiStep{
+			kind:   kind,
+			offset: offset + uintptr(i)*size,
+			size:   size,
+			ireg:   a.iregs,
+		})
+		a.iregs++
+	}
+	return true
+}
+
+// assignFloatN 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 floating-point registers.
+//
+// Returns whether assignment succeeded.
+func (a *abiSeq) assignFloatN(offset, size uintptr, n int) bool {
+	if n < 0 {
+		panic("invalid n")
+	}
+	if a.fregs+n > 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 de426b5..ddcfca9 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -23,15 +23,17 @@
 
 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 67dc485..e17d4ea 100644
--- a/src/reflect/makefunc.go
+++ b/src/reflect/makefunc.go
@@ -60,9 +60,9 @@
 	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 @@
 	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 a1cdf45..d528166 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1865,7 +1865,7 @@
 
 	// 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 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 @@
 	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 @@
 	}}
 	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 1f185b5..eae1b9b 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 @@
 
 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 @@
 	}
 	nout := t.NumOut()
 
-	// Compute frame type.
-	frametype, _, retOffset, _, framePool := funcLayout(t, rcvrtype)
+	// Register argument space.
+	var regArgs abi.RegArgs
 
-	// 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)
+	// Compute frame type.
+	frametype, framePool, abi := funcLayout(t, rcvrtype)
+
+	// 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)
+		}
 	}
-	off := uintptr(0)
+	frameSize := frametype.size
+
+	if debugReflectCall {
+		println("reflect.call", t.String())
+		abi.dump()
+	}
 
 	// 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(&regArgs.Ints[st.ireg]))
+		case abiStepFloatReg:
+			storeRcvr(rcvr, unsafe.Pointer(&regArgs.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
+		// 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(&regArgs.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(&regArgs.Floats[st.freg]), offset, st.size)
+			default:
+				panic("unknown ABI part kind")
+			}
 		}
-		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
-		}
-		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), &regArgs)
 
 	// For testing; see TestCallMethodJump.
 	if callGC {
@@ -482,34 +547,82 @@
 
 	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
 			}
-			off += tv.Size()
+
+			// 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(&regArgs.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(&regArgs.Floats[st.freg]), st.size)
+				case abiStepStack:
+					panic("register-based return value has stack component")
+				default:
+					panic("unknown ABI part kind")
+				}
+			}
+			ret[i] = Value{tv.common(), s, flagIndir | flag(tv.Kind())}
 		}
 	}
 
@@ -709,7 +822,8 @@
 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 @@
 		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), &regs)
 
 	// Copy return values.
 	// Ignore any changes to args and just copy return values.
@@ -2802,14 +2925,32 @@
 //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 27d8df9..72c7449 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<ABIInternal>(SB),NOSPLIT,$0-0
+	JMP	·sigpanic<ABIInternal>(SB)
+#endif
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s
index fa3b1be..471451d 100644
--- a/src/runtime/asm_386.s
+++ b/src/runtime/asm_386.s
@@ -273,25 +273,6 @@
  *  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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 4ac8708..05422c9 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -84,9 +84,7 @@
 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<ABIInternal>(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 @@
  *  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 @@
 	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 @@
 	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 @@
 
 	// 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 @@
 	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 @@
 	JMP	AX
 // Note: can't just "JMP NAME(SB)" - bad inlining results.
 
-TEXT ·reflectcall<ABIInternal>(SB), NOSPLIT, $0-32
-	MOVLQZX argsize+24(FP), CX
+TEXT ·reflectcall<ABIInternal>(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 @@
 	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 @@
 // 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 @@
 	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 @@
 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 @@
 	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<ABIInternal>(SB),NOSPLIT,$0-0
+#ifdef GOEXPERIMENT_REGABI
+	get_tls(R14)
+	MOVQ	g(R14), R14
+	XORPS	X15, X15
+#endif
+	JMP	·sigpanic<ABIInternal>(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 @@
 // 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<ABIInternal>(SB),NOSPLIT,$120
+TEXT runtime·gcWriteBarrier<ABIInternal>(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 @@
 	// 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 @@
 	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 @@
 	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·panicIndex<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	AX, x+0(FP)
 	MOVQ	CX, y+8(FP)
-	JMP	runtime·goPanicIndex(SB)
+	JMP	runtime·goPanicIndex<ABIInternal>(SB)
 TEXT runtime·panicIndexU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	AX, x+0(FP)
 	MOVQ	CX, y+8(FP)
-	JMP	runtime·goPanicIndexU(SB)
+	JMP	runtime·goPanicIndexU<ABIInternal>(SB)
 TEXT runtime·panicSliceAlen<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	CX, x+0(FP)
 	MOVQ	DX, y+8(FP)
-	JMP	runtime·goPanicSliceAlen(SB)
+	JMP	runtime·goPanicSliceAlen<ABIInternal>(SB)
 TEXT runtime·panicSliceAlenU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	CX, x+0(FP)
 	MOVQ	DX, y+8(FP)
-	JMP	runtime·goPanicSliceAlenU(SB)
+	JMP	runtime·goPanicSliceAlenU<ABIInternal>(SB)
 TEXT runtime·panicSliceAcap<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	CX, x+0(FP)
 	MOVQ	DX, y+8(FP)
-	JMP	runtime·goPanicSliceAcap(SB)
+	JMP	runtime·goPanicSliceAcap<ABIInternal>(SB)
 TEXT runtime·panicSliceAcapU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	CX, x+0(FP)
 	MOVQ	DX, y+8(FP)
-	JMP	runtime·goPanicSliceAcapU(SB)
+	JMP	runtime·goPanicSliceAcapU<ABIInternal>(SB)
 TEXT runtime·panicSliceB<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	AX, x+0(FP)
 	MOVQ	CX, y+8(FP)
-	JMP	runtime·goPanicSliceB(SB)
+	JMP	runtime·goPanicSliceB<ABIInternal>(SB)
 TEXT runtime·panicSliceBU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	AX, x+0(FP)
 	MOVQ	CX, y+8(FP)
-	JMP	runtime·goPanicSliceBU(SB)
+	JMP	runtime·goPanicSliceBU<ABIInternal>(SB)
 TEXT runtime·panicSlice3Alen<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	DX, x+0(FP)
 	MOVQ	BX, y+8(FP)
-	JMP	runtime·goPanicSlice3Alen(SB)
+	JMP	runtime·goPanicSlice3Alen<ABIInternal>(SB)
 TEXT runtime·panicSlice3AlenU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	DX, x+0(FP)
 	MOVQ	BX, y+8(FP)
-	JMP	runtime·goPanicSlice3AlenU(SB)
+	JMP	runtime·goPanicSlice3AlenU<ABIInternal>(SB)
 TEXT runtime·panicSlice3Acap<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	DX, x+0(FP)
 	MOVQ	BX, y+8(FP)
-	JMP	runtime·goPanicSlice3Acap(SB)
+	JMP	runtime·goPanicSlice3Acap<ABIInternal>(SB)
 TEXT runtime·panicSlice3AcapU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	DX, x+0(FP)
 	MOVQ	BX, y+8(FP)
-	JMP	runtime·goPanicSlice3AcapU(SB)
+	JMP	runtime·goPanicSlice3AcapU<ABIInternal>(SB)
 TEXT runtime·panicSlice3B<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	CX, x+0(FP)
 	MOVQ	DX, y+8(FP)
-	JMP	runtime·goPanicSlice3B(SB)
+	JMP	runtime·goPanicSlice3B<ABIInternal>(SB)
 TEXT runtime·panicSlice3BU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	CX, x+0(FP)
 	MOVQ	DX, y+8(FP)
-	JMP	runtime·goPanicSlice3BU(SB)
+	JMP	runtime·goPanicSlice3BU<ABIInternal>(SB)
 TEXT runtime·panicSlice3C<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	AX, x+0(FP)
 	MOVQ	CX, y+8(FP)
-	JMP	runtime·goPanicSlice3C(SB)
+	JMP	runtime·goPanicSlice3C<ABIInternal>(SB)
 TEXT runtime·panicSlice3CU<ABIInternal>(SB),NOSPLIT,$0-16
 	MOVQ	AX, x+0(FP)
 	MOVQ	CX, y+8(FP)
-	JMP	runtime·goPanicSlice3CU(SB)
+	JMP	runtime·goPanicSlice3CU<ABIInternal>(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 c54b4eb..23619b1 100644
--- a/src/runtime/asm_arm.s
+++ b/src/runtime/asm_arm.s
@@ -206,23 +206,6 @@
  *  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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 a09172f..0ab92be 100644
--- a/src/runtime/asm_arm64.s
+++ b/src/runtime/asm_arm64.s
@@ -113,23 +113,6 @@
  *  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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 19781f7..6949506 100644
--- a/src/runtime/asm_mips64x.s
+++ b/src/runtime/asm_mips64x.s
@@ -89,21 +89,6 @@
  *  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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 ee87d81..8e5753d 100644
--- a/src/runtime/asm_mipsx.s
+++ b/src/runtime/asm_mipsx.s
@@ -90,21 +90,6 @@
  *  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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 dc34c0e..834023c 100644
--- a/src/runtime/asm_ppc64x.s
+++ b/src/runtime/asm_ppc64x.s
@@ -128,23 +128,6 @@
  *  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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 01b42dc..31e324d 100644
--- a/src/runtime/asm_riscv64.s
+++ b/src/runtime/asm_riscv64.s
@@ -297,21 +297,6 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 7baef37..fbd185c 100644
--- a/src/runtime/asm_s390x.s
+++ b/src/runtime/asm_s390x.s
@@ -174,21 +174,6 @@
  *  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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 // 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 fcb780f..cf3d961 100644
--- a/src/runtime/asm_wasm.s
+++ b/src/runtime/asm_wasm.s
@@ -296,14 +296,14 @@
 		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<ABIInternal>(SB)
 	End
 
-	MOVW argsize+24(FP), R0
+	MOVW frameSize+32(FP), R0
 
 	DISPATCH(runtime·call16, 16)
 	DISPATCH(runtime·call32, 32)
@@ -335,18 +335,18 @@
 	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 @@
 	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 @@
 	I64Add; \
 	Set RET2; \
 	\
-	I64Load32U argsize+24(FP); \
+	I64Load32U stackArgsSize+24(FP); \
 	Get R0; \
 	I64Sub; \
 	Set RET3; \
@@ -387,12 +387,13 @@
 // 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 17d9d47..d75f864 100644
--- a/src/runtime/cgo/gcc_amd64.S
+++ b/src/runtime/cgo/gcc_amd64.S
@@ -30,9 +30,14 @@
 	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 51410d5..d5b7fd8 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 @@
 	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 d25db91..0003414 100644
--- a/src/runtime/cgo/gcc_dragonfly_amd64.c
+++ b/src/runtime/cgo/gcc_dragonfly_amd64.c
@@ -61,11 +61,6 @@
 	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 514a2f8..6071ec3 100644
--- a/src/runtime/cgo/gcc_freebsd_amd64.c
+++ b/src/runtime/cgo/gcc_freebsd_amd64.c
@@ -69,11 +69,6 @@
 	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 f2bf648..c25e7e7 100644
--- a/src/runtime/cgo/gcc_linux_amd64.c
+++ b/src/runtime/cgo/gcc_linux_amd64.c
@@ -89,11 +89,6 @@
 	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 dc966fc..9f4b031 100644
--- a/src/runtime/cgo/gcc_netbsd_amd64.c
+++ b/src/runtime/cgo/gcc_netbsd_amd64.c
@@ -62,11 +62,6 @@
 	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 @@
 	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 34319fb..09d2750 100644
--- a/src/runtime/cgo/gcc_openbsd_amd64.c
+++ b/src/runtime/cgo/gcc_openbsd_amd64.c
@@ -60,11 +60,6 @@
 	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 079bd12..e89e844 100644
--- a/src/runtime/cgo/gcc_solaris_amd64.c
+++ b/src/runtime/cgo/gcc_solaris_amd64.c
@@ -72,11 +72,6 @@
 	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 0f8c817..25cfd08 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 @@
 	 */
 	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 aba500a..af4960e 100644
--- a/src/runtime/cgo/libcgo.h
+++ b/src/runtime/cgo/libcgo.h
@@ -66,7 +66,7 @@
 /*
  * 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 2ff5bf6..df010f5 100644
--- a/src/runtime/duff_amd64.s
+++ b/src/runtime/duff_amd64.s
@@ -5,100 +5,100 @@
 #include "textflag.h"
 
 TEXT runtime·duffzero<ABIInternal>(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 2b5affc..4994347 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 @@
 // 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 f4dbd77..7d0313b 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 @@
 					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), &regs)
 				fingRunning = false
 
 				// Drop finalizer queue heap references
diff --git a/src/runtime/mkduff.go b/src/runtime/mkduff.go
index 94ae75f..ef297f0 100644
--- a/src/runtime/mkduff.go
+++ b/src/runtime/mkduff.go
@@ -62,15 +62,15 @@
 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<ABIInternal>(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 @@
 	//
 	// 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<ABIInternal>(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 428ff7f..abd1010 100644
--- a/src/runtime/os2_aix.go
+++ b/src/runtime/os2_aix.go
@@ -18,11 +18,11 @@
 
 //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 5b2ccdd..e320eaa 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 @@
 		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, &regs)
 	if p != nil {
 		p.pc = 0
 		p.sp = unsafe.Pointer(nil)
@@ -968,7 +975,9 @@
 			}
 		} 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), &regs)
 		}
 		p.argp = nil
 
diff --git a/src/runtime/race/output_test.go b/src/runtime/race/output_test.go
index 6949687..17dc320 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 @@
 			"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 @@
 	goos   string
 	gorace string
 	source string
-	re     string
+	re     []string
 }{
 	{"simple", "run", "", "atexit_sleep_ms=0", `
 package main
@@ -107,7 +123,7 @@
 	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 @@
 ==================
 Found 1 data race\(s\)
 exit status 66
-`},
+`}},
 
 	{"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", `
 package main
@@ -143,7 +159,7 @@
 	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 @@
 	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 @@
 	x = 43
 	<-done
 }
-`, `
+`, []string{`
 ==================
 exit status 66
-`},
+`}},
 
 	{"test_fails_on_race", "test", "", "atexit_sleep_ms=0", `
 package main_test
@@ -193,12 +209,12 @@
 	<-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 @@
 	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 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 @@
 ==================
 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 @@
 	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\(\)
+      <autogenerated>: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 @@
 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 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 @@
 	}
 	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 @@
 	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 @@
 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 9818bc6..c3b7bbf 100644
--- a/src/runtime/race_amd64.s
+++ b/src/runtime/race_amd64.s
@@ -146,8 +146,10 @@
 // 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 @@
 // 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 @@
 // 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 @@
 	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 @@
 	// 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 @@
 
 // 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 @@
 	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 @@
 // 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<ABIInternal>(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 @@
 	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 109f0da..b7c7b4c 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -853,7 +853,7 @@
 // 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 6ab1f75..3eeb5e0 100644
--- a/src/runtime/signal_amd64.go
+++ b/src/runtime/signal_amd64.go
@@ -65,11 +65,14 @@
 	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 2ee2c74..c0cc95e 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 @@
 // 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 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 @@
 
 // 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 630fb5d..0fe8c7e 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<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	0(DI), DI		// arg 1 exit status
@@ -22,7 +24,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·open_trampoline(SB),NOSPLIT,$0
+TEXT runtime·open_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	8(DI), SI		// arg 2 flags
@@ -33,7 +35,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·close_trampoline(SB),NOSPLIT,$0
+TEXT runtime·close_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	0(DI), DI		// arg 1 fd
@@ -41,7 +43,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·read_trampoline(SB),NOSPLIT,$0
+TEXT runtime·read_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 buf
@@ -57,7 +59,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·write_trampoline(SB),NOSPLIT,$0
+TEXT runtime·write_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 buf
@@ -73,7 +75,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pipe_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	CALL	libc_pipe(SB)		// pointer already in DI
@@ -84,7 +86,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0
+TEXT runtime·setitimer_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 new
@@ -94,7 +96,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·madvise_trampoline(SB), NOSPLIT, $0
+TEXT runtime·madvise_trampoline<ABIInternal>(SB), NOSPLIT, $0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 len
@@ -105,12 +107,12 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·mlock_trampoline(SB), NOSPLIT, $0
+TEXT runtime·mlock_trampoline<ABIInternal>(SB), NOSPLIT, $0
 	UNDEF // unimplemented
 
 GLOBL timebase<>(SB),NOPTR,$(machTimebaseInfo__size)
 
-TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$0
+TEXT runtime·nanotime_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	DI, BX
@@ -139,7 +141,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·walltime_trampoline(SB),NOSPLIT,$0
+TEXT runtime·walltime_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP			// make a frame; keep stack aligned
 	MOVQ	SP, BP
 	MOVQ	DI, SI			// arg 2 timespec
@@ -148,7 +150,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·sigaction_trampoline(SB),NOSPLIT,$0
+TEXT runtime·sigaction_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 new
@@ -161,7 +163,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0
+TEXT runtime·sigprocmask_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 new
@@ -174,7 +176,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·sigaltstack_trampoline(SB),NOSPLIT,$0
+TEXT runtime·sigaltstack_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 old
@@ -186,7 +188,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·raiseproc_trampoline(SB),NOSPLIT,$0
+TEXT runtime·raiseproc_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	0(DI), BX	// signal
@@ -212,7 +214,7 @@
 
 // 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<ABIInternal>(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 @@
 
 // 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<ABIInternal>(SB),NOSPLIT,$0
 	// If no traceback function, do usual sigtramp.
 	MOVQ	runtime·cgoTraceback(SB), AX
 	TESTQ	AX, AX
@@ -289,12 +291,12 @@
 	// 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<ABIInternal>(SB), R9
 	MOVQ	_cgo_callers(SB), AX
 	JMP	AX
 
 sigtramp:
-	JMP	runtime·sigtramp(SB)
+	JMP	runtime·sigtramp<ABIInternal>(SB)
 
 sigtrampnog:
 	// Signal arrived on a non-Go thread. If this is SIGPROF, get a
@@ -320,7 +322,7 @@
 	MOVQ	_cgo_callers(SB), AX
 	JMP	AX
 
-TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
+TEXT runtime·mmap_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP			// make a frame; keep stack aligned
 	MOVQ	SP, BP
 	MOVQ	DI, BX
@@ -343,7 +345,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·munmap_trampoline(SB),NOSPLIT,$0
+TEXT runtime·munmap_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 len
@@ -355,7 +357,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·usleep_trampoline(SB),NOSPLIT,$0
+TEXT runtime·usleep_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	0(DI), DI	// arg 1 usec
@@ -367,7 +369,7 @@
 	// Nothing to do on Darwin, pthread already set thread-local storage up.
 	RET
 
-TEXT runtime·sysctl_trampoline(SB),NOSPLIT,$0
+TEXT runtime·sysctl_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	8(DI), SI		// arg 2 miblen
@@ -380,7 +382,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·sysctlbyname_trampoline(SB),NOSPLIT,$0
+TEXT runtime·sysctlbyname_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 oldp
@@ -392,14 +394,14 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·kqueue_trampoline(SB),NOSPLIT,$0
+TEXT runtime·kqueue_trampoline<ABIInternal>(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<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI		// arg 2 keventt
@@ -418,7 +420,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0
+TEXT runtime·fcntl_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	4(DI), SI		// arg 2 cmd
@@ -475,7 +477,7 @@
 // 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<ABIInternal>(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 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_attr_getstacksize_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_attr_getstacksize_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 size
@@ -492,7 +494,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_attr_setdetachstate_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_attr_setdetachstate_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 state
@@ -501,7 +503,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_create_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_create_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	SUBQ	$16, SP
@@ -514,7 +516,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·raise_trampoline(SB),NOSPLIT,$0
+TEXT runtime·raise_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVL	0(DI), DI	// arg 1 signal
@@ -522,7 +524,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_mutex_init_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_mutex_init_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 attr
@@ -531,7 +533,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_mutex_lock_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_mutex_lock_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	0(DI), DI	// arg 1 mutex
@@ -539,7 +541,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_mutex_unlock_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_mutex_unlock_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	0(DI), DI	// arg 1 mutex
@@ -547,7 +549,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_cond_init_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_cond_init_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 attr
@@ -556,7 +558,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_cond_wait_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_cond_wait_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 mutex
@@ -565,7 +567,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_cond_timedwait_relative_np_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_cond_timedwait_relative_np_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 mutex
@@ -575,7 +577,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_cond_signal_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_cond_signal_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	0(DI), DI	// arg 1 cond
@@ -583,7 +585,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_self_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_self_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	DI, BX		// BX is caller-save
@@ -592,7 +594,7 @@
 	POPQ	BP
 	RET
 
-TEXT runtime·pthread_kill_trampoline(SB),NOSPLIT,$0
+TEXT runtime·pthread_kill_trampoline<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	MOVQ	8(DI), SI	// arg 2 sig
@@ -617,7 +619,7 @@
 //
 // 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<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	SUBQ	$16, SP
@@ -667,7 +669,7 @@
 //
 // 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<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	SUBQ	$16, SP
@@ -703,7 +705,7 @@
 
 // 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<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	SUBQ	$16, SP
@@ -756,7 +758,7 @@
 //
 // 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<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	SUBQ	$16, SP
@@ -809,7 +811,7 @@
 //
 // 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<ABIInternal>(SB),NOSPLIT,$0
 	PUSHQ	BP
 	MOVQ	SP, BP
 	SUBQ	$16, SP
@@ -845,7 +847,7 @@
 
 // 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<ABIInternal>(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 37cb8da..d48573c 100644
--- a/src/runtime/sys_linux_amd64.s
+++ b/src/runtime/sys_linux_amd64.s
@@ -215,9 +215,13 @@
 
 	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 @@
 	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 @@
 
 	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 @@
 	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 @@
 	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 e098e80..3310489 100644
--- a/src/runtime/syscall2_solaris.go
+++ b/src/runtime/syscall2_solaris.go
@@ -15,7 +15,6 @@
 //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 @@
 //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 7835b49..add40bb 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 @@
 
 	// 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, &regs)
 
 	// Extract the result.
 	a.result = *(*uintptr)(unsafe.Pointer(&frame[c.retOffset]))
diff --git a/src/runtime/textflag.h b/src/runtime/textflag.h
index daca36d..e727208 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 81455f3..18fc4bb 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -383,7 +383,7 @@
 }
 
 // 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 0000000..7f66f2a
--- /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 0000000..a3a547d
--- /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 6c7f852..fecd1b9 100644
--- a/src/strconv/eisel_lemire.go
+++ b/src/strconv/eisel_lemire.go
@@ -29,7 +29,7 @@
 	// 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 @@
 
 	// 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 @@
 	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 @@
 
 	// 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 @@
 	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 @@
 	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 bcbdbc5..4ffa10b 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 @@
 	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 2ebaf8d..e53d14b 100644
--- a/src/syscall/mkasm.go
+++ b/src/syscall/mkasm.go
@@ -53,7 +53,8 @@
 		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<ABIInternal>(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 67e8d1d..c1ed3a3 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 afdadbf..162e944 100644
--- a/src/syscall/syscall_darwin.go
+++ b/src/syscall/syscall_darwin.go
@@ -115,7 +115,6 @@
 
 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 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 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 22ddb78..687efff 100644
--- a/src/syscall/syscall_darwin_amd64.go
+++ b/src/syscall/syscall_darwin_amd64.go
@@ -56,7 +56,6 @@
 
 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 ecb9fff..ab57117 100644
--- a/src/syscall/syscall_darwin_arm64.go
+++ b/src/syscall/syscall_darwin_arm64.go
@@ -56,7 +56,6 @@
 
 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 c246c3a..4f2cdf8 100644
--- a/src/syscall/zsyscall_darwin_amd64.go
+++ b/src/syscall/zsyscall_darwin_amd64.go
@@ -20,7 +20,6 @@
 
 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 492f947..5eb48ce 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<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getfsstat(SB)
-TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setattrlist_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setattrlist(SB)
-TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fdopendir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fdopendir(SB)
-TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendfile_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendfile(SB)
-TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getgroups_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgroups(SB)
-TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgroups_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgroups(SB)
-TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_wait4_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_wait4(SB)
-TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_accept_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_accept(SB)
-TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_bind_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_bind(SB)
-TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_connect_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_connect(SB)
-TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socket_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socket(SB)
-TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockopt(SB)
-TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsockopt(SB)
-TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpeername_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpeername(SB)
-TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockname_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockname(SB)
-TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_shutdown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_shutdown(SB)
-TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socketpair_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socketpair(SB)
-TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvfrom_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvfrom(SB)
-TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendto_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendto(SB)
-TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvmsg(SB)
-TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendmsg(SB)
-TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kevent_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kevent(SB)
-TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_utimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_utimes(SB)
-TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_futimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_futimes(SB)
-TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fcntl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fcntl(SB)
-TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pipe_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pipe(SB)
-TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kill_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kill(SB)
-TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_access_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_access(SB)
-TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_adjtime_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_adjtime(SB)
-TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chdir(SB)
-TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chflags(SB)
-TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chmod(SB)
-TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chown(SB)
-TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chroot_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chroot(SB)
-TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_close_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_close(SB)
-TEXT ·libc_closedir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_closedir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_closedir(SB)
-TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup(SB)
-TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup2_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup2(SB)
-TEXT ·libc_exchangedata_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_exchangedata_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_exchangedata(SB)
-TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchdir(SB)
-TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchflags(SB)
-TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchmod(SB)
-TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchown(SB)
-TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_flock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_flock(SB)
-TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fpathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fpathconf(SB)
-TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fsync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fsync(SB)
-TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ftruncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ftruncate(SB)
-TEXT ·libc_getdtablesize_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getdtablesize_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getdtablesize(SB)
-TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getegid(SB)
-TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_geteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_geteuid(SB)
-TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgid(SB)
-TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgid(SB)
-TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgrp_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgrp(SB)
-TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpid(SB)
-TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getppid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getppid(SB)
-TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpriority(SB)
-TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrlimit(SB)
-TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrusage_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrusage(SB)
-TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsid(SB)
-TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getuid(SB)
-TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_issetugid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_issetugid(SB)
-TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kqueue_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kqueue(SB)
-TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lchown(SB)
-TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_link_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_link(SB)
-TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_listen_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_listen(SB)
-TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkdir(SB)
-TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkfifo_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkfifo(SB)
-TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mknod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mknod(SB)
-TEXT ·libc_mlock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mlock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mlock(SB)
-TEXT ·libc_mlockall_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mlockall_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mlockall(SB)
-TEXT ·libc_mprotect_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mprotect_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mprotect(SB)
-TEXT ·libc_munlock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munlock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munlock(SB)
-TEXT ·libc_munlockall_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munlockall_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munlockall(SB)
-TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_open_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_open(SB)
-TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pathconf(SB)
-TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pread_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pread(SB)
-TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pwrite_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pwrite(SB)
-TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_read_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_read(SB)
-TEXT ·libc_readdir_r_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_readdir_r_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_readdir_r(SB)
-TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_readlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_readlink(SB)
-TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rename_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rename(SB)
-TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_revoke_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_revoke(SB)
-TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rmdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rmdir(SB)
-TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lseek_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lseek(SB)
-TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_select_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_select(SB)
-TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setegid(SB)
-TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_seteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_seteuid(SB)
-TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgid(SB)
-TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setlogin_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setlogin(SB)
-TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpgid(SB)
-TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpriority(SB)
-TEXT ·libc_setprivexec_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setprivexec_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setprivexec(SB)
-TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setregid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setregid(SB)
-TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setreuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setreuid(SB)
-TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setrlimit(SB)
-TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsid(SB)
-TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_settimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_settimeofday(SB)
-TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setuid(SB)
-TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_symlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_symlink(SB)
-TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sync(SB)
-TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_truncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_truncate(SB)
-TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_umask_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_umask(SB)
-TEXT ·libc_undelete_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_undelete_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_undelete(SB)
-TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlink(SB)
-TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unmount_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unmount(SB)
-TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_write_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_write(SB)
-TEXT ·libc_writev_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_writev_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_writev(SB)
-TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mmap(SB)
-TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munmap(SB)
-TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fork_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fork(SB)
-TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ioctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ioctl(SB)
-TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_execve_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_execve(SB)
-TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_exit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_exit(SB)
-TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sysctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sysctl(SB)
-TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlinkat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlinkat(SB)
-TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_openat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_openat(SB)
-TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getcwd_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getcwd(SB)
-TEXT ·libc_fstat64_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstat64_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstat64(SB)
-TEXT ·libc_fstatfs64_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatfs64_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatfs64(SB)
-TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_gettimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_gettimeofday(SB)
-TEXT ·libc_lstat64_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lstat64_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lstat64(SB)
-TEXT ·libc_stat64_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_stat64_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_stat64(SB)
-TEXT ·libc_statfs64_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_statfs64_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_statfs64(SB)
-TEXT ·libc_fstatat64_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatat64_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatat64(SB)
-TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ptrace_trampoline<ABIInternal>(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 ede0091..0d8598d 100644
--- a/src/syscall/zsyscall_darwin_arm64.go
+++ b/src/syscall/zsyscall_darwin_arm64.go
@@ -20,7 +20,6 @@
 
 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 b606c6e..73e4a3f 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<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getfsstat(SB)
-TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setattrlist_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setattrlist(SB)
-TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fdopendir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fdopendir(SB)
-TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendfile_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendfile(SB)
-TEXT ·libc_getgroups_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getgroups_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgroups(SB)
-TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgroups_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgroups(SB)
-TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_wait4_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_wait4(SB)
-TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_accept_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_accept(SB)
-TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_bind_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_bind(SB)
-TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_connect_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_connect(SB)
-TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socket_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socket(SB)
-TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockopt(SB)
-TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsockopt(SB)
-TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpeername_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpeername(SB)
-TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockname_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockname(SB)
-TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_shutdown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_shutdown(SB)
-TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socketpair_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socketpair(SB)
-TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvfrom_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvfrom(SB)
-TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendto_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendto(SB)
-TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvmsg(SB)
-TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendmsg(SB)
-TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kevent_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kevent(SB)
-TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_utimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_utimes(SB)
-TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_futimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_futimes(SB)
-TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fcntl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fcntl(SB)
-TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pipe_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pipe(SB)
-TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kill_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kill(SB)
-TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_access_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_access(SB)
-TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_adjtime_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_adjtime(SB)
-TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chdir(SB)
-TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chflags(SB)
-TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chmod(SB)
-TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chown(SB)
-TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chroot_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chroot(SB)
-TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_close_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_close(SB)
-TEXT ·libc_closedir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_closedir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_closedir(SB)
-TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup(SB)
-TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup2_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup2(SB)
-TEXT ·libc_exchangedata_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_exchangedata_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_exchangedata(SB)
-TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchdir(SB)
-TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchflags(SB)
-TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchmod(SB)
-TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchown(SB)
-TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_flock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_flock(SB)
-TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fpathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fpathconf(SB)
-TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fsync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fsync(SB)
-TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ftruncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ftruncate(SB)
-TEXT ·libc_getdtablesize_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getdtablesize_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getdtablesize(SB)
-TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getegid(SB)
-TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_geteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_geteuid(SB)
-TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgid(SB)
-TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgid(SB)
-TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgrp_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgrp(SB)
-TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpid(SB)
-TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getppid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getppid(SB)
-TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpriority(SB)
-TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrlimit(SB)
-TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrusage_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrusage(SB)
-TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsid(SB)
-TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getuid(SB)
-TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_issetugid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_issetugid(SB)
-TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kqueue_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kqueue(SB)
-TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lchown(SB)
-TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_link_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_link(SB)
-TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_listen_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_listen(SB)
-TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkdir(SB)
-TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkfifo_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkfifo(SB)
-TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mknod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mknod(SB)
-TEXT ·libc_mlock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mlock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mlock(SB)
-TEXT ·libc_mlockall_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mlockall_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mlockall(SB)
-TEXT ·libc_mprotect_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mprotect_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mprotect(SB)
-TEXT ·libc_munlock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munlock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munlock(SB)
-TEXT ·libc_munlockall_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munlockall_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munlockall(SB)
-TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_open_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_open(SB)
-TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pathconf(SB)
-TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pread_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pread(SB)
-TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pwrite_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pwrite(SB)
-TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_read_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_read(SB)
-TEXT ·libc_readdir_r_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_readdir_r_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_readdir_r(SB)
-TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_readlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_readlink(SB)
-TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rename_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rename(SB)
-TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_revoke_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_revoke(SB)
-TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rmdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rmdir(SB)
-TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lseek_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lseek(SB)
-TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_select_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_select(SB)
-TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setegid(SB)
-TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_seteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_seteuid(SB)
-TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgid(SB)
-TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setlogin_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setlogin(SB)
-TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpgid(SB)
-TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpriority(SB)
-TEXT ·libc_setprivexec_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setprivexec_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setprivexec(SB)
-TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setregid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setregid(SB)
-TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setreuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setreuid(SB)
-TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setrlimit(SB)
-TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsid(SB)
-TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_settimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_settimeofday(SB)
-TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setuid(SB)
-TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_symlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_symlink(SB)
-TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sync(SB)
-TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_truncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_truncate(SB)
-TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_umask_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_umask(SB)
-TEXT ·libc_undelete_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_undelete_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_undelete(SB)
-TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlink(SB)
-TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unmount_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unmount(SB)
-TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_write_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_write(SB)
-TEXT ·libc_writev_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_writev_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_writev(SB)
-TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mmap(SB)
-TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munmap(SB)
-TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fork_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fork(SB)
-TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ioctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ioctl(SB)
-TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_execve_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_execve(SB)
-TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_exit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_exit(SB)
-TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sysctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sysctl(SB)
-TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlinkat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlinkat(SB)
-TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_openat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_openat(SB)
-TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getcwd_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getcwd(SB)
-TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstat(SB)
-TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatfs_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatfs(SB)
-TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_gettimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_gettimeofday(SB)
-TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lstat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lstat(SB)
-TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_stat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_stat(SB)
-TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_statfs_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_statfs(SB)
-TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatat(SB)
-TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ptrace_trampoline<ABIInternal>(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 e5c5dde..8256a45 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<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgroups(SB)
-TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgroups_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgroups(SB)
-TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_wait4_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_wait4(SB)
-TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_accept_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_accept(SB)
-TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_bind_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_bind(SB)
-TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_connect_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_connect(SB)
-TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socket_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socket(SB)
-TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockopt(SB)
-TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsockopt(SB)
-TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpeername_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpeername(SB)
-TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockname_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockname(SB)
-TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_shutdown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_shutdown(SB)
-TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socketpair_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socketpair(SB)
-TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvfrom_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvfrom(SB)
-TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendto_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendto(SB)
-TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvmsg(SB)
-TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendmsg(SB)
-TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kevent_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kevent(SB)
-TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_utimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_utimes(SB)
-TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_futimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_futimes(SB)
-TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fcntl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fcntl(SB)
-TEXT ·libc_pipe2_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pipe2_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pipe2(SB)
-TEXT ·libc_accept4_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_accept4_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_accept4(SB)
-TEXT ·libc_getdents_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getdents_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getdents(SB)
-TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_access_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_access(SB)
-TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_adjtime_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_adjtime(SB)
-TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chdir(SB)
-TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chflags(SB)
-TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chmod(SB)
-TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chown(SB)
-TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chroot_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chroot(SB)
-TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_close_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_close(SB)
-TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup(SB)
-TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup2_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup2(SB)
-TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchdir(SB)
-TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchflags(SB)
-TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchmod(SB)
-TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchown(SB)
-TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_flock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_flock(SB)
-TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fpathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fpathconf(SB)
-TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstat(SB)
-TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatfs_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatfs(SB)
-TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fsync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fsync(SB)
-TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ftruncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ftruncate(SB)
-TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getegid(SB)
-TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_geteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_geteuid(SB)
-TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgid(SB)
-TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgid(SB)
-TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgrp_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgrp(SB)
-TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpid(SB)
-TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getppid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getppid(SB)
-TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpriority(SB)
-TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrlimit(SB)
-TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrusage_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrusage(SB)
-TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsid(SB)
-TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_gettimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_gettimeofday(SB)
-TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getuid(SB)
-TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_issetugid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_issetugid(SB)
-TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kill_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kill(SB)
-TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kqueue_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kqueue(SB)
-TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lchown(SB)
-TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_link_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_link(SB)
-TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_listen_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_listen(SB)
-TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lstat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lstat(SB)
-TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkdir(SB)
-TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkfifo_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkfifo(SB)
-TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mknod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mknod(SB)
-TEXT ·libc_nanosleep_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_nanosleep_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_nanosleep(SB)
-TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_open_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_open(SB)
-TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pathconf(SB)
-TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pread_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pread(SB)
-TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pwrite_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pwrite(SB)
-TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_read_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_read(SB)
-TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_readlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_readlink(SB)
-TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rename_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rename(SB)
-TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_revoke_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_revoke(SB)
-TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rmdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rmdir(SB)
-TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_select_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_select(SB)
-TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setegid(SB)
-TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_seteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_seteuid(SB)
-TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgid(SB)
-TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setlogin_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setlogin(SB)
-TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpgid(SB)
-TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpriority(SB)
-TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setregid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setregid(SB)
-TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setreuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setreuid(SB)
-TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setrlimit(SB)
-TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsid(SB)
-TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_settimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_settimeofday(SB)
-TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setuid(SB)
-TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_stat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_stat(SB)
-TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_statfs_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_statfs(SB)
-TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_symlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_symlink(SB)
-TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sync(SB)
-TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_truncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_truncate(SB)
-TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_umask_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_umask(SB)
-TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlink(SB)
-TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unmount_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unmount(SB)
-TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_write_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_write(SB)
-TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mmap(SB)
-TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munmap(SB)
-TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_utimensat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_utimensat(SB)
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_syscall_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_syscall(SB)
-TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lseek_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lseek(SB)
-TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getcwd_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getcwd(SB)
-TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sysctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sysctl(SB)
-TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fork_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fork(SB)
-TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ioctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ioctl(SB)
-TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_execve_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_execve(SB)
-TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_exit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_exit(SB)
-TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ptrace_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ptrace(SB)
-TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getentropy_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getentropy(SB)
-TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatat(SB)
-TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlinkat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlinkat(SB)
-TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_openat_trampoline<ABIInternal>(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 37778b1..f6e0a8d 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<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgroups(SB)
-TEXT ·libc_setgroups_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgroups_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgroups(SB)
-TEXT ·libc_wait4_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_wait4_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_wait4(SB)
-TEXT ·libc_accept_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_accept_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_accept(SB)
-TEXT ·libc_bind_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_bind_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_bind(SB)
-TEXT ·libc_connect_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_connect_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_connect(SB)
-TEXT ·libc_socket_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socket_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socket(SB)
-TEXT ·libc_getsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockopt(SB)
-TEXT ·libc_setsockopt_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsockopt_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsockopt(SB)
-TEXT ·libc_getpeername_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpeername_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpeername(SB)
-TEXT ·libc_getsockname_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsockname_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsockname(SB)
-TEXT ·libc_shutdown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_shutdown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_shutdown(SB)
-TEXT ·libc_socketpair_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_socketpair_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_socketpair(SB)
-TEXT ·libc_recvfrom_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvfrom_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvfrom(SB)
-TEXT ·libc_sendto_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendto_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendto(SB)
-TEXT ·libc_recvmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_recvmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_recvmsg(SB)
-TEXT ·libc_sendmsg_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sendmsg_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sendmsg(SB)
-TEXT ·libc_kevent_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kevent_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kevent(SB)
-TEXT ·libc_utimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_utimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_utimes(SB)
-TEXT ·libc_futimes_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_futimes_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_futimes(SB)
-TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fcntl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fcntl(SB)
-TEXT ·libc_pipe2_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pipe2_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pipe2(SB)
-TEXT ·libc_accept4_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_accept4_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_accept4(SB)
-TEXT ·libc_getdents_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getdents_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getdents(SB)
-TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_access_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_access(SB)
-TEXT ·libc_adjtime_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_adjtime_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_adjtime(SB)
-TEXT ·libc_chdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chdir(SB)
-TEXT ·libc_chflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chflags(SB)
-TEXT ·libc_chmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chmod(SB)
-TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chown(SB)
-TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_chroot_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_chroot(SB)
-TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_close_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_close(SB)
-TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup(SB)
-TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_dup2_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_dup2(SB)
-TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchdir(SB)
-TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchflags_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchflags(SB)
-TEXT ·libc_fchmod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchmod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchmod(SB)
-TEXT ·libc_fchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fchown(SB)
-TEXT ·libc_flock_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_flock_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_flock(SB)
-TEXT ·libc_fpathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fpathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fpathconf(SB)
-TEXT ·libc_fstat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstat(SB)
-TEXT ·libc_fstatfs_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatfs_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatfs(SB)
-TEXT ·libc_fsync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fsync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fsync(SB)
-TEXT ·libc_ftruncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ftruncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ftruncate(SB)
-TEXT ·libc_getegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getegid(SB)
-TEXT ·libc_geteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_geteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_geteuid(SB)
-TEXT ·libc_getgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getgid(SB)
-TEXT ·libc_getpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgid(SB)
-TEXT ·libc_getpgrp_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpgrp_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpgrp(SB)
-TEXT ·libc_getpid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpid(SB)
-TEXT ·libc_getppid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getppid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getppid(SB)
-TEXT ·libc_getpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getpriority(SB)
-TEXT ·libc_getrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrlimit(SB)
-TEXT ·libc_getrusage_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getrusage_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getrusage(SB)
-TEXT ·libc_getsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getsid(SB)
-TEXT ·libc_gettimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_gettimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_gettimeofday(SB)
-TEXT ·libc_getuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getuid(SB)
-TEXT ·libc_issetugid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_issetugid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_issetugid(SB)
-TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kill_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kill(SB)
-TEXT ·libc_kqueue_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_kqueue_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_kqueue(SB)
-TEXT ·libc_lchown_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lchown_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lchown(SB)
-TEXT ·libc_link_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_link_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_link(SB)
-TEXT ·libc_listen_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_listen_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_listen(SB)
-TEXT ·libc_lstat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lstat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lstat(SB)
-TEXT ·libc_mkdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkdir(SB)
-TEXT ·libc_mkfifo_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mkfifo_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mkfifo(SB)
-TEXT ·libc_mknod_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mknod_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mknod(SB)
-TEXT ·libc_nanosleep_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_nanosleep_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_nanosleep(SB)
-TEXT ·libc_open_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_open_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_open(SB)
-TEXT ·libc_pathconf_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pathconf_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pathconf(SB)
-TEXT ·libc_pread_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pread_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pread(SB)
-TEXT ·libc_pwrite_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_pwrite_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_pwrite(SB)
-TEXT ·libc_read_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_read_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_read(SB)
-TEXT ·libc_readlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_readlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_readlink(SB)
-TEXT ·libc_rename_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rename_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rename(SB)
-TEXT ·libc_revoke_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_revoke_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_revoke(SB)
-TEXT ·libc_rmdir_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_rmdir_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_rmdir(SB)
-TEXT ·libc_select_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_select_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_select(SB)
-TEXT ·libc_setegid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setegid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setegid(SB)
-TEXT ·libc_seteuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_seteuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_seteuid(SB)
-TEXT ·libc_setgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setgid(SB)
-TEXT ·libc_setlogin_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setlogin_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setlogin(SB)
-TEXT ·libc_setpgid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpgid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpgid(SB)
-TEXT ·libc_setpriority_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setpriority_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setpriority(SB)
-TEXT ·libc_setregid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setregid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setregid(SB)
-TEXT ·libc_setreuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setreuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setreuid(SB)
-TEXT ·libc_setrlimit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setrlimit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setrlimit(SB)
-TEXT ·libc_setsid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setsid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setsid(SB)
-TEXT ·libc_settimeofday_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_settimeofday_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_settimeofday(SB)
-TEXT ·libc_setuid_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_setuid_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_setuid(SB)
-TEXT ·libc_stat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_stat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_stat(SB)
-TEXT ·libc_statfs_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_statfs_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_statfs(SB)
-TEXT ·libc_symlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_symlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_symlink(SB)
-TEXT ·libc_sync_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sync_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sync(SB)
-TEXT ·libc_truncate_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_truncate_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_truncate(SB)
-TEXT ·libc_umask_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_umask_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_umask(SB)
-TEXT ·libc_unlink_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlink_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlink(SB)
-TEXT ·libc_unmount_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unmount_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unmount(SB)
-TEXT ·libc_write_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_write_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_write(SB)
-TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_mmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_mmap(SB)
-TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_munmap_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_munmap(SB)
-TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_utimensat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_utimensat(SB)
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_syscall_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_syscall(SB)
-TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_lseek_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_lseek(SB)
-TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getcwd_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getcwd(SB)
-TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_sysctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_sysctl(SB)
-TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fork_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fork(SB)
-TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ioctl_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ioctl(SB)
-TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_execve_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_execve(SB)
-TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_exit_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_exit(SB)
-TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_ptrace_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_ptrace(SB)
-TEXT ·libc_getentropy_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_getentropy_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_getentropy(SB)
-TEXT ·libc_fstatat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_fstatat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_fstatat(SB)
-TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_unlinkat_trampoline<ABIInternal>(SB),NOSPLIT,$0-0
 	JMP	libc_unlinkat(SB)
-TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
+TEXT ·libc_openat_trampoline<ABIInternal>(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 0000000..d663337
--- /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 0000000..cff989b
--- /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 0000000..e7ecd58
--- /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 0000000..321b1ad
--- /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 e4db05d..812d41f 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 @@
 			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 5694673..2fc3375 100644
--- a/test/closure3.dir/main.go
+++ b/test/closure3.dir/main.go
@@ -93,11 +93,11 @@
 		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() { // 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 @@
 
 	{
 		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 @@
 		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 @@
 		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 @@
 
 //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 0000000..de8082b
--- /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 0000000..ee5dba6
--- /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 0000000..a7022b2
--- /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 0000000..b5592ad
--- /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 0000000..823333f
--- /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 f86da34..7579dd1 100644
--- a/test/codegen/condmove.go
+++ b/test/codegen/condmove.go
@@ -31,7 +31,7 @@
 	if x < y {
 		x = -y
 	}
-	// amd64:"CMOVQCS"
+	// amd64:"CMOVQ(HI|CS)"
 	// arm64:"CSEL\t(LO|HI)"
 	// wasm:"Select"
 	return x
@@ -41,7 +41,7 @@
 	if x < y {
 		x = -y
 	}
-	// amd64:"CMOVLCS"
+	// amd64:"CMOVL(HI|CS)"
 	// arm64:"CSEL\t(LO|HI)"
 	// wasm:"Select"
 	return x
@@ -51,7 +51,7 @@
 	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 3753498..d845da3 100644
--- a/test/codegen/spectre.go
+++ b/test/codegen/spectre.go
@@ -13,12 +13,12 @@
 }
 
 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 9eddc5b..c4bcb55 100644
--- a/test/codegen/structs.go
+++ b/test/codegen/structs.go
@@ -18,7 +18,7 @@
 }
 
 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 @@
 }
 
 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 048d0cb..d104a2f 100644
--- a/test/const2.go
+++ b/test/const2.go
@@ -19,3 +19,14 @@
 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 8037291..1312bbb 100644
--- a/test/deferfin.go
+++ b/test/deferfin.go
@@ -18,12 +18,8 @@
 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 @@
 		panic("not all finalizers are called")
 	}
 }
-
diff --git a/test/escape2.go b/test/escape2.go
index 5c6eb55..b9b723d 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -1547,7 +1547,7 @@
 	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 46e58f8..7c8208a 100644
--- a/test/escape2n.go
+++ b/test/escape2n.go
@@ -1547,7 +1547,7 @@
 	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 a4a9c14..4e50231 100644
--- a/test/escape4.go
+++ b/test/escape4.go
@@ -35,14 +35,14 @@
 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 @@
 			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 993e914..dc93f68 100644
--- a/test/escape_param.go
+++ b/test/escape_param.go
@@ -212,7 +212,7 @@
 
 // **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 @@
 	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 8c543c9..542a6ea 100644
--- a/test/fixedbugs/bug340.go
+++ b/test/fixedbugs/bug340.go
@@ -12,6 +12,7 @@
 	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 3df63b0..bae5ee0 100644
--- a/test/fixedbugs/bug462.go
+++ b/test/fixedbugs/bug462.go
@@ -13,7 +13,7 @@
 }
 
 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 964e5fd..9492ec1 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 fbdd4c3..c8ecfc5 100644
--- a/test/fixedbugs/issue13799.go
+++ b/test/fixedbugs/issue13799.go
@@ -60,7 +60,7 @@
 	}
 
 	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 @@
 	}
 
 	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 @@
 	}
 
 	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 @@
 	}
 
 	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 @@
 	}
 
 	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 @@
 	}
 
 	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 e58047e..33cf63a 100644
--- a/test/fixedbugs/issue15055.go
+++ b/test/fixedbugs/issue15055.go
@@ -8,10 +8,12 @@
 
 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 f91c749..7a0300a 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 c190515..1a513be 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 9e7649f..1d9a745 100644
--- a/test/fixedbugs/issue20415.go
+++ b/test/fixedbugs/issue20415.go
@@ -11,7 +11,7 @@
 // 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 @@
 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 @@
 
 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 53c4f61..f73e6d1 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 0000000..c8bf1f8
--- /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 ea53452..dc86c97 100644
--- a/test/fixedbugs/issue22822.go
+++ b/test/fixedbugs/issue22822.go
@@ -12,5 +12,7 @@
 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 0000000..770c48e
--- /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 af5c7a1..b9328a6 100644
--- a/test/fixedbugs/issue27595.go
+++ b/test/fixedbugs/issue27595.go
@@ -8,7 +8,7 @@
 
 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 d1992e1..54c9db9 100644
--- a/test/fixedbugs/issue28079b.go
+++ b/test/fixedbugs/issue28079b.go
@@ -13,5 +13,5 @@
 type T [uintptr(unsafe.Pointer(nil))]int // ERROR "non-constant array bound|array bound is not constant"
 
 func f() {
-	_ = complex(1<<uintptr(unsafe.Pointer(nil)), 0) // GCCGO_ERROR "non-integer type for left operand of shift"
+	_ = complex(1<<uintptr(unsafe.Pointer(nil)), 0) // ERROR "shift of type float64|non-integer type for left operand of shift"
 }
diff --git a/test/fixedbugs/issue30087.go b/test/fixedbugs/issue30087.go
index 3ad9c8c..a8f6202 100644
--- a/test/fixedbugs/issue30087.go
+++ b/test/fixedbugs/issue30087.go
@@ -7,8 +7,8 @@
 package main
 
 func main() {
-	var a, b = 1    // ERROR "assignment mismatch: 2 variables but 1 values|wrong number of initializations"
-	_ = 1, 2        // ERROR "assignment mismatch: 1 variables but 2 values|number of variables does not match"
-	c, d := 1       // ERROR "assignment mismatch: 2 variables but 1 values|wrong number of initializations"
+	var a, b = 1    // ERROR "assignment mismatch: 2 variables but 1 value|wrong number of initializations"
+	_ = 1, 2        // ERROR "assignment mismatch: 1 variable but 2 values|number of variables does not match"
+	c, d := 1       // ERROR "assignment mismatch: 2 variables but 1 value|wrong number of initializations"
 	e, f := 1, 2, 3 // ERROR "assignment mismatch: 2 variables but 3 values|wrong number of initializations"
 }
diff --git a/test/fixedbugs/issue31053.dir/main.go b/test/fixedbugs/issue31053.dir/main.go
index 895c262..3bc75d1 100644
--- a/test/fixedbugs/issue31053.dir/main.go
+++ b/test/fixedbugs/issue31053.dir/main.go
@@ -35,8 +35,8 @@
 	_ = f.Exported
 	_ = f.exported    // ERROR "f.exported undefined .type f1.Foo has no field or method exported, but does have Exported."
 	_ = f.Unexported  // ERROR "f.Unexported undefined .type f1.Foo has no field or method Unexported."
-	_ = f.unexported  // ERROR "f.unexported undefined .cannot refer to unexported field or method f1..\*Foo..unexported."
-	f.unexported = 10 // ERROR "f.unexported undefined .cannot refer to unexported field or method f1..\*Foo..unexported."
-	f.unexported()    // ERROR "f.unexported undefined .cannot refer to unexported field or method f1..\*Foo..unexported."
+	_ = f.unexported  // ERROR "f.unexported undefined .cannot refer to unexported field or method unexported."
+	f.unexported = 10 // ERROR "f.unexported undefined .cannot refer to unexported field or method unexported."
+	f.unexported()    // ERROR "f.unexported undefined .cannot refer to unexported field or method unexported."
 	_ = f.hook        // ERROR "f.hook undefined .cannot refer to unexported field or method hook."
 }
diff --git a/test/fixedbugs/issue43164.dir/a.go b/test/fixedbugs/issue43164.dir/a.go
new file mode 100644
index 0000000..fa10e85
--- /dev/null
+++ b/test/fixedbugs/issue43164.dir/a.go
@@ -0,0 +1,13 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import . "strings"
+
+var _ = Index // use strings
+
+type t struct{ Index int }
+
+var _ = t{Index: 0}
diff --git a/test/fixedbugs/issue43164.dir/b.go b/test/fixedbugs/issue43164.dir/b.go
new file mode 100644
index 0000000..b025927
--- /dev/null
+++ b/test/fixedbugs/issue43164.dir/b.go
@@ -0,0 +1,11 @@
+// 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 . "bytes"
+
+var _ = Index // use bytes
+
+var _ = t{Index: 0}
diff --git a/test/fixedbugs/issue43164.go b/test/fixedbugs/issue43164.go
new file mode 100644
index 0000000..f21d1d5
--- /dev/null
+++ b/test/fixedbugs/issue43164.go
@@ -0,0 +1,7 @@
+// compiledir
+
+// 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 ignored
diff --git a/test/fixedbugs/issue43167.go b/test/fixedbugs/issue43167.go
new file mode 100644
index 0000000..1d1b69a
--- /dev/null
+++ b/test/fixedbugs/issue43167.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 p
+
+import . "bytes"
+
+var _ Buffer // use package bytes
+
+var Index byte // ERROR "Index redeclared.*\n\tLINE-4: previous declaration during import .bytes.|already declared|redefinition"
diff --git a/test/fixedbugs/issue43384.go b/test/fixedbugs/issue43384.go
new file mode 100644
index 0000000..1bd793b
--- /dev/null
+++ b/test/fixedbugs/issue43384.go
@@ -0,0 +1,124 @@
+// errorcheck
+
+// Copyright 2020 The Go Authors. All rights reserved.  Use of this
+// source code is governed by a BSD-style license that can be found in
+// the LICENSE file.
+
+package p
+
+type T int
+
+func (T) Mv()  {}
+func (*T) Mp() {}
+
+type P1 struct{ T }
+type P2 struct{ *T }
+type P3 *struct{ T }
+type P4 *struct{ *T }
+
+func _() {
+	{
+		var p P1
+		p.Mv()
+		(&p).Mv()
+		(*&p).Mv()
+		p.Mp()
+		(&p).Mp()
+		(*&p).Mp()
+	}
+	{
+		var p P2
+		p.Mv()
+		(&p).Mv()
+		(*&p).Mv()
+		p.Mp()
+		(&p).Mp()
+		(*&p).Mp()
+	}
+	{
+		var p P3
+		p.Mv()     // ERROR "undefined"
+		(&p).Mv()  // ERROR "undefined"
+		(*&p).Mv() // ERROR "undefined"
+		(**&p).Mv()
+		(*p).Mv()
+		(&*p).Mv()
+		p.Mp()     // ERROR "undefined"
+		(&p).Mp()  // ERROR "undefined"
+		(*&p).Mp() // ERROR "undefined"
+		(**&p).Mp()
+		(*p).Mp()
+		(&*p).Mp()
+	}
+	{
+		var p P4
+		p.Mv()     // ERROR "undefined"
+		(&p).Mv()  // ERROR "undefined"
+		(*&p).Mv() // ERROR "undefined"
+		(**&p).Mv()
+		(*p).Mv()
+		(&*p).Mv()
+		p.Mp()     // ERROR "undefined"
+		(&p).Mp()  // ERROR "undefined"
+		(*&p).Mp() // ERROR "undefined"
+		(**&p).Mp()
+		(*p).Mp()
+		(&*p).Mp()
+	}
+}
+
+func _() {
+	type P5 struct{ T }
+	type P6 struct{ *T }
+	type P7 *struct{ T }
+	type P8 *struct{ *T }
+
+	{
+		var p P5
+		p.Mv()
+		(&p).Mv()
+		(*&p).Mv()
+		p.Mp()
+		(&p).Mp()
+		(*&p).Mp()
+	}
+	{
+		var p P6
+		p.Mv()
+		(&p).Mv()
+		(*&p).Mv()
+		p.Mp()
+		(&p).Mp()
+		(*&p).Mp()
+	}
+	{
+		var p P7
+		p.Mv()     // ERROR "undefined"
+		(&p).Mv()  // ERROR "undefined"
+		(*&p).Mv() // ERROR "undefined"
+		(**&p).Mv()
+		(*p).Mv()
+		(&*p).Mv()
+		p.Mp()     // ERROR "undefined"
+		(&p).Mp()  // ERROR "undefined"
+		(*&p).Mp() // ERROR "undefined"
+		(**&p).Mp()
+		(*p).Mp()
+		(&*p).Mp()
+	}
+	{
+		var p P8
+		p.Mv()     // ERROR "undefined"
+		(&p).Mv()  // ERROR "undefined"
+		(*&p).Mv() // ERROR "undefined"
+		(**&p).Mv()
+		(*p).Mv()
+		(&*p).Mv()
+		p.Mp()     // ERROR "undefined"
+		(&p).Mp()  // ERROR "undefined"
+		(*&p).Mp() // ERROR "undefined"
+		(**&p).Mp()
+		(*p).Mp()
+		(&*p).Mp()
+	}
+}
diff --git a/test/fixedbugs/issue43428.go b/test/fixedbugs/issue43428.go
new file mode 100644
index 0000000..773a3f3
--- /dev/null
+++ b/test/fixedbugs/issue43428.go
@@ -0,0 +1,25 @@
+// 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 "time"
+
+type T int
+
+func (T) Mv()  {}
+func (*T) Mp() {}
+
+var _ = []int{
+	T.Mv,    // ERROR "cannot use T\.Mv|incompatible type"
+	(*T).Mv, // ERROR "cannot use \(\*T\)\.Mv|incompatible type"
+	(*T).Mp, // ERROR "cannot use \(\*T\)\.Mp|incompatible type"
+
+	time.Time.GobEncode,    // ERROR "cannot use time\.Time\.GobEncode|incompatible type"
+	(*time.Time).GobEncode, // ERROR "cannot use \(\*time\.Time\)\.GobEncode|incompatible type"
+	(*time.Time).GobDecode, // ERROR "cannot use \(\*time\.Time\)\.GobDecode|incompatible type"
+
+}
diff --git a/test/fixedbugs/issue43444.go b/test/fixedbugs/issue43444.go
new file mode 100644
index 0000000..c430e1b
--- /dev/null
+++ b/test/fixedbugs/issue43444.go
@@ -0,0 +1,28 @@
+// run
+
+package main
+
+var sp = ""
+
+func f(name string, _ ...interface{}) int {
+	print(sp, name)
+	sp = " "
+	return 0
+}
+
+var a = f("a", x)
+var b = f("b", y)
+var c = f("c", z)
+var d = func() int {
+	if false {
+		_ = z
+	}
+	return f("d")
+}()
+var e = f("e")
+
+var x int
+var y int = 42
+var z int = func() int { return 42 }()
+
+func main() { println() }
diff --git a/test/fixedbugs/issue43444.out b/test/fixedbugs/issue43444.out
new file mode 100644
index 0000000..22d6a0d
--- /dev/null
+++ b/test/fixedbugs/issue43444.out
@@ -0,0 +1 @@
+e a b c d
diff --git a/test/fixedbugs/issue43479.dir/a.go b/test/fixedbugs/issue43479.dir/a.go
new file mode 100644
index 0000000..ed3e6a5
--- /dev/null
+++ b/test/fixedbugs/issue43479.dir/a.go
@@ -0,0 +1,27 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+type Here struct{ stuff int }
+type Info struct{ Dir string }
+
+func New() Here { return Here{} }
+func (h Here) Dir(p string) (Info, error)
+
+type I interface{ M(x string) }
+
+type T = struct {
+	Here
+	I
+}
+
+var X T
+
+var A = (*T).Dir
+var B = T.Dir
+var C = X.Dir
+var D = (*T).M
+var E = T.M
+var F = X.M
diff --git a/test/fixedbugs/issue43479.dir/b.go b/test/fixedbugs/issue43479.dir/b.go
new file mode 100644
index 0000000..02d1690
--- /dev/null
+++ b/test/fixedbugs/issue43479.dir/b.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.
+
+package b
+
+import "./a"
+
+var Here = a.New()
+var Dir = Here.Dir
+
+type T = struct {
+	a.Here
+	a.I
+}
+
+var X T
+
+// Test exporting the type of method values for anonymous structs with
+// promoted methods.
+var A = a.A
+var B = a.B
+var C = a.C
+var D = a.D
+var E = a.E
+var F = a.F
+var G = (*a.T).Dir
+var H = a.T.Dir
+var I = a.X.Dir
+var J = (*a.T).M
+var K = a.T.M
+var L = a.X.M
+var M = (*T).Dir
+var N = T.Dir
+var O = X.Dir
+var P = (*T).M
+var Q = T.M
+var R = X.M
diff --git a/test/fixedbugs/issue43479.go b/test/fixedbugs/issue43479.go
new file mode 100644
index 0000000..f21d1d5
--- /dev/null
+++ b/test/fixedbugs/issue43479.go
@@ -0,0 +1,7 @@
+// compiledir
+
+// 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 ignored
diff --git a/test/fixedbugs/issue43480.go b/test/fixedbugs/issue43480.go
new file mode 100644
index 0000000..d98ad3a
--- /dev/null
+++ b/test/fixedbugs/issue43480.go
@@ -0,0 +1,33 @@
+// 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.
+
+// Issue #43480: ICE on large uint64 constants in switch cases.
+
+package main
+
+func isPow10(x uint64) bool {
+	switch x {
+	case 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+		1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19:
+		return true
+	}
+	return false
+}
+
+func main() {
+	var x uint64 = 1
+
+	for {
+		if !isPow10(x) || isPow10(x-1) || isPow10(x+1) {
+			panic(x)
+		}
+		next := x * 10
+		if next/10 != x {
+			break // overflow
+		}
+		x = next
+	}
+}
diff --git a/test/fixedbugs/issue43633.dir/a.go b/test/fixedbugs/issue43633.dir/a.go
new file mode 100644
index 0000000..946a37e
--- /dev/null
+++ b/test/fixedbugs/issue43633.dir/a.go
@@ -0,0 +1,28 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+func F() bool {
+	{
+		x := false
+		_ = x
+	}
+	if false {
+		_ = func(x bool) {}
+	}
+	x := true
+	return x
+}
+
+func G() func() bool {
+	x := true
+	return func() bool {
+		{
+			x := false
+			_ = x
+		}
+		return x
+	}
+}
diff --git a/test/fixedbugs/issue43633.dir/main.go b/test/fixedbugs/issue43633.dir/main.go
new file mode 100644
index 0000000..320e000
--- /dev/null
+++ b/test/fixedbugs/issue43633.dir/main.go
@@ -0,0 +1,18 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "./a"
+
+var g = a.G()
+
+func main() {
+	if !a.F() {
+		panic("FAIL")
+	}
+	if !g() {
+		panic("FAIL")
+	}
+}
diff --git a/test/fixedbugs/issue43633.go b/test/fixedbugs/issue43633.go
new file mode 100644
index 0000000..40df49f
--- /dev/null
+++ b/test/fixedbugs/issue43633.go
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ignored
diff --git a/test/fixedbugs/issue43677.go b/test/fixedbugs/issue43677.go
new file mode 100644
index 0000000..1a68c8b
--- /dev/null
+++ b/test/fixedbugs/issue43677.go
@@ -0,0 +1,18 @@
+// compile
+
+// 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.
+
+// Issue #43677: ICE during compilation of dynamic initializers for
+// composite blank variables.
+
+package p
+
+func f() *int
+
+var _ = [2]*int{nil, f()}
+
+var _ = struct{ x, y *int }{nil, f()}
+
+var _ interface{} = f()
diff --git a/test/fixedbugs/issue43701.go b/test/fixedbugs/issue43701.go
new file mode 100644
index 0000000..6e16180
--- /dev/null
+++ b/test/fixedbugs/issue43701.go
@@ -0,0 +1,18 @@
+// compile
+
+// 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 p
+
+func f() {
+	var st struct {
+		s string
+		i int16
+	}
+	_ = func() {
+		var m map[int16]int
+		m[st.i] = 0
+	}
+}
diff --git a/test/fixedbugs/issue43762.go b/test/fixedbugs/issue43762.go
new file mode 100644
index 0000000..4544b6e
--- /dev/null
+++ b/test/fixedbugs/issue43762.go
@@ -0,0 +1,11 @@
+// errorcheck
+
+// 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 p
+
+var _ = true == '\\' // ERROR "invalid operation: true == '\\\\'"
+var _ = true == '\'' // ERROR "invalid operation: true == '\\''"
+var _ = true == '\n' // ERROR "invalid operation: true == '\\n'"
diff --git a/test/fixedbugs/issue43835.go b/test/fixedbugs/issue43835.go
index 449eb72..29a5194 100644
--- a/test/fixedbugs/issue43835.go
+++ b/test/fixedbugs/issue43835.go
@@ -13,6 +13,9 @@
 	if bad, _ := g(); bad {
 		panic("FAIL")
 	}
+	if bad, _ := h(); bad {
+		panic("FAIL")
+	}
 }
 
 func f() (bad bool) {
@@ -31,3 +34,12 @@
 	var p *int
 	return true, *p
 }
+
+
+func h() (_ bool, _ int) {
+	defer func() {
+		recover()
+	}()
+	var p *int
+	return true, *p
+}
diff --git a/test/fixedbugs/issue43962.dir/a.go b/test/fixedbugs/issue43962.dir/a.go
new file mode 100644
index 0000000..168b206
--- /dev/null
+++ b/test/fixedbugs/issue43962.dir/a.go
@@ -0,0 +1,5 @@
+// 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 init
diff --git a/test/fixedbugs/issue43962.dir/b.go b/test/fixedbugs/issue43962.dir/b.go
new file mode 100644
index 0000000..f55fea1
--- /dev/null
+++ b/test/fixedbugs/issue43962.dir/b.go
@@ -0,0 +1,7 @@
+// 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 b
+
+import "./a" // ERROR "cannot import package as init"
diff --git a/test/fixedbugs/issue43962.go b/test/fixedbugs/issue43962.go
new file mode 100644
index 0000000..dca4d07
--- /dev/null
+++ b/test/fixedbugs/issue43962.go
@@ -0,0 +1,9 @@
+// errorcheckdir
+
+// 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.
+
+// Issue 43962: Importing a package called "init" is an error.
+
+package ignored
diff --git a/test/fixedbugs/issue5493.go b/test/fixedbugs/issue5493.go
index 2ee0398..8f771bc 100644
--- a/test/fixedbugs/issue5493.go
+++ b/test/fixedbugs/issue5493.go
@@ -14,6 +14,7 @@
 )
 
 const N = 10
+
 var count int64
 
 func run() error {
@@ -31,10 +32,9 @@
 }
 
 func main() {
-	// Does not work on 32-bits, or with gccgo, 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" || runtime.Compiler == "gccgo" {
+	if runtime.Compiler == "gccgo" {
 		return
 	}
 	count = N
@@ -56,4 +56,3 @@
 		panic("not all finalizers are called")
 	}
 }
-
diff --git a/test/fixedbugs/issue6428.go b/test/fixedbugs/issue6428.go
new file mode 100644
index 0000000..c3f7b20
--- /dev/null
+++ b/test/fixedbugs/issue6428.go
@@ -0,0 +1,15 @@
+// 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 . "testing" // ERROR "imported and not used"
+
+type S struct {
+	T int
+}
+
+var _ = S{T: 0}
diff --git a/test/fixedbugs/issue7740.go b/test/fixedbugs/issue7740.go
index 8f1afe8..6bc6249 100644
--- a/test/fixedbugs/issue7740.go
+++ b/test/fixedbugs/issue7740.go
@@ -21,7 +21,7 @@
 	var prec float64
 	switch runtime.Compiler {
 	case "gc":
-		prec = 512
+		prec = math.Inf(1) // exact precision using rational arithmetic
 	case "gccgo":
 		prec = 256
 	default:
diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go
index 5dce557..65be4b5 100644
--- a/test/fixedbugs/issue7921.go
+++ b/test/fixedbugs/issue7921.go
@@ -41,7 +41,7 @@
 
 func bufferNoEscape4() []byte {
 	var b bytes.Buffer
-	b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m·3\]$" "inlining call to bytes.\(\*Buffer\).Grow$"
+	b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m\]$" "inlining call to bytes.\(\*Buffer\).Grow$" "string\(.*\) escapes to heap"
 	useBuffer(&b)
 	return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$"
 }
diff --git a/test/float_lit3.go b/test/float_lit3.go
index c4d1aa5..850d02c 100644
--- a/test/float_lit3.go
+++ b/test/float_lit3.go
@@ -37,12 +37,11 @@
 
 	// If the compiler's internal floating point representation
 	// is shorter than 1024 bits, it cannot distinguish max64+ulp64/2-1 and max64+ulp64/2.
-	// gc uses fewer than 1024 bits, so allow it to print the overflow error for the -1 case.
 	float64(max64 + ulp64/2 - two1024/two256), // ok
-	float64(max64 + ulp64/2 - 1),              // GC_ERROR "constant 1\.79769e\+308 overflows float64"
+	float64(max64 + ulp64/2 - 1),              // ok
 	float64(max64 + ulp64/2),                  // ERROR "constant 1\.79769e\+308 overflows float64"
 
 	float64(-max64 - ulp64/2 + two1024/two256), // ok
-	float64(-max64 - ulp64/2 + 1),              // GC_ERROR "constant -1\.79769e\+308 overflows float64"
+	float64(-max64 - ulp64/2 + 1),              // ok
 	float64(-max64 - ulp64/2),                  // ERROR "constant -1\.79769e\+308 overflows float64"
 }
diff --git a/test/inline.go b/test/inline.go
index d754f06..37965c0 100644
--- a/test/inline.go
+++ b/test/inline.go
@@ -58,7 +58,7 @@
 var somethingWrong error
 
 // local closures can be inlined
-func l(x, y int) (int, int, error) {
+func l(x, y int) (int, int, error) { // ERROR "can inline l"
 	e := func(err error) (int, int, error) { // ERROR "can inline l.func1" "func literal does not escape" "leaking param: err to result"
 		return 0, 0, err
 	}
@@ -90,19 +90,19 @@
 // make sure assignment inside closure is detected
 func o() int {
 	foo := func() int { return 1 } // ERROR "can inline o.func1" "func literal does not escape"
-	func(x int) {                  // ERROR "func literal does not escape"
+	func(x int) {                  // ERROR "can inline o.func2"
 		if x > 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 @@
 	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 @@
 	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 3f9451e..b705b97 100644
--- a/test/interface/explicit.go
+++ b/test/interface/explicit.go
@@ -100,6 +100,7 @@
 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 0000000..43e66a5
--- /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 3df7ab0..d52ce7f 100644
--- a/test/live.go
+++ b/test/live.go
@@ -718,5 +718,5 @@
 	}
 	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 0000000..d006d9c
--- /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 e0f2ed9..3345cfa 100644
--- a/test/nilptr3.go
+++ b/test/nilptr3.go
@@ -214,14 +214,6 @@
 	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 faa7b8c..8a3fa9b 100644
--- a/test/nosplit.go
+++ b/test/nosplit.go
@@ -353,7 +353,14 @@
 			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 3a87d02..57892f8 100644
--- a/test/reorder.go
+++ b/test/reorder.go
@@ -20,6 +20,8 @@
 	p7()
 	p8()
 	p9()
+	p10()
+	p11()
 }
 
 var gx []int
@@ -149,3 +151,17 @@
 		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 624f223..dba4d16 100644
--- a/test/run.go
+++ b/test/run.go
@@ -59,7 +59,7 @@
 
 	// 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 @@
 	// 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 @@
 					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 @@
 		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 @@
 				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 @@
 			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 @@
 			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 @@
 			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) 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 0000000..76f3fc9
--- /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"
+}