[dev.fuzz] all: merge master (af72ddf) into dev.fuzz

This now includes the fix in CL 350729, which means
we no longer need to skip the test in dev.fuzz.

Conflicts:

- src/cmd/compile/internal/noder/unified_test.go

Merge List:

+ 2021-09-20 af72ddfcd7 cmd/compile: extend dump-to-file to handle "genssa" (asm) case.
+ 2021-09-20 3c764babe7 cmd/go: write go.mod requirements more consistently for go 1.17+
+ 2021-09-20 6268468e02 cmd/link: generate DIE for types referenced only through dictionaries
+ 2021-09-20 6acac8b685 cmd/compile: delay all transforms for generic funcs/methods
+ 2021-09-20 988f18d61d go/types: export Named._Orig as Named.Origin
+ 2021-09-20 b6dddaccd7 cmd/compile: fix transform.AssignOp to deal with tricky case
+ 2021-09-20 9e60c37147 cmd/compile: document register-based ABI for ppc64
+ 2021-09-20 79159f2e83 cmd/compile: fix simplification rules on arm/arm64
+ 2021-09-20 eff27e858b cmd/compile: ensure constant shift amounts are in range for arm
+ 2021-09-20 9ebe7c8ec6 go/test: add a test for issue 48344
+ 2021-09-20 6f35430faa cmd/compile: allow rotates to be merged with logical ops on arm64
+ 2021-09-20 2d9b486420 cmd/compile: update doc at top of iexport.go on the changes for typeparams
+ 2021-09-20 a81b0dc6ee cmd/compile: rename instType -> instanceType
+ 2021-09-20 119213566a cmd/cgo: remove hardcoded '-pie' ldflag for linux/arm
+ 2021-09-20 a83a558733 cmd/compile: fix export/import of range loop.
+ 2021-09-19 315dbd10c9 cmd/compile: fold double negate on arm64
+ 2021-09-19 83b36ffb10 cmd/compile: implement constant rotates on arm64
+ 2021-09-19 771b8ea4f4 cmd/compile: fix missing markHiddenClosureDead in deadcode pass
+ 2021-09-18 c894b442d1 net/rpc: remove warnings on incompatible methods at registration
+ 2021-09-17 4b654c0eec cmd/compile: SSA ".this" variable
+ 2021-09-17 f01721efb9 cmd/compile: remove self copies in tail-call wrappers
+ 2021-09-17 163871feb1 time: re-add space-padded day of year to docs
+ 2021-09-17 ac7c34767d time: support fractional timezone minutes in MarshalBinary
+ 2021-09-17 07b30a4f77 cmd/compile: delay transformAssign if lhs/rhs have typeparam
+ 2021-09-17 c10b980220 cmd/compile: restore tail call for method wrappers
+ 2021-09-17 50e4508269 cmd/compile: fix import/export of Init and Def fields.
+ 2021-09-17 3fa35b5f97 go/types: ensure that we always get a new signature in expandNamed
+ 2021-09-17 3fa7dbeff5 cmd/go: fix GOARCH value in GOAMD64 docs
+ 2021-09-17 974b0166d6 syscall: implement Pipe using pipe2 syscall on all linux platforms
+ 2021-09-17 1a49dcb82f syscall: remove //sysnb comment generating Setreuid for linux/arm64
+ 2021-09-17 cea7a71d40 cmd/compile: fix generic type handling in crawler
+ 2021-09-17 74e384f50d internal/poll: inject a hook into the runtime finalizer to count the closed pipes
+ 2021-09-17 323c6f74d3 log: don't format if writing to io.Discard
+ 2021-09-17 7f36ef0aff cmd/compile/internal/noder: hide TestUnifiedCompare behind -cmp flag
+ 2021-09-17 70493b3eb0 runtime/cgo: save and restore X3 (aka GP) for crosscall1 on riscv64
+ 2021-09-17 6d02ce8584 runtime: fix prettyprinting of parametric types in gdb
+ 2021-09-17 6602c86a38 cmd/internal/obj/riscv: improve instruction validation
+ 2021-09-17 14e812bfc5 syscall: do not use handle lists on windows when NoInheritHandles is true
+ 2021-09-16 8d2a9c32a2 all: remove incorrectly repeated words in comments
+ 2021-09-16 af9da137a9 A+C: update name to real name and add to AUTHORS
+ 2021-09-16 265b59aefd cmd/cgo: for godefs, don't let field prefix removal cause duplicates
+ 2021-09-16 4efdaa7bc7 testing: skip panics when picking the line number for decoration
+ 2021-09-16 e09dcc211a go/types, types2: add an additional shift test case
+ 2021-09-16 5402b4376c spec: fix incorrect type in a shift example
+ 2021-09-16 d09e09bc61 cmd/compile: fixing writebarrier.go for -G=3
+ 2021-09-16 bcdc61d830 cmd/compile: preserve statements better in expandCalls
+ 2021-09-16 48e2b1ea91 cmd/compile: fix LocResults formatting
+ 2021-09-16 b1bedc0774 cmd/go: add GOAMD64 environment variable
+ 2021-09-16 04f5116c98 cmd/go: clean paths before checking same directory
+ 2021-09-16 e7dbe3908e cmd/cgo: add missing tab in exports for a result of void
+ 2021-09-15 cfa233d76b cmd/compile: remove unneeded early transforms, with dictionary change
+ 2021-09-15 59a9a035ff cmd/compile: switch to computing dict format on instantiated functions
+ 2021-09-15 0edc6c4fa0 cmd/internal/obj/ppc64: generate prologue code compatible with new ABI
+ 2021-09-15 03df68d3c3 runtime: fix setting of cpu features for amd64
+ 2021-09-15 6196979365 cmd/go/internal/modload: prevent tidy downgrading disambiguating modules
+ 2021-09-15 72bb8185b5 cmd/compile: emit DWARF info about dictionary entries
+ 2021-09-15 5b48fca1fa cmd/compile: mark wrapper functions with DW_AT_trampoline
+ 2021-09-15 e4dfd788e6 go/internal/gcimporter,cmd/compile: minor clean-up in iimport.go
+ 2021-09-15 4847c47cb8 cmd/compile/internal/types2: eliminate Named.instPos
+ 2021-09-15 3100f54f20 cmd/compile/internal/types2: merge Named type loading and expansion
+ 2021-09-15 738cebb174 cmd/compile/internal/types2: implement Identical for *Union types
+ 2021-09-15 b26d325cb1 cmd/compile/internal/types2: remove some unnecessary loading/expansion of Named types
+ 2021-09-15 9fc28892cb cmd/compile/internal/types2: export TypeHash, return value without blanks
+ 2021-09-15 2da3375e9b runtime: in adjustTimers back up as far as necessary
+ 2021-09-15 c7f2f51fed cmd/go: remove subcommand prefix from error messages
+ 2021-09-15 0bb40b08c4 go/types: implement Identical for *Union types
+ 2021-09-15 cb4e1de021 go/types: minor cleanup of instantiation
+ 2021-09-15 a0f3129466 go/types: instantiate methods when instantiating Named types
+ 2021-09-14 bf26e43d0f go/types: eliminate Named.instPos
+ 2021-09-14 2933c451a0 go/types: merge Named type loading and expansion
+ 2021-09-14 137543bb93 cmd/compile: set IsShape based on type being in the Shapes pkg
+ 2021-09-14 3a72175cdc cmd/compile: fix test/typeparam/mdempsky/4.go for -G=3
+ 2021-09-14 b2c04f0d48 runtime: avoid loop variable capture in test
+ 2021-09-14 181e8cde30 go/internal/gcimporter: remove outdated comment
+ 2021-09-14 8699425b55 syscall: remove use of IN_KUBERNETES in test
+ 2021-09-14 b3c6de9dcd cmd/internal/obj/ppc64: allow VR register arguments to VS registers
+ 2021-09-14 ee91bb8319 cmd/compile: prevent typecheck importer reading type parameter twice
+ 2021-09-14 2953cd0083 go/internal/gcimporter: prevent importReader reading type parameter twice
+ 2021-09-14 b8c802b116 cmd/compile: prevent importReader reading type parameter twice
+ 2021-09-14 4a4221e818 all: remove some unused code
+ 2021-09-14 71adc658de runtime: change time.now to ABIInternal
+ 2021-09-14 146e8d4994 reflect: use Value.Len instead of conversion to slice header
+ 2021-09-13 9a58aa267e spec: fix prose about terminating statements
+ 2021-09-13 42057e9848 cmd/compile: save the note of fields when translating struct
+ 2021-09-13 960d036f8f cmd/go: add missing parenthesis in a call to "PrintVersion"
+ 2021-09-13 81a4fe6fd2 cmd/link/internal/ld: re-enable DWARF tests on solaris/illumos
+ 2021-09-13 f93a63addb reflect: add a floating point section to DeepEqual tests
+ 2021-09-13 a0c409cbc8 reflect: add fast paths for common, simple Kinds to DeepEqual
+ 2021-09-13 ac40c9872f reflect: fix _faststr optimization
+ 2021-09-13 c8a58f29dc cmd/go: add test to check for a potential workspace loading issue
+ 2021-09-13 e74e363a6b strings: add Clone function
+ 2021-09-13 bced369a50 cmd/link: minor code cleanup in dwarf gen
+ 2021-09-13 c3b217a0e5 cmd/go: document 'go install cmd@version' ignores vendor directories
+ 2021-09-12 ad97d204f0 go/types: remove some unnecessary loading/expansion of Named types
+ 2021-09-12 0d8a4bfc96 bufio: add Writer.AvailableBuffer
+ 2021-09-11 23832ba2e2 reflect: optimize for maps with string keys
+ 2021-09-11 a50225a0dc bufio: make Reader.Reset and Writer.Reset work on the zero value
+ 2021-09-10 cf2fe5d6f1 doc/asm: fix HTML markup
+ 2021-09-10 1bf2cd1291 debug/elf: retain original error message when getSymbols fails.
+ 2021-09-10 5a4b9f9494 time: reference -tags=timetzdata in testing panic
+ 2021-09-10 025308fe08 testing: increase alternation precedence
+ 2021-09-10 5a94a90d84 cmd/compile/internal/types2: better error message for invalid array decls
+ 2021-09-10 da1aa65053 cmd/compile/internal/syntax: correct follow token for type parameter lists
+ 2021-09-10 96ab854ab0 cmd/compile/internal: better AST line highlight in ssa.html
+ 2021-09-10 90c5660616 embed: guarantee the returned file of FS.Open implements io.Seeker
+ 2021-09-10 c69f5c0d76 cmd/compile: add support for Abs and Copysign intrinsics on riscv64
+ 2021-09-10 2091bd3f26 cmd/compile: simiplify arm64 bitfield optimizations
+ 2021-09-09 b32209d22d cmd/compile: fix test case for unified IR (fix build)
+ 2021-09-09 1a708bcf1d cmd/compile: don't crash while reporting invalid alias cycle
+ 2021-09-09 426ff3746f cmd/cgo, runtime/cgo: avoid GCC/clang conversion warnings
+ 2021-09-09 73483df406 cmd/compile/internal/syntax: better error message for missing type constraint
+ 2021-09-09 e1c3f2158f time: propagate "," separator for fractional seconds into Format
+ 2021-09-09 c981874a5a cmd/compile: fix implement for closure in a global assignment
+ 2021-09-09 2c4f389c02 cmd/link: enable internal linker in more cases for ppc64le
+ 2021-09-09 fb84e99eb7 test: add compiler regress tests for #46461
+ 2021-09-09 b9e1a24581 cmd/compile: fix case where init info of OAS node is dropped
+ 2021-09-09 f9271e4f85 go/types, types2: rename RParams -> RecvTypeParams
+ 2021-09-09 ea434450c2 reflect: add hooks for dealing with narrow width floats
+ 2021-09-09 a53e3d5f88 net: deprecate (net.Error).Temporary
+ 2021-09-09 19457a58e5 cmd/compile: stenciled conversions might be NOPs
+ 2021-09-09 a295b3cec8 test: re-enable AsmCheck tests for types2-based frontends
+ 2021-09-09 66f0d35f71 go/types: reduce number of delayed functions
+ 2021-09-09 d2a77f1c76 go/types: handle recursive type parameter constraints
+ 2021-09-09 9e1eea6f8b go/types: detect constraint type inference cycles
+ 2021-09-09 b86e8dd0f3 test/typeparam: fix issue48094b test build
+ 2021-09-09 c84f3a4004 syscall: drop fallback to pipe in Pipe on linux/arm
+ 2021-09-09 376a079762 cmd/compile: fix unified IR panic when expanding nested inline function
+ 2021-09-09 6edc57983a internal/poll: report open fds when TestSplicePipePool fails
+ 2021-09-09 2481f6e367 cmd/compile: fix wrong instantiated type for embedded receiver
+ 2021-09-09 d62866ef79 cmd/compile: move checkptr alignment to SSA generation
+ 2021-09-09 8fad81cd62 cmd/compile: fold handling OCONV logic to separate function
+ 2021-09-09 9cbc76bdf9 cmd/internal/obj/arm64: add checks for incorrect use of REGTMP register
+ 2021-09-09 42563f89d7 cmd/compile: remove 'ext' fields from unified IR reader/writer types
+ 2021-09-09 4c52eac49b cmd/compile: simplify value coding for unified IR
+ 2021-09-09 e30a09013b cmd/compile: extrapolate $GOROOT in unified IR
+ 2021-09-08 a1f6208e56 go/types, types2: add Environment to Config
+ 2021-09-08 f5f8a911d8 cmd/compile/internal/types2: spell out 'Type' in type parameter APIs
+ 2021-09-08 bff39cf6cb cmd/compile: add automated rewrite cycle detection
+ 2021-09-08 b61e1ed863 cmd/compile/internal/types2: temporarily pin the Checker to Interface during checking
+ 2021-09-08 47f3e1e02c cmd/compile/internal/types2: move NewTypeParam off of Checker
+ 2021-09-08 ccc927b8f6 cmd/compile/internal/types2: move typeHash to environment.go
+ 2021-09-08 30e9bfbcef cmd/compile/internal/types2: implement deduplication of instances using the Environment
+ 2021-09-08 0406d3a8e5 go/ast: rename MultiIndexExpr to IndexListExpr

Change-Id: I7f917d45b0507c122c212305144b0b455618ff54
diff --git a/AUTHORS b/AUTHORS
index 95d3158..8d8d836 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1479,6 +1479,7 @@
 Zhongtao Chen <chenzhongtao@126.com>
 Zhou Peng <p@ctriple.cn>
 Ziad Hatahet <hatahet@gmail.com>
+Zizhao Zhang <btw515wolf2@gmail.com>
 Zorion Arrizabalaga <zorionk@gmail.com>
 Максим Федосеев <max.faceless.frei@gmail.com>
 Роман Хавроненко <hagen1778@gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 1984d44..74d4687 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1109,7 +1109,6 @@
 Ian Leue <ian@appboy.com>
 Ian Mckay <iann0036@gmail.com>
 Ian Tay <iantay@google.com>
-Ian Woolf <btw515wolf2@gmail.com>
 Ian Zapolsky <ianzapolsky@gmail.com>
 Ibrahim AshShohail <ibra.sho@gmail.com>
 Icarus Sparry <golang@icarus.freeuk.com>
@@ -2749,6 +2748,7 @@
 Zhou Peng <p@ctriple.cn>
 Ziad Hatahet <hatahet@gmail.com>
 Ziheng Liu <lzhfromustc@gmail.com>
+Zizhao Zhang <btw515wolf2@gmail.com>
 Zorion Arrizabalaga <zorionk@gmail.com>
 Zvonimir Pavlinovic <zpavlinovic@google.com>
 Zyad A. Ali <zyad.ali.me@gmail.com>
diff --git a/doc/asm.html b/doc/asm.html
index 51f85eb..f7787a4 100644
--- a/doc/asm.html
+++ b/doc/asm.html
@@ -125,8 +125,8 @@
 One is in constant evaluation.
 Constant expressions in the assembler are parsed using Go's operator
 precedence, not the C-like precedence of the original.
-Thus <code>3&amp;1<<2</code> is 4, not 0—it parses as <code>(3&amp;1)<<2</code>
-not <code>3&amp;(1<<2)</code>.
+Thus <code>3&amp;1&lt;&lt;2</code> is 4, not 0—it parses as <code>(3&amp;1)&lt;&lt;2</code>
+not <code>3&amp;(1&lt;&lt;2)</code>.
 Also, constants are always evaluated as 64-bit unsigned integers.
 Thus <code>-2</code> is not the integer value minus two,
 but the unsigned 64-bit integer with the same bit pattern.
@@ -914,8 +914,6 @@
 Reference: <a href="/pkg/cmd/internal/obj/ppc64">Go PPC64 Assembly Instructions Reference Manual</a>
 </p>
 
-</ul>
-
 <h3 id="s390x">IBM z/Architecture, a.k.a. s390x</h3>
 
 <p>
diff --git a/doc/go_spec.html b/doc/go_spec.html
index 3e97974..c8051f5 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
 <!--{
 	"Title": "The Go Programming Language Specification",
-	"Subtitle": "Version of Aug 23, 2021",
+	"Subtitle": "Version of Sep 16, 2021",
 	"Path": "/ref/spec"
 }-->
 
@@ -3614,7 +3614,7 @@
 var j int32 = 1&lt;&lt;s             // 1 has type int32; j == 0
 var k = uint64(1&lt;&lt;s)           // 1 has type uint64; k == 1&lt;&lt;33
 var m int = 1.0&lt;&lt;s             // 1.0 has type int; m == 1&lt;&lt;33
-var n = 1.0&lt;&lt;s == j            // 1.0 has type int; n == true
+var n = 1.0&lt;&lt;s == j            // 1.0 has type int32; n == true
 var o = 1&lt;&lt;s == 2&lt;&lt;s           // 1 and 2 have type int; o == false
 var p = 1&lt;&lt;s == 1&lt;&lt;33          // 1 has type int; p == true
 var u = 1.0&lt;&lt;s                 // illegal: 1.0 has type float64, cannot shift
@@ -4561,9 +4561,8 @@
 <h3 id="Terminating_statements">Terminating statements</h3>
 
 <p>
-A <i>terminating statement</i> prevents execution of all statements that lexically
-appear after it in the same <a href="#Blocks">block</a>. The following statements
-are terminating:
+A <i>terminating statement</i> interrupts the regular flow of control in
+a <a href="#Blocks">block</a>. The following statements are terminating:
 </p>
 
 <ol>
diff --git a/misc/cgo/testgodefs/testdata/issue48396.go b/misc/cgo/testgodefs/testdata/issue48396.go
new file mode 100644
index 0000000..d4c1924
--- /dev/null
+++ b/misc/cgo/testgodefs/testdata/issue48396.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.
+//
+// +build ignore
+
+package main
+
+/*
+// from <linux/kcm.h>
+struct issue48396 {
+	int fd;
+	int bpf_fd;
+};
+*/
+import "C"
+
+type Issue48396 C.struct_issue48396
diff --git a/misc/cgo/testgodefs/testdata/main.go b/misc/cgo/testgodefs/testdata/main.go
index 4a3f6a7..5c670f3 100644
--- a/misc/cgo/testgodefs/testdata/main.go
+++ b/misc/cgo/testgodefs/testdata/main.go
@@ -28,6 +28,9 @@
 // Test that #define'd type is fully defined
 var _ = issue38649{X: 0}
 
+// Test that prefixes do not cause duplicate field names.
+var _ = Issue48396{Fd: 1, Bpf_fd: 2}
+
 func main() {
 	pass := true
 
diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go
index aae3404..7628ffc 100644
--- a/misc/cgo/testgodefs/testgodefs_test.go
+++ b/misc/cgo/testgodefs/testgodefs_test.go
@@ -25,6 +25,7 @@
 	"issue37621",
 	"issue38649",
 	"issue39534",
+	"issue48396",
 }
 
 func TestGoDefs(t *testing.T) {
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go
index ec928e7..506b84f 100644
--- a/src/bufio/bufio.go
+++ b/src/bufio/bufio.go
@@ -68,7 +68,12 @@
 
 // Reset discards any buffered data, resets all state, and switches
 // the buffered reader to read from r.
+// Calling Reset on the zero value of Reader initializes the internal buffer
+// to the default size.
 func (b *Reader) Reset(r io.Reader) {
+	if b.buf == nil {
+		b.buf = make([]byte, defaultBufSize)
+	}
 	b.reset(b.buf, r)
 }
 
@@ -590,7 +595,12 @@
 
 // Reset discards any unflushed buffered data, clears any error, and
 // resets b to write its output to w.
+// Calling Reset on the zero value of Writer initializes the internal buffer
+// to the default size.
 func (b *Writer) Reset(w io.Writer) {
+	if b.buf == nil {
+		b.buf = make([]byte, defaultBufSize)
+	}
 	b.err = nil
 	b.n = 0
 	b.wr = w
@@ -623,6 +633,14 @@
 // Available returns how many bytes are unused in the buffer.
 func (b *Writer) Available() int { return len(b.buf) - b.n }
 
+// AvailableBuffer returns an empty buffer with b.Available() capacity.
+// This buffer is intended to be appended to and
+// passed to an immediately succeeding Write call.
+// The buffer is only valid until the next write operation on b.
+func (b *Writer) AvailableBuffer() []byte {
+	return b.buf[b.n:][:0]
+}
+
 // Buffered returns the number of bytes that have been written into the current buffer.
 func (b *Writer) Buffered() int { return b.n }
 
diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go
index ebcc711..04a810c 100644
--- a/src/bufio/bufio_test.go
+++ b/src/bufio/bufio_test.go
@@ -10,6 +10,8 @@
 	"errors"
 	"fmt"
 	"io"
+	"math/rand"
+	"strconv"
 	"strings"
 	"testing"
 	"testing/iotest"
@@ -608,6 +610,37 @@
 	}
 }
 
+func TestWriterAppend(t *testing.T) {
+	got := new(bytes.Buffer)
+	var want []byte
+	rn := rand.New(rand.NewSource(0))
+	w := NewWriterSize(got, 64)
+	for i := 0; i < 100; i++ {
+		// Obtain a buffer to append to.
+		b := w.AvailableBuffer()
+		if w.Available() != cap(b) {
+			t.Fatalf("Available() = %v, want %v", w.Available(), cap(b))
+		}
+
+		// While not recommended, it is valid to append to a shifted buffer.
+		// This forces Write to copy the the input.
+		if rn.Intn(8) == 0 && cap(b) > 0 {
+			b = b[1:1:cap(b)]
+		}
+
+		// Append a random integer of varying width.
+		n := int64(rn.Intn(1 << rn.Intn(30)))
+		want = append(strconv.AppendInt(want, n, 10), ' ')
+		b = append(strconv.AppendInt(b, n, 10), ' ')
+		w.Write(b)
+	}
+	w.Flush()
+
+	if !bytes.Equal(got.Bytes(), want) {
+		t.Errorf("output mismatch:\ngot  %s\nwant %s", got.Bytes(), want)
+	}
+}
+
 // Check that write errors are returned properly.
 
 type errorWriterTest struct {
@@ -1312,6 +1345,7 @@
 	if string(buf) != "foo" {
 		t.Errorf("buf = %q; want foo", buf)
 	}
+
 	r.Reset(strings.NewReader("bar bar"))
 	all, err := io.ReadAll(r)
 	if err != nil {
@@ -1320,12 +1354,23 @@
 	if string(all) != "bar bar" {
 		t.Errorf("ReadAll = %q; want bar bar", all)
 	}
+
+	*r = Reader{} // zero out the Reader
+	r.Reset(strings.NewReader("bar bar"))
+	all, err = io.ReadAll(r)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if string(all) != "bar bar" {
+		t.Errorf("ReadAll = %q; want bar bar", all)
+	}
 }
 
 func TestWriterReset(t *testing.T) {
-	var buf1, buf2 bytes.Buffer
+	var buf1, buf2, buf3 bytes.Buffer
 	w := NewWriter(&buf1)
 	w.WriteString("foo")
+
 	w.Reset(&buf2) // and not flushed
 	w.WriteString("bar")
 	w.Flush()
@@ -1335,6 +1380,17 @@
 	if buf2.String() != "bar" {
 		t.Errorf("buf2 = %q; want bar", buf2.String())
 	}
+
+	*w = Writer{}  // zero out the Writer
+	w.Reset(&buf3) // and not flushed
+	w.WriteString("bar")
+	w.Flush()
+	if buf1.String() != "" {
+		t.Errorf("buf1 = %q; want empty", buf1.String())
+	}
+	if buf3.String() != "bar" {
+		t.Errorf("buf3 = %q; want bar", buf3.String())
+	}
 }
 
 func TestReaderDiscard(t *testing.T) {
diff --git a/src/bufio/example_test.go b/src/bufio/example_test.go
index 8885d40..a864d11 100644
--- a/src/bufio/example_test.go
+++ b/src/bufio/example_test.go
@@ -20,6 +20,18 @@
 	// Output: Hello, world!
 }
 
+func ExampleWriter_AvailableBuffer() {
+	w := bufio.NewWriter(os.Stdout)
+	for _, i := range []int64{1, 2, 3, 4} {
+		b := w.AvailableBuffer()
+		b = strconv.AppendInt(b, i, 10)
+		b = append(b, ' ')
+		w.Write(b)
+	}
+	w.Flush()
+	// Output: 1 2 3 4
+}
+
 // The simplest use of a Scanner, to read standard input as a set of lines.
 func ExampleScanner_lines() {
 	scanner := bufio.NewScanner(os.Stdin)
diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s
index 8b12b16..7b00643 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64error.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64error.s
@@ -406,12 +406,12 @@
 	VBIF	V0.D2, V1.D2, V2.D2                              // ERROR "invalid arrangement"
 	VUADDW	V9.B8, V12.H8, V14.B8                            // ERROR "invalid arrangement"
 	VUADDW2	V9.B8, V12.S4, V14.S4                            // ERROR "operand mismatch"
-	VUMAX   V1.D2, V2.D2, V3.D2                              // ERROR "invalid arrangement"
-	VUMIN   V1.D2, V2.D2, V3.D2                              // ERROR "invalid arrangement"
+	VUMAX	V1.D2, V2.D2, V3.D2                              // ERROR "invalid arrangement"
+	VUMIN	V1.D2, V2.D2, V3.D2                              // ERROR "invalid arrangement"
 	VUMAX	V1.B8, V2.B8, V3.B16                             // ERROR "operand mismatch"
 	VUMIN	V1.H4, V2.S4, V3.H4                              // ERROR "operand mismatch"
 	VSLI	$64, V7.D2, V8.D2                                // ERROR "shift out of range"
-	VUSRA   $0, V7.D2, V8.D2                                 // ERROR "shift out of range"
+	VUSRA	$0, V7.D2, V8.D2                                 // ERROR "shift out of range"
 	CASPD	(R3, R4), (R2), (R8, R9)                         // ERROR "source register pair must start from even register"
 	CASPD	(R2, R3), (R2), (R9, R10)                        // ERROR "destination register pair must start from even register"
 	CASPD	(R2, R4), (R2), (R8, R9)                         // ERROR "source register pair must be contiguous"
@@ -419,8 +419,15 @@
 	ADD	R1>>2, RSP, R3                                   // ERROR "illegal combination"
 	ADDS	R2<<3, R3, RSP                                   // ERROR "unexpected SP reference"
 	CMP	R1<<5, RSP                                       // ERROR "the left shift amount out of range 0 to 4"
-	MOVD.P  y+8(FP), R1                                      // ERROR "illegal combination"
-	MOVD.W  x-8(SP), R1                                      // ERROR "illegal combination"
-	LDP.P   x+8(FP), (R0, R1)                                // ERROR "illegal combination"
-	LDP.W   x+8(SP), (R0, R1)                                // ERROR "illegal combination"
+	MOVD.P	y+8(FP), R1                                      // ERROR "illegal combination"
+	MOVD.W	x-8(SP), R1                                      // ERROR "illegal combination"
+	LDP.P	x+8(FP), (R0, R1)                                // ERROR "illegal combination"
+	LDP.W	x+8(SP), (R0, R1)                                // ERROR "illegal combination"
+	ADD	$0x1234567, R27, R3                              // ERROR "cannot use REGTMP as source"
+	ADD	$0x3fffffffc000, R27, R5                         // ERROR "cannot use REGTMP as source"
+	AND	$0x22220000, R27, R4                             // ERROR "cannot use REGTMP as source"
+	ANDW	$0x6006000060060, R27, R5                        // ERROR "cannot use REGTMP as source"
+	STP	(R3, R4), 0x1234567(R27)                         // ERROR "REGTMP used in large offset store"
+	LDP	0x1234567(R27), (R3, R4)                         // ERROR "REGTMP used in large offset load"
+	STP	(R26, R27), 700(R2)                              // ERROR "cannot use REGTMP as source"
 	RET
diff --git a/src/cmd/asm/internal/asm/testdata/ppc64.s b/src/cmd/asm/internal/asm/testdata/ppc64.s
index b6c0aa5..28ceb62 100644
--- a/src/cmd/asm/internal/asm/testdata/ppc64.s
+++ b/src/cmd/asm/internal/asm/testdata/ppc64.s
@@ -649,6 +649,8 @@
 	LXVB16X (R3)(R4), VS1           // 7c241ed8
 	LXVW4X (R3)(R4), VS1            // 7c241e18
 	LXV 16(R3), VS1                 // f4230011
+	LXV 16(R3), VS33                // f4230019
+	LXV 16(R3), V1                  // f4230019
 	LXVL R3, R4, VS1                // 7c23221a
 	LXVLL R3, R4, VS1               // 7c23225a
 	LXVX R3, R4, VS1                // 7c232218
@@ -668,8 +670,13 @@
 	MTFPRD R3, F0                   // 7c030166
 	MFVRD V0, R3                    // 7c030067
 	MFVSRLD VS63,R4                 // 7fe40267
+	MFVSRLD V31,R4                  // 7fe40267
 	MFVSRWZ VS33,R4                 // 7c2400e7
+	MFVSRWZ V1,R4                   // 7c2400e7
 	MTVSRD R3, VS1                  // 7c230166
+	MTVSRDD R3, R4, VS1             // 7c232366
+	MTVSRDD R3, R4, VS33            // 7c232367
+	MTVSRDD R3, R4, V1              // 7c232367
 	MTVRD R3, V13                   // 7da30167
 	MTVSRWA R4, VS31                // 7fe401a6
 	MTVSRWS R4, VS32                // 7c040327
@@ -678,6 +685,8 @@
 	XXBRW VS1, VS2                  // f04f0f6c
 	XXBRH VS2, VS3                  // f067176c
 	XXLAND VS1, VS2, VS3            // f0611410
+	XXLAND V1, V2, V3               // f0611417
+	XXLAND VS33, VS34, VS35         // f0611417
 	XXLANDC VS1, VS2, VS3           // f0611450
 	XXLEQV VS0, VS1, VS2            // f0400dd0
 	XXLNAND VS0, VS1, VS2           // f0400d90
@@ -687,11 +696,17 @@
 	XXLORQ VS1, VS2, VS3            // f0611490
 	XXLXOR VS1, VS2, VS3            // f06114d0
 	XXSEL VS1, VS2, VS3, VS4        // f08110f0
+	XXSEL VS33, VS34, VS35, VS36    // f08110ff
+	XXSEL V1, V2, V3, V4            // f08110ff
 	XXMRGHW VS1, VS2, VS3           // f0611090
 	XXMRGLW VS1, VS2, VS3           // f0611190
 	XXSPLTW VS1, $1, VS2            // f0410a90
+	XXSPLTW VS33, $1, VS34          // f0410a93
+	XXSPLTW V1, $1, V2              // f0410a93
 	XXPERM VS1, VS2, VS3            // f06110d0
 	XXSLDWI VS1, VS2, $1, VS3       // f0611110
+	XXSLDWI V1, V2, $1, V3          // f0611117
+	XXSLDWI VS33, VS34, $1, VS35    // f0611117
 	XSCVDPSP VS1, VS2               // f0400c24
 	XVCVDPSP VS1, VS2               // f0400e24
 	XSCVSXDDP VS1, VS2              // f0400de0
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s
index 1977d92..64b94a2 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64.s
@@ -382,10 +382,12 @@
 	SNEZ	X15, X15				// b337f000
 
 	// F extension
+	FABSS	F0, F1					// d3200020
 	FNEGS	F0, F1					// d3100020
 	FNES	F0, F1, X7				// d3a300a093c31300
 
 	// D extension
+	FABSD	F0, F1					// d3200022
 	FNEGD	F0, F1					// d3100022
 	FNED	F0, F1, X5				// d3a200a293c21200
 	FLTD	F0, F1, X5				// d39200a2
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 92adb1e..6b3112b 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -3030,6 +3030,31 @@
 // so that all fields are exported.
 func godefsFields(fld []*ast.Field) {
 	prefix := fieldPrefix(fld)
+
+	// Issue 48396: check for duplicate field names.
+	if prefix != "" {
+		names := make(map[string]bool)
+	fldLoop:
+		for _, f := range fld {
+			for _, n := range f.Names {
+				name := n.Name
+				if name == "_" {
+					continue
+				}
+				if name != prefix {
+					name = strings.TrimPrefix(n.Name, prefix)
+				}
+				name = upper(name)
+				if names[name] {
+					// Field name conflict: don't remove prefix.
+					prefix = ""
+					break fldLoop
+				}
+				names[name] = true
+			}
+		}
+	}
+
 	npad := 0
 	for _, f := range fld {
 		for _, n := range f.Names {
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 3badd73..93cc0c6 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -1054,9 +1054,10 @@
 
 		fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
 
+		fmt.Fprintf(fgo2, "\t")
+
 		if gccResult != "void" {
 			// Write results back to frame.
-			fmt.Fprintf(fgo2, "\t")
 			forFieldList(fntype.Results,
 				func(i int, aname string, atype ast.Expr) {
 					if i > 0 {
@@ -1458,10 +1459,10 @@
   (have a negative array count) and an inscrutable error will come
   out of the compiler and hopefully mention "name".
 */
-#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1];
+#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2UL+1UL];
 
 /* Check at compile time that the sizes we use match our expectations. */
-#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n)
+#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), (size_t)n, _cgo_sizeof_##t##_is_not_##n)
 
 __cgo_size_assert(char, 1)
 __cgo_size_assert(short, 2)
diff --git a/src/cmd/compile/abi-internal.md b/src/cmd/compile/abi-internal.md
index 3619aea..50d8ed9 100644
--- a/src/cmd/compile/abi-internal.md
+++ b/src/cmd/compile/abi-internal.md
@@ -627,6 +627,105 @@
 Functions are allowed to modify it between calls (as long as they
 restore it), but as of this writing Go code never does.
 
+### ppc64 architecture
+
+The ppc64 architecture uses R3 – R10 and R14 – R17 for integer arguments
+and results.
+
+It uses F1 – F12 for floating-point arguments and results.
+
+Register R31 is a permanent scratch register in Go.
+
+Special-purpose registers used within Go generated code and Go
+assembly code are as follows:
+
+| Register | Call meaning | Return meaning | Body meaning |
+| --- | --- | --- | --- |
+| R0  | Zero value | Same | Same |
+| R1  | Stack pointer | Same | Same |
+| R2  | TOC register | Same | Same |
+| R11 | Closure context pointer | Scratch | Scratch |
+| R12 | Function address on indirect calls | Scratch | Scratch |
+| R13 | TLS pointer | Same | Same |
+| R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero |
+| R30 | Current goroutine | Same | Same |
+| R31 | Scratch | Scratch | Scratch |
+| LR  | Link register | Link register | Scratch |
+*Rationale*: These register meanings are compatible with Go’s
+stack-based calling convention.
+
+The link register, LR, holds the function return
+address at the function entry and is set to the correct return
+address before exiting the function. It is also used
+in some cases as the function address when doing an indirect call.
+
+The register R2 contains the address of the TOC (table of contents) which
+contains data or code addresses used when generating position independent
+code. Non-Go code generated when using cgo contains TOC-relative addresses
+which depend on R2 holding a valid TOC. Go code compiled with -shared or
+-dynlink initializes and maintains R2 and uses it in some cases for
+function calls; Go code compiled without these options does not modify R2.
+
+When making a function call R12 contains the function address for use by the
+code to generate R2 at the beginning of the function. R12 can be used for
+other purposes within the body of the function, such as trampoline generation.
+
+R20 and R21 are used in duffcopy and duffzero which could be generated
+before arguments are saved so should not be used for register arguments.
+
+The Count register CTR can be used as the call target for some branch instructions.
+It holds the return address when preemption has occurred.
+
+On PPC64 when a float32 is loaded it becomes a float64 in the register, which is
+different from other platforms and that needs to be recognized by the internal
+implementation of reflection so that float32 arguments are passed correctly.
+
+Registers R18 - R29 and F13 - F31 are considered scratch registers.
+
+#### Stack layout
+
+The stack pointer, R1, grows down and is aligned to 8 bytes in Go, but changed
+to 16 bytes when calling cgo.
+
+A function's stack frame, after the frame is created, is laid out as
+follows:
+
+    +------------------------------+
+    | ... locals ...               |
+    | ... outgoing arguments ...   |
+    | 24  TOC register R2 save     | When compiled with -shared/-dynlink
+    | 16  Unused in Go             | Not used in Go
+    |  8  CR save                  | nonvolatile CR fields
+    |  0  return PC                | ← R1 points to
+    +------------------------------+ ↓ lower addresses
+
+The "return PC" is loaded to the link register, LR, as part of the
+ppc64 `BL` operations.
+
+On entry to a non-leaf function, the stack frame size is subtracted from R1 to
+create its stack frame, and saves the value of LR at the bottom of the frame.
+
+A leaf function that does not require any stack space does not modify R1 and
+does not save LR.
+
+*NOTE*: We might need to save the frame pointer on the stack as
+in the PPC64 ELF v2 ABI so Go can inter-operate with platform debuggers
+and profilers.
+
+This stack layout is used by both register-based (ABIInternal) and
+stack-based (ABI0) calling conventions.
+
+#### Flags
+
+The condition register consists of 8 condition code register fields
+CR0-CR7. Go generated code only sets and uses CR0, commonly set by
+compare functions and use to determine the target of a conditional
+branch. The generated code does not set or use CR1-CR7.
+
+The floating point status and control register (FPSCR) is initialized
+to 0 by the kernel at startup of the Go program and not changed by
+the Go generated code.
+
 ## Future directions
 
 ### Spill path improvements
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index fc547eb..30131bd 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -1008,7 +1008,7 @@
 		}
 		r := v.Reg()
 		getgFromTLS(s, r)
-	case ssa.OpAMD64CALLstatic:
+	case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail:
 		if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
 			// zeroing X15 when entering ABIInternal from ABI0
 			if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
@@ -1017,6 +1017,10 @@
 			// set G register from TLS
 			getgFromTLS(s, x86.REG_R14)
 		}
+		if v.Op == ssa.OpAMD64CALLtail {
+			s.TailCall(v)
+			break
+		}
 		s.Call(v)
 		if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
 			// zeroing X15 when entering ABIInternal from ABI0
@@ -1314,22 +1318,9 @@
 			p.To.Type = obj.TYPE_BRANCH
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 	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
-			if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
-				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
-		p.To.Sym = b.Aux.(*obj.LSym)
 
 	case ssa.BlockAMD64EQF:
 		s.CombJump(b, next, &eqfJumps)
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 4b083ce..8aac80a 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -696,6 +696,8 @@
 		p.To.Reg = v.Reg()
 	case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter:
 		s.Call(v)
+	case ssa.OpARMCALLtail:
+		s.TailCall(v)
 	case ssa.OpARMCALLudiv:
 		p := s.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
@@ -936,17 +938,11 @@
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
 
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
 
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.ARET)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
-
 	case ssa.BlockARMEQ, ssa.BlockARMNE,
 		ssa.BlockARMLT, ssa.BlockARMGE,
 		ssa.BlockARMLE, ssa.BlockARMGT,
diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go
index b985246..4770a0c 100644
--- a/src/cmd/compile/internal/arm64/ssa.go
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -315,6 +315,8 @@
 		genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
 	case ssa.OpARM64MVNshiftRA, ssa.OpARM64NEGshiftRA:
 		genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
+	case ssa.OpARM64MVNshiftRO:
+		genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt)
 	case ssa.OpARM64ADDshiftLL,
 		ssa.OpARM64SUBshiftLL,
 		ssa.OpARM64ANDshiftLL,
@@ -342,6 +344,13 @@
 		ssa.OpARM64ORNshiftRA,
 		ssa.OpARM64BICshiftRA:
 		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
+	case ssa.OpARM64ANDshiftRO,
+		ssa.OpARM64ORshiftRO,
+		ssa.OpARM64XORshiftRO,
+		ssa.OpARM64EONshiftRO,
+		ssa.OpARM64ORNshiftRO,
+		ssa.OpARM64BICshiftRO:
+		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt)
 	case ssa.OpARM64MOVDconst:
 		p := s.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_CONST
@@ -389,6 +398,8 @@
 		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt)
 	case ssa.OpARM64CMPshiftRA, ssa.OpARM64CMNshiftRA, ssa.OpARM64TSTshiftRA:
 		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt)
+	case ssa.OpARM64TSTshiftRO:
+		genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_ROR, v.AuxInt)
 	case ssa.OpARM64MOVDaddr:
 		p := s.Prog(arm64.AMOVD)
 		p.From.Type = obj.TYPE_ADDR
@@ -1046,6 +1057,8 @@
 		p4.To.SetTarget(p)
 	case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
 		s.Call(v)
+	case ssa.OpARM64CALLtail:
+		s.TailCall(v)
 	case ssa.OpARM64LoweredWB:
 		p := s.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
@@ -1241,17 +1254,11 @@
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
 
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
 
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.ARET)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
-
 	case ssa.BlockARM64EQ, ssa.BlockARM64NE,
 		ssa.BlockARM64LT, ssa.BlockARM64GE,
 		ssa.BlockARM64LE, ssa.BlockARM64GT,
diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go
index 3658c89..65a48b6 100644
--- a/src/cmd/compile/internal/deadcode/deadcode.go
+++ b/src/cmd/compile/internal/deadcode/deadcode.go
@@ -117,6 +117,7 @@
 		}
 
 		if cut {
+			ir.VisitList((*nn)[i+1:len(*nn)], markHiddenClosureDead)
 			*nn = (*nn)[:i+1]
 			break
 		}
diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go
index 30472a9..3007262 100644
--- a/src/cmd/compile/internal/dwarfgen/dwarf.go
+++ b/src/cmd/compile/internal/dwarfgen/dwarf.go
@@ -217,6 +217,7 @@
 			DeclCol:       declpos.RelCol(),
 			InlIndex:      int32(inlIndex),
 			ChildIndex:    -1,
+			DictIndex:     n.DictIndex,
 		})
 		// Record go type of to insure that it gets emitted by the linker.
 		fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
@@ -374,6 +375,7 @@
 		DeclCol:       declpos.RelCol(),
 		InlIndex:      int32(inlIndex),
 		ChildIndex:    -1,
+		DictIndex:     n.DictIndex,
 	}
 }
 
@@ -478,6 +480,7 @@
 		DeclCol:     declpos.RelCol(),
 		InlIndex:    int32(inlIndex),
 		ChildIndex:  -1,
+		DictIndex:   n.DictIndex,
 	}
 	list := debug.LocationLists[varID]
 	if len(list) != 0 {
diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go
index c71848b..0afb5d6 100644
--- a/src/cmd/compile/internal/escape/stmt.go
+++ b/src/cmd/compile/internal/escape/stmt.go
@@ -180,7 +180,8 @@
 		e.goDeferStmt(n)
 
 	case ir.OTAILCALL:
-		// TODO(mdempsky): Treat like a normal call? esc.go used to just ignore it.
+		n := n.(*ir.TailCallStmt)
+		e.call(nil, n.Call)
 	}
 }
 
diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go
index 38cb8db..a92720d 100644
--- a/src/cmd/compile/internal/importer/iimport.go
+++ b/src/cmd/compile/internal/importer/iimport.go
@@ -72,7 +72,7 @@
 	structType
 	interfaceType
 	typeParamType
-	instType
+	instanceType
 	unionType
 )
 
@@ -314,21 +314,21 @@
 			tparams = r.tparamList()
 		}
 		sig := r.signature(nil)
-		sig.SetTParams(tparams)
+		sig.SetTypeParams(tparams)
 		r.declare(types2.NewFunc(pos, r.currPkg, name, sig))
 
 	case 'T', 'U':
-		var tparams []*types2.TypeParam
-		if tag == 'U' {
-			tparams = r.tparamList()
-		}
-
 		// Types can be recursive. We need to setup a stub
 		// declaration before recursing.
 		obj := types2.NewTypeName(pos, r.currPkg, name, nil)
 		named := types2.NewNamed(obj, nil, nil)
-		named.SetTParams(tparams)
+		// Declare obj before calling r.tparamList, so the new type name is recognized
+		// if used in the constraint of one of its own typeparams (see #48280).
 		r.declare(obj)
+		if tag == 'U' {
+			tparams := r.tparamList()
+			named.SetTypeParams(tparams)
+		}
 
 		underlying := r.p.typAt(r.uint64(), named).Underlying()
 		named.SetUnderlying(underlying)
@@ -343,13 +343,13 @@
 				// If the receiver has any targs, set those as the
 				// rparams of the method (since those are the
 				// typeparams being used in the method sig/body).
-				targs := baseType(msig.Recv().Type()).TArgs()
+				targs := baseType(msig.Recv().Type()).TypeArgs()
 				if targs.Len() > 0 {
 					rparams := make([]*types2.TypeParam, targs.Len())
 					for i := range rparams {
 						rparams[i] = types2.AsTypeParam(targs.At(i))
 					}
-					msig.SetRParams(rparams)
+					msig.SetRecvTypeParams(rparams)
 				}
 
 				named.AddMethod(types2.NewFunc(mpos, r.currPkg, mname, msig))
@@ -365,7 +365,7 @@
 		}
 		name0, sub := parseSubscript(name)
 		tn := types2.NewTypeName(pos, r.currPkg, name0, nil)
-		t := (*types2.Checker)(nil).NewTypeParam(tn, nil)
+		t := types2.NewTypeParam(tn, nil)
 		if sub == 0 {
 			errorf("missing subscript")
 		}
@@ -646,7 +646,7 @@
 		r.p.doDecl(pkg, name)
 		return r.p.tparamIndex[id]
 
-	case instType:
+	case instanceType:
 		if r.p.exportVersion < iexportVersionGenerics {
 			errorf("unexpected instantiation type")
 		}
@@ -661,7 +661,7 @@
 		baseType := r.typ()
 		// The imported instantiated type doesn't include any methods, so
 		// we must always use the methods of the base (orig) type.
-		// TODO provide a non-nil *Checker
+		// TODO provide a non-nil *Environment
 		t, _ := types2.Instantiate(nil, baseType, targs, false)
 		return t
 
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go
index 0733731..04d7518 100644
--- a/src/cmd/compile/internal/inline/inl.go
+++ b/src/cmd/compile/internal/inline/inl.go
@@ -544,6 +544,9 @@
 			call := call.(*ir.CallExpr)
 			call.NoInline = true
 		}
+	case ir.OTAILCALL:
+		n := n.(*ir.TailCallStmt)
+		n.Call.NoInline = true // Not inline a tail call for now. Maybe we could inline it just like RETURN fn(arg)?
 
 	// TODO do them here (or earlier),
 	// so escape analysis can avoid more heapmoves.
diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go
index baf0117..f526d98 100644
--- a/src/cmd/compile/internal/ir/expr.go
+++ b/src/cmd/compile/internal/ir/expr.go
@@ -570,11 +570,10 @@
 // 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
-	CheckPtrCall *CallExpr `mknode:"-"`
+	X    Node
+	Low  Node
+	High Node
+	Max  Node
 }
 
 func NewSliceExpr(pos src.XPos, op Op, x, low, high, max Node) *SliceExpr {
diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go
index d19fe45..2950535 100644
--- a/src/cmd/compile/internal/ir/fmt.go
+++ b/src/cmd/compile/internal/ir/fmt.go
@@ -386,7 +386,7 @@
 
 	case OTAILCALL:
 		n := n.(*TailCallStmt)
-		fmt.Fprintf(s, "tailcall %v", n.Target)
+		fmt.Fprintf(s, "tailcall %v", n.Call)
 
 	case OINLMARK:
 		n := n.(*InlineMarkStmt)
@@ -559,7 +559,7 @@
 	}
 
 	nprec := OpPrec[n.Op()]
-	if n.Op() == OTYPE && n.Type().IsPtr() {
+	if n.Op() == OTYPE && n.Type() != nil && n.Type().IsPtr() {
 		nprec = OpPrec[ODEREF]
 	}
 
@@ -1147,6 +1147,7 @@
 			}
 			// TODO(mdempsky): Print line pragma details too.
 			file := filepath.Base(pos.Filename())
+			// Note: this output will be parsed by ssa/html.go:(*HTMLWriter).WriteAST. Keep in sync.
 			fmt.Fprintf(w, "%s:%d:%d", file, pos.Line(), pos.Col())
 		}
 	}
diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go
index a7ff4ac..eeb7408 100644
--- a/src/cmd/compile/internal/ir/mini.go
+++ b/src/cmd/compile/internal/ir/mini.go
@@ -62,7 +62,7 @@
 
 func (n *miniNode) Typecheck() uint8 { return n.bits.get2(miniTypecheckShift) }
 func (n *miniNode) SetTypecheck(x uint8) {
-	if x > 3 {
+	if x > 2 {
 		panic(fmt.Sprintf("cannot SetTypecheck %d", x))
 	}
 	n.bits.set2(miniTypecheckShift, x)
diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go
index 9fb2237..dcfff7d 100644
--- a/src/cmd/compile/internal/ir/name.go
+++ b/src/cmd/compile/internal/ir/name.go
@@ -40,6 +40,7 @@
 	Class     Class      // uint8
 	pragma    PragmaFlag // int16
 	flags     bitset16
+	DictIndex uint16 // index of the dictionary entry describing the type of this variable declaration plus 1
 	sym       *types.Sym
 	Func      *Func // TODO(austin): nil for I.M, eqFor, hashfor, and hashmem
 	Offset_   int64
diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go
index aa41c03..4498888 100644
--- a/src/cmd/compile/internal/ir/node_gen.go
+++ b/src/cmd/compile/internal/ir/node_gen.go
@@ -1331,15 +1331,15 @@
 	if doNodes(n.init, do) {
 		return true
 	}
-	if n.Target != nil && do(n.Target) {
+	if n.Call != nil && do(n.Call) {
 		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)
+	if n.Call != nil {
+		n.Call = edit(n.Call).(*CallExpr)
 	}
 }
 
diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go
index 69a74b9..3482d79 100644
--- a/src/cmd/compile/internal/ir/stmt.go
+++ b/src/cmd/compile/internal/ir/stmt.go
@@ -385,14 +385,11 @@
 // code generation to jump directly to another function entirely.
 type TailCallStmt struct {
 	miniStmt
-	Target *Name
+	Call *CallExpr // the underlying call
 }
 
-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}
+func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt {
+	n := &TailCallStmt{Call: call}
 	n.pos = pos
 	n.op = OTAILCALL
 	return n
diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go
index 1e82618..1435e43 100644
--- a/src/cmd/compile/internal/ir/symtab.go
+++ b/src/cmd/compile/internal/ir/symtab.go
@@ -11,33 +11,34 @@
 
 // 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
+	AssertE2I         *obj.LSym
+	AssertE2I2        *obj.LSym
+	AssertI2I         *obj.LSym
+	AssertI2I2        *obj.LSym
+	CheckPtrAlignment *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
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
index e0447f3..6326f96 100644
--- a/src/cmd/compile/internal/mips/ssa.go
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -475,6 +475,8 @@
 		p6.To.SetTarget(p2)
 	case ssa.OpMIPSCALLstatic, ssa.OpMIPSCALLclosure, ssa.OpMIPSCALLinter:
 		s.Call(v)
+	case ssa.OpMIPSCALLtail:
+		s.TailCall(v)
 	case ssa.OpMIPSLoweredWB:
 		p := s.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
@@ -841,14 +843,9 @@
 			p.To.Type = obj.TYPE_BRANCH
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.ARET)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
 	case ssa.BlockMIPSEQ, ssa.BlockMIPSNE,
 		ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ,
 		ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ,
diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go
index e821a00..990b978 100644
--- a/src/cmd/compile/internal/mips64/ssa.go
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -491,6 +491,8 @@
 		p6.To.SetTarget(p2)
 	case ssa.OpMIPS64CALLstatic, ssa.OpMIPS64CALLclosure, ssa.OpMIPS64CALLinter:
 		s.Call(v)
+	case ssa.OpMIPS64CALLtail:
+		s.TailCall(v)
 	case ssa.OpMIPS64LoweredWB:
 		p := s.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
@@ -808,14 +810,9 @@
 			p.To.Type = obj.TYPE_BRANCH
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.ARET)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
 	case ssa.BlockMIPS64EQ, ssa.BlockMIPS64NE,
 		ssa.BlockMIPS64LTZ, ssa.BlockMIPS64GEZ,
 		ssa.BlockMIPS64LEZ, ssa.BlockMIPS64GTZ,
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go
index de481fb..c9ab31f 100644
--- a/src/cmd/compile/internal/noder/decl.go
+++ b/src/cmd/compile/internal/noder/decl.go
@@ -190,7 +190,7 @@
 	// object to new type pragmas.]
 	ntyp.SetUnderlying(g.typeExpr(decl.Type))
 
-	tparams := otyp.(*types2.Named).TParams()
+	tparams := otyp.(*types2.Named).TypeParams()
 	if n := tparams.Len(); n > 0 {
 		rparams := make([]*types.Type, n)
 		for i := range rparams {
diff --git a/src/cmd/compile/internal/noder/decoder.go b/src/cmd/compile/internal/noder/decoder.go
index 3dc61c6..2c18727 100644
--- a/src/cmd/compile/internal/noder/decoder.go
+++ b/src/cmd/compile/internal/noder/decoder.go
@@ -255,7 +255,8 @@
 	return res
 }
 
-func (r *decoder) rawValue() constant.Value {
+func (r *decoder) value() constant.Value {
+	r.sync(syncValue)
 	isComplex := r.bool()
 	val := r.scalar()
 	if isComplex {
diff --git a/src/cmd/compile/internal/noder/encoder.go b/src/cmd/compile/internal/noder/encoder.go
index d8ab0f6..b07b3a4 100644
--- a/src/cmd/compile/internal/noder/encoder.go
+++ b/src/cmd/compile/internal/noder/encoder.go
@@ -237,7 +237,8 @@
 	}
 }
 
-func (w *encoder) rawValue(val constant.Value) {
+func (w *encoder) value(val constant.Value) {
+	w.sync(syncValue)
 	if w.bool(val.Kind() == constant.Complex) {
 		w.scalar(constant.Real(val))
 		w.scalar(constant.Imag(val))
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
index 7dbbc88..9cd9545 100644
--- a/src/cmd/compile/internal/noder/expr.go
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -250,44 +250,6 @@
 		// only be fully transformed once it has an instantiated type.
 		n := ir.NewSelectorExpr(pos, ir.OXDOT, x, typecheck.Lookup(expr.Sel.Value))
 		typed(g.typ(typ), n)
-
-		// Fill in n.Selection for a generic method reference or a bound
-		// interface method, even though we won't use it directly, since it
-		// is useful for analysis. Specifically do not fill in for fields or
-		// other interfaces methods (method call on an interface value), so
-		// n.Selection being non-nil means a method reference for a generic
-		// type or a method reference due to a bound.
-		obj2 := g.info.Selections[expr].Obj()
-		sig := types2.AsSignature(obj2.Type())
-		if sig == nil || sig.Recv() == nil {
-			return n
-		}
-		index := g.info.Selections[expr].Index()
-		last := index[len(index)-1]
-		// recvType is the receiver of the method being called.  Because of the
-		// way methods are imported, g.obj(obj2) doesn't work across
-		// packages, so we have to lookup the method via the receiver type.
-		recvType := deref2(sig.Recv().Type())
-		if types2.AsInterface(recvType.Underlying()) != nil {
-			fieldType := n.X.Type()
-			for _, ix := range index[:len(index)-1] {
-				fieldType = deref(fieldType).Field(ix).Type
-			}
-			if fieldType.Kind() == types.TTYPEPARAM {
-				n.Selection = fieldType.Bound().AllMethods().Index(last)
-				//fmt.Printf(">>>>> %v: Bound call %v\n", base.FmtPos(pos), n.Sel)
-			} else {
-				assert(fieldType.Kind() == types.TINTER)
-				//fmt.Printf(">>>>> %v: Interface call %v\n", base.FmtPos(pos), n.Sel)
-			}
-			return n
-		}
-
-		recvObj := types2.AsNamed(recvType).Obj()
-		recv := g.pkg(recvObj.Pkg()).Lookup(recvObj.Name()).Def
-		n.Selection = recv.Type().Methods().Index(last)
-		//fmt.Printf(">>>>> %v: Method call %v\n", base.FmtPos(pos), n.Sel)
-
 		return n
 	}
 
@@ -344,7 +306,7 @@
 			if wantPtr {
 				recvType2Base = types2.AsPointer(recvType2).Elem()
 			}
-			if types2.AsNamed(recvType2Base).TParams().Len() > 0 {
+			if types2.AsNamed(recvType2Base).TypeParams().Len() > 0 {
 				// recvType2 is the original generic type that is
 				// instantiated for this method call.
 				// selinfo.Recv() is the instantiated type
@@ -360,12 +322,10 @@
 				n.(*ir.SelectorExpr).Selection.Nname = method
 				typed(method.Type(), n)
 
-				// selinfo.Targs() are the types used to
-				// instantiate the type of receiver
-				targs2 := getTargs(selinfo)
-				targs := make([]ir.Node, targs2.Len())
+				xt := deref(x.Type())
+				targs := make([]ir.Node, len(xt.RParams()))
 				for i := range targs {
-					targs[i] = ir.TypeNode(g.typ(targs2.At(i)))
+					targs[i] = ir.TypeNode(xt.RParams()[i])
 				}
 
 				// Create function instantiation with the type
@@ -388,16 +348,6 @@
 	return n
 }
 
-// getTargs gets the targs associated with the receiver of a selected method
-func getTargs(selinfo *types2.Selection) *types2.TypeList {
-	r := deref2(selinfo.Recv())
-	n := types2.AsNamed(r)
-	if n == nil {
-		base.Fatalf("Incorrect type for selinfo %v", selinfo)
-	}
-	return n.TArgs()
-}
-
 func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
 	return g.exprs(unpackListExpr(expr))
 }
@@ -440,9 +390,10 @@
 			} else {
 				key = g.expr(elem.Key)
 			}
-			exprs[i] = ir.NewKeyExpr(g.pos(elem), key, g.expr(elem.Value))
+			value := wrapname(g.pos(elem.Value), g.expr(elem.Value))
+			exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value)
 		default:
-			exprs[i] = g.expr(elem)
+			exprs[i] = wrapname(g.pos(elem), g.expr(elem))
 		}
 	}
 
diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go
index 9487e76..636b5d6 100644
--- a/src/cmd/compile/internal/noder/helpers.go
+++ b/src/cmd/compile/internal/noder/helpers.go
@@ -95,16 +95,12 @@
 		return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
 	case ir.OADD:
 		n := ir.NewBinaryExpr(pos, op, x, y)
-		if x.Type().HasTParam() || y.Type().HasTParam() {
-			// Delay transformAdd() if either arg has a type param,
-			// since it needs to know the exact types to decide whether
-			// to transform OADD to OADDSTR.
-			n.SetType(typ)
-			n.SetTypecheck(3)
-			return n
-		}
 		typed(typ, n)
-		return transformAdd(n)
+		r := ir.Node(n)
+		if !delayTransform() {
+			r = transformAdd(n)
+		}
+		return r
 	default:
 		return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y))
 	}
@@ -189,17 +185,6 @@
 		// A function instantiation (even if fully concrete) shouldn't be
 		// transformed yet, because we need to add the dictionary during the
 		// transformation.
-		//
-		// However, if we have a function type (even though it is
-		// parameterized), then we can add in any needed CONVIFACE nodes via
-		// typecheckaste(). We need to call transformArgs() to deal first
-		// with the f(g(()) case where g returns multiple return values. We
-		// can't do anything if fun is a type param (which is probably
-		// described by a structural constraint)
-		if fun.Type().Kind() == types.TFUNC {
-			transformArgs(n)
-			typecheckaste(ir.OCALL, fun, n.IsDDD, fun.Type().Params(), n.Args, true)
-		}
 		return typed(typ, n)
 	}
 
@@ -212,22 +197,10 @@
 
 func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
 	n := ir.NewBinaryExpr(pos, op, x, y)
-	if x.Type().HasTParam() || y.Type().HasTParam() {
-		xIsInt := x.Type().IsInterface()
-		yIsInt := y.Type().IsInterface()
-		if !(xIsInt && !yIsInt || !xIsInt && yIsInt) {
-			// If either arg is a type param, then we can still do the
-			// transformCompare() if we know that one arg is an interface
-			// and the other is not. Otherwise, we delay
-			// transformCompare(), since it needs to know the exact types
-			// to decide on any needed conversions.
-			n.SetType(typ)
-			n.SetTypecheck(3)
-			return n
-		}
-	}
 	typed(typ, n)
-	transformCompare(n)
+	if !delayTransform() {
+		transformCompare(n)
+	}
 	return n
 }
 
@@ -299,15 +272,11 @@
 
 func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node {
 	n := ir.NewIndexExpr(pos, x, index)
-	if x.Type().HasTParam() {
-		// transformIndex needs to know exact type
-		n.SetType(typ)
-		n.SetTypecheck(3)
-		return n
-	}
 	typed(typ, n)
-	// transformIndex will modify n.Type() for OINDEXMAP.
-	transformIndex(n)
+	if !delayTransform() {
+		// transformIndex will modify n.Type() for OINDEXMAP.
+		transformIndex(n)
+	}
 	return n
 }
 
@@ -317,14 +286,10 @@
 		op = ir.OSLICE3
 	}
 	n := ir.NewSliceExpr(pos, op, x, low, high, max)
-	if x.Type().HasTParam() {
-		// transformSlice needs to know if x.Type() is a string or an array or a slice.
-		n.SetType(typ)
-		n.SetTypecheck(3)
-		return n
-	}
 	typed(typ, n)
-	transformSlice(n)
+	if !delayTransform() {
+		transformSlice(n)
+	}
 	return n
 }
 
@@ -366,3 +331,9 @@
 	}
 	return ir.NewAssignOpStmt(pos, op, x, bl)
 }
+
+// delayTransform returns true if we should delay all transforms, because we are
+// creating the nodes for a generic function/method.
+func delayTransform() bool {
+	return ir.CurFunc != nil && ir.CurFunc.Type().HasTParam()
+}
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
index c26340c..f13f8ca 100644
--- a/src/cmd/compile/internal/noder/import.go
+++ b/src/cmd/compile/internal/noder/import.go
@@ -43,12 +43,12 @@
 // for an imported package by overloading writeNewExportFunc, then
 // that payload will be mapped into memory and passed to
 // newReadImportFunc.
-var newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
+var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
 	panic("unexpected new export data payload")
 }
 
 type gcimports struct {
-	check    *types2.Checker
+	env      *types2.Environment
 	packages map[string]*types2.Package
 }
 
@@ -61,7 +61,7 @@
 		panic("mode must be 0")
 	}
 
-	_, pkg, err := readImportFile(path, typecheck.Target, m.check, m.packages)
+	_, pkg, err := readImportFile(path, typecheck.Target, m.env, m.packages)
 	return pkg, err
 }
 
@@ -224,7 +224,7 @@
 // readImportFile reads the import file for the given package path and
 // returns its types.Pkg representation. If packages is non-nil, the
 // types2.Package representation is also returned.
-func readImportFile(path string, target *ir.Package, check *types2.Checker, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
+func readImportFile(path string, target *ir.Package, env *types2.Environment, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
 	path, err = resolveImportPath(path)
 	if err != nil {
 		return
@@ -279,7 +279,7 @@
 			return
 		}
 
-		pkg2, err = newReadImportFunc(data, pkg1, check, packages)
+		pkg2, err = newReadImportFunc(data, pkg1, env, packages)
 	} else {
 		// We only have old data. Oh well, fall back to the legacy importers.
 		haveLegacyImports = true
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index a67b399..4f1b4e6 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -34,10 +34,13 @@
 	}
 
 	// typechecking
+	env := types2.NewEnvironment()
 	importer := gcimports{
+		env:      env,
 		packages: map[string]*types2.Package{"unsafe": types2.Unsafe},
 	}
 	conf := types2.Config{
+		Environment:           env,
 		GoVersion:             base.Flag.Lang,
 		IgnoreLabels:          true, // parser already checked via syntax.CheckBranches mode
 		CompilerErrorMessages: true, // use error strings matching existing compiler errors
@@ -60,9 +63,7 @@
 		// expand as needed
 	}
 
-	pkg := types2.NewPackage(base.Ctxt.Pkgpath, "")
-	importer.check = types2.NewChecker(&conf, pkg, info)
-	err := importer.check.Files(files)
+	pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
 
 	base.ExitIfErrors()
 	if err != nil {
@@ -96,39 +97,42 @@
 	}
 }
 
-// gfInfo is information gathered on a generic function.
-type gfInfo struct {
-	tparams      []*types.Type
+// dictInfo is the dictionary format for an instantiation of a generic function with
+// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe
+// the actual dictionary entries in order, and the remaining fields are other info
+// needed in doing dictionary processing during compilation.
+type dictInfo struct {
+	// Types substituted for the type parameters, which are shape types.
+	shapeParams []*types.Type
+	// All types derived from those typeparams used in the instantiation.
 	derivedTypes []*types.Type
-	// Nodes in generic function that requires a subdictionary. Includes
+	// Nodes in the instantiation that requires a subdictionary. Includes
 	// method and function calls (OCALL), function values (OFUNCINST), method
 	// values/expressions (OXDOT).
 	subDictCalls []ir.Node
-	// Nodes in generic functions that are a conversion from a typeparam/derived
+	// Nodes in the instantiation that are a conversion from a typeparam/derived
 	// type to a specific interface.
 	itabConvs []ir.Node
+
+	// Mapping from each shape type that substitutes a type param, to its
+	// type bound (which is also substitued with shapes if it is parameterized)
+	shapeToBound map[*types.Type]*types.Type
+
 	// For type switches on nonempty interfaces, a map from OTYPE entries of
-	// HasTParam type, to the interface type we're switching from.
-	// TODO: what if the type we're switching from is a shape type?
+	// HasShape type, to the interface type we're switching from.
 	type2switchType map[ir.Node]*types.Type
-}
-
-// instInfo is information gathered on an gcshape (or fully concrete)
-// instantiation of a function.
-type instInfo struct {
-	fun       *ir.Func // The instantiated function (with body)
-	dictParam *ir.Name // The node inside fun that refers to the dictionary param
-
-	gf     *ir.Name // The associated generic function
-	gfInfo *gfInfo
 
 	startSubDict  int // Start of dict entries for subdictionaries
 	startItabConv int // Start of dict entries for itab conversions
 	dictLen       int // Total number of entries in dictionary
+}
 
-	// Map from nodes in instantiated fun (OCALL, OCALLMETHOD, OFUNCINST, and
-	// OMETHEXPR) to the associated dictionary entry for a sub-dictionary
-	dictEntryMap map[ir.Node]int
+// instInfo is information gathered on an shape instantiation of a function.
+type instInfo struct {
+	fun       *ir.Func // The instantiated function (with body)
+	dictParam *ir.Name // The node inside fun that refers to the dictionary param
+
+	dictInfo *dictInfo
 }
 
 type irgen struct {
@@ -154,13 +158,8 @@
 
 	dnum int // for generating unique dictionary variables
 
-	// Map from generic function to information about its type params, derived
-	// types, and subdictionaries.
-	gfInfoMap map[*types.Sym]*gfInfo
-
 	// Map from a name of function that been instantiated to information about
-	// its instantiated function, associated generic function/method, and the
-	// mapping from IR nodes to dictionary entries.
+	// its instantiated function (including dictionary format).
 	instInfoMap map[*types.Sym]*instInfo
 
 	// dictionary syms which we need to finish, by writing out any itabconv
@@ -178,10 +177,11 @@
 }
 
 type delayInfo struct {
-	gf    *ir.Name
-	targs []*types.Type
-	sym   *types.Sym
-	off   int
+	gf     *ir.Name
+	targs  []*types.Type
+	sym    *types.Sym
+	off    int
+	isMeth bool
 }
 
 type typeDelayInfo struct {
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 2f18a2f..7c14fcf 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -1537,7 +1537,7 @@
 	return mkname(p.name(name))
 }
 
-func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
+func wrapname(pos src.XPos, 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() {
@@ -1547,13 +1547,17 @@
 		}
 		fallthrough
 	case ir.ONAME, ir.ONONAME, ir.OPACK:
-		p := ir.NewParenExpr(p.pos(n), x)
+		p := ir.NewParenExpr(pos, x)
 		p.SetImplicit(true)
 		return p
 	}
 	return x
 }
 
+func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
+	return wrapname(p.pos(n), x)
+}
+
 func (p *noder) setlineno(n syntax.Node) {
 	if n != nil {
 		base.Pos = p.pos(n)
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 204d25b..48f4368 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -10,6 +10,7 @@
 	"bytes"
 	"fmt"
 	"go/constant"
+	"internal/buildcfg"
 	"strings"
 
 	"cmd/compile/internal/base"
@@ -78,8 +79,6 @@
 
 	p *pkgReader
 
-	ext *reader
-
 	dict *readerDict
 
 	// TODO(mdempsky): The state below is all specific to reading
@@ -194,15 +193,32 @@
 	r := pr.newReader(relocPosBase, idx, syncPosBase)
 	var b *src.PosBase
 
-	filename := r.string()
+	absFilename := r.string()
+	filename := absFilename
+
+	// For build artifact stability, the export data format only
+	// contains the "absolute" filename as returned by objabi.AbsFile.
+	// However, some tests (e.g., test/run.go's asmcheck tests) expect
+	// to see the full, original filename printed out. Re-expanding
+	// "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to
+	// satisfy this.
+	//
+	// TODO(mdempsky): De-duplicate this logic with similar logic in
+	// cmd/link/internal/ld's expandGoroot. However, this will probably
+	// require being more consistent about when we use native vs UNIX
+	// file paths.
+	const dollarGOROOT = "$GOROOT"
+	if strings.HasPrefix(filename, dollarGOROOT) {
+		filename = buildcfg.GOROOT + filename[len(dollarGOROOT):]
+	}
 
 	if r.bool() {
-		b = src.NewFileBase(filename, filename)
+		b = src.NewFileBase(filename, absFilename)
 	} else {
 		pos := r.pos0()
 		line := r.uint()
 		col := r.uint()
-		b = src.NewLinePragmaBase(pos, filename, filename, line, col)
+		b = src.NewLinePragmaBase(pos, filename, absFilename, line, col)
 	}
 
 	pr.posBases[idx] = b
@@ -568,10 +584,10 @@
 	dict := pr.objDictIdx(sym, idx, implicits, explicits)
 
 	r := pr.newReader(relocObj, idx, syncObject1)
-	r.ext = pr.newReader(relocObjExt, idx, syncObject1)
+	rext := pr.newReader(relocObjExt, idx, syncObject1)
 
 	r.dict = dict
-	r.ext.dict = dict
+	rext.dict = dict
 
 	sym = r.mangle(sym)
 	if !sym.IsBlank() && sym.Def != nil {
@@ -608,7 +624,8 @@
 
 	case objConst:
 		name := do(ir.OLITERAL, false)
-		typ, val := r.value()
+		typ := r.typ()
+		val := FixValue(typ, r.value())
 		setType(name, typ)
 		setValue(name, val)
 		return name
@@ -623,7 +640,7 @@
 		name.Func = ir.NewFunc(r.pos())
 		name.Func.Nname = name
 
-		r.ext.funcExt(name)
+		rext.funcExt(name)
 		return name
 
 	case objType:
@@ -632,7 +649,7 @@
 		setType(name, typ)
 
 		// Important: We need to do this before SetUnderlying.
-		r.ext.typeExt(name)
+		rext.typeExt(name)
 
 		// We need to defer CheckSize until we've called SetUnderlying to
 		// handle recursive types.
@@ -642,7 +659,7 @@
 
 		methods := make([]*types.Field, r.len())
 		for i := range methods {
-			methods[i] = r.method()
+			methods[i] = r.method(rext)
 		}
 		if len(methods) != 0 {
 			typ.Methods().Set(methods)
@@ -655,7 +672,7 @@
 	case objVar:
 		name := do(ir.ONAME, false)
 		setType(name, r.typ())
-		r.ext.varExt(name)
+		rext.varExt(name)
 		return name
 	}
 }
@@ -737,13 +754,7 @@
 	}
 }
 
-func (r *reader) value() (*types.Type, constant.Value) {
-	r.sync(syncValue)
-	typ := r.typ()
-	return typ, FixValue(typ, r.rawValue())
-}
-
-func (r *reader) method() *types.Field {
+func (r *reader) method(rext *reader) *types.Field {
 	r.sync(syncMethod)
 	pos := r.pos()
 	pkg, sym := r.selector()
@@ -759,7 +770,7 @@
 	name.Func = ir.NewFunc(r.pos())
 	name.Func.Nname = name
 
-	r.ext.funcExt(name)
+	rext.funcExt(name)
 
 	meth := types.NewField(name.Func.Pos(), sym, typ)
 	meth.Nname = name
@@ -916,6 +927,11 @@
 // constructed.
 var todoBodies []*ir.Func
 
+// todoBodiesDone signals that we constructed all function in todoBodies.
+// This is necessary to prevent reader.addBody adds thing to todoBodies
+// when nested inlining happens.
+var todoBodiesDone = false
+
 func (r *reader) addBody(fn *ir.Func) {
 	pri := pkgReaderIndex{r.p, r.reloc(relocBody), r.dict}
 	bodyReader[fn] = pri
@@ -926,7 +942,7 @@
 		return
 	}
 
-	if r.curfn == nil {
+	if r.curfn == nil && !todoBodiesDone {
 		todoBodies = append(todoBodies, fn)
 		return
 	}
@@ -1538,7 +1554,8 @@
 
 	case exprConst:
 		pos := r.pos()
-		typ, val := r.value()
+		typ := r.typ()
+		val := FixValue(typ, r.value())
 		op := r.op()
 		orig := r.string()
 		return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go
index 296d842..dcd9a65 100644
--- a/src/cmd/compile/internal/noder/reader2.go
+++ b/src/cmd/compile/internal/noder/reader2.go
@@ -7,8 +7,6 @@
 package noder
 
 import (
-	"go/constant"
-
 	"cmd/compile/internal/base"
 	"cmd/compile/internal/syntax"
 	"cmd/compile/internal/types2"
@@ -18,7 +16,7 @@
 type pkgReader2 struct {
 	pkgDecoder
 
-	check   *types2.Checker
+	env     *types2.Environment
 	imports map[string]*types2.Package
 
 	posBases []*syntax.PosBase
@@ -26,11 +24,11 @@
 	typs     []types2.Type
 }
 
-func readPackage2(check *types2.Checker, imports map[string]*types2.Package, input pkgDecoder) *types2.Package {
+func readPackage2(env *types2.Environment, imports map[string]*types2.Package, input pkgDecoder) *types2.Package {
 	pr := pkgReader2{
 		pkgDecoder: input,
 
-		check:   check,
+		env:     env,
 		imports: imports,
 
 		posBases: make([]*syntax.PosBase, input.numElems(relocPosBase)),
@@ -233,7 +231,7 @@
 		obj, targs := r.obj()
 		name := obj.(*types2.TypeName)
 		if len(targs) != 0 {
-			t, _ := types2.Instantiate(types2.NewEnvironment(r.p.check), name.Type(), targs, false)
+			t, _ := types2.Instantiate(r.p.env, name.Type(), targs, false)
 			return t
 		}
 		return name.Type()
@@ -388,14 +386,15 @@
 
 		case objConst:
 			pos := r.pos()
-			typ, val := r.value()
+			typ := r.typ()
+			val := r.value()
 			return types2.NewConst(pos, objPkg, objName, typ, val)
 
 		case objFunc:
 			pos := r.pos()
 			tparams := r.typeParamNames()
 			sig := r.signature(nil)
-			sig.SetTParams(tparams)
+			sig.SetTypeParams(tparams)
 			return types2.NewFunc(pos, objPkg, objName, sig)
 
 		case objType:
@@ -428,11 +427,6 @@
 	return objPkg, objName
 }
 
-func (r *reader2) value() (types2.Type, constant.Value) {
-	r.sync(syncValue)
-	return r.typ(), r.rawValue()
-}
-
 func (pr *pkgReader2) objDictIdx(idx int) *reader2Dict {
 	r := pr.newReader(relocObjDict, idx, syncObject1)
 
@@ -481,7 +475,7 @@
 		pkg, name := r.localIdent()
 
 		tname := types2.NewTypeName(pos, pkg, name, nil)
-		r.dict.tparams[i] = r.p.check.NewTypeParam(tname, nil)
+		r.dict.tparams[i] = types2.NewTypeParam(tname, nil)
 	}
 
 	for i, bound := range r.dict.bounds {
@@ -498,7 +492,7 @@
 
 	rparams := r.typeParamNames()
 	sig := r.signature(r.param())
-	sig.SetRParams(rparams)
+	sig.SetRecvTypeParams(rparams)
 
 	_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
 	return types2.NewFunc(pos, pkg, name, sig)
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 1c22fc2..7fca674 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -44,7 +44,6 @@
 // process.
 func (g *irgen) stencil() {
 	g.instInfoMap = make(map[*types.Sym]*instInfo)
-	g.gfInfoMap = make(map[*types.Sym]*gfInfo)
 
 	// Instantiate the methods of instantiated generic types that we have seen so far.
 	g.instantiateMethods()
@@ -106,7 +105,7 @@
 				inst := call.X.(*ir.InstExpr)
 				nameNode, isMeth := g.getInstNameNode(inst)
 				targs := typecheck.TypesOf(inst.Targs)
-				st := g.getInstantiation(nameNode, targs, isMeth)
+				st := g.getInstantiation(nameNode, targs, isMeth).fun
 				dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
 				if infoPrintMode {
 					dictkind := "Main dictionary"
@@ -165,7 +164,7 @@
 				// to OCALLFUNC and does typecheckaste/assignconvfn.
 				transformCall(call)
 
-				st := g.getInstantiation(gf, targs, true)
+				st := g.getInstantiation(gf, targs, true).fun
 				dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
 				// We have to be using a subdictionary, since this is
 				// a generic method call.
@@ -232,6 +231,31 @@
 	}
 
 	g.finalizeSyms()
+
+	// All the instantiations and dictionaries have been created. Now go through
+	// each instantiation and transform the various operations that need to make
+	// use of their dictionary.
+	l := len(g.instInfoMap)
+	for _, info := range g.instInfoMap {
+		g.dictPass(info)
+		if doubleCheck {
+			ir.Visit(info.fun, func(n ir.Node) {
+				if n.Op() != ir.OCONVIFACE {
+					return
+				}
+				c := n.(*ir.ConvExpr)
+				if c.X.Type().HasShape() && !c.X.Type().IsInterface() {
+					ir.Dump("BAD FUNCTION", info.fun)
+					ir.Dump("BAD CONVERSION", c)
+					base.Fatalf("converting shape type to interface")
+				}
+			})
+		}
+		if base.Flag.W > 1 {
+			ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
+		}
+	}
+	assert(l == len(g.instInfoMap))
 }
 
 // buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR
@@ -274,7 +298,7 @@
 		// For method values, the target expects a dictionary and the receiver
 		// as its first two arguments.
 		// dictValue is the value to use for the dictionary argument.
-		target = g.getInstantiation(gf, targs, rcvrValue != nil)
+		target = g.getInstantiation(gf, targs, rcvrValue != nil).fun
 		dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil)
 		if infoPrintMode {
 			dictkind := "Main dictionary"
@@ -321,7 +345,7 @@
 			// Remember if value method, so we can detect (*T).M case.
 			valueMethod = true
 		}
-		target = g.getInstantiation(gf, targs, true)
+		target = g.getInstantiation(gf, targs, true).fun
 		dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
 		if infoPrintMode {
 			dictkind := "Main dictionary"
@@ -396,13 +420,19 @@
 	if rcvrValue != nil {
 		rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum))
 		g.dnum++
-		rcvrVar.Class = ir.PAUTO
 		typed(rcvrValue.Type(), rcvrVar)
-		rcvrVar.Curfn = outer
 		rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
 		rcvrAssign.SetTypecheck(1)
 		rcvrVar.Defn = rcvrAssign
-		outer.Dcl = append(outer.Dcl, rcvrVar)
+		if outer == nil {
+			rcvrVar.Class = ir.PEXTERN
+			g.target.Decls = append(g.target.Decls, rcvrAssign)
+			g.target.Externs = append(g.target.Externs, rcvrVar)
+		} else {
+			rcvrVar.Class = ir.PAUTO
+			rcvrVar.Curfn = outer
+			outer.Dcl = append(outer.Dcl, rcvrVar)
+		}
 	}
 
 	// Build body of closure. This involves just calling the wrapped function directly
@@ -538,13 +568,18 @@
 	var dict ir.Node
 	usingSubdict := false
 	if declInfo != nil {
-		// Get the dictionary arg via sub-dictionary reference
-		entry, ok := declInfo.dictEntryMap[n]
+		entry := -1
+		for i, de := range declInfo.dictInfo.subDictCalls {
+			if n == de {
+				entry = declInfo.dictInfo.startSubDict + i
+				break
+			}
+		}
 		// If the entry is not found, it may be that this node did not have
 		// any type args that depend on type params, so we need a main
 		// dictionary, not a sub-dictionary.
-		if ok {
-			dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictLen)
+		if entry >= 0 {
+			dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen)
 			usingSubdict = true
 		}
 	}
@@ -571,7 +606,7 @@
 // getInstantiation gets the instantiantion and dictionary of the function or method nameNode
 // with the type arguments shapes. If the instantiated function is not already
 // cached, then it calls genericSubst to create the new instantiation.
-func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *ir.Func {
+func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo {
 	checkFetchBody(nameNode)
 
 	// Convert any non-shape type arguments to their shape, so we can reduce the
@@ -580,12 +615,12 @@
 	// specified concrete type args.
 	var s1 []*types.Type
 	for i, t := range shapes {
-		if !t.HasShape() {
+		if !t.IsShape() {
 			if s1 == nil {
 				s1 = make([]*types.Type, len(shapes))
 				copy(s1[0:i], shapes[0:i])
 			}
-			s1[i] = typecheck.Shapify(t)
+			s1[i] = typecheck.Shapify(t, i)
 		} else if s1 != nil {
 			s1[i] = shapes[i]
 		}
@@ -599,28 +634,28 @@
 	if info == nil {
 		// If instantiation doesn't exist yet, create it and add
 		// to the list of decls.
-		gfInfo := g.getGfInfo(nameNode)
 		info = &instInfo{
-			gf:            nameNode,
-			gfInfo:        gfInfo,
-			startSubDict:  len(shapes) + len(gfInfo.derivedTypes),
-			startItabConv: len(shapes) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls),
-			dictLen:       len(shapes) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls) + len(gfInfo.itabConvs),
-			dictEntryMap:  make(map[ir.Node]int),
+			dictInfo: &dictInfo{},
 		}
-		// genericSubst fills in info.dictParam and info.dictEntryMap.
+		info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type)
+
+		// genericSubst fills in info.dictParam and info.tparamToBound.
 		st := g.genericSubst(sym, nameNode, shapes, isMeth, info)
 		info.fun = st
 		g.instInfoMap[sym] = info
+
+		// getInstInfo fills in info.dictInfo.
+		g.getInstInfo(st, shapes, info)
+		if base.Flag.W > 1 {
+			ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
+		}
+
 		// This ensures that the linker drops duplicates of this instantiation.
 		// All just works!
 		st.SetDupok(true)
 		g.target.Decls = append(g.target.Decls, st)
-		if base.Flag.W > 1 {
-			ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
-		}
 	}
-	return info.fun
+	return info
 }
 
 // Struct containing info needed for doing the substitution as we create the
@@ -641,7 +676,7 @@
 // args shapes. For a method with a generic receiver, it returns an instantiated
 // function type where the receiver becomes the first parameter. For either a generic
 // method or function, a dictionary parameter is the added as the very first
-// parameter. genericSubst fills in info.dictParam and info.dictEntryMap.
+// parameter. genericSubst fills in info.dictParam and info.tparamToBound.
 func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
 	var tparams []*types.Type
 	if isMethod {
@@ -738,23 +773,13 @@
 		base.Fatalf("defnMap is not empty")
 	}
 
-	ir.CurFunc = savef
-
-	if doubleCheck {
-		ir.Visit(newf, func(n ir.Node) {
-			if n.Op() != ir.OCONVIFACE {
-				return
-			}
-			c := n.(*ir.ConvExpr)
-			if c.X.Type().HasShape() && !c.X.Type().IsInterface() {
-				ir.Dump("BAD FUNCTION", newf)
-				ir.Dump("BAD CONVERSION", c)
-				base.Fatalf("converting shape type to interface")
-			}
-		})
+	for i, tp := range tparams {
+		info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound())
 	}
 
-	return newf
+	ir.CurFunc = savef
+
+	return subst.newf
 }
 
 // localvar creates a new name node for the specified local variable and enters it
@@ -773,6 +798,7 @@
 	m.Func = name.Func
 	subst.ts.Vars[name] = m
 	m.SetTypecheck(1)
+	m.DictIndex = name.DictIndex
 	if name.Defn != nil {
 		if name.Defn.Op() == ir.ONAME {
 			// This is a closure variable, so its Defn is the outer
@@ -863,11 +889,11 @@
 // refers to a type param or a derived type that uses type params). It uses the
 // specified dictionary dictParam, rather than the one in info.dictParam.
 func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node {
-	if i < 0 || i >= info.startSubDict {
+	if i < 0 || i >= info.dictInfo.startSubDict {
 		base.Fatalf(fmt.Sprintf("bad dict index %d", i))
 	}
 
-	r := getDictionaryEntry(pos, info.dictParam, i, info.startSubDict)
+	r := getDictionaryEntry(pos, info.dictParam, i, info.dictInfo.startSubDict)
 	// change type of retrieved dictionary entry to *byte, which is the
 	// standard typing of a *runtime._type in the compiler
 	typed(types.Types[types.TUINT8].PtrTo(), r)
@@ -929,17 +955,6 @@
 			}
 		}
 
-		for i, de := range subst.info.gfInfo.subDictCalls {
-			if de == x {
-				// Remember the dictionary entry associated with this
-				// node in the instantiated function
-				// TODO: make sure this remains correct with respect to the
-				// transformations below.
-				subst.info.dictEntryMap[m] = subst.info.startSubDict + i
-				break
-			}
-		}
-
 		ir.EditChildren(m, edit)
 
 		m.SetTypecheck(1)
@@ -980,6 +995,9 @@
 			case ir.OSEND:
 				transformSend(m.(*ir.SendStmt))
 
+			case ir.OSELECT:
+				transformSelect(m.(*ir.SelectStmt))
+
 			}
 		}
 
@@ -1012,36 +1030,9 @@
 			// we find in the OCALL case below that the method value
 			// is actually called.
 			mse := m.(*ir.SelectorExpr)
-			if src := mse.X.Type(); src.IsShape() {
-				// The only dot on a shape type value are methods.
-				if mse.X.Op() == ir.OTYPE {
-					// Method expression T.M
-					m = subst.g.buildClosure2(subst, m, x)
-					// No need for transformDot - buildClosure2 has already
-					// transformed to OCALLINTER/ODOTINTER.
-				} else {
-					// Implement x.M as a conversion-to-bound-interface
-					//  1) convert x to the bound interface
-					//  2) call M on that interface
-					gsrc := x.(*ir.SelectorExpr).X.Type()
-					bound := gsrc.Bound()
-					dst := bound
-					if dst.HasTParam() {
-						dst = subst.ts.Typ(dst)
-					}
-					if src.IsInterface() {
-						// If type arg is an interface (unusual case),
-						// we do a type assert to the type bound.
-						mse.X = assertToBound(subst.info, subst.info.dictParam, m.Pos(), mse.X, bound, dst)
-					} else {
-						mse.X = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), mse.X, x, dst, gsrc)
-					}
-					transformDot(mse, false)
-				}
-			} else {
+			if src := mse.X.Type(); !src.IsShape() {
 				transformDot(mse, false)
 			}
-			m.SetTypecheck(1)
 
 		case ir.OCALL:
 			call := m.(*ir.CallExpr)
@@ -1096,7 +1087,7 @@
 				// or channel receive to compute function value.
 				transformCall(call)
 
-			case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.ODYNAMICDOTTYPE:
+			case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
 				transformCall(call)
 
 			case ir.OFUNCINST:
@@ -1104,6 +1095,7 @@
 				// in stencil() once we have created & attached the
 				// instantiation to be called.
 
+			case ir.OXDOT, ir.ODOTTYPE, ir.ODOTTYPE2:
 			default:
 				base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op()))
 			}
@@ -1135,7 +1127,7 @@
 			// Copy that closure variable to a local one.
 			// Note: this allows the dictionary to be captured by child closures.
 			// See issue 47723.
-			ldict := ir.NewNameAt(x.Pos(), subst.info.gf.Sym().Pkg.Lookup(".dict"))
+			ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(".dict"))
 			typed(types.Types[types.TUINTPTR], ldict)
 			ldict.Class = ir.PAUTO
 			ldict.Curfn = newfn
@@ -1147,16 +1139,11 @@
 			// Create inst info for the instantiated closure. The dict
 			// param is the closure variable for the dictionary of the
 			// outer function. Since the dictionary is shared, use the
-			// same entries for startSubDict, dictLen, dictEntryMap.
+			// same dictInfo.
 			cinfo := &instInfo{
-				fun:           newfn,
-				dictParam:     ldict,
-				gf:            subst.info.gf,
-				gfInfo:        subst.info.gfInfo,
-				startSubDict:  subst.info.startSubDict,
-				startItabConv: subst.info.startItabConv,
-				dictLen:       subst.info.dictLen,
-				dictEntryMap:  subst.info.dictEntryMap,
+				fun:       newfn,
+				dictParam: ldict,
+				dictInfo:  subst.info.dictInfo,
 			}
 			subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo
 
@@ -1175,76 +1162,6 @@
 			m = ir.UseClosure(newfn.OClosure, subst.g.target)
 			m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
 
-		case ir.OCONVIFACE:
-			x := x.(*ir.ConvExpr)
-			// Note: x's argument is still typed as a type parameter.
-			// m's argument now has an instantiated type.
-			if x.X.Type().HasTParam() || (x.X.Type().IsInterface() && x.Type().HasTParam()) {
-				m = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, x, m.Type(), x.X.Type())
-			}
-		case ir.ODOTTYPE, ir.ODOTTYPE2:
-			if !x.Type().HasTParam() {
-				break
-			}
-			dt := m.(*ir.TypeAssertExpr)
-			var rt ir.Node
-			if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() {
-				ix := findDictType(subst.info, x.Type())
-				assert(ix >= 0)
-				rt = getDictionaryType(subst.info, subst.info.dictParam, dt.Pos(), ix)
-			} else {
-				// nonempty interface to noninterface. Need an itab.
-				ix := -1
-				for i, ic := range subst.info.gfInfo.itabConvs {
-					if ic == x {
-						ix = subst.info.startItabConv + i
-						break
-					}
-				}
-				assert(ix >= 0)
-				rt = getDictionaryEntry(dt.Pos(), subst.info.dictParam, ix, subst.info.dictLen)
-			}
-			op := ir.ODYNAMICDOTTYPE
-			if x.Op() == ir.ODOTTYPE2 {
-				op = ir.ODYNAMICDOTTYPE2
-			}
-			m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt)
-			m.SetType(dt.Type())
-			m.SetTypecheck(1)
-		case ir.OCASE:
-			if _, ok := x.(*ir.CommClause); ok {
-				// This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE?
-				break
-			}
-			x := x.(*ir.CaseClause)
-			m := m.(*ir.CaseClause)
-			for i, c := range x.List {
-				if c.Op() == ir.OTYPE && c.Type().HasTParam() {
-					// Use a *runtime._type for the dynamic type.
-					ix := findDictType(subst.info, c.Type())
-					assert(ix >= 0)
-					dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen))
-
-					// For type switch from nonempty interfaces to non-interfaces, we need an itab as well.
-					if !m.List[i].Type().IsInterface() {
-						if _, ok := subst.info.gfInfo.type2switchType[c]; ok {
-							// Type switch from nonempty interface. We need a *runtime.itab
-							// for the dynamic type.
-							ix := -1
-							for i, ic := range subst.info.gfInfo.itabConvs {
-								if ic == c {
-									ix = subst.info.startItabConv + i
-									break
-								}
-							}
-							assert(ix >= 0)
-							dt.ITab = getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen)
-						}
-					}
-					typed(m.List[i].Type(), dt)
-					m.List[i] = dt
-				}
-			}
 		}
 		return m
 	}
@@ -1252,30 +1169,169 @@
 	return edit(n)
 }
 
+// dictPass takes a function instantiation and does the transformations on the
+// operations that need to make use of the dictionary param.
+func (g *irgen) dictPass(info *instInfo) {
+	savef := ir.CurFunc
+	ir.CurFunc = info.fun
+
+	var edit func(ir.Node) ir.Node
+	edit = func(m ir.Node) ir.Node {
+		ir.EditChildren(m, edit)
+
+		switch m.Op() {
+		case ir.OCLOSURE:
+			newf := m.(*ir.ClosureExpr).Func
+			ir.CurFunc = newf
+			outerinfo := info
+			info = g.instInfoMap[newf.Nname.Sym()]
+
+			body := newf.Body
+			for i, n := range body {
+				body[i] = edit(n)
+			}
+
+			info = outerinfo
+			ir.CurFunc = info.fun
+
+		case ir.OXDOT:
+			mse := m.(*ir.SelectorExpr)
+			src := mse.X.Type()
+			assert(src.IsShape())
+
+			// The only dot on a shape type value are methods.
+			if mse.X.Op() == ir.OTYPE {
+				// Method expression T.M
+				m = g.buildClosure2(info, m)
+				// No need for transformDot - buildClosure2 has already
+				// transformed to OCALLINTER/ODOTINTER.
+			} else {
+				// Implement x.M as a conversion-to-bound-interface
+				//  1) convert x to the bound interface
+				//  2) call M on that interface
+				dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
+				if src.IsInterface() {
+					// If type arg is an interface (unusual case),
+					// we do a type assert to the type bound.
+					mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
+				} else {
+					mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst)
+				}
+				transformDot(mse, false)
+			}
+		case ir.OCALL:
+			op := m.(*ir.CallExpr).X.Op()
+			if op != ir.OFUNCINST {
+				assert(op == ir.OMETHVALUE || op == ir.OCLOSURE || op == ir.ODYNAMICDOTTYPE || op == ir.ODYNAMICDOTTYPE2)
+				transformCall(m.(*ir.CallExpr))
+			}
+
+		case ir.OCONVIFACE:
+			if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
+				// Was T->interface{}, after stenciling it is now interface{}->interface{}.
+				// No longer need the conversion. See issue 48276.
+				m.(*ir.ConvExpr).SetOp(ir.OCONVNOP)
+				break
+			}
+			mce := m.(*ir.ConvExpr)
+			// Note: x's argument is still typed as a type parameter.
+			// m's argument now has an instantiated type.
+			if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) {
+				m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type())
+			}
+		case ir.ODOTTYPE, ir.ODOTTYPE2:
+			if !m.Type().HasShape() {
+				break
+			}
+			dt := m.(*ir.TypeAssertExpr)
+			var rt ir.Node
+			if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() {
+				ix := findDictType(info, m.Type())
+				assert(ix >= 0)
+				rt = getDictionaryType(info, info.dictParam, dt.Pos(), ix)
+			} else {
+				// nonempty interface to noninterface. Need an itab.
+				ix := -1
+				for i, ic := range info.dictInfo.itabConvs {
+					if ic == m {
+						ix = info.dictInfo.startItabConv + i
+						break
+					}
+				}
+				assert(ix >= 0)
+				rt = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
+			}
+			op := ir.ODYNAMICDOTTYPE
+			if m.Op() == ir.ODOTTYPE2 {
+				op = ir.ODYNAMICDOTTYPE2
+			}
+			m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt)
+			m.SetType(dt.Type())
+			m.SetTypecheck(1)
+		case ir.OCASE:
+			if _, ok := m.(*ir.CommClause); ok {
+				// This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE?
+				break
+			}
+			m := m.(*ir.CaseClause)
+			for i, c := range m.List {
+				if c.Op() == ir.OTYPE && c.Type().HasShape() {
+					// Use a *runtime._type for the dynamic type.
+					ix := findDictType(info, m.List[i].Type())
+					assert(ix >= 0)
+					dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen))
+
+					// For type switch from nonempty interfaces to non-interfaces, we need an itab as well.
+					if !m.List[i].Type().IsInterface() {
+						if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok {
+							// Type switch from nonempty interface. We need a *runtime.itab
+							// for the dynamic type.
+							ix := -1
+							for i, ic := range info.dictInfo.itabConvs {
+								if ic == m.List[i] {
+									ix = info.dictInfo.startItabConv + i
+									break
+								}
+							}
+							assert(ix >= 0)
+							dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
+						}
+					}
+					typed(m.List[i].Type(), dt)
+					m.List[i] = dt
+				}
+			}
+
+		}
+		return m
+	}
+	edit(info.fun)
+	ir.CurFunc = savef
+}
+
 // findDictType looks for type t in the typeparams or derived types in the generic
 // function info.gfInfo. This will indicate the dictionary entry with the
 // correct concrete type for the associated instantiated function.
 func findDictType(info *instInfo, t *types.Type) int {
-	for i, dt := range info.gfInfo.tparams {
+	for i, dt := range info.dictInfo.shapeParams {
 		if dt == t {
 			return i
 		}
 	}
-	for i, dt := range info.gfInfo.derivedTypes {
-		if types.Identical(dt, t) {
-			return i + len(info.gfInfo.tparams)
+	for i, dt := range info.dictInfo.derivedTypes {
+		if types.IdenticalStrict(dt, t) {
+			return i + len(info.dictInfo.shapeParams)
 		}
 	}
 	return -1
 }
 
-// convertUsingDictionary converts value v from instantiated type src to an interface
-// type dst, by returning a new set of nodes that make use of a dictionary entry. src
-// is the generic (not shape) type, and gn is the original generic node of the
-// CONVIFACE node or XDOT node (for a bound method call) that is causing the
+// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface
+// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the
+// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the
 // conversion.
-func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, gn ir.Node, dst, src *types.Type) ir.Node {
-	assert(src.HasTParam() || src.IsInterface() && gn.Type().HasTParam())
+func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node {
+	assert(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape())
 	assert(dst.IsInterface())
 
 	if v.Type().IsInterface() {
@@ -1288,8 +1344,7 @@
 			v.SetTypecheck(1)
 			return v
 		}
-		gdst := gn.Type() // pre-stenciled destination type
-		if !gdst.HasTParam() {
+		if !in.Type().HasShape() {
 			// Regular OCONVIFACE works if the destination isn't parameterized.
 			v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
 			v.SetTypecheck(1)
@@ -1311,7 +1366,7 @@
 		types.CalcSize(fn.Type())
 		call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil)
 		typed(types.Types[types.TUINT8].PtrTo(), call)
-		ix := findDictType(info, gdst)
+		ix := findDictType(info, in.Type())
 		assert(ix >= 0)
 		inter := getDictionaryType(info, dictParam, pos, ix)
 		call.Args = []ir.Node{inter, itab}
@@ -1327,16 +1382,16 @@
 		// will be more efficient than converting to an empty interface first
 		// and then type asserting to dst.
 		ix := -1
-		for i, ic := range info.gfInfo.itabConvs {
-			if ic == gn {
-				ix = info.startItabConv + i
+		for i, ic := range info.dictInfo.itabConvs {
+			if ic == in {
+				ix = info.dictInfo.startItabConv + i
 				break
 			}
 		}
 		assert(ix >= 0)
-		rt = getDictionaryEntry(pos, dictParam, ix, info.dictLen)
+		rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen)
 	} else {
-		ix := findDictType(info, src)
+		ix := findDictType(info, v.Type())
 		assert(ix >= 0)
 		// Load the actual runtime._type of the type parameter from the dictionary.
 		rt = getDictionaryType(info, dictParam, pos, ix)
@@ -1424,6 +1479,7 @@
 	} else {
 		// TODO: This is somewhat overkill, we really only need it
 		// for types that are put into interfaces.
+		// Note: this relocation is also used in cmd/link/internal/ld/dwarf.go
 		reflectdata.MarkTypeUsedInInterface(t, lsym)
 	}
 }
@@ -1452,8 +1508,6 @@
 		return sym
 	}
 
-	info := g.getGfInfo(gf)
-
 	infoPrint("=== Creating dictionary %v\n", sym.Name)
 	off := 0
 	// Emit an entry for each targ (concrete type or gcshape).
@@ -1463,8 +1517,12 @@
 		off = objw.SymPtr(lsym, off, s, 0)
 		markTypeUsed(t, lsym)
 	}
+
+	instInfo := g.getInstantiation(gf, targs, isMeth)
+	info := instInfo.dictInfo
+
 	subst := typecheck.Tsubster{
-		Tparams: info.tparams,
+		Tparams: info.shapeParams,
 		Targs:   targs,
 	}
 	// Emit an entry for each derived type (after substituting targs)
@@ -1479,18 +1537,33 @@
 	for _, n := range info.subDictCalls {
 		var sym *types.Sym
 		switch n.Op() {
-		case ir.OCALL:
+		case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
 			call := n.(*ir.CallExpr)
-			if call.X.Op() == ir.OXDOT {
+			if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH {
 				var nameNode *ir.Name
 				se := call.X.(*ir.SelectorExpr)
-				if types.IsInterfaceMethod(se.Selection.Type) {
+				if se.X.Type().IsShape() {
 					// This is a method call enabled by a type bound.
+
+					// We need this extra check for type expressions, which
+					// don't add in the implicit XDOTs.
 					tmpse := ir.NewSelectorExpr(base.Pos, ir.OXDOT, se.X, se.Sel)
 					tmpse = typecheck.AddImplicitDots(tmpse)
 					tparam := tmpse.X.Type()
-					assert(tparam.IsTypeParam())
-					recvType := targs[tparam.Index()]
+					if !tparam.IsShape() {
+						// The method expression is not
+						// really on a typeparam.
+						break
+					}
+					ix := -1
+					for i, shape := range info.shapeParams {
+						if shape == tparam {
+							ix = i
+							break
+						}
+					}
+					assert(ix >= 0)
+					recvType := targs[ix]
 					if recvType.IsInterface() || len(recvType.RParams()) == 0 {
 						// No sub-dictionary entry is
 						// actually needed, since the
@@ -1509,8 +1582,10 @@
 				} else {
 					// This is the case of a normal
 					// method call on a generic type.
-					nameNode = call.X.(*ir.SelectorExpr).Selection.Nname.(*ir.Name)
-					subtargs := deref(call.X.(*ir.SelectorExpr).X.Type()).RParams()
+					recvType := deref(call.X.(*ir.SelectorExpr).X.Type())
+					genRecvType := recvType.OrigSym().Def.Type()
+					nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
+					subtargs := recvType.RParams()
 					s2targs := make([]*types.Type, len(subtargs))
 					for i, t := range subtargs {
 						s2targs[i] = subst.Typ(t)
@@ -1543,14 +1618,16 @@
 			}
 			sym = g.getDictionarySym(nameNode, subtargs, false)
 
-		case ir.OXDOT:
+		case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
 			selExpr := n.(*ir.SelectorExpr)
-			subtargs := deref(selExpr.X.Type()).RParams()
+			recvType := deref(selExpr.Selection.Type.Recv().Type)
+			genRecvType := recvType.OrigSym().Def.Type()
+			subtargs := recvType.RParams()
 			s2targs := make([]*types.Type, len(subtargs))
 			for i, t := range subtargs {
 				s2targs[i] = subst.Typ(t)
 			}
-			nameNode := selExpr.Selection.Nname.(*ir.Name)
+			nameNode := typecheck.Lookdot1(selExpr, selExpr.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
 			sym = g.getDictionarySym(nameNode, s2targs, true)
 
 		default:
@@ -1567,11 +1644,13 @@
 		}
 	}
 
+	g.instantiateMethods()
 	delay := &delayInfo{
-		gf:    gf,
-		targs: targs,
-		sym:   sym,
-		off:   off,
+		gf:     gf,
+		targs:  targs,
+		sym:    sym,
+		off:    off,
+		isMeth: isMeth,
 	}
 	g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
 	return sym
@@ -1587,10 +1666,11 @@
 		infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
 
 		lsym := d.sym.Linksym()
-		info := g.getGfInfo(d.gf)
+		instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth)
+		info := instInfo.dictInfo
 
 		subst := typecheck.Tsubster{
-			Tparams: info.tparams,
+			Tparams: info.shapeParams,
 			Targs:   d.targs,
 		}
 
@@ -1598,10 +1678,10 @@
 		for _, n := range info.itabConvs {
 			var srctype, dsttype *types.Type
 			switch n.Op() {
-			case ir.OXDOT:
+			case ir.OXDOT, ir.OMETHVALUE:
 				se := n.(*ir.SelectorExpr)
 				srctype = subst.Typ(se.X.Type())
-				dsttype = subst.Typ(se.X.Type().Bound())
+				dsttype = subst.Typ(info.shapeToBound[se.X.Type()])
 				found := false
 				for i, m := range dsttype.AllMethods().Slice() {
 					if se.Sel == m.Sym {
@@ -1632,6 +1712,9 @@
 				d.off = objw.Uintptr(lsym, d.off, 0)
 				infoPrint(" + Unused itab entry for %v\n", srctype)
 			} else {
+				// Make sure all new fully-instantiated types have
+				// their methods created before generating any itabs.
+				g.instantiateMethods()
 				itabLsym := reflectdata.ITabLsym(srctype, dsttype)
 				d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
 				infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype)
@@ -1670,65 +1753,50 @@
 	return np
 }
 
-// hasTParamNodes returns true if the type of any node in targs has a typeparam.
-func hasTParamNodes(targs []ir.Node) bool {
+// hasShapeNodes returns true if the type of any node in targs has a shape.
+func hasShapeNodes(targs []ir.Node) bool {
 	for _, n := range targs {
-		if n.Type().HasTParam() {
+		if n.Type().HasShape() {
 			return true
 		}
 	}
 	return false
 }
 
-// hasTParamNodes returns true if any type in targs has a typeparam.
-func hasTParamTypes(targs []*types.Type) bool {
+// hasShapeTypes returns true if any type in targs has a shape.
+func hasShapeTypes(targs []*types.Type) bool {
 	for _, t := range targs {
-		if t.HasTParam() {
+		if t.HasShape() {
 			return true
 		}
 	}
 	return false
 }
 
-// getGfInfo get information for a generic function - type params, derived generic
-// types, and subdictionaries.
-func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
-	infop := g.gfInfoMap[gn.Sym()]
-	if infop != nil {
-		return infop
-	}
+// getInstInfo get the dictionary format for a function instantiation- type params, derived
+// types, and needed subdictionaries and itabs.
+func (g *irgen) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
+	info := instInfo.dictInfo
+	info.shapeParams = shapes
 
-	checkFetchBody(gn)
-	var info gfInfo
-	gf := gn.Func
-	recv := gf.Type().Recv()
-	if recv != nil {
-		info.tparams = deref(recv.Type).RParams()
-	} else {
-		tparams := gn.Type().TParams().FieldSlice()
-		info.tparams = make([]*types.Type, len(tparams))
-		for i, f := range tparams {
-			info.tparams[i] = f.Type
-		}
-	}
-
-	for _, t := range info.tparams {
-		b := t.Bound()
-		if b.HasTParam() {
+	for _, t := range info.shapeParams {
+		b := info.shapeToBound[t]
+		if b.HasShape() {
 			// If a type bound is parameterized (unusual case), then we
 			// may need its derived type to do a type assert when doing a
 			// bound call for a type arg that is an interface.
-			addType(&info, nil, b)
+			addType(info, nil, b)
 		}
 	}
 
-	for _, n := range gf.Dcl {
-		addType(&info, n, n.Type())
+	for _, n := range st.Dcl {
+		addType(info, n, n.Type())
+		n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
 	}
 
 	if infoPrintMode {
-		fmt.Printf(">>> GfInfo for %v\n", gn)
-		for _, t := range info.tparams {
+		fmt.Printf(">>> InstInfo for %v\n", st)
+		for _, t := range info.shapeParams {
 			fmt.Printf("  Typeparam %v\n", t)
 		}
 	}
@@ -1736,14 +1804,14 @@
 	var visitFunc func(ir.Node)
 	visitFunc = func(n ir.Node) {
 		if n.Op() == ir.OFUNCINST && !n.(*ir.InstExpr).Implicit() {
-			if hasTParamNodes(n.(*ir.InstExpr).Targs) {
+			if hasShapeNodes(n.(*ir.InstExpr).Targs) {
 				infoPrint("  Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
 				info.subDictCalls = append(info.subDictCalls, n)
 			}
-		} else if n.Op() == ir.OXDOT && !n.(*ir.SelectorExpr).Implicit() &&
-			n.(*ir.SelectorExpr).Selection != nil &&
+		} else if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && !n.(*ir.SelectorExpr).Implicit() &&
+			!types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
 			len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
-			if hasTParamTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
+			if hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
 				if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
 					infoPrint("  Closure&subdictionary required at generic meth expr %v\n", n)
 				} else {
@@ -1754,34 +1822,33 @@
 		}
 		if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
 			n.(*ir.CallExpr).X.(*ir.InstExpr).SetImplicit(true)
-			if hasTParamNodes(n.(*ir.CallExpr).X.(*ir.InstExpr).Targs) {
+			if hasShapeNodes(n.(*ir.CallExpr).X.(*ir.InstExpr).Targs) {
 				infoPrint("  Subdictionary at generic function/method call: %v - %v\n", n.(*ir.CallExpr).X.(*ir.InstExpr).X, n)
 				info.subDictCalls = append(info.subDictCalls, n)
 			}
 		}
-		if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT &&
-			n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil &&
+		if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH &&
+			//n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil &&
 			len(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
 			n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
-			if hasTParamTypes(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) {
+			if hasShapeTypes(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) {
 				infoPrint("  Subdictionary at generic method call: %v\n", n)
 				info.subDictCalls = append(info.subDictCalls, n)
 			}
 		}
 		if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT &&
-			n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil &&
-			deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).IsTypeParam() {
+			isShapeDeref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()) {
 			n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
 			infoPrint("  Optional subdictionary at generic bound call: %v\n", n)
 			info.subDictCalls = append(info.subDictCalls, n)
 		}
 		if n.Op() == ir.OCONVIFACE && n.Type().IsInterface() &&
 			!n.Type().IsEmptyInterface() &&
-			n.(*ir.ConvExpr).X.Type().HasTParam() {
+			n.(*ir.ConvExpr).X.Type().HasShape() {
 			infoPrint("  Itab for interface conv: %v\n", n)
 			info.itabConvs = append(info.itabConvs, n)
 		}
-		if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsTypeParam() {
+		if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsShape() {
 			infoPrint("  Itab for bound call: %v\n", n)
 			info.itabConvs = append(info.itabConvs, n)
 		}
@@ -1793,14 +1860,18 @@
 			// Visit the closure body and add all relevant entries to the
 			// dictionary of the outer function (closure will just use
 			// the dictionary of the outer function).
-			for _, n1 := range n.(*ir.ClosureExpr).Func.Body {
+			cfunc := n.(*ir.ClosureExpr).Func
+			for _, n1 := range cfunc.Body {
 				ir.Visit(n1, visitFunc)
 			}
+			for _, n := range cfunc.Dcl {
+				n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
+			}
 		}
 		if n.Op() == ir.OSWITCH && n.(*ir.SwitchStmt).Tag != nil && n.(*ir.SwitchStmt).Tag.Op() == ir.OTYPESW && !n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
 			for _, cc := range n.(*ir.SwitchStmt).Cases {
 				for _, c := range cc.List {
-					if c.Op() == ir.OTYPE && c.Type().HasTParam() {
+					if c.Op() == ir.OTYPE && c.Type().HasShape() {
 						// Type switch from a non-empty interface - might need an itab.
 						infoPrint("  Itab for type switch: %v\n", c)
 						info.itabConvs = append(info.itabConvs, c)
@@ -1812,41 +1883,48 @@
 				}
 			}
 		}
-		addType(&info, n, n.Type())
+		addType(info, n, n.Type())
 	}
 
-	for _, stmt := range gf.Body {
+	for _, stmt := range st.Body {
 		ir.Visit(stmt, visitFunc)
 	}
 	if infoPrintMode {
 		for _, t := range info.derivedTypes {
 			fmt.Printf("  Derived type %v\n", t)
 		}
-		fmt.Printf(">>> Done Gfinfo\n")
+		fmt.Printf(">>> Done Instinfo\n")
 	}
-	g.gfInfoMap[gn.Sym()] = &info
-	return &info
+	info.startSubDict = len(info.shapeParams) + len(info.derivedTypes)
+	info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls)
+	info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
+}
+
+// isShapeDeref returns true if t is either a shape or a pointer to a shape. (We
+// can't just use deref(t).IsShape(), since a shape type is a complex type and may
+// have a pointer as part of its shape.)
+func isShapeDeref(t *types.Type) bool {
+	return t.IsShape() || t.IsPtr() && t.Elem().IsShape()
 }
 
 // addType adds t to info.derivedTypes if it is parameterized type (which is not
-// just a simple type param) that is different from any existing type on
+// just a simple shape) that is different from any existing type on
 // info.derivedTypes.
-func addType(info *gfInfo, n ir.Node, t *types.Type) {
-	if t == nil || !t.HasTParam() {
+func addType(info *dictInfo, n ir.Node, t *types.Type) {
+	if t == nil || !t.HasShape() {
 		return
 	}
-	if t.IsTypeParam() && t.Underlying() == t {
+	if t.IsShape() {
 		return
 	}
 	if t.Kind() == types.TFUNC && n != nil &&
-		(t.Recv() != nil ||
-			n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
+		(t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
 		// Don't use the type of a named generic function or method,
 		// since that is parameterized by other typeparams.
 		// (They all come from arguments of a FUNCINST node.)
 		return
 	}
-	if doubleCheck && !parameterizedBy(t, info.tparams) {
+	if doubleCheck && !parameterizedBy(t, info.shapeParams) {
 		base.Fatalf("adding type with invalid parameters %+v", t)
 	}
 	if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
@@ -1855,7 +1933,7 @@
 	}
 	// Ignore a derived type we've already added.
 	for _, et := range info.derivedTypes {
-		if types.Identical(t, et) {
+		if types.IdenticalStrict(t, et) {
 			return
 		}
 	}
@@ -1881,8 +1959,7 @@
 		}
 		return true
 	}
-	switch t.Kind() {
-	case types.TTYPEPARAM:
+	if t.IsShape() {
 		// Check if t is one of the allowed parameters in scope.
 		for _, p := range params {
 			if p == t {
@@ -1892,6 +1969,8 @@
 		// Couldn't find t in the list of allowed parameters.
 		return false
 
+	}
+	switch t.Kind() {
 	case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
 		return parameterizedBy1(t.Elem(), params, visited)
 
@@ -1982,17 +2061,17 @@
 }
 
 // assertToBound returns a new node that converts a node rcvr with interface type to
-// the 'dst' interface type.  bound is the unsubstituted form of dst.
-func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, bound, dst *types.Type) ir.Node {
-	if bound.HasTParam() {
-		ix := findDictType(info, bound)
+// the 'dst' interface type.
+func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node {
+	if dst.HasShape() {
+		ix := findDictType(info, dst)
 		assert(ix >= 0)
 		rt := getDictionaryType(info, dictVar, pos, ix)
 		rcvr = ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt)
 		typed(dst, rcvr)
 	} else {
 		rcvr = ir.NewTypeAssertExpr(pos, rcvr, nil)
-		typed(bound, rcvr)
+		typed(dst, rcvr)
 	}
 	return rcvr
 }
@@ -2004,9 +2083,8 @@
 //
 // The returned closure is fully substituted and has already had any needed
 // transformations done.
-func (g *irgen) buildClosure2(subst *subster, m, x ir.Node) ir.Node {
-	outer := subst.newf
-	info := subst.info
+func (g *irgen) buildClosure2(info *instInfo, m ir.Node) ir.Node {
+	outer := info.fun
 	pos := m.Pos()
 	typ := m.Type() // type of the closure
 
@@ -2029,24 +2107,18 @@
 	rcvr := args[0]
 	args = args[1:]
 	assert(m.(*ir.SelectorExpr).X.Type().IsShape())
-	gsrc := x.(*ir.SelectorExpr).X.Type()
-	bound := gsrc.Bound()
-	dst := bound
-	if dst.HasTParam() {
-		dst = subst.ts.Typ(bound)
-	}
+	dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
 	if m.(*ir.SelectorExpr).X.Type().IsInterface() {
 		// If type arg is an interface (unusual case), we do a type assert to
 		// the type bound.
-		rcvr = assertToBound(info, dictVar, pos, rcvr, bound, dst)
+		rcvr = assertToBound(info, dictVar, pos, rcvr, dst)
 	} else {
-		rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, x, dst, gsrc)
+		rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst)
 	}
-	dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, x.(*ir.SelectorExpr).Sel)
+	dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, m.(*ir.SelectorExpr).Sel)
 	dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1)
 
-	// Do a type substitution on the generic bound, in case it is parameterized.
-	typed(subst.ts.Typ(x.(*ir.SelectorExpr).Selection.Type), dot)
+	typed(dot.Selection.Type, dot)
 	innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args)
 	t := m.Type()
 	if t.NumResults() == 0 {
diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go
index 146761c..805a471 100644
--- a/src/cmd/compile/internal/noder/stmt.go
+++ b/src/cmd/compile/internal/noder/stmt.go
@@ -37,16 +37,12 @@
 	case *syntax.BlockStmt:
 		return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt))
 	case *syntax.ExprStmt:
-		return g.expr(stmt.X)
+		return wrapname(g.pos(stmt.X), g.expr(stmt.X))
 	case *syntax.SendStmt:
 		n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
-		if n.Chan.Type().HasTParam() || n.Value.Type().HasTParam() {
-			// Delay transforming the send if the channel or value
-			// have a type param.
-			n.SetTypecheck(3)
-			return n
+		if !delayTransform() {
+			transformSend(n)
 		}
-		transformSend(n)
 		n.SetTypecheck(1)
 		return n
 	case *syntax.DeclStmt:
@@ -66,11 +62,9 @@
 				lhs := g.expr(stmt.Lhs)
 				n = ir.NewAssignOpStmt(g.pos(stmt), op, lhs, rhs)
 			}
-			if n.X.Typecheck() == 3 {
-				n.SetTypecheck(3)
-				return n
+			if !delayTransform() {
+				transformAsOp(n)
 			}
-			transformAsOp(n)
 			n.SetTypecheck(1)
 			return n
 		}
@@ -79,49 +73,24 @@
 		rhs := g.exprList(stmt.Rhs)
 		names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
 
-		// We must delay transforming the assign statement if any of the
-		// lhs or rhs nodes are also delayed, since transformAssign needs
-		// to know the types of the left and right sides in various cases.
-		delay := false
-		for _, e := range lhs {
-			if e.Typecheck() == 3 {
-				delay = true
-				break
-			}
-		}
-		for _, e := range rhs {
-			if e.Typecheck() == 3 {
-				delay = true
-				break
-			}
-		}
-
 		if len(lhs) == 1 && len(rhs) == 1 {
 			n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
 			n.Def = initDefn(n, names)
 
-			if delay {
-				earlyTransformAssign(n, lhs, rhs)
+			if !delayTransform() {
+				lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
+				transformAssign(n, lhs, rhs)
 				n.X, n.Y = lhs[0], rhs[0]
-				n.SetTypecheck(3)
-				return n
 			}
-
-			lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
-			transformAssign(n, lhs, rhs)
-			n.X, n.Y = lhs[0], rhs[0]
 			n.SetTypecheck(1)
 			return n
 		}
 
 		n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
 		n.Def = initDefn(n, names)
-		if delay {
-			earlyTransformAssign(n, lhs, rhs)
-			n.SetTypecheck(3)
-			return n
+		if !delayTransform() {
+			transformAssign(n, n.Lhs, n.Rhs)
 		}
-		transformAssign(n, n.Lhs, n.Rhs)
 		n.SetTypecheck(1)
 		return n
 
@@ -131,21 +100,9 @@
 		return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
 	case *syntax.ReturnStmt:
 		n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
-		for _, e := range n.Results {
-			if e.Type().HasTParam() {
-				// Delay transforming the return statement if any of the
-				// return values have a type param.
-				if !ir.HasNamedResults(ir.CurFunc) {
-					transformArgs(n)
-					// But add CONVIFACE nodes where needed if
-					// any of the return values have interface type.
-					typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, true)
-				}
-				n.SetTypecheck(3)
-				return n
-			}
+		if !delayTransform() {
+			transformReturn(n)
 		}
-		transformReturn(n)
 		n.SetTypecheck(1)
 		return n
 	case *syntax.IfStmt:
@@ -154,7 +111,10 @@
 		return g.forStmt(stmt)
 	case *syntax.SelectStmt:
 		n := g.selectStmt(stmt)
-		transformSelect(n.(*ir.SelectStmt))
+
+		if !delayTransform() {
+			transformSelect(n.(*ir.SelectStmt))
+		}
 		n.SetTypecheck(1)
 		return n
 	case *syntax.SwitchStmt:
diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go
index b278f3d..953036e 100644
--- a/src/cmd/compile/internal/noder/transform.go
+++ b/src/cmd/compile/internal/noder/transform.go
@@ -157,7 +157,7 @@
 		n.SetOp(ir.OCALLFUNC)
 	}
 
-	typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, false)
+	typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args)
 	if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 {
 		typecheck.FixMethodCall(n)
 	}
@@ -195,7 +195,7 @@
 			aop, _ := typecheck.Assignop(lt, rt)
 			if aop != ir.OXXX {
 				types.CalcSize(lt)
-				if lt.HasTParam() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 {
+				if lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 {
 					l = ir.NewConvExpr(base.Pos, aop, rt, l)
 					l.SetTypecheck(1)
 				}
@@ -365,59 +365,6 @@
 	}
 }
 
-// Version of transformAssign that can run on generic code that adds CONVIFACE calls
-// as needed (and rewrites multi-value calls).
-func earlyTransformAssign(stmt ir.Node, lhs, rhs []ir.Node) {
-	cr := len(rhs)
-	if len(rhs) == 1 {
-		if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() {
-			cr = rtyp.NumFields()
-		}
-	}
-
-	// x,y,z = f()
-	_, isCallExpr := rhs[0].(*ir.CallExpr)
-	if isCallExpr && cr > len(rhs) {
-		stmt := stmt.(*ir.AssignListStmt)
-		stmt.SetOp(ir.OAS2FUNC)
-		r := rhs[0].(*ir.CallExpr)
-		rtyp := r.Type()
-
-		mismatched := false
-		failed := false
-		for i := range lhs {
-			result := rtyp.Field(i).Type
-
-			if lhs[i].Type() == nil || result == nil {
-				failed = true
-			} else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) {
-				mismatched = true
-			}
-		}
-		if mismatched && !failed {
-			typecheck.RewriteMultiValueCall(stmt, r)
-		}
-		return
-	}
-
-	// x, ok = y
-	if len(lhs) != len(rhs) {
-		assert(len(lhs) == 2 && len(rhs) == 1)
-		// TODO(danscales): deal with case where x or ok is an interface
-		// type. We want to add CONVIFACE now, but that is tricky, because
-		// the rhs may be AS2MAPR, AS2RECV, etc. which has two result values,
-		// and that is not rewritten until the order phase (o.stmt, as2ok).
-		return
-	}
-
-	// Check for interface conversion on each assignment
-	for i, r := range rhs {
-		if lhs[i].Type() != nil && lhs[i].Type().IsInterface() {
-			rhs[i] = assignconvfn(r, lhs[i].Type())
-		}
-	}
-}
-
 // Corresponds to typecheck.typecheckargs.  Really just deals with multi-value calls.
 func transformArgs(n ir.InitNode) {
 	var list []ir.Node
@@ -457,11 +404,15 @@
 		return n
 	}
 
-	if types.Identical(n.Type(), t) {
+	if n.Op() == ir.OPAREN {
+		n = n.(*ir.ParenExpr).X
+	}
+
+	if types.IdenticalStrict(n.Type(), t) {
 		return n
 	}
 
-	op, why := typecheck.Assignop(n.Type(), t)
+	op, why := Assignop(n.Type(), t)
 	if op == ir.OXXX {
 		base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why)
 	}
@@ -472,11 +423,35 @@
 	return r
 }
 
+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 (taking shapes into account)
+	if types.Identical(src, dst) {
+		// We already know from assignconvfn above that IdenticalStrict(src,
+		// dst) is false, so the types are not exactly the same and one of
+		// src or dst is a shape. If dst is an interface (which means src is
+		// an interface too), we need a real OCONVIFACE op; otherwise we need a
+		// OCONVNOP. See issue #48453.
+		if dst.IsInterface() {
+			return ir.OCONVIFACE, ""
+		} else {
+			return ir.OCONVNOP, ""
+		}
+	}
+	return typecheck.Assignop1(src, dst)
+}
+
 // Corresponds to typecheck.typecheckaste, but we add an extra flag convifaceOnly
 // only. If convifaceOnly is true, we only do interface conversion. We use this to do
 // early insertion of CONVIFACE nodes during noder2, when the function or args may
 // have typeparams.
-func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes, convifaceOnly bool) {
+func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) {
 	var t *types.Type
 	var i int
 
@@ -495,7 +470,7 @@
 			if isddd {
 				n = nl[i]
 				ir.SetPos(n)
-				if n.Type() != nil && (!convifaceOnly || t.IsInterface()) {
+				if n.Type() != nil {
 					nl[i] = assignconvfn(n, t)
 				}
 				return
@@ -505,7 +480,7 @@
 			for ; i < len(nl); i++ {
 				n = nl[i]
 				ir.SetPos(n)
-				if n.Type() != nil && (!convifaceOnly || t.IsInterface()) {
+				if n.Type() != nil {
 					nl[i] = assignconvfn(n, t.Elem())
 				}
 			}
@@ -514,7 +489,7 @@
 
 		n = nl[i]
 		ir.SetPos(n)
-		if n.Type() != nil && (!convifaceOnly || t.IsInterface()) {
+		if n.Type() != nil {
 			nl[i] = assignconvfn(n, t)
 		}
 		i++
@@ -536,7 +511,7 @@
 		return
 	}
 
-	typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl, false)
+	typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl)
 }
 
 // transformSelect transforms a select node, creating an assignment list as needed
@@ -554,6 +529,7 @@
 				}
 				selrecv.Def = def
 				selrecv.SetTypecheck(1)
+				selrecv.SetInit(n.Init())
 				ncase.Comm = selrecv
 			}
 			switch n.Op() {
diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go
index 5c9aafe..03fb96c 100644
--- a/src/cmd/compile/internal/noder/types.go
+++ b/src/cmd/compile/internal/noder/types.go
@@ -91,7 +91,7 @@
 		// since that is the only use of a generic type that doesn't
 		// involve instantiation. We just translate the named type in the
 		// normal way below using g.obj().
-		if typ.TParams() != nil && typ.TArgs() != nil {
+		if typ.TypeParams() != nil && typ.TypeArgs() != nil {
 			// typ is an instantiation of a defined (named) generic type.
 			// This instantiation should also be a defined (named) type.
 			// types2 gives us the substituted type in t.Underlying()
@@ -101,7 +101,7 @@
 			//
 			// When converted to types.Type, typ has a unique name,
 			// based on the names of the type arguments.
-			instName := g.instTypeName2(typ.Obj().Name(), typ.TArgs())
+			instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs())
 			s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
 			if s.Def != nil {
 				// We have already encountered this instantiation.
@@ -135,7 +135,7 @@
 			// non-generic types used to instantiate this type. We'll
 			// use these when instantiating the methods of the
 			// instantiated type.
-			targs := typ.TArgs()
+			targs := typ.TypeArgs()
 			rparams := make([]*types.Type, targs.Len())
 			for i := range rparams {
 				rparams[i] = g.typ1(targs.At(i))
@@ -272,7 +272,7 @@
 // instantiated types, and for actually generating the methods for instantiated
 // types.
 func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
-	targs2 := typ.TArgs()
+	targs2 := typ.TypeArgs()
 	targs := make([]*types.Type, targs2.Len())
 	for i := range targs {
 		targs[i] = g.typ1(targs2.At(i))
@@ -296,7 +296,7 @@
 			// generic type, so we have to do a substitution to get
 			// the name/type of the method of the instantiated type,
 			// using m.Type().RParams() and typ.TArgs()
-			inst2 := g.instTypeName2("", typ.TArgs())
+			inst2 := g.instTypeName2("", typ.TypeArgs())
 			name := meth.Sym().Name
 			i1 := strings.Index(name, "[")
 			i2 := strings.Index(name[i1:], "]")
@@ -309,7 +309,7 @@
 				meth2 = newsym.Def.(*ir.Name)
 			} else {
 				meth2 = ir.NewNameAt(meth.Pos(), newsym)
-				rparams := types2.AsSignature(m.Type()).RParams()
+				rparams := types2.AsSignature(m.Type()).RecvTypeParams()
 				tparams := make([]*types.Type, rparams.Len())
 				for i := range tparams {
 					tparams[i] = g.typ1(rparams.At(i))
@@ -336,7 +336,7 @@
 }
 
 func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
-	tparams2 := sig.TParams()
+	tparams2 := sig.TypeParams()
 	tparams := make([]*types.Field, tparams2.Len())
 	for i := range tparams {
 		tp := tparams2.At(i).Obj()
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index bf63608..3d4650a 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -78,12 +78,12 @@
 		base.Errorf("cannot use -G and -d=quirksmode together")
 	}
 
-	newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
+	newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
 		pr := newPkgDecoder(pkg1.Path, data)
 
 		// Read package descriptors for both types2 and compiler backend.
 		readPackage(newPkgReader(pr), pkg1)
-		pkg2 = readPackage2(check, packages, pr)
+		pkg2 = readPackage2(env, packages, pr)
 		return
 	}
 
@@ -106,7 +106,6 @@
 	readPackage(localPkgReader, types.LocalPkg)
 
 	r := localPkgReader.newReader(relocMeta, privateRootIdx, syncPrivate)
-	r.ext = r
 	r.pkgInit(types.LocalPkg, target)
 
 	// Type-check any top-level assignments. We ignore non-assignments
@@ -137,6 +136,7 @@
 		}
 	}
 	todoBodies = nil
+	todoBodiesDone = true
 
 	// Check that nothing snuck past typechecking.
 	for _, n := range target.Decls {
@@ -190,7 +190,6 @@
 
 	{
 		w := privateRootWriter
-		w.ext = w
 		w.pkgInit(noders)
 		w.flush()
 	}
diff --git a/src/cmd/compile/internal/noder/unified_test.go b/src/cmd/compile/internal/noder/unified_test.go
index 7f0bca2..d7334df 100644
--- a/src/cmd/compile/internal/noder/unified_test.go
+++ b/src/cmd/compile/internal/noder/unified_test.go
@@ -16,6 +16,7 @@
 )
 
 var (
+	flagCmp      = flag.Bool("cmp", false, "enable TestUnifiedCompare")
 	flagPkgs     = flag.String("pkgs", "std", "list of packages to compare (ignored in -short mode)")
 	flagAll      = flag.Bool("all", false, "enable testing of all GOOS/GOARCH targets")
 	flagParallel = flag.Bool("parallel", false, "test GOOS/GOARCH targets in parallel")
@@ -37,7 +38,12 @@
 // command's -run flag for subtest matching is recommended for less
 // powerful machines.
 func TestUnifiedCompare(t *testing.T) {
-	t.Skip("TODO(#48265): this fails on testing/internal/testdeps, possibly due to type aliases. Fix before merging to master.")
+	// TODO(mdempsky): Either re-enable or delete. Disabled for now to
+	// avoid impeding others' forward progress.
+	if !*flagCmp {
+		t.Skip("skipping TestUnifiedCompare (use -cmp to enable)")
+	}
+
 	targets, err := exec.Command("go", "tool", "dist", "list").Output()
 	if err != nil {
 		t.Fatal(err)
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index 1405c77..6a66bea 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -75,14 +75,6 @@
 
 	encoder
 
-	// For writing out object descriptions, ext points to the extension
-	// writer for where we can write the compiler's private extension
-	// details for the object.
-	//
-	// TODO(mdempsky): This is a little hacky, but works easiest with
-	// the way things are currently.
-	ext *writer
-
 	// TODO(mdempsky): We should be able to prune localsIdx whenever a
 	// scope closes, and then maybe we can just use the same map for
 	// storing the TypeParams too (as their TypeName instead).
@@ -299,16 +291,16 @@
 		// Type aliases can refer to uninstantiated generic types, so we
 		// might see len(TParams) != 0 && len(TArgs) == 0 here.
 		// TODO(mdempsky): Revisit after #46477 is resolved.
-		assert(typ.TParams().Len() == typ.TArgs().Len() || typ.TArgs().Len() == 0)
+		assert(typ.TypeParams().Len() == typ.TypeArgs().Len() || typ.TypeArgs().Len() == 0)
 
 		// TODO(mdempsky): Why do we need to loop here?
 		orig := typ
-		for orig.TArgs() != nil {
+		for orig.TypeArgs() != nil {
 			orig = orig.Orig()
 		}
 
 		w.code(typeNamed)
-		w.obj(orig.Obj(), typ.TArgs())
+		w.obj(orig.Obj(), typ.TypeArgs())
 
 	case *types2.TypeParam:
 		index := func() int {
@@ -345,7 +337,7 @@
 		w.typ(typ.Elem())
 
 	case *types2.Signature:
-		assert(typ.TParams() == nil)
+		assert(typ.TypeParams() == nil)
 		w.code(typeSignature)
 		w.signature(typ)
 
@@ -405,7 +397,7 @@
 	for i := 0; i < typ.NumExplicitMethods(); i++ {
 		m := typ.ExplicitMethod(i)
 		sig := m.Type().(*types2.Signature)
-		assert(sig.TParams() == nil)
+		assert(sig.TypeParams() == nil)
 
 		w.pos(m)
 		w.selector(m)
@@ -504,21 +496,21 @@
 	}
 
 	w := pw.newWriter(relocObj, syncObject1)
-	w.ext = pw.newWriter(relocObjExt, syncObject1)
+	wext := pw.newWriter(relocObjExt, syncObject1)
 	wname := pw.newWriter(relocName, syncObject1)
 	wdict := pw.newWriter(relocObjDict, syncObject1)
 
 	pw.globalsIdx[obj] = w.idx // break cycles
-	assert(w.ext.idx == w.idx)
+	assert(wext.idx == w.idx)
 	assert(wname.idx == w.idx)
 	assert(wdict.idx == w.idx)
 
 	w.dict = dict
-	w.ext.dict = dict
+	wext.dict = dict
 
-	code := w.doObj(obj)
+	code := w.doObj(wext, obj)
 	w.flush()
-	w.ext.flush()
+	wext.flush()
 
 	wname.qualifiedIdent(obj)
 	wname.code(code)
@@ -530,7 +522,7 @@
 	return w.idx
 }
 
-func (w *writer) doObj(obj types2.Object) codeObj {
+func (w *writer) doObj(wext *writer, obj types2.Object) codeObj {
 	if obj.Pkg() != w.p.curpkg {
 		return objStub
 	}
@@ -542,7 +534,8 @@
 
 	case *types2.Const:
 		w.pos(obj)
-		w.value(obj.Type(), obj.Val())
+		w.typ(obj.Type())
+		w.value(obj.Val())
 		return objConst
 
 	case *types2.Func:
@@ -551,10 +544,10 @@
 		sig := obj.Type().(*types2.Signature)
 
 		w.pos(obj)
-		w.typeParamNames(sig.TParams())
+		w.typeParamNames(sig.TypeParams())
 		w.signature(sig)
 		w.pos(decl)
-		w.ext.funcExt(obj)
+		wext.funcExt(obj)
 		return objFunc
 
 	case *types2.TypeName:
@@ -568,16 +561,16 @@
 		}
 
 		named := obj.Type().(*types2.Named)
-		assert(named.TArgs() == nil)
+		assert(named.TypeArgs() == nil)
 
 		w.pos(obj)
-		w.typeParamNames(named.TParams())
-		w.ext.typeExt(obj)
+		w.typeParamNames(named.TypeParams())
+		wext.typeExt(obj)
 		w.typExpr(decl.Type)
 
 		w.len(named.NumMethods())
 		for i := 0; i < named.NumMethods(); i++ {
-			w.method(named.Method(i))
+			w.method(wext, named.Method(i))
 		}
 
 		return objType
@@ -585,7 +578,7 @@
 	case *types2.Var:
 		w.pos(obj)
 		w.typ(obj.Type())
-		w.ext.varExt(obj)
+		wext.varExt(obj)
 		return objVar
 	}
 }
@@ -598,12 +591,6 @@
 	w.typ(tv.Type)
 }
 
-func (w *writer) value(typ types2.Type, val constant.Value) {
-	w.sync(syncValue)
-	w.typ(typ)
-	w.rawValue(val)
-}
-
 // objDict writes the dictionary needed for reading the given object.
 func (w *writer) objDict(obj types2.Object, dict *writerDict) {
 	// TODO(mdempsky): Split objDict into multiple entries? reader.go
@@ -642,7 +629,7 @@
 	assert(len(dict.funcs) == nfuncs)
 }
 
-func (w *writer) typeParamNames(tparams *types2.TParamList) {
+func (w *writer) typeParamNames(tparams *types2.TypeParamList) {
 	w.sync(syncTypeParamNames)
 
 	ntparams := tparams.Len()
@@ -653,7 +640,7 @@
 	}
 }
 
-func (w *writer) method(meth *types2.Func) {
+func (w *writer) method(wext *writer, meth *types2.Func) {
 	decl, ok := w.p.funDecls[meth]
 	assert(ok)
 	sig := meth.Type().(*types2.Signature)
@@ -661,12 +648,12 @@
 	w.sync(syncMethod)
 	w.pos(meth)
 	w.selector(meth)
-	w.typeParamNames(sig.RParams())
+	w.typeParamNames(sig.RecvTypeParams())
 	w.param(sig.Recv())
 	w.signature(sig)
 
 	w.pos(decl) // XXX: Hack to workaround linker limitations.
-	w.ext.funcExt(meth)
+	wext.funcExt(meth)
 }
 
 // qualifiedIdent writes out the name of an object declared at package
@@ -1199,7 +1186,8 @@
 
 			w.code(exprConst)
 			w.pos(pos)
-			w.value(tv.Type, tv.Value)
+			w.typ(tv.Type)
+			w.value(tv.Value)
 
 			// TODO(mdempsky): These details are only important for backend
 			// diagnostics. Explore writing them out separately.
@@ -1677,7 +1665,7 @@
 		obj := w.p.info.Defs[decl.Name].(*types2.Func)
 		sig := obj.Type().(*types2.Signature)
 
-		if sig.RParams() != nil || sig.TParams() != nil {
+		if sig.RecvTypeParams() != nil || sig.TypeParams() != nil {
 			break // skip generic functions
 		}
 
@@ -1711,7 +1699,7 @@
 		// TODO(mdempsky): Revisit after #46477 is resolved.
 		if name.IsAlias() {
 			named, ok := name.Type().(*types2.Named)
-			if ok && named.TParams().Len() != 0 && named.TArgs().Len() == 0 {
+			if ok && named.TypeParams().Len() != 0 && named.TypeArgs().Len() == 0 {
 				break
 			}
 		}
@@ -1858,17 +1846,17 @@
 }
 
 // objTypeParams returns the type parameters on the given object.
-func objTypeParams(obj types2.Object) *types2.TParamList {
+func objTypeParams(obj types2.Object) *types2.TypeParamList {
 	switch obj := obj.(type) {
 	case *types2.Func:
 		sig := obj.Type().(*types2.Signature)
 		if sig.Recv() != nil {
-			return sig.RParams()
+			return sig.RecvTypeParams()
 		}
-		return sig.TParams()
+		return sig.TypeParams()
 	case *types2.TypeName:
 		if !obj.IsAlias() {
-			return obj.Type().(*types2.Named).TParams()
+			return obj.Type().(*types2.Named).TypeParams()
 		}
 	}
 	return nil
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index 11226f6..e366e06 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -1829,6 +1829,9 @@
 	case ssa.OpPPC64CALLstatic:
 		s.Call(v)
 
+	case ssa.OpPPC64CALLtail:
+		s.TailCall(v)
+
 	case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter:
 		p := s.Prog(ppc64.AMOVD)
 		p.From.Type = obj.TYPE_REG
@@ -1980,14 +1983,9 @@
 			p.To.Type = obj.TYPE_BRANCH
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.AJMP)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
 
 	case ssa.BlockPPC64EQ, ssa.BlockPPC64NE,
 		ssa.BlockPPC64LT, ssa.BlockPPC64GE,
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index 183ede7..6dbe3cb 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -7,7 +7,6 @@
 import (
 	"encoding/binary"
 	"fmt"
-	"internal/buildcfg"
 	"os"
 	"sort"
 	"strings"
@@ -1869,15 +1868,11 @@
 	// Disable tailcall for RegabiArgs for now. The IR does not connect the
 	// arguments with the OTAILCALL node, and the arguments are not marshaled
 	// correctly.
-	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) && !buildcfg.Experiment.RegabiArgs && !generic {
-		// 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)))
+	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) && !generic {
+		call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
+		call.Args = ir.ParamNames(tfn.Type())
+		call.IsDDD = tfn.Type().IsVariadic()
+		fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
 	} else {
 		fn.SetWrapper(true) // ignore frame for panic+recover matching
 		var call *ir.CallExpr
@@ -1921,7 +1916,7 @@
 			// Target method uses shaped names.
 			targs2 := make([]*types.Type, len(targs))
 			for i, t := range targs {
-				targs2[i] = typecheck.Shapify(t)
+				targs2[i] = typecheck.Shapify(t, i)
 			}
 			targs = targs2
 
diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go
index 30b6d96..1359b6a 100644
--- a/src/cmd/compile/internal/riscv64/ssa.go
+++ b/src/cmd/compile/internal/riscv64/ssa.go
@@ -272,7 +272,8 @@
 		ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS,
 		ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES,
 		ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD,
-		ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED:
+		ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED,
+		ssa.OpRISCV64FSGNJD:
 		r := v.Reg()
 		r1 := v.Args[0].Reg()
 		r2 := v.Args[1].Reg()
@@ -329,7 +330,7 @@
 		p.SetRestArgs([]obj.Addr{{Type: obj.TYPE_REG, Reg: r3}})
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
-	case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
+	case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
 		ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX,
 		ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS,
 		ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD,
@@ -412,6 +413,8 @@
 		p.To.Reg = v.Reg()
 	case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter:
 		s.Call(v)
+	case ssa.OpRISCV64CALLtail:
+		s.TailCall(v)
 	case ssa.OpRISCV64LoweredWB:
 		p := s.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
@@ -724,14 +727,9 @@
 			p.To.Type = obj.TYPE_BRANCH
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.ARET)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
 	case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ,
 		ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ,
 		ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU:
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
index ddc05b3..deb6c79 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -556,6 +556,8 @@
 		p.To.Reg = v.Reg()
 	case ssa.OpS390XCALLstatic, ssa.OpS390XCALLclosure, ssa.OpS390XCALLinter:
 		s.Call(v)
+	case ssa.OpS390XCALLtail:
+		s.TailCall(v)
 	case ssa.OpS390XLoweredWB:
 		p := s.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
@@ -899,17 +901,11 @@
 			s.Br(s390x.ABR, b.Succs[0].Block())
 		}
 		return
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 		return
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
 		return
-	case ssa.BlockRetJmp:
-		p := s.Prog(s390x.ABR)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
-		return
 	}
 
 	// Handle s390x-specific blocks. These blocks all have a
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index 969fd96..28edfd2 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -66,9 +66,6 @@
 			if !b.Controls[0].Type.IsMemory() {
 				f.Fatalf("retjmp block %s has non-memory control value %s", b, b.Controls[0].LongString())
 			}
-			if b.Aux == nil {
-				f.Fatalf("retjmp block %s has nil Aux field", b)
-			}
 		case BlockPlain:
 			if len(b.Succs) != 1 {
 				f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs))
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index cd8eba4..f87ea5b 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -10,9 +10,11 @@
 	"fmt"
 	"hash/crc32"
 	"internal/buildcfg"
+	"io"
 	"log"
 	"math/rand"
 	"os"
+	"path/filepath"
 	"regexp"
 	"runtime"
 	"sort"
@@ -59,7 +61,7 @@
 		printFunc(f)
 	}
 	f.HTMLWriter.WritePhase("start", "start")
-	if BuildDump != "" && BuildDump == f.Name {
+	if BuildDump[f.Name] {
 		f.dumpFile("build")
 	}
 	if checkEnabled {
@@ -163,25 +165,37 @@
 	phaseName = ""
 }
 
-// dumpFile creates a file from the phase name and function name
-// Dumping is done to files to avoid buffering huge strings before
-// output.
-func (f *Func) dumpFile(phaseName string) {
+// DumpFileForPhase creates a file from the function name and phase name,
+// warning and returning nil if this is not possible.
+func (f *Func) DumpFileForPhase(phaseName string) io.WriteCloser {
 	f.dumpFileSeq++
 	fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, int(f.dumpFileSeq), phaseName)
 	fname = strings.Replace(fname, " ", "_", -1)
 	fname = strings.Replace(fname, "/", "_", -1)
 	fname = strings.Replace(fname, ":", "_", -1)
 
+	if ssaDir := os.Getenv("GOSSADIR"); ssaDir != "" {
+		fname = filepath.Join(ssaDir, fname)
+	}
+
 	fi, err := os.Create(fname)
 	if err != nil {
 		f.Warnl(src.NoXPos, "Unable to create after-phase dump file %s", fname)
-		return
+		return nil
 	}
+	return fi
+}
 
-	p := stringFuncPrinter{w: fi}
-	fprintFunc(p, f)
-	fi.Close()
+// dumpFile creates a file from the phase name and function name
+// Dumping is done to files to avoid buffering huge strings before
+// output.
+func (f *Func) dumpFile(phaseName string) {
+	fi := f.DumpFileForPhase(phaseName)
+	if fi != nil {
+		p := stringFuncPrinter{w: fi}
+		fprintFunc(p, f)
+		fi.Close()
+	}
 }
 
 type pass struct {
@@ -224,7 +238,9 @@
 var BuildDebug int
 var BuildTest int
 var BuildStats int
-var BuildDump string // name of function to dump after initial build of ssa
+var BuildDump map[string]bool = make(map[string]bool) // names of functions to dump after initial build of ssa
+
+var GenssaDump map[string]bool = make(map[string]bool) // names of functions to dump after ssa has been converted to asm
 
 // PhaseOption sets the specified flag in the specified ssa phase,
 // returning empty string if this was successful or a string explaining
@@ -248,7 +264,7 @@
 	switch phase {
 	case "", "help":
 		lastcr := 0
-		phasenames := "    check, all, build, intrinsics"
+		phasenames := "    check, all, build, intrinsics, genssa"
 		for _, p := range passes {
 			pn := strings.Replace(p.name, " ", "_", -1)
 			if len(pn)+len(phasenames)-lastcr > 70 {
@@ -278,6 +294,7 @@
 
 Phase "all" supports flags "time", "mem", and "dump".
 Phase "intrinsics" supports flags "on", "off", and "debug".
+Phase "genssa" (assembly generation) supports the flag "dump".
 
 If the "dump" flag is specified, the output is written on a file named
 <phase>__<function_name>_<seq>.dump; otherwise it is directed to stdout.
@@ -339,10 +356,11 @@
 		case "dump":
 			alldump = val != 0
 			if alldump {
-				BuildDump = valString
+				BuildDump[valString] = true
+				GenssaDump[valString] = true
 			}
 		default:
-			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
+			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/all/{time,mem,dump=function_name})", flag, phase)
 		}
 	}
 
@@ -355,7 +373,7 @@
 		case "debug":
 			IntrinsicsDebug = val
 		default:
-			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
+			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/intrinsics/{on,off,debug})", flag, phase)
 		}
 		return ""
 	}
@@ -368,9 +386,18 @@
 		case "stats":
 			BuildStats = val
 		case "dump":
-			BuildDump = valString
+			BuildDump[valString] = true
 		default:
-			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
+			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/build/{debug,test,stats,dump=function_name})", flag, phase)
+		}
+		return ""
+	}
+	if phase == "genssa" {
+		switch flag {
+		case "dump":
+			GenssaDump[valString] = true
+		default:
+			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/genssa/dump=function_name)", flag, phase)
 		}
 		return ""
 	}
diff --git a/src/cmd/compile/internal/ssa/debug_lines_test.go b/src/cmd/compile/internal/ssa/debug_lines_test.go
new file mode 100644
index 0000000..c5a0fe4
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/debug_lines_test.go
@@ -0,0 +1,213 @@
+// 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 ssa_test
+
+import (
+	"bufio"
+	"bytes"
+	"flag"
+	"runtime"
+	"sort"
+
+	// "flag"
+	"fmt"
+	"internal/testenv"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"reflect"
+	"regexp"
+	"strconv"
+	"testing"
+)
+
+// Matches lines in genssa output that are marked "isstmt", and the parenthesized plus-prefixed line number is a submatch
+var asmLine *regexp.Regexp = regexp.MustCompile(`^\s[vb][0-9]+\s+[0-9]+\s\(\+([0-9]+)\)`)
+
+// this matches e.g.                            `   v123456789   000007   (+9876654310) MOVUPS	X15, ""..autotmp_2-32(SP)`
+
+// Matches lines in genssa output that describe an inlined file (on a Unix filesystem).  Note it expects an unadventurous choice of basename.
+var inlineLine *regexp.Regexp = regexp.MustCompile(`^#\s/.*/[-a-zA-Z0-9_]+\.go:([0-9]+)`)
+
+// this matches e.g.                                 #  /pa/inline-dumpxxxx.go:6
+
+var testGoArchFlag = flag.String("arch", "", "run test for specified architecture")
+
+func testGoArch() string {
+	if *testGoArchFlag == "" {
+		return runtime.GOARCH
+	}
+	return *testGoArchFlag
+}
+
+func TestDebugLines(t *testing.T) {
+	if runtime.GOOS == "windows" {
+		t.Skip("Windows lacks $HOME which complicates workaround for 'missing $GOPATH'") // $HOME needed to work around #43938
+	}
+	// This test is potentially fragile, the goal is that debugging should step properly through "sayhi"
+	// If the blocks are reordered in a way that changes the statement order but execution flows correctly,
+	// then rearrange the expected numbers.  Register abi and not-register-abi also have different sequences,
+	// at least for now.
+
+	switch testGoArch() {
+	case "arm64", "amd64": // register ABI
+		testDebugLines(t, "sayhi.go", "sayhi", []int{8, 9, 10, 11})
+
+	case "arm", "386": // probably not register ABI for a while
+		testDebugLines(t, "sayhi.go", "sayhi", []int{9, 10, 11})
+
+	default: // expect ppc64le and riscv will pick up register ABI soonish, not sure about others
+		t.Skip("skipped for many architectures, also changes w/ register ABI")
+	}
+}
+
+func TestInlineLines(t *testing.T) {
+	if runtime.GOOS == "windows" {
+		t.Skip("Windows lacks $HOME which complicates workaround for 'missing $GOPATH'") // $HOME needed to work around #43938
+	}
+	if runtime.GOARCH != "amd64" && *testGoArchFlag == "" {
+		// As of september 2021, works for everything except mips64, but still potentially fragile
+		t.Skip("only runs for amd64 unless -arch explicitly supplied")
+	}
+
+	want := [][]int{{3}, {4, 10}, {4, 10, 16}, {4, 10}, {4, 11, 16}, {4, 11}, {4}, {5, 10}, {5, 10, 16}, {5, 10}, {5, 11, 16}, {5, 11}, {5}}
+	testInlineStack(t, "inline-dump.go", "f", want)
+}
+
+func compileAndDump(t *testing.T, file, function, moreGCFlags string) []byte {
+	testenv.MustHaveGoBuild(t)
+
+	tmpdir, err := ioutil.TempDir("", "debug_lines_test")
+	if err != nil {
+		panic(fmt.Sprintf("Problem creating TempDir, error %v", err))
+	}
+	if testing.Verbose() {
+		fmt.Printf("Preserving temporary directory %s\n", tmpdir)
+	} else {
+		defer os.RemoveAll(tmpdir)
+	}
+
+	source, err := filepath.Abs(filepath.Join("testdata", file))
+	if err != nil {
+		panic(fmt.Sprintf("Could not get abspath of testdata directory and file, %v", err))
+	}
+
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "foo.o", "-gcflags=-d=ssa/genssa/dump="+function+" "+moreGCFlags, source)
+	cmd.Dir = tmpdir
+	cmd.Env = replaceEnv(cmd.Env, "GOSSADIR", tmpdir)
+	cmd.Env = replaceEnv(cmd.Env, "HOME", os.Getenv("HOME")) // workaround for #43938
+	testGoos := "linux"                                      // default to linux
+	if testGoArch() == "wasm" {
+		testGoos = "js"
+	}
+	cmd.Env = replaceEnv(cmd.Env, "GOOS", testGoos)
+	cmd.Env = replaceEnv(cmd.Env, "GOARCH", testGoArch())
+
+	if testing.Verbose() {
+		fmt.Printf("About to run %s\n", asCommandLine("", cmd))
+	}
+
+	var stdout, stderr bytes.Buffer
+	cmd.Stdout = &stdout
+	cmd.Stderr = &stderr
+
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("error running cmd %s: %v\nstdout:\n%sstderr:\n%s\n", asCommandLine("", cmd), err, stdout.String(), stderr.String())
+	}
+
+	if s := stderr.String(); s != "" {
+		t.Fatalf("Wanted empty stderr, instead got:\n%s\n", s)
+	}
+
+	dumpFile := filepath.Join(tmpdir, function+"_01__genssa.dump")
+	dumpBytes, err := os.ReadFile(dumpFile)
+	if err != nil {
+		t.Fatalf("Could not read dump file %s, err=%v", dumpFile, err)
+	}
+	return dumpBytes
+}
+
+func sortInlineStacks(x [][]int) {
+	sort.Slice(x, func(i, j int) bool {
+		if len(x[i]) != len(x[j]) {
+			return len(x[i]) < len(x[j])
+		}
+		for k := range x[i] {
+			if x[i][k] != x[j][k] {
+				return x[i][k] < x[j][k]
+			}
+		}
+		return false
+	})
+}
+
+// testInlineStack ensures that inlining is described properly in the comments in the dump file
+func testInlineStack(t *testing.T, file, function string, wantStacks [][]int) {
+	// this is an inlining reporting test, not an optimization test.  -N makes it less fragile
+	dumpBytes := compileAndDump(t, file, function, "-N")
+	dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
+	dumpLineNum := 0
+	var gotStmts []int
+	var gotStacks [][]int
+	for dump.Scan() {
+		line := dump.Text()
+		dumpLineNum++
+		matches := inlineLine.FindStringSubmatch(line)
+		if len(matches) == 2 {
+			stmt, err := strconv.ParseInt(matches[1], 10, 32)
+			if err != nil {
+				t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err)
+			}
+			if testing.Verbose() {
+				fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line)
+			}
+			gotStmts = append(gotStmts, int(stmt))
+		} else if len(gotStmts) > 0 {
+			gotStacks = append(gotStacks, gotStmts)
+			gotStmts = nil
+		}
+	}
+	if len(gotStmts) > 0 {
+		gotStacks = append(gotStacks, gotStmts)
+		gotStmts = nil
+	}
+	sortInlineStacks(gotStacks)
+	sortInlineStacks(wantStacks)
+	if !reflect.DeepEqual(wantStacks, gotStacks) {
+		t.Errorf("wanted inlines %+v but got %+v", wantStacks, gotStacks)
+	}
+
+}
+
+// testDebugLines compiles testdata/<file> with flags -N -l and -d=ssa/genssa/dump=<function>
+// then verifies that the statement-marked lines in that file are the same as those in wantStmts
+// These files must all be short because this is super-fragile.
+// "go build" is run in a temporary directory that is normally deleted, unless -test.v
+func testDebugLines(t *testing.T, file, function string, wantStmts []int) {
+	dumpBytes := compileAndDump(t, file, function, "-N -l")
+	dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
+	var gotStmts []int
+	dumpLineNum := 0
+	for dump.Scan() {
+		line := dump.Text()
+		dumpLineNum++
+		matches := asmLine.FindStringSubmatch(line)
+		if len(matches) == 2 {
+			stmt, err := strconv.ParseInt(matches[1], 10, 32)
+			if err != nil {
+				t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err)
+			}
+			if testing.Verbose() {
+				fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line)
+			}
+			gotStmts = append(gotStmts, int(stmt))
+		}
+	}
+	if !reflect.DeepEqual(wantStmts, gotStmts) {
+		t.Errorf("wanted stmts %v but got %v", wantStmts, gotStmts)
+	}
+
+}
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index b37d3b8..a0f0e65 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -176,7 +176,7 @@
 type expandState struct {
 	f                  *Func
 	abi1               *abi.ABIConfig
-	debug              bool
+	debug              int // odd values log lost statement markers, so likely settings are 1 (stmts), 2 (expansion), and 3 (both)
 	canSSAType         func(*types.Type) bool
 	regSize            int64
 	sp                 *Value
@@ -302,7 +302,7 @@
 //
 // 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, regOffset Abi1RO) []*LocalSlot {
-	if x.debug {
+	if x.debug > 1 {
 		x.indent(3)
 		defer x.indent(-3)
 		x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset)
@@ -325,7 +325,7 @@
 		} else {
 			x.f.Fatalf("Unexpected %s type, selector=%s, leaf=%s\n", selector.Op.String(), selector.LongString(), leaf.LongString())
 		}
-		if x.debug {
+		if x.debug > 1 {
 			x.Printf("---%s, break\n", selector.Op.String())
 		}
 	case OpArg:
@@ -335,7 +335,7 @@
 			} else {
 				x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
 			}
-			if x.debug {
+			if x.debug > 1 {
 				x.Printf("---OpArg, break\n")
 			}
 			break
@@ -381,7 +381,7 @@
 		// This case removes that StructSelect.
 		if leafType != selector.Type {
 			if x.f.Config.SoftFloat && selector.Type.IsFloat() {
-				if x.debug {
+				if x.debug > 1 {
 					x.Printf("---OpLoad, break\n")
 				}
 				break // softfloat pass will take care of that
@@ -468,7 +468,7 @@
 					} else {
 						w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
 						leaf.copyOf(w)
-						if x.debug {
+						if x.debug > 1 {
 							x.Printf("---new %s\n", w.LongString())
 						}
 					}
@@ -687,7 +687,7 @@
 			panic(fmt.Errorf("offset %d of requested register %d should be zero, source=%s", offs[loadRegOffset], loadRegOffset, source.LongString()))
 		}
 
-		if x.debug {
+		if x.debug > 1 {
 			x.Printf("decompose arg %s has %d locs\n", source.LongString(), len(locs))
 		}
 
@@ -836,7 +836,7 @@
 // pos and b locate the store instruction, 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, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
-	if x.debug {
+	if x.debug > 1 {
 		x.indent(3)
 		defer x.indent(-3)
 		x.Printf("storeOneArg(%s;  %s;  %s; aO=%d; sO=%d; lrO=%d; %s)\n", source.LongString(), mem.String(), t.String(), argOffset, storeOffset, loadRegOffset, storeRc.String())
@@ -877,7 +877,7 @@
 // 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, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
-	if x.debug {
+	if x.debug > 1 {
 		x.indent(3)
 		defer x.indent(-3)
 		x.Printf("storeArgOrLoad(%s;  %s;  %s; %d; %s)\n", source.LongString(), mem.String(), t.String(), storeOffset, storeRc.String())
@@ -1060,7 +1060,7 @@
 		dst := x.offsetFrom(b, storeRc.storeDest, storeOffset, types.NewPtr(t))
 		s = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem)
 	}
-	if x.debug {
+	if x.debug > 1 {
 		x.Printf("-->storeArg returns %s, storeRc=%s\n", s.LongString(), storeRc.String())
 	}
 	return s
@@ -1071,18 +1071,23 @@
 // to account for any parameter stores required.
 // Any of the old Args that have their use count fall to zero are marked OpInvalid.
 func (x *expandState) rewriteArgs(v *Value, firstArg int) {
-	if x.debug {
+	if x.debug > 1 {
 		x.indent(3)
 		defer x.indent(-3)
 		x.Printf("rewriteArgs(%s; %d)\n", v.LongString(), firstArg)
 	}
 	// Thread the stores on the memory arg
 	aux := v.Aux.(*AuxCall)
-	pos := v.Pos.WithNotStmt()
 	m0 := v.MemoryArg()
 	mem := m0
 	newArgs := []*Value{}
 	oldArgs := []*Value{}
+	sp := x.sp
+	if v.Op == OpTailLECall {
+		// For tail call, we unwind the frame before the call so we'll use the caller's
+		// SP.
+		sp = x.f.Entry.NewValue0(src.NoXPos, OpGetCallerSP, x.typs.Uintptr)
+	}
 	for i, a := range v.Args[firstArg : len(v.Args)-1] { // skip leading non-parameter SSA Args and trailing mem SSA Arg.
 		oldArgs = append(oldArgs, a)
 		auxI := int64(i)
@@ -1093,9 +1098,20 @@
 			if a.MemoryArg() != m0 {
 				x.f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString())
 			}
+			if v.Op == OpTailLECall {
+				// It's common for a tail call passing the same arguments (e.g. method wrapper),
+				// so this would be a self copy. Detect this and optimize it out.
+				a0 := a.Args[0]
+				if a0.Op == OpLocalAddr {
+					n := a0.Aux.(*ir.Name)
+					if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset {
+						continue
+					}
+				}
+			}
 			// "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
 			// TODO(register args) this will be more complicated with registers in the picture.
-			mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, pos)
+			mem = x.rewriteDereference(v.Block, sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos)
 		} else {
 			var rc registerCursor
 			var result *[]*Value
@@ -1105,11 +1121,19 @@
 			} else {
 				aOffset = aux.OffsetOfArg(auxI)
 			}
-			if x.debug {
+			if v.Op == OpTailLECall && a.Op == OpArg && a.AuxInt == 0 {
+				// It's common for a tail call passing the same arguments (e.g. method wrapper),
+				// so this would be a self copy. Detect this and optimize it out.
+				n := a.Aux.(*ir.Name)
+				if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset {
+					continue
+				}
+			}
+			if x.debug > 1 {
 				x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset)
 			}
-			rc.init(aRegs, aux.abiInfo, result, x.sp)
-			mem = x.storeArgOrLoad(pos, v.Block, a, mem, aType, aOffset, 0, rc)
+			rc.init(aRegs, aux.abiInfo, result, sp)
+			mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc)
 		}
 	}
 	var preArgStore [2]*Value
@@ -1120,16 +1144,31 @@
 	v.AddArg(mem)
 	for _, a := range oldArgs {
 		if a.Uses == 0 {
-			if x.debug {
-				x.Printf("...marking %v unused\n", a.LongString())
-			}
-			a.invalidateRecursively()
+			x.invalidateRecursively(a)
 		}
 	}
 
 	return
 }
 
+func (x *expandState) invalidateRecursively(a *Value) {
+	var s string
+	if x.debug > 0 {
+		plus := " "
+		if a.Pos.IsStmt() == src.PosIsStmt {
+			plus = " +"
+		}
+		s = a.String() + plus + a.Pos.LineNumber() + " " + a.LongString()
+		if x.debug > 1 {
+			x.Printf("...marking %v unused\n", s)
+		}
+	}
+	lost := a.invalidateRecursively()
+	if x.debug&1 != 0 && lost { // For odd values of x.debug, do this.
+		x.Printf("Lost statement marker in %s on former %s\n", base.Ctxt.Pkgpath+"."+x.f.Name, s)
+	}
+}
+
 // 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
@@ -1148,7 +1187,7 @@
 	x := &expandState{
 		f:                  f,
 		abi1:               f.ABI1,
-		debug:              f.pass.debug > 0,
+		debug:              f.pass.debug,
 		canSSAType:         f.fe.CanSSA,
 		regSize:            f.Config.RegSize,
 		sp:                 sp,
@@ -1170,7 +1209,7 @@
 		x.loRo, x.hiRo = 0, 1
 	}
 
-	if x.debug {
+	if x.debug > 1 {
 		x.Printf("\nexpandsCalls(%s)\n", f.Name)
 	}
 
@@ -1193,7 +1232,7 @@
 		for _, v := range b.Values {
 			firstArg := 0
 			switch v.Op {
-			case OpStaticLECall:
+			case OpStaticLECall, OpTailLECall:
 			case OpInterLECall:
 				firstArg = 1
 			case OpClosureLECall:
@@ -1210,9 +1249,8 @@
 			m0 := v.MemoryArg()
 			mem := m0
 			aux := f.OwnAux
-			pos := v.Pos.WithNotStmt()
 			allResults := []*Value{}
-			if x.debug {
+			if x.debug > 1 {
 				x.Printf("multiValueExit rewriting %s\n", v.LongString())
 			}
 			var oldArgs []*Value
@@ -1233,7 +1271,7 @@
 						}
 						continue
 					}
-					mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, pos)
+					mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, a.Pos)
 				} else {
 					if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr {
 						addr := a.Args[0] // This is a self-move. // TODO(register args) do what here for registers?
@@ -1257,13 +1295,13 @@
 			b.SetControl(v)
 			for _, a := range oldArgs {
 				if a.Uses == 0 {
-					if x.debug {
+					if x.debug > 1 {
 						x.Printf("...marking %v unused\n", a.LongString())
 					}
-					a.invalidateRecursively()
+					x.invalidateRecursively(a)
 				}
 			}
-			if x.debug {
+			if x.debug > 1 {
 				x.Printf("...multiValueExit new result %s\n", v.LongString())
 			}
 			x.indent(-3)
@@ -1317,7 +1355,7 @@
 				switch w.Op {
 				case OpStructSelect, OpArraySelect, OpSelectN, OpArg:
 					val2Preds[w] += 1
-					if x.debug {
+					if x.debug > 1 {
 						x.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w])
 					}
 				}
@@ -1326,7 +1364,7 @@
 			case OpSelectN:
 				if _, ok := val2Preds[v]; !ok {
 					val2Preds[v] = 0
-					if x.debug {
+					if x.debug > 1 {
 						x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
 					}
 				}
@@ -1337,7 +1375,7 @@
 				}
 				if _, ok := val2Preds[v]; !ok {
 					val2Preds[v] = 0
-					if x.debug {
+					if x.debug > 1 {
 						x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
 					}
 				}
@@ -1451,7 +1489,7 @@
 		if dupe == nil {
 			x.commonSelectors[sk] = v
 		} else if x.sdom.IsAncestorEq(dupe.Block, v.Block) {
-			if x.debug {
+			if x.debug > 1 {
 				x.Printf("Duplicate, make %s copy of %s\n", v, dupe)
 			}
 			v.copyOf(dupe)
@@ -1467,12 +1505,12 @@
 
 	// Rewrite selectors.
 	for i, v := range allOrdered {
-		if x.debug {
+		if x.debug > 1 {
 			b := v.Block
 			x.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses)
 		}
 		if v.Uses == 0 {
-			v.invalidateRecursively()
+			x.invalidateRecursively(v)
 			continue
 		}
 		if v.Op == OpCopy {
@@ -1512,6 +1550,10 @@
 				v.Op = OpStaticCall
 				rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
 				v.Type = types.NewResults(append(rts, types.TypeMem))
+			case OpTailLECall:
+				v.Op = OpTailCall
+				rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
+				v.Type = types.NewResults(append(rts, types.TypeMem))
 			case OpClosureLECall:
 				v.Op = OpClosureCall
 				rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
@@ -1583,7 +1625,7 @@
 				v.SetArg(i, aa)
 				for a.Uses == 0 {
 					b := a.Args[0]
-					a.invalidateRecursively()
+					x.invalidateRecursively(a)
 					a = b
 				}
 			}
@@ -1619,7 +1661,7 @@
 // rewriteArgToMemOrRegs converts OpArg v in-place into the register version of v,
 // if that is appropriate.
 func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value {
-	if x.debug {
+	if x.debug > 1 {
 		x.indent(3)
 		defer x.indent(-3)
 		x.Printf("rewriteArgToMemOrRegs(%s)\n", v.LongString())
@@ -1650,7 +1692,7 @@
 	default:
 		panic(badVal("Saw unexpanded OpArg", v))
 	}
-	if x.debug {
+	if x.debug > 1 {
 		x.Printf("-->%s\n", v.LongString())
 	}
 	return v
@@ -1660,7 +1702,7 @@
 // or rewrites it into a copy of the appropriate OpArgXXX.  The actual OpArgXXX is determined by combining baseArg (an OpArg)
 // with offset, regOffset, and t to determine which portion of it to reference (either all or a part, in memory or in registers).
 func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, regOffset Abi1RO, t *types.Type, pos src.XPos) *Value {
-	if x.debug {
+	if x.debug > 1 {
 		x.indent(3)
 		defer x.indent(-3)
 		x.Printf("newArgToMemOrRegs(base=%s; toReplace=%s; t=%s; memOff=%d; regOff=%d)\n", baseArg.String(), toReplace.LongString(), t.String(), offset, regOffset)
@@ -1696,7 +1738,7 @@
 		if toReplace != nil {
 			toReplace.copyOf(w)
 		}
-		if x.debug {
+		if x.debug > 1 {
 			x.Printf("-->%s\n", w.LongString())
 		}
 		return w
@@ -1727,7 +1769,7 @@
 	if toReplace != nil {
 		toReplace.copyOf(w)
 	}
-	if x.debug {
+	if x.debug > 1 {
 		x.Printf("-->%s\n", w.LongString())
 	}
 	return w
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index fac876c..7728a39 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -43,7 +43,7 @@
 	logfiles       map[string]writeSyncer
 	HTMLWriter     *HTMLWriter    // html writer, for debugging
 	DebugTest      bool           // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases
-	PrintOrHtmlSSA bool           // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false.
+	PrintOrHtmlSSA bool           // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false.  There's an odd dependence on this in debug.go for method logf.
 	ruleMatches    map[string]int // number of times countRule was called during compilation for any given string
 	ABI0           *abi.ABIConfig // A copy, for no-sync access
 	ABI1           *abi.ABIConfig // A copy, for no-sync access
diff --git a/src/cmd/compile/internal/ssa/gen/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules
index 199b73c..7bdebed 100644
--- a/src/cmd/compile/internal/ssa/gen/386.rules
+++ b/src/cmd/compile/internal/ssa/gen/386.rules
@@ -317,6 +317,7 @@
 (StaticCall ...) => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // Miscellaneous
 (IsNonNil p) => (SETNE (TESTL p p))
diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go
index 91f33c8..3512d60 100644
--- a/src/cmd/compile/internal/ssa/gen/386Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/386Ops.go
@@ -455,6 +455,7 @@
 		},
 
 		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                              // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                // tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                        // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index 5b127c9..bfed3bc 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -408,6 +408,7 @@
 (StaticCall ...) => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // Lowering conditional moves
 // If the condition is a SETxx, we can just run a CMOV from the comparison that was
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index 52ea7ac..51cbf5f 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -765,6 +765,7 @@
 
 		// With a register ABI, the actual register info for these instructions (i.e., what is used in regalloc) is augmented with per-call-site bindings of additional arguments to specific in and out registers.
 		{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                              // call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                // tail call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                        // call fn by pointer.  arg0=codeptr, last arg=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules
index bcacbaf..2bc58a3 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM.rules
@@ -351,6 +351,7 @@
 (StaticCall ...) => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // checks
 (NilCheck ...) => (LoweredNilCheck ...)
@@ -497,9 +498,9 @@
 (XOR x (MOVWconst [c])) => (XORconst [c] x)
 (BIC x (MOVWconst [c])) => (BICconst [c] x)
 
-(SLL x (MOVWconst [c])) => (SLLconst x [c&31]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=32)
-(SRL x (MOVWconst [c])) => (SRLconst x [c&31])
-(SRA x (MOVWconst [c])) => (SRAconst x [c&31])
+(SLL x (MOVWconst [c])) && 0 <= c && c < 32 => (SLLconst x [c])
+(SRL x (MOVWconst [c])) && 0 <= c && c < 32 => (SRLconst x [c])
+(SRA x (MOVWconst [c])) && 0 <= c && c < 32 => (SRAconst x [c])
 
 (CMP x (MOVWconst [c])) => (CMPconst [c] x)
 (CMP (MOVWconst [c]) x) => (InvertFlags (CMPconst [c] x))
@@ -507,6 +508,8 @@
 (TST x (MOVWconst [c])) => (TSTconst [c] x)
 (TEQ x (MOVWconst [c])) => (TEQconst [c] x)
 
+(SRR x (MOVWconst [c])) => (SRRconst x [c&31])
+
 // Canonicalize the order of arguments to comparisons - helps with CSE.
 (CMP x y) && canonLessThan(x,y) => (InvertFlags (CMP y x))
 
@@ -1072,60 +1075,60 @@
 (CMNshiftRL x (MOVWconst [c]) [d]) => (CMNconst x [int32(uint32(c)>>uint64(d))])
 (CMNshiftRA x (MOVWconst [c]) [d]) => (CMNconst x [c>>uint64(d)])
 
-(ADDshiftLLreg x y (MOVWconst [c])) => (ADDshiftLL x y [c])
-(ADDshiftRLreg x y (MOVWconst [c])) => (ADDshiftRL x y [c])
-(ADDshiftRAreg x y (MOVWconst [c])) => (ADDshiftRA x y [c])
-(ADCshiftLLreg x y (MOVWconst [c]) flags) => (ADCshiftLL x y [c] flags)
-(ADCshiftRLreg x y (MOVWconst [c]) flags) => (ADCshiftRL x y [c] flags)
-(ADCshiftRAreg x y (MOVWconst [c]) flags) => (ADCshiftRA x y [c] flags)
-(ADDSshiftLLreg x y (MOVWconst [c])) => (ADDSshiftLL x y [c])
-(ADDSshiftRLreg x y (MOVWconst [c])) => (ADDSshiftRL x y [c])
-(ADDSshiftRAreg x y (MOVWconst [c])) => (ADDSshiftRA x y [c])
-(SUBshiftLLreg x y (MOVWconst [c])) => (SUBshiftLL x y [c])
-(SUBshiftRLreg x y (MOVWconst [c])) => (SUBshiftRL x y [c])
-(SUBshiftRAreg x y (MOVWconst [c])) => (SUBshiftRA x y [c])
-(SBCshiftLLreg x y (MOVWconst [c]) flags) => (SBCshiftLL x y [c] flags)
-(SBCshiftRLreg x y (MOVWconst [c]) flags) => (SBCshiftRL x y [c] flags)
-(SBCshiftRAreg x y (MOVWconst [c]) flags) => (SBCshiftRA x y [c] flags)
-(SUBSshiftLLreg x y (MOVWconst [c])) => (SUBSshiftLL x y [c])
-(SUBSshiftRLreg x y (MOVWconst [c])) => (SUBSshiftRL x y [c])
-(SUBSshiftRAreg x y (MOVWconst [c])) => (SUBSshiftRA x y [c])
-(RSBshiftLLreg x y (MOVWconst [c])) => (RSBshiftLL x y [c])
-(RSBshiftRLreg x y (MOVWconst [c])) => (RSBshiftRL x y [c])
-(RSBshiftRAreg x y (MOVWconst [c])) => (RSBshiftRA x y [c])
-(RSCshiftLLreg x y (MOVWconst [c]) flags) => (RSCshiftLL x y [c] flags)
-(RSCshiftRLreg x y (MOVWconst [c]) flags) => (RSCshiftRL x y [c] flags)
-(RSCshiftRAreg x y (MOVWconst [c]) flags) => (RSCshiftRA x y [c] flags)
-(RSBSshiftLLreg x y (MOVWconst [c])) => (RSBSshiftLL x y [c])
-(RSBSshiftRLreg x y (MOVWconst [c])) => (RSBSshiftRL x y [c])
-(RSBSshiftRAreg x y (MOVWconst [c])) => (RSBSshiftRA x y [c])
-(ANDshiftLLreg x y (MOVWconst [c])) => (ANDshiftLL x y [c])
-(ANDshiftRLreg x y (MOVWconst [c])) => (ANDshiftRL x y [c])
-(ANDshiftRAreg x y (MOVWconst [c])) => (ANDshiftRA x y [c])
-(ORshiftLLreg x y (MOVWconst [c])) => (ORshiftLL x y [c])
-(ORshiftRLreg x y (MOVWconst [c])) => (ORshiftRL x y [c])
-(ORshiftRAreg x y (MOVWconst [c])) => (ORshiftRA x y [c])
-(XORshiftLLreg x y (MOVWconst [c])) => (XORshiftLL x y [c])
-(XORshiftRLreg x y (MOVWconst [c])) => (XORshiftRL x y [c])
-(XORshiftRAreg x y (MOVWconst [c])) => (XORshiftRA x y [c])
-(BICshiftLLreg x y (MOVWconst [c])) => (BICshiftLL x y [c])
-(BICshiftRLreg x y (MOVWconst [c])) => (BICshiftRL x y [c])
-(BICshiftRAreg x y (MOVWconst [c])) => (BICshiftRA x y [c])
-(MVNshiftLLreg x (MOVWconst [c])) => (MVNshiftLL x [c])
-(MVNshiftRLreg x (MOVWconst [c])) => (MVNshiftRL x [c])
-(MVNshiftRAreg x (MOVWconst [c])) => (MVNshiftRA x [c])
-(CMPshiftLLreg x y (MOVWconst [c])) => (CMPshiftLL x y [c])
-(CMPshiftRLreg x y (MOVWconst [c])) => (CMPshiftRL x y [c])
-(CMPshiftRAreg x y (MOVWconst [c])) => (CMPshiftRA x y [c])
-(TSTshiftLLreg x y (MOVWconst [c])) => (TSTshiftLL x y [c])
-(TSTshiftRLreg x y (MOVWconst [c])) => (TSTshiftRL x y [c])
-(TSTshiftRAreg x y (MOVWconst [c])) => (TSTshiftRA x y [c])
-(TEQshiftLLreg x y (MOVWconst [c])) => (TEQshiftLL x y [c])
-(TEQshiftRLreg x y (MOVWconst [c])) => (TEQshiftRL x y [c])
-(TEQshiftRAreg x y (MOVWconst [c])) => (TEQshiftRA x y [c])
-(CMNshiftLLreg x y (MOVWconst [c])) => (CMNshiftLL x y [c])
-(CMNshiftRLreg x y (MOVWconst [c])) => (CMNshiftRL x y [c])
-(CMNshiftRAreg x y (MOVWconst [c])) => (CMNshiftRA x y [c])
+(ADDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftLL x y [c])
+(ADDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRL x y [c])
+(ADDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRA x y [c])
+(ADCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftLL x y [c] flags)
+(ADCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRL x y [c] flags)
+(ADCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRA x y [c] flags)
+(ADDSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftLL x y [c])
+(ADDSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRL x y [c])
+(ADDSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRA x y [c])
+(SUBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftLL x y [c])
+(SUBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRL x y [c])
+(SUBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRA x y [c])
+(SBCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftLL x y [c] flags)
+(SBCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRL x y [c] flags)
+(SBCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRA x y [c] flags)
+(SUBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftLL x y [c])
+(SUBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRL x y [c])
+(SUBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRA x y [c])
+(RSBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftLL x y [c])
+(RSBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRL x y [c])
+(RSBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRA x y [c])
+(RSCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftLL x y [c] flags)
+(RSCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRL x y [c] flags)
+(RSCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRA x y [c] flags)
+(RSBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftLL x y [c])
+(RSBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRL x y [c])
+(RSBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRA x y [c])
+(ANDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftLL x y [c])
+(ANDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRL x y [c])
+(ANDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRA x y [c])
+(ORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftLL x y [c])
+(ORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRL x y [c])
+(ORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRA x y [c])
+(XORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftLL x y [c])
+(XORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRL x y [c])
+(XORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRA x y [c])
+(BICshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftLL x y [c])
+(BICshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRL x y [c])
+(BICshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRA x y [c])
+(MVNshiftLLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftLL x [c])
+(MVNshiftRLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRL x [c])
+(MVNshiftRAreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRA x [c])
+(CMPshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftLL x y [c])
+(CMPshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRL x y [c])
+(CMPshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRA x y [c])
+(TSTshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftLL x y [c])
+(TSTshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRL x y [c])
+(TSTshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRA x y [c])
+(TEQshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftLL x y [c])
+(TEQshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRL x y [c])
+(TEQshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRA x y [c])
+(CMNshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftLL x y [c])
+(CMNshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRL x y [c])
+(CMNshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRA x y [c])
 
 // Generate rotates
 (ADDshiftLL [c] (SRLconst x [32-c]) x) => (SRRconst [32-c] x)
@@ -1135,7 +1138,6 @@
 ( ORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [   c] x)
 (XORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [   c] x)
 
-(RotateLeft32 x (MOVWconst [c])) => (SRRconst [-c&31] x)
 (RotateLeft16 <t> x (MOVWconst [c])) => (Or16 (Lsh16x32 <t> x (MOVWconst [c&15])) (Rsh16Ux32 <t> x (MOVWconst [-c&15])))
 (RotateLeft8 <t> x (MOVWconst [c])) => (Or8 (Lsh8x32 <t> x (MOVWconst [c&7])) (Rsh8Ux32 <t> x (MOVWconst [-c&7])))
 (RotateLeft32 x y) => (SRR x (RSBconst [0] <y.Type> y))
@@ -1237,24 +1239,24 @@
 (AND x (MVN y)) => (BIC x y)
 
 // simplification with *shift ops
-(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
-(RSBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(RSBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(RSBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
-(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y
-(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y
-(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y
-(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y
-(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y
-(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y
-(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
-(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
+(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
+(RSBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(RSBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(RSBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
+(ANDshiftLL y:(SLLconst x [c]) x [c]) => y
+(ANDshiftRL y:(SRLconst x [c]) x [c]) => y
+(ANDshiftRA y:(SRAconst x [c]) x [c]) => y
+(ORshiftLL y:(SLLconst x [c]) x [c]) => y
+(ORshiftRL y:(SRLconst x [c]) x [c]) => y
+(ORshiftRA y:(SRAconst x [c]) x [c]) => y
+(XORshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(XORshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(XORshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
+(BICshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(BICshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(BICshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
 (AND x (MVNshiftLL y [c])) => (BICshiftLL x y [c])
 (AND x (MVNshiftRL y [c])) => (BICshiftRL x y [c])
 (AND x (MVNshiftRA y [c])) => (BICshiftRA x y [c])
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules
index ca9d4a4..4b66883 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules
@@ -503,6 +503,7 @@
 (StaticCall ...) => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // checks
 (NilCheck ...) => (LoweredNilCheck ...)
@@ -1174,6 +1175,9 @@
 (CMPW x (MOVDconst [c])) => (CMPWconst [int32(c)] x)
 (CMPW (MOVDconst [c]) x) => (InvertFlags (CMPWconst [int32(c)] x))
 
+(ROR x (MOVDconst [c])) => (RORconst x [c&63])
+(RORW x (MOVDconst [c])) => (RORWconst x [c&31])
+
 // Canonicalize the order of arguments to comparisons - helps with CSE.
 ((CMP|CMPW) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW) y x))
 
@@ -1359,6 +1363,7 @@
 (XOR x (MVN y)) => (EON x y)
 (OR  x (MVN y)) => (ORN x y)
 (MVN (XOR x y)) => (EON x y)
+(NEG (NEG x)) => x
 
 (CSEL [cc] (MOVDconst [-1]) (MOVDconst [0]) flag) => (CSETM [cc] flag)
 (CSEL [cc] (MOVDconst [0]) (MOVDconst [-1]) flag) => (CSETM [arm64Negate(cc)] flag)
@@ -1596,6 +1601,7 @@
 (MVN x:(SLLconst [c] y)) && clobberIfDead(x) => (MVNshiftLL [c] y)
 (MVN x:(SRLconst [c] y)) && clobberIfDead(x) => (MVNshiftRL [c] y)
 (MVN x:(SRAconst [c] y)) && clobberIfDead(x) => (MVNshiftRA [c] y)
+(MVN x:(RORconst [c] y)) && clobberIfDead(x) => (MVNshiftRO [c] y)
 (ADD x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ADDshiftLL x0 y [c])
 (ADD x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ADDshiftRL x0 y [c])
 (ADD x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ADDshiftRA x0 y [c])
@@ -1605,21 +1611,27 @@
 (AND x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ANDshiftLL x0 y [c])
 (AND x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ANDshiftRL x0 y [c])
 (AND x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ANDshiftRA x0 y [c])
+(AND x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ANDshiftRO x0 y [c])
 (OR  x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORshiftLL  x0 y [c]) // useful for combined load
 (OR  x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORshiftRL  x0 y [c])
 (OR  x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORshiftRA  x0 y [c])
+(OR  x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORshiftRO  x0 y [c])
 (XOR x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (XORshiftLL x0 y [c])
 (XOR x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (XORshiftRL x0 y [c])
 (XOR x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (XORshiftRA x0 y [c])
+(XOR x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (XORshiftRO x0 y [c])
 (BIC x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (BICshiftLL x0 y [c])
 (BIC x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (BICshiftRL x0 y [c])
 (BIC x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (BICshiftRA x0 y [c])
+(BIC x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (BICshiftRO x0 y [c])
 (ORN x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORNshiftLL x0 y [c])
 (ORN x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORNshiftRL x0 y [c])
 (ORN x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORNshiftRA x0 y [c])
+(ORN x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORNshiftRO x0 y [c])
 (EON x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (EONshiftLL x0 y [c])
 (EON x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (EONshiftRL x0 y [c])
 (EON x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (EONshiftRA x0 y [c])
+(EON x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (EONshiftRO x0 y [c])
 (CMP x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (CMPshiftLL x0 y [c])
 (CMP x0:(SLLconst [c] y) x1) && clobberIfDead(x0) => (InvertFlags (CMPshiftLL x1 y [c]))
 (CMP x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (CMPshiftRL x0 y [c])
@@ -1632,6 +1644,7 @@
 (TST x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (TSTshiftLL x0 y [c])
 (TST x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (TSTshiftRL x0 y [c])
 (TST x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (TSTshiftRA x0 y [c])
+(TST x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (TSTshiftRO x0 y [c])
 
 // prefer *const ops to *shift ops
 (ADDshiftLL (MOVDconst [c]) x [d]) => (ADDconst [c] (SLLconst <x.Type> x [d]))
@@ -1640,12 +1653,15 @@
 (ANDshiftLL (MOVDconst [c]) x [d]) => (ANDconst [c] (SLLconst <x.Type> x [d]))
 (ANDshiftRL (MOVDconst [c]) x [d]) => (ANDconst [c] (SRLconst <x.Type> x [d]))
 (ANDshiftRA (MOVDconst [c]) x [d]) => (ANDconst [c] (SRAconst <x.Type> x [d]))
+(ANDshiftRO (MOVDconst [c]) x [d]) => (ANDconst [c] (RORconst <x.Type> x [d]))
 (ORshiftLL  (MOVDconst [c]) x [d]) => (ORconst  [c] (SLLconst <x.Type> x [d]))
 (ORshiftRL  (MOVDconst [c]) x [d]) => (ORconst  [c] (SRLconst <x.Type> x [d]))
 (ORshiftRA  (MOVDconst [c]) x [d]) => (ORconst  [c] (SRAconst <x.Type> x [d]))
+(ORshiftRO  (MOVDconst [c]) x [d]) => (ORconst  [c] (RORconst <x.Type> x [d]))
 (XORshiftLL (MOVDconst [c]) x [d]) => (XORconst [c] (SLLconst <x.Type> x [d]))
 (XORshiftRL (MOVDconst [c]) x [d]) => (XORconst [c] (SRLconst <x.Type> x [d]))
 (XORshiftRA (MOVDconst [c]) x [d]) => (XORconst [c] (SRAconst <x.Type> x [d]))
+(XORshiftRO (MOVDconst [c]) x [d]) => (XORconst [c] (RORconst <x.Type> x [d]))
 (CMPshiftLL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SLLconst <x.Type> x [d])))
 (CMPshiftRL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRLconst <x.Type> x [d])))
 (CMPshiftRA (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRAconst <x.Type> x [d])))
@@ -1655,11 +1671,13 @@
 (TSTshiftLL (MOVDconst [c]) x [d]) => (TSTconst [c] (SLLconst <x.Type> x [d]))
 (TSTshiftRL (MOVDconst [c]) x [d]) => (TSTconst [c] (SRLconst <x.Type> x [d]))
 (TSTshiftRA (MOVDconst [c]) x [d]) => (TSTconst [c] (SRAconst <x.Type> x [d]))
+(TSTshiftRO (MOVDconst [c]) x [d]) => (TSTconst [c] (RORconst <x.Type> x [d]))
 
 // constant folding in *shift ops
 (MVNshiftLL (MOVDconst [c]) [d]) => (MOVDconst [^int64(uint64(c)<<uint64(d))])
 (MVNshiftRL (MOVDconst [c]) [d]) => (MOVDconst [^int64(uint64(c)>>uint64(d))])
 (MVNshiftRA (MOVDconst [c]) [d]) => (MOVDconst [^(c>>uint64(d))])
+(MVNshiftRO (MOVDconst [c]) [d]) => (MOVDconst [^rotateRight64(c, d)])
 (NEGshiftLL (MOVDconst [c]) [d]) => (MOVDconst [-int64(uint64(c)<<uint64(d))])
 (NEGshiftRL (MOVDconst [c]) [d]) => (MOVDconst [-int64(uint64(c)>>uint64(d))])
 (NEGshiftRA (MOVDconst [c]) [d]) => (MOVDconst [-(c>>uint64(d))])
@@ -1672,21 +1690,27 @@
 (ANDshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [int64(uint64(c)<<uint64(d))])
 (ANDshiftRL x (MOVDconst [c]) [d]) => (ANDconst x [int64(uint64(c)>>uint64(d))])
 (ANDshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [c>>uint64(d)])
+(ANDshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [rotateRight64(c, d)])
 (ORshiftLL  x (MOVDconst [c]) [d]) => (ORconst  x [int64(uint64(c)<<uint64(d))])
 (ORshiftRL  x (MOVDconst [c]) [d]) => (ORconst  x [int64(uint64(c)>>uint64(d))])
 (ORshiftRA  x (MOVDconst [c]) [d]) => (ORconst  x [c>>uint64(d)])
+(ORshiftRO  x (MOVDconst [c]) [d]) => (ORconst  x [rotateRight64(c, d)])
 (XORshiftLL x (MOVDconst [c]) [d]) => (XORconst x [int64(uint64(c)<<uint64(d))])
 (XORshiftRL x (MOVDconst [c]) [d]) => (XORconst x [int64(uint64(c)>>uint64(d))])
 (XORshiftRA x (MOVDconst [c]) [d]) => (XORconst x [c>>uint64(d)])
+(XORshiftRO x (MOVDconst [c]) [d]) => (XORconst x [rotateRight64(c, d)])
 (BICshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [^int64(uint64(c)<<uint64(d))])
 (BICshiftRL x (MOVDconst [c]) [d]) => (ANDconst x [^int64(uint64(c)>>uint64(d))])
 (BICshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [^(c>>uint64(d))])
+(BICshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [^rotateRight64(c, d)])
 (ORNshiftLL x (MOVDconst [c]) [d]) => (ORconst  x [^int64(uint64(c)<<uint64(d))])
 (ORNshiftRL x (MOVDconst [c]) [d]) => (ORconst  x [^int64(uint64(c)>>uint64(d))])
 (ORNshiftRA x (MOVDconst [c]) [d]) => (ORconst  x [^(c>>uint64(d))])
+(ORNshiftRO x (MOVDconst [c]) [d]) => (ORconst  x [^rotateRight64(c, d)])
 (EONshiftLL x (MOVDconst [c]) [d]) => (XORconst x [^int64(uint64(c)<<uint64(d))])
 (EONshiftRL x (MOVDconst [c]) [d]) => (XORconst x [^int64(uint64(c)>>uint64(d))])
 (EONshiftRA x (MOVDconst [c]) [d]) => (XORconst x [^(c>>uint64(d))])
+(EONshiftRO x (MOVDconst [c]) [d]) => (XORconst x [^rotateRight64(c, d)])
 (CMPshiftLL x (MOVDconst [c]) [d]) => (CMPconst x [int64(uint64(c)<<uint64(d))])
 (CMPshiftRL x (MOVDconst [c]) [d]) => (CMPconst x [int64(uint64(c)>>uint64(d))])
 (CMPshiftRA x (MOVDconst [c]) [d]) => (CMPconst x [c>>uint64(d)])
@@ -1696,29 +1720,36 @@
 (TSTshiftLL x (MOVDconst [c]) [d]) => (TSTconst x [int64(uint64(c)<<uint64(d))])
 (TSTshiftRL x (MOVDconst [c]) [d]) => (TSTconst x [int64(uint64(c)>>uint64(d))])
 (TSTshiftRA x (MOVDconst [c]) [d]) => (TSTconst x [c>>uint64(d)])
+(TSTshiftRO x (MOVDconst [c]) [d]) => (TSTconst x [rotateRight64(c, d)])
 
 // simplification with *shift ops
-(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0])
-(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y
-(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y
-(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y
-(ORshiftLL  x y:(SLLconst x [c]) [d]) && c==d => y
-(ORshiftRL  x y:(SRLconst x [c]) [d]) && c==d => y
-(ORshiftRA  x y:(SRAconst x [c]) [d]) && c==d => y
-(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0])
-(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0])
-(EONshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(EONshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(EONshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(ORNshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(ORNshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(ORNshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1])
+(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0])
+(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0])
+(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0])
+(ANDshiftLL y:(SLLconst x [c]) x [c]) => y
+(ANDshiftRL y:(SRLconst x [c]) x [c]) => y
+(ANDshiftRA y:(SRAconst x [c]) x [c]) => y
+(ANDshiftRO y:(RORconst x [c]) x [c]) => y
+(ORshiftLL  y:(SLLconst x [c]) x [c]) => y
+(ORshiftRL  y:(SRLconst x [c]) x [c]) => y
+(ORshiftRA  y:(SRAconst x [c]) x [c]) => y
+(ORshiftRO  y:(RORconst x [c]) x [c]) => y
+(XORshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0])
+(XORshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0])
+(XORshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0])
+(XORshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0])
+(EONshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1])
+(EONshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1])
+(EONshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1])
+(EONshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1])
 
 // Generate rotates with const shift
 (ADDshiftLL [c] (SRLconst x [64-c]) x) => (RORconst [64-c] x)
@@ -1824,6 +1855,7 @@
 // sbfiz
 // (x << lc) >> rc
 (SRAconst [rc] (SLLconst [lc] x)) && lc > rc => (SBFIZ [armBFAuxInt(lc-rc, 64-lc)] x)
+// int64(x << lc)
 (MOVWreg (SLLconst [lc] x)) && lc < 32 => (SBFIZ [armBFAuxInt(lc, 32-lc)] x)
 (MOVHreg (SLLconst [lc] x)) && lc < 16 => (SBFIZ [armBFAuxInt(lc, 16-lc)] x)
 (MOVBreg (SLLconst [lc] x)) && lc < 8 => (SBFIZ [armBFAuxInt(lc, 8-lc)] x)
@@ -1835,6 +1867,7 @@
 // sbfx
 // (x << lc) >> rc
 (SRAconst [rc] (SLLconst [lc] x)) && lc <= rc => (SBFX [armBFAuxInt(rc-lc, 64-rc)] x)
+// int64(x) >> rc
 (SRAconst [rc] (MOVWreg x)) && rc < 32 => (SBFX [armBFAuxInt(rc, 32-rc)] x)
 (SRAconst [rc] (MOVHreg x)) && rc < 16 => (SBFX [armBFAuxInt(rc, 16-rc)] x)
 (SRAconst [rc] (MOVBreg x)) && rc < 8 => (SBFX [armBFAuxInt(rc, 8-rc)] x)
@@ -1851,42 +1884,43 @@
 	=> (SBFX [armBFAuxInt(sc-bfc.getARM64BFlsb(), bfc.getARM64BFlsb()+bfc.getARM64BFwidth()-sc)] x)
 
 // ubfiz
+// (x << lc) >> rc
+(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x)
+// uint64(x) << lc
+(SLLconst [lc] (MOVWUreg x))  => (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x)
+(SLLconst [lc] (MOVHUreg x))  => (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x)
+(SLLconst [lc] (MOVBUreg x))  => (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x)
+// uint64(x << lc)
+(MOVWUreg (SLLconst [lc] x)) && lc < 32 => (UBFIZ [armBFAuxInt(lc, 32-lc)] x)
+(MOVHUreg (SLLconst [lc] x)) && lc < 16 => (UBFIZ [armBFAuxInt(lc, 16-lc)] x)
+(MOVBUreg (SLLconst [lc] x)) && lc < 8 => (UBFIZ [armBFAuxInt(lc, 8-lc)] x)
+
+// merge ANDconst into ubfiz
 // (x & ac) << sc
 (SLLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, 0)
 	=> (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x)
-(SLLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFIZ [armBFAuxInt(sc, 32)] x)
-(SLLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFIZ [armBFAuxInt(sc, 16)] x)
-(SLLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFIZ [armBFAuxInt(sc, 8)] x)
 // (x << sc) & ac
 (ANDconst [ac] (SLLconst [sc] x)) && isARM64BFMask(sc, ac, sc)
 	=> (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
-(MOVWUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, sc)
-	=> (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
-(MOVHUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, sc)
-	=> (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
-(MOVBUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, sc)
-	=> (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
-// (x << lc) >> rc
-(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x)
 
 // ubfx
+// (x << lc) >> rc
+(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x)
+// uint64(x) >> rc
+(SRLconst [rc] (MOVWUreg x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32-rc)] x)
+(SRLconst [rc] (MOVHUreg x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16-rc)] x)
+(SRLconst [rc] (MOVBUreg x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8-rc)] x)
+// uint64(x >> rc)
+(MOVWUreg (SRLconst [rc] x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32)] x)
+(MOVHUreg (SRLconst [rc] x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16)] x)
+(MOVBUreg (SRLconst [rc] x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8)] x)
+// merge ANDconst into ubfx
 // (x >> sc) & ac
 (ANDconst [ac] (SRLconst [sc] x)) && isARM64BFMask(sc, ac, 0)
 	=> (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x)
-(MOVWUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFX [armBFAuxInt(sc, 32)] x)
-(MOVHUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFX [armBFAuxInt(sc, 16)] x)
-(MOVBUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFX [armBFAuxInt(sc, 8)] x)
 // (x & ac) >> sc
 (SRLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, sc)
 	=> (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
-(SRLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, sc)
-	=> (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
-(SRLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, sc)
-	=> (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
-(SRLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, sc)
-	=> (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
-// (x << lc) >> rc
-(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x)
 
 // merge ubfx and zerso-extension into ubfx
 (MOVWUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 32 => (UBFX [bfc] x)
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
index acfb288..e3ebb6e 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
@@ -302,6 +302,7 @@
 		{name: "MVNshiftLL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"},                   // ^(arg0<<auxInt), auxInt should be in the range 0 to 63.
 		{name: "MVNshiftRL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"},                   // ^(arg0>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "MVNshiftRA", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"},                   // ^(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+		{name: "MVNshiftRO", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"},                   // ^(arg0 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
 		{name: "NEGshiftLL", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"},                   // -(arg0<<auxInt), auxInt should be in the range 0 to 63.
 		{name: "NEGshiftRL", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"},                   // -(arg0>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "NEGshiftRA", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"},                   // -(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63.
@@ -314,21 +315,27 @@
 		{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1<<auxInt), auxInt should be in the range 0 to 63.
 		{name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+		{name: "ANDshiftRO", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
 		{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1<<auxInt, auxInt should be in the range 0 to 63.
 		{name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
+		{name: "ORshiftRO", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63.
 		{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1<<auxInt, auxInt should be in the range 0 to 63.
 		{name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
+		{name: "XORshiftRO", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63.
 		{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1<<auxInt), auxInt should be in the range 0 to 63.
 		{name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+		{name: "BICshiftRO", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
 		{name: "EONshiftLL", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"},                   // arg0 ^ ^(arg1<<auxInt), auxInt should be in the range 0 to 63.
 		{name: "EONshiftRL", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"},                   // arg0 ^ ^(arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "EONshiftRA", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"},                   // arg0 ^ ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+		{name: "EONshiftRO", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"},                   // arg0 ^ ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
 		{name: "ORNshiftLL", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"},                   // arg0 | ^(arg1<<auxInt), auxInt should be in the range 0 to 63.
 		{name: "ORNshiftRL", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"},                   // arg0 | ^(arg1>>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "ORNshiftRA", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"},                   // arg0 | ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+		{name: "ORNshiftRO", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"},                   // arg0 | ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
 		{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<<auxInt, auxInt should be in the range 0 to 63.
 		{name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
@@ -338,6 +345,7 @@
 		{name: "TSTshiftLL", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1<<auxInt) compare to 0, auxInt should be in the range 0 to 63.
 		{name: "TSTshiftRL", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1>>auxInt) compare to 0, unsigned shift, auxInt should be in the range 0 to 63.
 		{name: "TSTshiftRA", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1>>auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63.
+		{name: "TSTshiftRO", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1 ROR auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63.
 
 		// bitfield ops
 		// for all bitfield ops lsb is auxInt>>8, width is auxInt&0xff
@@ -484,6 +492,7 @@
 
 		// function calls
 		{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                 // tail call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, last arg=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go
index d1f8603..75ba769 100644
--- a/src/cmd/compile/internal/ssa/gen/ARMOps.go
+++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go
@@ -228,14 +228,15 @@
 
 		// shifts
 		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                    // arg0 << arg1, shift amount is mod 256
-		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt
+		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, 0 <= auxInt < 32
 		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                    // arg0 >> arg1, unsigned, shift amount is mod 256
-		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned
+		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned, 0 <= auxInt < 32
 		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                    // arg0 >> arg1, signed, shift amount is mod 256
-		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed
+		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, 0 <= auxInt < 32
 		{name: "SRR", argLength: 2, reg: gp21},                                // arg0 right rotate by arg1 bits
-		{name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"},             // arg0 right rotate by auxInt bits
+		{name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"},             // arg0 right rotate by auxInt bits, 0 <= auxInt < 32
 
+		// auxInt for all of these satisfy 0 <= auxInt < 32
 		{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1<<auxInt
 		{name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, unsigned shift
 		{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, signed shift
@@ -431,6 +432,7 @@
 
 		// function calls
 		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                              // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                // tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                        // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS.rules b/src/cmd/compile/internal/ssa/gen/MIPS.rules
index 4ac9668..639dda4 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPS.rules
+++ b/src/cmd/compile/internal/ssa/gen/MIPS.rules
@@ -334,6 +334,7 @@
 (StaticCall ...)  => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...)   => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // atomic intrinsics
 (AtomicLoad(8|32) ...) => (LoweredAtomicLoad(8|32) ...)
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64.rules b/src/cmd/compile/internal/ssa/gen/MIPS64.rules
index fd04a6c..292ff2f 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPS64.rules
+++ b/src/cmd/compile/internal/ssa/gen/MIPS64.rules
@@ -379,6 +379,7 @@
 (StaticCall ...) => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // atomic intrinsics
 (AtomicLoad(8|32|64) ...) => (LoweredAtomicLoad(8|32|64) ...)
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
index a18cd42..54c0741 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
@@ -276,6 +276,7 @@
 
 		// function calls
 		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                 // tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go
index 8177c7e..5f73e9f 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go
+++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go
@@ -258,6 +258,7 @@
 
 		// function calls
 		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                 //  tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index ce4b324..411bb8d 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -670,6 +670,7 @@
 (StaticCall ...) => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // Miscellaneous
 (GetClosurePtr ...) => (LoweredGetClosurePtr ...)
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
index d7d8a33..a14d9cd 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
@@ -429,6 +429,7 @@
 		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
 
 		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                       // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                         // tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},            // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64.rules b/src/cmd/compile/internal/ssa/gen/RISCV64.rules
index b711550..4290d1b 100644
--- a/src/cmd/compile/internal/ssa/gen/RISCV64.rules
+++ b/src/cmd/compile/internal/ssa/gen/RISCV64.rules
@@ -96,6 +96,10 @@
 (Sqrt ...) => (FSQRTD ...)
 (Sqrt32 ...) => (FSQRTS ...)
 
+(Copysign ...) => (FSGNJD ...)
+
+(Abs ...) => (FABSD ...)
+
 (FMA ...) => (FMADDD ...)
 
 // Sign and zero extension.
@@ -542,6 +546,7 @@
 (StaticCall  ...) => (CALLstatic  ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall   ...) => (CALLinter   ...)
+(TailCall ...) => (CALLtail ...)
 
 // Atomic Intrinsics
 (AtomicLoad8   ...) => (LoweredAtomicLoad8  ...)
diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
index de189e4..8a3fdf7 100644
--- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
@@ -241,6 +241,7 @@
 
 		// Calls
 		{name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true},         // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: 1, reg: call, aux: "CallOff", call: true},           // tail call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: 3, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: 2, reg: callInter, aux: "CallOff", call: true},     // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
@@ -432,6 +433,8 @@
 		{name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD", commutative: true, typ: "Float64"},                                       // -(arg0 * arg1) - arg2
 		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", typ: "Float64"},                                                            // sqrt(arg0)
 		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", typ: "Float64"},                                                              // -arg0
+		{name: "FABSD", argLength: 1, reg: fp11, asm: "FABSD", typ: "Float64"},                                                              // abs(arg0)
+		{name: "FSGNJD", argLength: 2, reg: fp21, asm: "FSGNJD", typ: "Float64"},                                                            // copy sign of arg1 to arg0
 		{name: "FMVDX", argLength: 1, reg: gpfp, asm: "FMVDX", typ: "Float64"},                                                              // reinterpret arg0 as float
 		{name: "FCVTDW", argLength: 1, reg: gpfp, asm: "FCVTDW", typ: "Float64"},                                                            // float64(low 32 bits of arg0)
 		{name: "FCVTDL", argLength: 1, reg: gpfp, asm: "FCVTDL", typ: "Float64"},                                                            // float64(arg0)
diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules
index 88762f7..b3928c6 100644
--- a/src/cmd/compile/internal/ssa/gen/S390X.rules
+++ b/src/cmd/compile/internal/ssa/gen/S390X.rules
@@ -434,6 +434,7 @@
 (StaticCall ...) => (CALLstatic ...)
 (ClosureCall ...) => (CALLclosure ...)
 (InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
 
 // Miscellaneous
 (IsNonNil p) => (LOCGR {s390x.NotEqual} (MOVDconst [0]) (MOVDconst [1]) (CMPconst p [0]))
diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go
index 00fce8e..9b6ac2b 100644
--- a/src/cmd/compile/internal/ssa/gen/S390XOps.go
+++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go
@@ -480,6 +480,7 @@
 		{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
 
 		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                                  // tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
 		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/Wasm.rules b/src/cmd/compile/internal/ssa/gen/Wasm.rules
index 7ad3d1c..9e683b1 100644
--- a/src/cmd/compile/internal/ssa/gen/Wasm.rules
+++ b/src/cmd/compile/internal/ssa/gen/Wasm.rules
@@ -307,6 +307,7 @@
 (StaticCall ...) => (LoweredStaticCall ...)
 (ClosureCall ...) => (LoweredClosureCall ...)
 (InterCall ...) => (LoweredInterCall ...)
+(TailCall ...) => (LoweredTailCall ...)
 
 // Miscellaneous
 (Convert ...) => (LoweredConvert ...)
diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go
index 7f7ae5e..0d73271 100644
--- a/src/cmd/compile/internal/ssa/gen/WasmOps.go
+++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go
@@ -124,6 +124,7 @@
 
 	var WasmOps = []opData{
 		{name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true},                                // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+		{name: "LoweredTailCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true},                                  // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
 		{name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
 		{name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", call: true},          // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index c183aed..9845529 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -106,7 +106,7 @@
 
 	// For shifts, AxB means the shifted value has A bits and the shift amount has B bits.
 	// Shift amounts are considered unsigned.
-	// If arg1 is known to be less than the number of bits in arg0,
+	// If arg1 is known to be nonnegative and less than the number of bits in arg0,
 	// then auxInt may be set to 1.
 	// This enables better code generation on some platforms.
 	{name: "Lsh8x8", argLength: 2, aux: "Bool"}, // arg0 << arg1
@@ -417,10 +417,12 @@
 	{name: "ClosureCall", argLength: -1, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2..argN-1 are register inputs, argN=memory.  auxint=arg size.  Returns Result of register results, plus memory.
 	{name: "StaticCall", argLength: -1, aux: "CallOff", call: true},  // call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory.  auxint=arg size.  Returns Result of register results, plus memory.
 	{name: "InterCall", argLength: -1, aux: "CallOff", call: true},   // interface call.  arg0=code pointer, arg1..argN-1 are register inputs, argN=memory, auxint=arg size.  Returns Result of register results, plus memory.
+	{name: "TailCall", argLength: -1, aux: "CallOff", call: true},    // tail call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory.  auxint=arg size.  Returns Result of register results, plus memory.
 
 	{name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr,  arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
 	{name: "StaticLECall", argLength: -1, aux: "CallOff", call: true},  // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
 	{name: "InterLECall", argLength: -1, aux: "CallOff", call: true},   // late-expanded interface call. arg0=code pointer, arg1..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
+	{name: "TailLECall", argLength: -1, aux: "CallOff", call: true},    // late-expanded static tail call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
 
 	// Conversions: signed extensions, zero (unsigned) extensions, truncations
 	{name: "SignExt8to16", argLength: 1, typ: "Int16"},
@@ -638,7 +640,7 @@
 	{name: "If", controls: 1},     // if Controls[0] goto Succs[0] else goto Succs[1]
 	{name: "Defer", controls: 1},  // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type)
 	{name: "Ret", controls: 1},    // no successors, Controls[0] value is memory result
-	{name: "RetJmp", controls: 1}, // no successors, Controls[0] value is memory result, jumps to b.Aux.(*gc.Sym)
+	{name: "RetJmp", controls: 1}, // no successors, Controls[0] value is a tail call
 	{name: "Exit", controls: 1},   // no successors, Controls[0] value generates a panic
 
 	// transient block state used for dead code removal
diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go
index 4d19119..d9a78b3 100644
--- a/src/cmd/compile/internal/ssa/html.go
+++ b/src/cmd/compile/internal/ssa/html.go
@@ -903,15 +903,12 @@
 			if strings.HasPrefix(l, "buildssa") {
 				escaped = fmt.Sprintf("<b>%v</b>", l)
 			} else {
-				// Parse the line number from the format l(123).
-				idx := strings.Index(l, " l(")
-				if idx != -1 {
-					subl := l[idx+3:]
-					idxEnd := strings.Index(subl, ")")
-					if idxEnd != -1 {
-						if _, err := strconv.Atoi(subl[:idxEnd]); err == nil {
-							lineNo = subl[:idxEnd]
-						}
+				// Parse the line number from the format file:line:col.
+				// See the implementation in ir/fmt.go:dumpNodeHeader.
+				sl := strings.Split(l, ":")
+				if len(sl) >= 3 {
+					if _, err := strconv.Atoi(sl[len(sl)-2]); err == nil {
+						lineNo = sl[len(sl)-2]
 					}
 				}
 				escaped = html.EscapeString(l)
@@ -1221,7 +1218,7 @@
 	}
 }
 
-func (p htmlFuncPrinter) endBlock(b *Block) {
+func (p htmlFuncPrinter) endBlock(b *Block, reachable bool) {
 	if len(b.Values) > 0 { // end list of values
 		io.WriteString(p.w, "</ul>")
 		io.WriteString(p.w, "</li>")
diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go
index 252c47c..b575feb 100644
--- a/src/cmd/compile/internal/ssa/location.go
+++ b/src/cmd/compile/internal/ssa/location.go
@@ -91,8 +91,8 @@
 type LocResults []Location
 
 func (t LocResults) String() string {
-	s := "<"
-	a := ""
+	s := ""
+	a := "<"
 	for _, r := range t {
 		a += s
 		s = ","
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 573559d..eb7e4b9 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -515,6 +515,7 @@
 	Op386DUFFZERO
 	Op386REPSTOSL
 	Op386CALLstatic
+	Op386CALLtail
 	Op386CALLclosure
 	Op386CALLinter
 	Op386DUFFCOPY
@@ -993,6 +994,7 @@
 	OpAMD64DUFFZERO
 	OpAMD64REPSTOSQ
 	OpAMD64CALLstatic
+	OpAMD64CALLtail
 	OpAMD64CALLclosure
 	OpAMD64CALLinter
 	OpAMD64DUFFCOPY
@@ -1269,6 +1271,7 @@
 	OpARMCMOVWLSconst
 	OpARMSRAcond
 	OpARMCALLstatic
+	OpARMCALLtail
 	OpARMCALLclosure
 	OpARMCALLinter
 	OpARMLoweredNilCheck
@@ -1409,6 +1412,7 @@
 	OpARM64MVNshiftLL
 	OpARM64MVNshiftRL
 	OpARM64MVNshiftRA
+	OpARM64MVNshiftRO
 	OpARM64NEGshiftLL
 	OpARM64NEGshiftRL
 	OpARM64NEGshiftRA
@@ -1421,21 +1425,27 @@
 	OpARM64ANDshiftLL
 	OpARM64ANDshiftRL
 	OpARM64ANDshiftRA
+	OpARM64ANDshiftRO
 	OpARM64ORshiftLL
 	OpARM64ORshiftRL
 	OpARM64ORshiftRA
+	OpARM64ORshiftRO
 	OpARM64XORshiftLL
 	OpARM64XORshiftRL
 	OpARM64XORshiftRA
+	OpARM64XORshiftRO
 	OpARM64BICshiftLL
 	OpARM64BICshiftRL
 	OpARM64BICshiftRA
+	OpARM64BICshiftRO
 	OpARM64EONshiftLL
 	OpARM64EONshiftRL
 	OpARM64EONshiftRA
+	OpARM64EONshiftRO
 	OpARM64ORNshiftLL
 	OpARM64ORNshiftRL
 	OpARM64ORNshiftRA
+	OpARM64ORNshiftRO
 	OpARM64CMPshiftLL
 	OpARM64CMPshiftRL
 	OpARM64CMPshiftRA
@@ -1445,6 +1455,7 @@
 	OpARM64TSTshiftLL
 	OpARM64TSTshiftRL
 	OpARM64TSTshiftRA
+	OpARM64TSTshiftRO
 	OpARM64BFI
 	OpARM64BFXIL
 	OpARM64SBFIZ
@@ -1552,6 +1563,7 @@
 	OpARM64CSNEG
 	OpARM64CSETM
 	OpARM64CALLstatic
+	OpARM64CALLtail
 	OpARM64CALLclosure
 	OpARM64CALLinter
 	OpARM64LoweredNilCheck
@@ -1697,6 +1709,7 @@
 	OpMIPSMOVFD
 	OpMIPSMOVDF
 	OpMIPSCALLstatic
+	OpMIPSCALLtail
 	OpMIPSCALLclosure
 	OpMIPSCALLinter
 	OpMIPSLoweredAtomicLoad8
@@ -1813,6 +1826,7 @@
 	OpMIPS64MOVFD
 	OpMIPS64MOVDF
 	OpMIPS64CALLstatic
+	OpMIPS64CALLtail
 	OpMIPS64CALLclosure
 	OpMIPS64CALLinter
 	OpMIPS64DUFFZERO
@@ -2025,6 +2039,7 @@
 	OpPPC64LoweredRound32F
 	OpPPC64LoweredRound64F
 	OpPPC64CALLstatic
+	OpPPC64CALLtail
 	OpPPC64CALLclosure
 	OpPPC64CALLinter
 	OpPPC64LoweredZero
@@ -2128,6 +2143,7 @@
 	OpRISCV64SLTIU
 	OpRISCV64MOVconvert
 	OpRISCV64CALLstatic
+	OpRISCV64CALLtail
 	OpRISCV64CALLclosure
 	OpRISCV64CALLinter
 	OpRISCV64DUFFZERO
@@ -2183,6 +2199,8 @@
 	OpRISCV64FNMSUBD
 	OpRISCV64FSQRTD
 	OpRISCV64FNEGD
+	OpRISCV64FABSD
+	OpRISCV64FSGNJD
 	OpRISCV64FMVDX
 	OpRISCV64FCVTDW
 	OpRISCV64FCVTDL
@@ -2384,6 +2402,7 @@
 	OpS390XMOVDstoreconst
 	OpS390XCLEAR
 	OpS390XCALLstatic
+	OpS390XCALLtail
 	OpS390XCALLclosure
 	OpS390XCALLinter
 	OpS390XInvertFlags
@@ -2437,6 +2456,7 @@
 	OpS390XLoweredZero
 
 	OpWasmLoweredStaticCall
+	OpWasmLoweredTailCall
 	OpWasmLoweredClosureCall
 	OpWasmLoweredInterCall
 	OpWasmLoweredAddr
@@ -2783,9 +2803,11 @@
 	OpClosureCall
 	OpStaticCall
 	OpInterCall
+	OpTailCall
 	OpClosureLECall
 	OpStaticLECall
 	OpInterLECall
+	OpTailLECall
 	OpSignExt8to16
 	OpSignExt8to32
 	OpSignExt8to64
@@ -5905,6 +5927,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       3,
@@ -13102,6 +13134,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       -1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			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
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       -1,
@@ -16938,6 +16980,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       3,
@@ -18764,6 +18816,20 @@
 		},
 	},
 	{
+		name:    "MVNshiftRO",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
 		name:    "NEGshiftLL",
 		auxType: auxInt64,
 		argLen:  1,
@@ -18941,6 +19007,21 @@
 		},
 	},
 	{
+		name:    "ANDshiftRO",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
 		name:    "ORshiftLL",
 		auxType: auxInt64,
 		argLen:  2,
@@ -18986,6 +19067,21 @@
 		},
 	},
 	{
+		name:    "ORshiftRO",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
 		name:    "XORshiftLL",
 		auxType: auxInt64,
 		argLen:  2,
@@ -19031,6 +19127,21 @@
 		},
 	},
 	{
+		name:    "XORshiftRO",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
 		name:    "BICshiftLL",
 		auxType: auxInt64,
 		argLen:  2,
@@ -19076,6 +19187,21 @@
 		},
 	},
 	{
+		name:    "BICshiftRO",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
 		name:    "EONshiftLL",
 		auxType: auxInt64,
 		argLen:  2,
@@ -19121,6 +19247,21 @@
 		},
 	},
 	{
+		name:    "EONshiftRO",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AEON,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
 		name:    "ORNshiftLL",
 		auxType: auxInt64,
 		argLen:  2,
@@ -19166,6 +19307,21 @@
 		},
 	},
 	{
+		name:    "ORNshiftRO",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AORN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
 		name:    "CMPshiftLL",
 		auxType: auxInt64,
 		argLen:  2,
@@ -19274,6 +19430,18 @@
 		},
 	},
 	{
+		name:    "TSTshiftRO",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ATST,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
 		name:         "BFI",
 		auxType:      auxARM64BitField,
 		argLen:       2,
@@ -20705,6 +20873,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       -1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       -1,
@@ -22638,6 +22816,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       3,
@@ -24197,6 +24385,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       3,
@@ -27025,6 +27223,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       3,
@@ -28431,6 +28639,15 @@
 		},
 	},
 	{
+		name:    "CALLtail",
+		auxType: auxCallOff,
+		argLen:  1,
+		call:    true,
+		reg: regInfo{
+			clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+		},
+	},
+	{
 		name:    "CALLclosure",
 		auxType: auxCallOff,
 		argLen:  3,
@@ -29188,6 +29405,33 @@
 		},
 	},
 	{
+		name:   "FABSD",
+		argLen: 1,
+		asm:    riscv.AFABSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FSGNJD",
+		argLen: 2,
+		asm:    riscv.AFSGNJD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
 		name:   "FMVDX",
 		argLen: 1,
 		asm:    riscv.AFMVDX,
@@ -32159,6 +32403,16 @@
 		},
 	},
 	{
+		name:         "CALLtail",
+		auxType:      auxCallOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+		},
+	},
+	{
 		name:         "CALLclosure",
 		auxType:      auxCallOff,
 		argLen:       3,
@@ -32829,6 +33083,15 @@
 		},
 	},
 	{
+		name:    "LoweredTailCall",
+		auxType: auxCallOff,
+		argLen:  1,
+		call:    true,
+		reg: regInfo{
+			clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g
+		},
+	},
+	{
 		name:    "LoweredClosureCall",
 		auxType: auxCallOff,
 		argLen:  3,
@@ -35605,6 +35868,13 @@
 		generic: true,
 	},
 	{
+		name:    "TailCall",
+		auxType: auxCallOff,
+		argLen:  -1,
+		call:    true,
+		generic: true,
+	},
+	{
 		name:    "ClosureLECall",
 		auxType: auxCallOff,
 		argLen:  -1,
@@ -35626,6 +35896,13 @@
 		generic: true,
 	},
 	{
+		name:    "TailLECall",
+		auxType: auxCallOff,
+		argLen:  -1,
+		call:    true,
+		generic: true,
+	},
+	{
 		name:    "SignExt8to16",
 		argLen:  1,
 		generic: true,
diff --git a/src/cmd/compile/internal/ssa/print.go b/src/cmd/compile/internal/ssa/print.go
index d917183..96cd2c7 100644
--- a/src/cmd/compile/internal/ssa/print.go
+++ b/src/cmd/compile/internal/ssa/print.go
@@ -6,6 +6,7 @@
 
 import (
 	"bytes"
+	"cmd/internal/src"
 	"crypto/sha256"
 	"fmt"
 	"io"
@@ -17,22 +18,30 @@
 
 func hashFunc(f *Func) []byte {
 	h := sha256.New()
-	p := stringFuncPrinter{w: h}
+	p := stringFuncPrinter{w: h, printDead: true}
 	fprintFunc(p, f)
 	return h.Sum(nil)
 }
 
 func (f *Func) String() string {
 	var buf bytes.Buffer
-	p := stringFuncPrinter{w: &buf}
+	p := stringFuncPrinter{w: &buf, printDead: true}
 	fprintFunc(p, f)
 	return buf.String()
 }
 
+// rewriteHash returns a hash of f suitable for detecting rewrite cycles.
+func (f *Func) rewriteHash() string {
+	h := sha256.New()
+	p := stringFuncPrinter{w: h, printDead: false}
+	fprintFunc(p, f)
+	return fmt.Sprintf("%x", h.Sum(nil))
+}
+
 type funcPrinter interface {
 	header(f *Func)
 	startBlock(b *Block, reachable bool)
-	endBlock(b *Block)
+	endBlock(b *Block, reachable bool)
 	value(v *Value, live bool)
 	startDepCycle()
 	endDepCycle()
@@ -40,7 +49,8 @@
 }
 
 type stringFuncPrinter struct {
-	w io.Writer
+	w         io.Writer
+	printDead bool
 }
 
 func (p stringFuncPrinter) header(f *Func) {
@@ -50,6 +60,9 @@
 }
 
 func (p stringFuncPrinter) startBlock(b *Block, reachable bool) {
+	if !p.printDead && !reachable {
+		return
+	}
 	fmt.Fprintf(p.w, "  b%d:", b.ID)
 	if len(b.Preds) > 0 {
 		io.WriteString(p.w, " <-")
@@ -64,14 +77,33 @@
 	io.WriteString(p.w, "\n")
 }
 
-func (p stringFuncPrinter) endBlock(b *Block) {
+func (p stringFuncPrinter) endBlock(b *Block, reachable bool) {
+	if !p.printDead && !reachable {
+		return
+	}
 	fmt.Fprintln(p.w, "    "+b.LongString())
 }
 
+func StmtString(p src.XPos) string {
+	linenumber := "(?) "
+	if p.IsKnown() {
+		pfx := ""
+		if p.IsStmt() == src.PosIsStmt {
+			pfx = "+"
+		}
+		if p.IsStmt() == src.PosNotStmt {
+			pfx = "-"
+		}
+		linenumber = fmt.Sprintf("(%s%d) ", pfx, p.Line())
+	}
+	return linenumber
+}
+
 func (p stringFuncPrinter) value(v *Value, live bool) {
-	fmt.Fprint(p.w, "    ")
-	//fmt.Fprint(p.w, v.Block.Func.fe.Pos(v.Pos))
-	//fmt.Fprint(p.w, ": ")
+	if !p.printDead && !live {
+		return
+	}
+	fmt.Fprintf(p.w, "    %s", StmtString(v.Pos))
 	fmt.Fprint(p.w, v.LongString())
 	if !live {
 		fmt.Fprint(p.w, " DEAD")
@@ -103,7 +135,7 @@
 				p.value(v, live[v.ID])
 				printed[v.ID] = true
 			}
-			p.endBlock(b)
+			p.endBlock(b, reachable[b.ID])
 			continue
 		}
 
@@ -151,7 +183,7 @@
 			}
 		}
 
-		p.endBlock(b)
+		p.endBlock(b, reachable[b.ID])
 	}
 	for _, name := range f.Names {
 		p.named(*name, f.NamedValues[*name])
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index 5d46876..79f9efa 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -36,6 +36,8 @@
 	if debug > 1 {
 		fmt.Printf("%s: rewriting for %s\n", f.pass.name, f.Name)
 	}
+	var iters int
+	var states map[string]bool
 	for {
 		change := false
 		for _, b := range f.Blocks {
@@ -146,6 +148,30 @@
 		if !change {
 			break
 		}
+		iters++
+		if iters > 1000 || debug >= 2 {
+			// We've done a suspiciously large number of rewrites (or we're in debug mode).
+			// As of Sep 2021, 90% of rewrites complete in 4 iterations or fewer
+			// and the maximum value encountered during make.bash is 12.
+			// Start checking for cycles. (This is too expensive to do routinely.)
+			if states == nil {
+				states = make(map[string]bool)
+			}
+			h := f.rewriteHash()
+			if _, ok := states[h]; ok {
+				// We've found a cycle.
+				// To diagnose it, set debug to 2 and start again,
+				// so that we'll print all rules applied until we complete another cycle.
+				// If debug is already >= 2, we've already done that, so it's time to crash.
+				if debug < 2 {
+					debug = 2
+					states = make(map[string]bool)
+				} else {
+					f.Fatalf("rewrite cycle detected")
+				}
+			}
+			states[h] = true
+		}
 	}
 	// remove clobbered values
 	for _, b := range f.Blocks {
@@ -1541,12 +1567,16 @@
 	return int64(bits.RotateLeft32(uint32(v), int(rotate)))
 }
 
+func rotateRight64(v, rotate int64) int64 {
+	return int64(bits.RotateLeft64(uint64(v), int(-rotate)))
+}
+
 // encodes the lsb and width for arm(64) bitfield ops into the expected auxInt format.
 func armBFAuxInt(lsb, width int64) arm64BitField {
 	if lsb < 0 || lsb > 63 {
 		panic("ARM(64) bit field lsb constant out of range")
 	}
-	if width < 1 || width > 64 {
+	if width < 1 || lsb+width > 64 {
 		panic("ARM(64) bit field width constant out of range")
 	}
 	return arm64BitField(width | lsb<<8)
diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go
index 1ec2d26..34f3786 100644
--- a/src/cmd/compile/internal/ssa/rewrite386.go
+++ b/src/cmd/compile/internal/ssa/rewrite386.go
@@ -652,6 +652,9 @@
 	case OpSubPtr:
 		v.Op = Op386SUBL
 		return true
+	case OpTailCall:
+		v.Op = Op386CALLtail
+		return true
 	case OpTrunc16to8:
 		v.Op = OpCopy
 		return true
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index aa9293e..e20161c 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -1103,6 +1103,9 @@
 	case OpSubPtr:
 		v.Op = OpAMD64SUBQ
 		return true
+	case OpTailCall:
+		v.Op = OpAMD64CALLtail
+		return true
 	case OpTrunc:
 		return rewriteValueAMD64_OpTrunc(v)
 	case OpTrunc16to8:
diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go
index febb556..496f9b4 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM.go
@@ -338,6 +338,8 @@
 		return rewriteValueARM_OpARMSRL(v)
 	case OpARMSRLconst:
 		return rewriteValueARM_OpARMSRLconst(v)
+	case OpARMSRR:
+		return rewriteValueARM_OpARMSRR(v)
 	case OpARMSUB:
 		return rewriteValueARM_OpARMSUB(v)
 	case OpARMSUBD:
@@ -855,6 +857,9 @@
 	case OpSubPtr:
 		v.Op = OpARMSUB
 		return true
+	case OpTailCall:
+		v.Op = OpARMCALLtail
+		return true
 	case OpTrunc16to8:
 		v.Op = OpCopy
 		return true
@@ -1119,6 +1124,7 @@
 		return true
 	}
 	// match: (ADCshiftLLreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (ADCshiftLL x y [c] flags)
 	for {
 		x := v_0
@@ -1128,6 +1134,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADCshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -1199,6 +1208,7 @@
 		return true
 	}
 	// match: (ADCshiftRAreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (ADCshiftRA x y [c] flags)
 	for {
 		x := v_0
@@ -1208,6 +1218,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADCshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -1279,6 +1292,7 @@
 		return true
 	}
 	// match: (ADCshiftRLreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (ADCshiftRL x y [c] flags)
 	for {
 		x := v_0
@@ -1288,6 +1302,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADCshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -1740,6 +1757,7 @@
 		return true
 	}
 	// match: (ADDSshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ADDSshiftLL x y [c])
 	for {
 		x := v_0
@@ -1748,6 +1766,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADDSshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -1814,6 +1835,7 @@
 		return true
 	}
 	// match: (ADDSshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ADDSshiftRA x y [c])
 	for {
 		x := v_0
@@ -1822,6 +1844,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADDSshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -1888,6 +1913,7 @@
 		return true
 	}
 	// match: (ADDSshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ADDSshiftRL x y [c])
 	for {
 		x := v_0
@@ -1896,6 +1922,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADDSshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -2124,6 +2153,7 @@
 		return true
 	}
 	// match: (ADDshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ADDshiftLL x y [c])
 	for {
 		x := v_0
@@ -2132,6 +2162,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADDshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -2198,6 +2231,7 @@
 		return true
 	}
 	// match: (ADDshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ADDshiftRA x y [c])
 	for {
 		x := v_0
@@ -2206,6 +2240,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADDshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -2288,6 +2325,7 @@
 		return true
 	}
 	// match: (ADDshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ADDshiftRL x y [c])
 	for {
 		x := v_0
@@ -2296,6 +2334,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMADDshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -2614,18 +2655,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ANDshiftLL x y:(SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (ANDshiftLL y:(SLLconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARMSLLconst {
+		c := auxIntToInt32(v.AuxInt)
+		y := v_0
+		if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -2655,6 +2694,7 @@
 		return true
 	}
 	// match: (ANDshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ANDshiftLL x y [c])
 	for {
 		x := v_0
@@ -2663,6 +2703,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMANDshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -2705,18 +2748,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ANDshiftRA x y:(SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (ANDshiftRA y:(SRAconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARMSRAconst {
+		c := auxIntToInt32(v.AuxInt)
+		y := v_0
+		if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -2746,6 +2787,7 @@
 		return true
 	}
 	// match: (ANDshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ANDshiftRA x y [c])
 	for {
 		x := v_0
@@ -2754,6 +2796,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMANDshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -2796,18 +2841,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ANDshiftRL x y:(SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (ANDshiftRL y:(SRLconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARMSRLconst {
+		c := auxIntToInt32(v.AuxInt)
+		y := v_0
+		if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -2837,6 +2880,7 @@
 		return true
 	}
 	// match: (ANDshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ANDshiftRL x y [c])
 	for {
 		x := v_0
@@ -2845,6 +2889,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMANDshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -3091,17 +3138,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (BICshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (BICshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSLLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -3115,6 +3160,7 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (BICshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (BICshiftLL x y [c])
 	for {
 		x := v_0
@@ -3123,6 +3169,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMBICshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -3147,17 +3196,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (BICshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (BICshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRAconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -3171,6 +3218,7 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (BICshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (BICshiftRA x y [c])
 	for {
 		x := v_0
@@ -3179,6 +3227,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMBICshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -3203,17 +3254,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (BICshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (BICshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -3227,6 +3276,7 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (BICshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (BICshiftRL x y [c])
 	for {
 		x := v_0
@@ -3235,6 +3285,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMBICshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -3437,6 +3490,7 @@
 		return true
 	}
 	// match: (CMNshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (CMNshiftLL x y [c])
 	for {
 		x := v_0
@@ -3445,6 +3499,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMCMNshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -3511,6 +3568,7 @@
 		return true
 	}
 	// match: (CMNshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (CMNshiftRA x y [c])
 	for {
 		x := v_0
@@ -3519,6 +3577,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMCMNshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -3585,6 +3646,7 @@
 		return true
 	}
 	// match: (CMNshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (CMNshiftRL x y [c])
 	for {
 		x := v_0
@@ -3593,6 +3655,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMCMNshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -4090,6 +4155,7 @@
 		return true
 	}
 	// match: (CMPshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (CMPshiftLL x y [c])
 	for {
 		x := v_0
@@ -4098,6 +4164,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMCMPshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -4168,6 +4237,7 @@
 		return true
 	}
 	// match: (CMPshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (CMPshiftRA x y [c])
 	for {
 		x := v_0
@@ -4176,6 +4246,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMCMPshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -4246,6 +4319,7 @@
 		return true
 	}
 	// match: (CMPshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (CMPshiftRL x y [c])
 	for {
 		x := v_0
@@ -4254,6 +4328,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMCMPshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -8101,6 +8178,7 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (MVNshiftLLreg x (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (MVNshiftLL x [c])
 	for {
 		x := v_0
@@ -8108,6 +8186,9 @@
 			break
 		}
 		c := auxIntToInt32(v_1.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMMVNshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg(x)
@@ -8135,6 +8216,7 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (MVNshiftRAreg x (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (MVNshiftRA x [c])
 	for {
 		x := v_0
@@ -8142,6 +8224,9 @@
 			break
 		}
 		c := auxIntToInt32(v_1.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMMVNshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg(x)
@@ -8169,6 +8254,7 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (MVNshiftRLreg x (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (MVNshiftRL x [c])
 	for {
 		x := v_0
@@ -8176,6 +8262,9 @@
 			break
 		}
 		c := auxIntToInt32(v_1.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMMVNshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg(x)
@@ -8556,18 +8645,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORshiftLL x y:(SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (ORshiftLL y:(SLLconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARMSLLconst {
+		c := auxIntToInt32(v.AuxInt)
+		y := v_0
+		if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -8597,6 +8684,7 @@
 		return true
 	}
 	// match: (ORshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ORshiftLL x y [c])
 	for {
 		x := v_0
@@ -8605,6 +8693,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMORshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -8647,18 +8738,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORshiftRA x y:(SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (ORshiftRA y:(SRAconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARMSRAconst {
+		c := auxIntToInt32(v.AuxInt)
+		y := v_0
+		if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -8688,6 +8777,7 @@
 		return true
 	}
 	// match: (ORshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ORshiftRA x y [c])
 	for {
 		x := v_0
@@ -8696,6 +8786,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMORshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -8754,18 +8847,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORshiftRL x y:(SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (ORshiftRL y:(SRLconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARMSRLconst {
+		c := auxIntToInt32(v.AuxInt)
+		y := v_0
+		if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -8795,6 +8886,7 @@
 		return true
 	}
 	// match: (ORshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (ORshiftRL x y [c])
 	for {
 		x := v_0
@@ -8803,6 +8895,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMORshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -9090,6 +9185,7 @@
 		return true
 	}
 	// match: (RSBSshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (RSBSshiftLL x y [c])
 	for {
 		x := v_0
@@ -9098,6 +9194,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSBSshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -9164,6 +9263,7 @@
 		return true
 	}
 	// match: (RSBSshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (RSBSshiftRA x y [c])
 	for {
 		x := v_0
@@ -9172,6 +9272,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSBSshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -9238,6 +9341,7 @@
 		return true
 	}
 	// match: (RSBSshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (RSBSshiftRL x y [c])
 	for {
 		x := v_0
@@ -9246,6 +9350,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSBSshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -9346,17 +9453,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (RSBshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (RSBshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSLLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -9387,6 +9492,7 @@
 		return true
 	}
 	// match: (RSBshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (RSBshiftLL x y [c])
 	for {
 		x := v_0
@@ -9395,6 +9501,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSBshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -9437,17 +9546,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (RSBshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (RSBshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRAconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -9478,6 +9585,7 @@
 		return true
 	}
 	// match: (RSBshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (RSBshiftRA x y [c])
 	for {
 		x := v_0
@@ -9486,6 +9594,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSBshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -9528,17 +9639,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (RSBshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (RSBshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -9569,6 +9678,7 @@
 		return true
 	}
 	// match: (RSBshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (RSBshiftRL x y [c])
 	for {
 		x := v_0
@@ -9577,6 +9687,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSBshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -9683,6 +9796,7 @@
 		return true
 	}
 	// match: (RSCshiftLLreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (RSCshiftLL x y [c] flags)
 	for {
 		x := v_0
@@ -9692,6 +9806,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSCshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -9763,6 +9880,7 @@
 		return true
 	}
 	// match: (RSCshiftRAreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (RSCshiftRA x y [c] flags)
 	for {
 		x := v_0
@@ -9772,6 +9890,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSCshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -9843,6 +9964,7 @@
 		return true
 	}
 	// match: (RSCshiftRLreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (RSCshiftRL x y [c] flags)
 	for {
 		x := v_0
@@ -9852,6 +9974,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMRSCshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -10166,6 +10291,7 @@
 		return true
 	}
 	// match: (SBCshiftLLreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (SBCshiftLL x y [c] flags)
 	for {
 		x := v_0
@@ -10175,6 +10301,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSBCshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -10246,6 +10375,7 @@
 		return true
 	}
 	// match: (SBCshiftRAreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (SBCshiftRA x y [c] flags)
 	for {
 		x := v_0
@@ -10255,6 +10385,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSBCshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -10326,6 +10459,7 @@
 		return true
 	}
 	// match: (SBCshiftRLreg x y (MOVWconst [c]) flags)
+	// cond: 0 <= c && c < 32
 	// result: (SBCshiftRL x y [c] flags)
 	for {
 		x := v_0
@@ -10335,6 +10469,9 @@
 		}
 		c := auxIntToInt32(v_2.AuxInt)
 		flags := v_3
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSBCshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg3(x, y, flags)
@@ -10346,15 +10483,19 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (SLL x (MOVWconst [c]))
-	// result: (SLLconst x [c&31])
+	// cond: 0 <= c && c < 32
+	// result: (SLLconst x [c])
 	for {
 		x := v_0
 		if v_1.Op != OpARMMOVWconst {
 			break
 		}
 		c := auxIntToInt32(v_1.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSLLconst)
-		v.AuxInt = int32ToAuxInt(c & 31)
+		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg(x)
 		return true
 	}
@@ -10380,15 +10521,19 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (SRA x (MOVWconst [c]))
-	// result: (SRAconst x [c&31])
+	// cond: 0 <= c && c < 32
+	// result: (SRAconst x [c])
 	for {
 		x := v_0
 		if v_1.Op != OpARMMOVWconst {
 			break
 		}
 		c := auxIntToInt32(v_1.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSRAconst)
-		v.AuxInt = int32ToAuxInt(c & 31)
+		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg(x)
 		return true
 	}
@@ -10472,15 +10617,19 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	// match: (SRL x (MOVWconst [c]))
-	// result: (SRLconst x [c&31])
+	// cond: 0 <= c && c < 32
+	// result: (SRLconst x [c])
 	for {
 		x := v_0
 		if v_1.Op != OpARMMOVWconst {
 			break
 		}
 		c := auxIntToInt32(v_1.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSRLconst)
-		v.AuxInt = int32ToAuxInt(c & 31)
+		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg(x)
 		return true
 	}
@@ -10520,6 +10669,24 @@
 	}
 	return false
 }
+func rewriteValueARM_OpARMSRR(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	// match: (SRR x (MOVWconst [c]))
+	// result: (SRRconst x [c&31])
+	for {
+		x := v_0
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := auxIntToInt32(v_1.AuxInt)
+		v.reset(OpARMSRRconst)
+		v.AuxInt = int32ToAuxInt(c & 31)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
 func rewriteValueARM_OpARMSUB(v *Value) bool {
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
@@ -11058,6 +11225,7 @@
 		return true
 	}
 	// match: (SUBSshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (SUBSshiftLL x y [c])
 	for {
 		x := v_0
@@ -11066,6 +11234,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSUBSshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11132,6 +11303,7 @@
 		return true
 	}
 	// match: (SUBSshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (SUBSshiftRA x y [c])
 	for {
 		x := v_0
@@ -11140,6 +11312,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSUBSshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11206,6 +11381,7 @@
 		return true
 	}
 	// match: (SUBSshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (SUBSshiftRL x y [c])
 	for {
 		x := v_0
@@ -11214,6 +11390,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSUBSshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11368,17 +11547,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SUBshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (SUBshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSLLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -11409,6 +11586,7 @@
 		return true
 	}
 	// match: (SUBshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (SUBshiftLL x y [c])
 	for {
 		x := v_0
@@ -11417,6 +11595,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSUBshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11459,17 +11640,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SUBshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (SUBshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRAconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -11500,6 +11679,7 @@
 		return true
 	}
 	// match: (SUBshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (SUBshiftRA x y [c])
 	for {
 		x := v_0
@@ -11508,6 +11688,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSUBshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11550,17 +11733,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SUBshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (SUBshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -11591,6 +11772,7 @@
 		return true
 	}
 	// match: (SUBshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (SUBshiftRL x y [c])
 	for {
 		x := v_0
@@ -11599,6 +11781,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMSUBshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11801,6 +11986,7 @@
 		return true
 	}
 	// match: (TEQshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (TEQshiftLL x y [c])
 	for {
 		x := v_0
@@ -11809,6 +11995,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMTEQshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11875,6 +12064,7 @@
 		return true
 	}
 	// match: (TEQshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (TEQshiftRA x y [c])
 	for {
 		x := v_0
@@ -11883,6 +12073,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMTEQshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -11949,6 +12142,7 @@
 		return true
 	}
 	// match: (TEQshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (TEQshiftRL x y [c])
 	for {
 		x := v_0
@@ -11957,6 +12151,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMTEQshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -12159,6 +12356,7 @@
 		return true
 	}
 	// match: (TSTshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (TSTshiftLL x y [c])
 	for {
 		x := v_0
@@ -12167,6 +12365,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMTSTshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -12233,6 +12434,7 @@
 		return true
 	}
 	// match: (TSTshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (TSTshiftRA x y [c])
 	for {
 		x := v_0
@@ -12241,6 +12443,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMTSTshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -12307,6 +12512,7 @@
 		return true
 	}
 	// match: (TSTshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (TSTshiftRL x y [c])
 	for {
 		x := v_0
@@ -12315,6 +12521,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMTSTshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -12595,17 +12804,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (XORshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (XORshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSLLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -12636,6 +12843,7 @@
 		return true
 	}
 	// match: (XORshiftLLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (XORshiftLL x y [c])
 	for {
 		x := v_0
@@ -12644,6 +12852,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMXORshiftLL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -12686,17 +12897,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (XORshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (XORshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRAconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -12727,6 +12936,7 @@
 		return true
 	}
 	// match: (XORshiftRAreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (XORshiftRA x y [c])
 	for {
 		x := v_0
@@ -12735,6 +12945,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMXORshiftRA)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -12793,17 +13006,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (XORshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (XORshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVWconst [0])
 	for {
-		d := auxIntToInt32(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARMSRLconst {
+		c := auxIntToInt32(v.AuxInt)
+		if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt32(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARMMOVWconst)
@@ -12834,6 +13045,7 @@
 		return true
 	}
 	// match: (XORshiftRLreg x y (MOVWconst [c]))
+	// cond: 0 <= c && c < 32
 	// result: (XORshiftRL x y [c])
 	for {
 		x := v_0
@@ -12842,6 +13054,9 @@
 			break
 		}
 		c := auxIntToInt32(v_2.AuxInt)
+		if !(0 <= c && c < 32) {
+			break
+		}
 		v.reset(OpARMXORshiftRL)
 		v.AuxInt = int32ToAuxInt(c)
 		v.AddArg2(x, y)
@@ -14901,19 +15116,6 @@
 	v_1 := v.Args[1]
 	v_0 := v.Args[0]
 	b := v.Block
-	// match: (RotateLeft32 x (MOVWconst [c]))
-	// result: (SRRconst [-c&31] x)
-	for {
-		x := v_0
-		if v_1.Op != OpARMMOVWconst {
-			break
-		}
-		c := auxIntToInt32(v_1.AuxInt)
-		v.reset(OpARMSRRconst)
-		v.AuxInt = int32ToAuxInt(-c & 31)
-		v.AddArg(x)
-		return true
-	}
 	// match: (RotateLeft32 x y)
 	// result: (SRR x (RSBconst [0] <y.Type> y))
 	for {
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go
index c62ff73..614e71f 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM64.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -29,6 +29,8 @@
 		return rewriteValueARM64_OpARM64ANDshiftRA(v)
 	case OpARM64ANDshiftRL:
 		return rewriteValueARM64_OpARM64ANDshiftRL(v)
+	case OpARM64ANDshiftRO:
+		return rewriteValueARM64_OpARM64ANDshiftRO(v)
 	case OpARM64BIC:
 		return rewriteValueARM64_OpARM64BIC(v)
 	case OpARM64BICshiftLL:
@@ -37,6 +39,8 @@
 		return rewriteValueARM64_OpARM64BICshiftRA(v)
 	case OpARM64BICshiftRL:
 		return rewriteValueARM64_OpARM64BICshiftRL(v)
+	case OpARM64BICshiftRO:
+		return rewriteValueARM64_OpARM64BICshiftRO(v)
 	case OpARM64CMN:
 		return rewriteValueARM64_OpARM64CMN(v)
 	case OpARM64CMNW:
@@ -89,6 +93,8 @@
 		return rewriteValueARM64_OpARM64EONshiftRA(v)
 	case OpARM64EONshiftRL:
 		return rewriteValueARM64_OpARM64EONshiftRL(v)
+	case OpARM64EONshiftRO:
+		return rewriteValueARM64_OpARM64EONshiftRO(v)
 	case OpARM64Equal:
 		return rewriteValueARM64_OpARM64Equal(v)
 	case OpARM64FADDD:
@@ -295,6 +301,8 @@
 		return rewriteValueARM64_OpARM64MVNshiftRA(v)
 	case OpARM64MVNshiftRL:
 		return rewriteValueARM64_OpARM64MVNshiftRL(v)
+	case OpARM64MVNshiftRO:
+		return rewriteValueARM64_OpARM64MVNshiftRO(v)
 	case OpARM64NEG:
 		return rewriteValueARM64_OpARM64NEG(v)
 	case OpARM64NEGshiftLL:
@@ -315,6 +323,8 @@
 		return rewriteValueARM64_OpARM64ORNshiftRA(v)
 	case OpARM64ORNshiftRL:
 		return rewriteValueARM64_OpARM64ORNshiftRL(v)
+	case OpARM64ORNshiftRO:
+		return rewriteValueARM64_OpARM64ORNshiftRO(v)
 	case OpARM64ORconst:
 		return rewriteValueARM64_OpARM64ORconst(v)
 	case OpARM64ORshiftLL:
@@ -323,6 +333,12 @@
 		return rewriteValueARM64_OpARM64ORshiftRA(v)
 	case OpARM64ORshiftRL:
 		return rewriteValueARM64_OpARM64ORshiftRL(v)
+	case OpARM64ORshiftRO:
+		return rewriteValueARM64_OpARM64ORshiftRO(v)
+	case OpARM64ROR:
+		return rewriteValueARM64_OpARM64ROR(v)
+	case OpARM64RORW:
+		return rewriteValueARM64_OpARM64RORW(v)
 	case OpARM64RORWconst:
 		return rewriteValueARM64_OpARM64RORWconst(v)
 	case OpARM64RORconst:
@@ -367,6 +383,8 @@
 		return rewriteValueARM64_OpARM64TSTshiftRA(v)
 	case OpARM64TSTshiftRL:
 		return rewriteValueARM64_OpARM64TSTshiftRL(v)
+	case OpARM64TSTshiftRO:
+		return rewriteValueARM64_OpARM64TSTshiftRO(v)
 	case OpARM64UBFIZ:
 		return rewriteValueARM64_OpARM64UBFIZ(v)
 	case OpARM64UBFX:
@@ -389,6 +407,8 @@
 		return rewriteValueARM64_OpARM64XORshiftRA(v)
 	case OpARM64XORshiftRL:
 		return rewriteValueARM64_OpARM64XORshiftRL(v)
+	case OpARM64XORshiftRO:
+		return rewriteValueARM64_OpARM64XORshiftRO(v)
 	case OpAbs:
 		v.Op = OpARM64FABSD
 		return true
@@ -1042,6 +1062,9 @@
 	case OpSubPtr:
 		v.Op = OpARM64SUB
 		return true
+	case OpTailCall:
+		v.Op = OpARM64CALLtail
+		return true
 	case OpTrunc:
 		v.Op = OpARM64FRINTZD
 		return true
@@ -2107,6 +2130,28 @@
 		}
 		break
 	}
+	// match: (AND x0 x1:(RORconst [c] y))
+	// cond: clobberIfDead(x1)
+	// result: (ANDshiftRO x0 y [c])
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			x0 := v_0
+			x1 := v_1
+			if x1.Op != OpARM64RORconst {
+				continue
+			}
+			c := auxIntToInt64(x1.AuxInt)
+			y := x1.Args[0]
+			if !(clobberIfDead(x1)) {
+				continue
+			}
+			v.reset(OpARM64ANDshiftRO)
+			v.AuxInt = int64ToAuxInt(c)
+			v.AddArg2(x0, y)
+			return true
+		}
+		break
+	}
 	return false
 }
 func rewriteValueARM64_OpARM64ANDconst(v *Value) bool {
@@ -2269,18 +2314,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ANDshiftLL x y:(SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (ANDshiftLL y:(SLLconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARM64SLLconst {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -2323,18 +2366,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ANDshiftRA x y:(SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (ANDshiftRA y:(SRAconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARM64SRAconst {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -2377,18 +2418,68 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ANDshiftRL x y:(SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (ANDshiftRL y:(SRLconst x [c]) x [c])
 	// result: y
 	for {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c {
+			break
+		}
+		x := y.Args[0]
+		if x != v_1 {
+			break
+		}
+		v.copyOf(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ANDshiftRO(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	b := v.Block
+	// match: (ANDshiftRO (MOVDconst [c]) x [d])
+	// result: (ANDconst [c] (RORconst <x.Type> x [d]))
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_0.AuxInt)
+		x := v_1
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = int64ToAuxInt(c)
+		v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+		v0.AuxInt = int64ToAuxInt(d)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftRO x (MOVDconst [c]) [d])
+	// result: (ANDconst x [rotateRight64(c, d)])
+	for {
 		d := auxIntToInt64(v.AuxInt)
 		x := v_0
-		y := v_1
-		if y.Op != OpARM64SRLconst {
+		if v_1.Op != OpARM64MOVDconst {
 			break
 		}
-		c := auxIntToInt64(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		c := auxIntToInt64(v_1.AuxInt)
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDshiftRO y:(RORconst x [c]) x [c])
+	// result: y
+	for {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c {
+			break
+		}
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -2480,6 +2571,25 @@
 		v.AddArg2(x0, y)
 		return true
 	}
+	// match: (BIC x0 x1:(RORconst [c] y))
+	// cond: clobberIfDead(x1)
+	// result: (BICshiftRO x0 y [c])
+	for {
+		x0 := v_0
+		x1 := v_1
+		if x1.Op != OpARM64RORconst {
+			break
+		}
+		c := auxIntToInt64(x1.AuxInt)
+		y := x1.Args[0]
+		if !(clobberIfDead(x1)) {
+			break
+		}
+		v.reset(OpARM64BICshiftRO)
+		v.AuxInt = int64ToAuxInt(c)
+		v.AddArg2(x0, y)
+		return true
+	}
 	return false
 }
 func rewriteValueARM64_OpARM64BICshiftLL(v *Value) bool {
@@ -2499,17 +2609,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (BICshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (BICshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SLLconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -2535,17 +2643,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (BICshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (BICshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SRAconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -2571,17 +2677,49 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (BICshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (BICshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
+			break
+		}
+		x := v_0.Args[0]
+		if x != v_1 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64ToAuxInt(0)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64BICshiftRO(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	// match: (BICshiftRO x (MOVDconst [c]) [d])
+	// result: (ANDconst x [^rotateRight64(c, d)])
+	for {
 		d := auxIntToInt64(v.AuxInt)
 		x := v_0
-		if v_1.Op != OpARM64SRLconst {
+		if v_1.Op != OpARM64MOVDconst {
 			break
 		}
 		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICshiftRO (RORconst x [c]) x [c])
+	// result: (MOVDconst [0])
+	for {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+			break
+		}
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -3930,6 +4068,25 @@
 		v.AddArg2(x0, y)
 		return true
 	}
+	// match: (EON x0 x1:(RORconst [c] y))
+	// cond: clobberIfDead(x1)
+	// result: (EONshiftRO x0 y [c])
+	for {
+		x0 := v_0
+		x1 := v_1
+		if x1.Op != OpARM64RORconst {
+			break
+		}
+		c := auxIntToInt64(x1.AuxInt)
+		y := x1.Args[0]
+		if !(clobberIfDead(x1)) {
+			break
+		}
+		v.reset(OpARM64EONshiftRO)
+		v.AuxInt = int64ToAuxInt(c)
+		v.AddArg2(x0, y)
+		return true
+	}
 	return false
 }
 func rewriteValueARM64_OpARM64EONshiftLL(v *Value) bool {
@@ -3949,17 +4106,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (EONshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (EONshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVDconst [-1])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SLLconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -3985,17 +4140,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (EONshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (EONshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVDconst [-1])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SRAconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -4021,17 +4174,49 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (EONshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (EONshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVDconst [-1])
 	for {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
+			break
+		}
+		x := v_0.Args[0]
+		if x != v_1 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64ToAuxInt(-1)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64EONshiftRO(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	// match: (EONshiftRO x (MOVDconst [c]) [d])
+	// result: (XORconst x [^rotateRight64(c, d)])
+	for {
 		d := auxIntToInt64(v.AuxInt)
 		x := v_0
-		if v_1.Op != OpARM64SRLconst {
+		if v_1.Op != OpARM64MOVDconst {
 			break
 		}
 		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		v.reset(OpARM64XORconst)
+		v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (EONshiftRO (RORconst x [c]) x [c])
+	// result: (MOVDconst [-1])
+	for {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+			break
+		}
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -7157,37 +7342,37 @@
 		v.AuxInt = int64ToAuxInt(0)
 		return true
 	}
-	// match: (MOVBUreg (SLLconst [sc] x))
-	// cond: isARM64BFMask(sc, 1<<8-1, sc)
-	// result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
+	// match: (MOVBUreg (SLLconst [lc] x))
+	// cond: lc < 8
+	// result: (UBFIZ [armBFAuxInt(lc, 8-lc)] x)
 	for {
 		if v_0.Op != OpARM64SLLconst {
 			break
 		}
-		sc := auxIntToInt64(v_0.AuxInt)
+		lc := auxIntToInt64(v_0.AuxInt)
 		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<8-1, sc)) {
+		if !(lc < 8) {
 			break
 		}
 		v.reset(OpARM64UBFIZ)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc)))
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 8-lc))
 		v.AddArg(x)
 		return true
 	}
-	// match: (MOVBUreg (SRLconst [sc] x))
-	// cond: isARM64BFMask(sc, 1<<8-1, 0)
-	// result: (UBFX [armBFAuxInt(sc, 8)] x)
+	// match: (MOVBUreg (SRLconst [rc] x))
+	// cond: rc < 8
+	// result: (UBFX [armBFAuxInt(rc, 8)] x)
 	for {
 		if v_0.Op != OpARM64SRLconst {
 			break
 		}
-		sc := auxIntToInt64(v_0.AuxInt)
+		rc := auxIntToInt64(v_0.AuxInt)
 		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<8-1, 0)) {
+		if !(rc < 8) {
 			break
 		}
 		v.reset(OpARM64UBFX)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8))
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8))
 		v.AddArg(x)
 		return true
 	}
@@ -10703,37 +10888,37 @@
 		v.AuxInt = int64ToAuxInt(0)
 		return true
 	}
-	// match: (MOVHUreg (SLLconst [sc] x))
-	// cond: isARM64BFMask(sc, 1<<16-1, sc)
-	// result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
+	// match: (MOVHUreg (SLLconst [lc] x))
+	// cond: lc < 16
+	// result: (UBFIZ [armBFAuxInt(lc, 16-lc)] x)
 	for {
 		if v_0.Op != OpARM64SLLconst {
 			break
 		}
-		sc := auxIntToInt64(v_0.AuxInt)
+		lc := auxIntToInt64(v_0.AuxInt)
 		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<16-1, sc)) {
+		if !(lc < 16) {
 			break
 		}
 		v.reset(OpARM64UBFIZ)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc)))
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 16-lc))
 		v.AddArg(x)
 		return true
 	}
-	// match: (MOVHUreg (SRLconst [sc] x))
-	// cond: isARM64BFMask(sc, 1<<16-1, 0)
-	// result: (UBFX [armBFAuxInt(sc, 16)] x)
+	// match: (MOVHUreg (SRLconst [rc] x))
+	// cond: rc < 16
+	// result: (UBFX [armBFAuxInt(rc, 16)] x)
 	for {
 		if v_0.Op != OpARM64SRLconst {
 			break
 		}
-		sc := auxIntToInt64(v_0.AuxInt)
+		rc := auxIntToInt64(v_0.AuxInt)
 		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<16-1, 0)) {
+		if !(rc < 16) {
 			break
 		}
 		v.reset(OpARM64UBFX)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16))
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16))
 		v.AddArg(x)
 		return true
 	}
@@ -12849,37 +13034,37 @@
 		v.AuxInt = int64ToAuxInt(0)
 		return true
 	}
-	// match: (MOVWUreg (SLLconst [sc] x))
-	// cond: isARM64BFMask(sc, 1<<32-1, sc)
-	// result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
+	// match: (MOVWUreg (SLLconst [lc] x))
+	// cond: lc < 32
+	// result: (UBFIZ [armBFAuxInt(lc, 32-lc)] x)
 	for {
 		if v_0.Op != OpARM64SLLconst {
 			break
 		}
-		sc := auxIntToInt64(v_0.AuxInt)
+		lc := auxIntToInt64(v_0.AuxInt)
 		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<32-1, sc)) {
+		if !(lc < 32) {
 			break
 		}
 		v.reset(OpARM64UBFIZ)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc)))
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 32-lc))
 		v.AddArg(x)
 		return true
 	}
-	// match: (MOVWUreg (SRLconst [sc] x))
-	// cond: isARM64BFMask(sc, 1<<32-1, 0)
-	// result: (UBFX [armBFAuxInt(sc, 32)] x)
+	// match: (MOVWUreg (SRLconst [rc] x))
+	// cond: rc < 32
+	// result: (UBFX [armBFAuxInt(rc, 32)] x)
 	for {
 		if v_0.Op != OpARM64SRLconst {
 			break
 		}
-		sc := auxIntToInt64(v_0.AuxInt)
+		rc := auxIntToInt64(v_0.AuxInt)
 		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<32-1, 0)) {
+		if !(rc < 32) {
 			break
 		}
 		v.reset(OpARM64UBFX)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32))
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32))
 		v.AddArg(x)
 		return true
 	}
@@ -15608,6 +15793,24 @@
 		v.AddArg(y)
 		return true
 	}
+	// match: (MVN x:(RORconst [c] y))
+	// cond: clobberIfDead(x)
+	// result: (MVNshiftRO [c] y)
+	for {
+		x := v_0
+		if x.Op != OpARM64RORconst {
+			break
+		}
+		c := auxIntToInt64(x.AuxInt)
+		y := x.Args[0]
+		if !(clobberIfDead(x)) {
+			break
+		}
+		v.reset(OpARM64MVNshiftRO)
+		v.AuxInt = int64ToAuxInt(c)
+		v.AddArg(y)
+		return true
+	}
 	return false
 }
 func rewriteValueARM64_OpARM64MVNshiftLL(v *Value) bool {
@@ -15658,6 +15861,22 @@
 	}
 	return false
 }
+func rewriteValueARM64_OpARM64MVNshiftRO(v *Value) bool {
+	v_0 := v.Args[0]
+	// match: (MVNshiftRO (MOVDconst [c]) [d])
+	// result: (MOVDconst [^rotateRight64(c, d)])
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_0.AuxInt)
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+		return true
+	}
+	return false
+}
 func rewriteValueARM64_OpARM64NEG(v *Value) bool {
 	v_0 := v.Args[0]
 	// match: (NEG (MUL x y))
@@ -15684,6 +15903,16 @@
 		v.AddArg2(x, y)
 		return true
 	}
+	// match: (NEG (NEG x))
+	// result: x
+	for {
+		if v_0.Op != OpARM64NEG {
+			break
+		}
+		x := v_0.Args[0]
+		v.copyOf(x)
+		return true
+	}
 	// match: (NEG (MOVDconst [c]))
 	// result: (MOVDconst [-c])
 	for {
@@ -15937,6 +16166,28 @@
 		}
 		break
 	}
+	// match: (OR x0 x1:(RORconst [c] y))
+	// cond: clobberIfDead(x1)
+	// result: (ORshiftRO x0 y [c])
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			x0 := v_0
+			x1 := v_1
+			if x1.Op != OpARM64RORconst {
+				continue
+			}
+			c := auxIntToInt64(x1.AuxInt)
+			y := x1.Args[0]
+			if !(clobberIfDead(x1)) {
+				continue
+			}
+			v.reset(OpARM64ORshiftRO)
+			v.AuxInt = int64ToAuxInt(c)
+			v.AddArg2(x0, y)
+			return true
+		}
+		break
+	}
 	// match: (OR (SLL x (ANDconst <t> [63] y)) (CSEL0 <typ.UInt64> [cc] (SRL <typ.UInt64> x (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y))) (CMPconst [64] (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y)))))
 	// cond: cc == OpARM64LessThanU
 	// result: (ROR x (NEG <t> y))
@@ -17906,6 +18157,25 @@
 		v.AddArg2(x0, y)
 		return true
 	}
+	// match: (ORN x0 x1:(RORconst [c] y))
+	// cond: clobberIfDead(x1)
+	// result: (ORNshiftRO x0 y [c])
+	for {
+		x0 := v_0
+		x1 := v_1
+		if x1.Op != OpARM64RORconst {
+			break
+		}
+		c := auxIntToInt64(x1.AuxInt)
+		y := x1.Args[0]
+		if !(clobberIfDead(x1)) {
+			break
+		}
+		v.reset(OpARM64ORNshiftRO)
+		v.AuxInt = int64ToAuxInt(c)
+		v.AddArg2(x0, y)
+		return true
+	}
 	return false
 }
 func rewriteValueARM64_OpARM64ORNshiftLL(v *Value) bool {
@@ -17925,17 +18195,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORNshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (ORNshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVDconst [-1])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SLLconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -17961,17 +18229,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORNshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (ORNshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVDconst [-1])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SRAconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -17997,17 +18263,49 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORNshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (ORNshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVDconst [-1])
 	for {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
+			break
+		}
+		x := v_0.Args[0]
+		if x != v_1 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64ToAuxInt(-1)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ORNshiftRO(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	// match: (ORNshiftRO x (MOVDconst [c]) [d])
+	// result: (ORconst x [^rotateRight64(c, d)])
+	for {
 		d := auxIntToInt64(v.AuxInt)
 		x := v_0
-		if v_1.Op != OpARM64SRLconst {
+		if v_1.Op != OpARM64MOVDconst {
 			break
 		}
 		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		v.reset(OpARM64ORconst)
+		v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORNshiftRO (RORconst x [c]) x [c])
+	// result: (MOVDconst [-1])
+	for {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+			break
+		}
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -18120,18 +18418,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORshiftLL x y:(SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (ORshiftLL y:(SLLconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARM64SLLconst {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -19800,18 +20096,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORshiftRA x y:(SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (ORshiftRA y:(SRAconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARM64SRAconst {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -19854,18 +20148,16 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (ORshiftRL x y:(SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (ORshiftRL y:(SRLconst x [c]) x [c])
 	// result: y
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		y := v_1
-		if y.Op != OpARM64SRLconst {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(y.AuxInt)
-		if x != y.Args[0] || !(c == d) {
+		x := y.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.copyOf(y)
@@ -19953,6 +20245,94 @@
 	}
 	return false
 }
+func rewriteValueARM64_OpARM64ORshiftRO(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	b := v.Block
+	// match: (ORshiftRO (MOVDconst [c]) x [d])
+	// result: (ORconst [c] (RORconst <x.Type> x [d]))
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_0.AuxInt)
+		x := v_1
+		v.reset(OpARM64ORconst)
+		v.AuxInt = int64ToAuxInt(c)
+		v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+		v0.AuxInt = int64ToAuxInt(d)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftRO x (MOVDconst [c]) [d])
+	// result: (ORconst x [rotateRight64(c, d)])
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		x := v_0
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_1.AuxInt)
+		v.reset(OpARM64ORconst)
+		v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORshiftRO y:(RORconst x [c]) x [c])
+	// result: y
+	for {
+		c := auxIntToInt64(v.AuxInt)
+		y := v_0
+		if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c {
+			break
+		}
+		x := y.Args[0]
+		if x != v_1 {
+			break
+		}
+		v.copyOf(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ROR(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	// match: (ROR x (MOVDconst [c]))
+	// result: (RORconst x [c&63])
+	for {
+		x := v_0
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_1.AuxInt)
+		v.reset(OpARM64RORconst)
+		v.AuxInt = int64ToAuxInt(c & 63)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64RORW(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	// match: (RORW x (MOVDconst [c]))
+	// result: (RORWconst x [c&31])
+	for {
+		x := v_0
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_1.AuxInt)
+		v.reset(OpARM64RORWconst)
+		v.AuxInt = int64ToAuxInt(c & 31)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
 func rewriteValueARM64_OpARM64RORWconst(v *Value) bool {
 	v_0 := v.Args[0]
 	// match: (RORWconst [c] (RORWconst [d] x))
@@ -20130,6 +20510,45 @@
 		v.AddArg(x)
 		return true
 	}
+	// match: (SLLconst [lc] (MOVWUreg x))
+	// result: (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x)
+	for {
+		lc := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVWUreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64UBFIZ)
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(32, 64-lc)))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SLLconst [lc] (MOVHUreg x))
+	// result: (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x)
+	for {
+		lc := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVHUreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64UBFIZ)
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(16, 64-lc)))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SLLconst [lc] (MOVBUreg x))
+	// result: (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x)
+	for {
+		lc := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVBUreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64UBFIZ)
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(8, 64-lc)))
+		v.AddArg(x)
+		return true
+	}
 	// match: (SLLconst [sc] (ANDconst [ac] x))
 	// cond: isARM64BFMask(sc, ac, 0)
 	// result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x)
@@ -20148,57 +20567,6 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SLLconst [sc] (MOVWUreg x))
-	// cond: isARM64BFMask(sc, 1<<32-1, 0)
-	// result: (UBFIZ [armBFAuxInt(sc, 32)] x)
-	for {
-		sc := auxIntToInt64(v.AuxInt)
-		if v_0.Op != OpARM64MOVWUreg {
-			break
-		}
-		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<32-1, 0)) {
-			break
-		}
-		v.reset(OpARM64UBFIZ)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32))
-		v.AddArg(x)
-		return true
-	}
-	// match: (SLLconst [sc] (MOVHUreg x))
-	// cond: isARM64BFMask(sc, 1<<16-1, 0)
-	// result: (UBFIZ [armBFAuxInt(sc, 16)] x)
-	for {
-		sc := auxIntToInt64(v.AuxInt)
-		if v_0.Op != OpARM64MOVHUreg {
-			break
-		}
-		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<16-1, 0)) {
-			break
-		}
-		v.reset(OpARM64UBFIZ)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16))
-		v.AddArg(x)
-		return true
-	}
-	// match: (SLLconst [sc] (MOVBUreg x))
-	// cond: isARM64BFMask(sc, 1<<8-1, 0)
-	// result: (UBFIZ [armBFAuxInt(sc, 8)] x)
-	for {
-		sc := auxIntToInt64(v.AuxInt)
-		if v_0.Op != OpARM64MOVBUreg {
-			break
-		}
-		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<8-1, 0)) {
-			break
-		}
-		v.reset(OpARM64UBFIZ)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8))
-		v.AddArg(x)
-		return true
-	}
 	// match: (SLLconst [sc] (UBFIZ [bfc] x))
 	// cond: sc+bfc.getARM64BFwidth()+bfc.getARM64BFlsb() < 64
 	// result: (UBFIZ [armBFAuxInt(bfc.getARM64BFlsb()+sc, bfc.getARM64BFwidth())] x)
@@ -20488,75 +20856,6 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SRLconst [sc] (ANDconst [ac] x))
-	// cond: isARM64BFMask(sc, ac, sc)
-	// result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
-	for {
-		sc := auxIntToInt64(v.AuxInt)
-		if v_0.Op != OpARM64ANDconst {
-			break
-		}
-		ac := auxIntToInt64(v_0.AuxInt)
-		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, ac, sc)) {
-			break
-		}
-		v.reset(OpARM64UBFX)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc)))
-		v.AddArg(x)
-		return true
-	}
-	// match: (SRLconst [sc] (MOVWUreg x))
-	// cond: isARM64BFMask(sc, 1<<32-1, sc)
-	// result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
-	for {
-		sc := auxIntToInt64(v.AuxInt)
-		if v_0.Op != OpARM64MOVWUreg {
-			break
-		}
-		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<32-1, sc)) {
-			break
-		}
-		v.reset(OpARM64UBFX)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc)))
-		v.AddArg(x)
-		return true
-	}
-	// match: (SRLconst [sc] (MOVHUreg x))
-	// cond: isARM64BFMask(sc, 1<<16-1, sc)
-	// result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
-	for {
-		sc := auxIntToInt64(v.AuxInt)
-		if v_0.Op != OpARM64MOVHUreg {
-			break
-		}
-		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<16-1, sc)) {
-			break
-		}
-		v.reset(OpARM64UBFX)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc)))
-		v.AddArg(x)
-		return true
-	}
-	// match: (SRLconst [sc] (MOVBUreg x))
-	// cond: isARM64BFMask(sc, 1<<8-1, sc)
-	// result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
-	for {
-		sc := auxIntToInt64(v.AuxInt)
-		if v_0.Op != OpARM64MOVBUreg {
-			break
-		}
-		x := v_0.Args[0]
-		if !(isARM64BFMask(sc, 1<<8-1, sc)) {
-			break
-		}
-		v.reset(OpARM64UBFX)
-		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc)))
-		v.AddArg(x)
-		return true
-	}
 	// match: (SRLconst [rc] (SLLconst [lc] x))
 	// cond: lc < rc
 	// result: (UBFX [armBFAuxInt(rc-lc, 64-rc)] x)
@@ -20575,6 +20874,75 @@
 		v.AddArg(x)
 		return true
 	}
+	// match: (SRLconst [rc] (MOVWUreg x))
+	// cond: rc < 32
+	// result: (UBFX [armBFAuxInt(rc, 32-rc)] x)
+	for {
+		rc := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVWUreg {
+			break
+		}
+		x := v_0.Args[0]
+		if !(rc < 32) {
+			break
+		}
+		v.reset(OpARM64UBFX)
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32-rc))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRLconst [rc] (MOVHUreg x))
+	// cond: rc < 16
+	// result: (UBFX [armBFAuxInt(rc, 16-rc)] x)
+	for {
+		rc := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVHUreg {
+			break
+		}
+		x := v_0.Args[0]
+		if !(rc < 16) {
+			break
+		}
+		v.reset(OpARM64UBFX)
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16-rc))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRLconst [rc] (MOVBUreg x))
+	// cond: rc < 8
+	// result: (UBFX [armBFAuxInt(rc, 8-rc)] x)
+	for {
+		rc := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVBUreg {
+			break
+		}
+		x := v_0.Args[0]
+		if !(rc < 8) {
+			break
+		}
+		v.reset(OpARM64UBFX)
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8-rc))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRLconst [sc] (ANDconst [ac] x))
+	// cond: isARM64BFMask(sc, ac, sc)
+	// result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
+	for {
+		sc := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64ANDconst {
+			break
+		}
+		ac := auxIntToInt64(v_0.AuxInt)
+		x := v_0.Args[0]
+		if !(isARM64BFMask(sc, ac, sc)) {
+			break
+		}
+		v.reset(OpARM64UBFX)
+		v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc)))
+		v.AddArg(x)
+		return true
+	}
 	// match: (SRLconst [sc] (UBFX [bfc] x))
 	// cond: sc < bfc.getARM64BFwidth()
 	// result: (UBFX [armBFAuxInt(bfc.getARM64BFlsb()+sc, bfc.getARM64BFwidth()-sc)] x)
@@ -20981,17 +21349,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SUBshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (SUBshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SLLconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -21017,17 +21383,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SUBshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (SUBshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SRAconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -21053,17 +21417,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (SUBshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (SUBshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SRLconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -21157,6 +21519,28 @@
 		}
 		break
 	}
+	// match: (TST x0 x1:(RORconst [c] y))
+	// cond: clobberIfDead(x1)
+	// result: (TSTshiftRO x0 y [c])
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			x0 := v_0
+			x1 := v_1
+			if x1.Op != OpARM64RORconst {
+				continue
+			}
+			c := auxIntToInt64(x1.AuxInt)
+			y := x1.Args[0]
+			if !(clobberIfDead(x1)) {
+				continue
+			}
+			v.reset(OpARM64TSTshiftRO)
+			v.AuxInt = int64ToAuxInt(c)
+			v.AddArg2(x0, y)
+			return true
+		}
+		break
+	}
 	return false
 }
 func rewriteValueARM64_OpARM64TSTW(v *Value) bool {
@@ -21323,6 +21707,43 @@
 	}
 	return false
 }
+func rewriteValueARM64_OpARM64TSTshiftRO(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	b := v.Block
+	// match: (TSTshiftRO (MOVDconst [c]) x [d])
+	// result: (TSTconst [c] (RORconst <x.Type> x [d]))
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_0.AuxInt)
+		x := v_1
+		v.reset(OpARM64TSTconst)
+		v.AuxInt = int64ToAuxInt(c)
+		v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+		v0.AuxInt = int64ToAuxInt(d)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (TSTshiftRO x (MOVDconst [c]) [d])
+	// result: (TSTconst x [rotateRight64(c, d)])
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		x := v_0
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_1.AuxInt)
+		v.reset(OpARM64TSTconst)
+		v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
 func rewriteValueARM64_OpARM64UBFIZ(v *Value) bool {
 	v_0 := v.Args[0]
 	// match: (UBFIZ [bfc] (SLLconst [sc] x))
@@ -21782,6 +22203,28 @@
 		}
 		break
 	}
+	// match: (XOR x0 x1:(RORconst [c] y))
+	// cond: clobberIfDead(x1)
+	// result: (XORshiftRO x0 y [c])
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			x0 := v_0
+			x1 := v_1
+			if x1.Op != OpARM64RORconst {
+				continue
+			}
+			c := auxIntToInt64(x1.AuxInt)
+			y := x1.Args[0]
+			if !(clobberIfDead(x1)) {
+				continue
+			}
+			v.reset(OpARM64XORshiftRO)
+			v.AuxInt = int64ToAuxInt(c)
+			v.AddArg2(x0, y)
+			return true
+		}
+		break
+	}
 	// match: (XOR (SLL x (ANDconst <t> [63] y)) (CSEL0 <typ.UInt64> [cc] (SRL <typ.UInt64> x (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y))) (CMPconst [64] (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y)))))
 	// cond: cc == OpARM64LessThanU
 	// result: (ROR x (NEG <t> y))
@@ -22152,17 +22595,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (XORshiftLL x (SLLconst x [c]) [d])
-	// cond: c==d
+	// match: (XORshiftLL (SLLconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SLLconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -22364,17 +22805,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (XORshiftRA x (SRAconst x [c]) [d])
-	// cond: c==d
+	// match: (XORshiftRA (SRAconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SRAconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -22418,17 +22857,15 @@
 		v.AddArg(x)
 		return true
 	}
-	// match: (XORshiftRL x (SRLconst x [c]) [d])
-	// cond: c==d
+	// match: (XORshiftRL (SRLconst x [c]) x [c])
 	// result: (MOVDconst [0])
 	for {
-		d := auxIntToInt64(v.AuxInt)
-		x := v_0
-		if v_1.Op != OpARM64SRLconst {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
 			break
 		}
-		c := auxIntToInt64(v_1.AuxInt)
-		if x != v_1.Args[0] || !(c == d) {
+		x := v_0.Args[0]
+		if x != v_1 {
 			break
 		}
 		v.reset(OpARM64MOVDconst)
@@ -22471,6 +22908,58 @@
 	}
 	return false
 }
+func rewriteValueARM64_OpARM64XORshiftRO(v *Value) bool {
+	v_1 := v.Args[1]
+	v_0 := v.Args[0]
+	b := v.Block
+	// match: (XORshiftRO (MOVDconst [c]) x [d])
+	// result: (XORconst [c] (RORconst <x.Type> x [d]))
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_0.AuxInt)
+		x := v_1
+		v.reset(OpARM64XORconst)
+		v.AuxInt = int64ToAuxInt(c)
+		v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+		v0.AuxInt = int64ToAuxInt(d)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRO x (MOVDconst [c]) [d])
+	// result: (XORconst x [rotateRight64(c, d)])
+	for {
+		d := auxIntToInt64(v.AuxInt)
+		x := v_0
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := auxIntToInt64(v_1.AuxInt)
+		v.reset(OpARM64XORconst)
+		v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORshiftRO (RORconst x [c]) x [c])
+	// result: (MOVDconst [0])
+	for {
+		c := auxIntToInt64(v.AuxInt)
+		if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+			break
+		}
+		x := v_0.Args[0]
+		if x != v_1 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64ToAuxInt(0)
+		return true
+	}
+	return false
+}
 func rewriteValueARM64_OpAddr(v *Value) bool {
 	v_0 := v.Args[0]
 	// match: (Addr {sym} base)
diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS.go b/src/cmd/compile/internal/ssa/rewriteMIPS.go
index 429369d..811ea9d 100644
--- a/src/cmd/compile/internal/ssa/rewriteMIPS.go
+++ b/src/cmd/compile/internal/ssa/rewriteMIPS.go
@@ -544,6 +544,9 @@
 	case OpSubPtr:
 		v.Op = OpMIPSSUB
 		return true
+	case OpTailCall:
+		v.Op = OpMIPSCALLtail
+		return true
 	case OpTrunc16to8:
 		v.Op = OpCopy
 		return true
diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS64.go b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
index 772d7b6..1fbd556 100644
--- a/src/cmd/compile/internal/ssa/rewriteMIPS64.go
+++ b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
@@ -625,6 +625,9 @@
 	case OpSubPtr:
 		v.Op = OpMIPS64SUBV
 		return true
+	case OpTailCall:
+		v.Op = OpMIPS64CALLtail
+		return true
 	case OpTrunc16to8:
 		v.Op = OpCopy
 		return true
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 96dee0b..b35331a 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -772,6 +772,9 @@
 	case OpSubPtr:
 		v.Op = OpPPC64SUB
 		return true
+	case OpTailCall:
+		v.Op = OpPPC64CALLtail
+		return true
 	case OpTrunc:
 		v.Op = OpPPC64FTRUNC
 		return true
diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
index 743ff50..f856a26 100644
--- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go
+++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
@@ -8,6 +8,9 @@
 
 func rewriteValueRISCV64(v *Value) bool {
 	switch v.Op {
+	case OpAbs:
+		v.Op = OpRISCV64FABSD
+		return true
 	case OpAdd16:
 		v.Op = OpRISCV64ADD
 		return true
@@ -134,6 +137,9 @@
 	case OpConvert:
 		v.Op = OpRISCV64MOVconvert
 		return true
+	case OpCopysign:
+		v.Op = OpRISCV64FSGNJD
+		return true
 	case OpCvt32Fto32:
 		v.Op = OpRISCV64FCVTWS
 		return true
@@ -633,6 +639,9 @@
 	case OpSubPtr:
 		v.Op = OpRISCV64SUB
 		return true
+	case OpTailCall:
+		v.Op = OpRISCV64CALLtail
+		return true
 	case OpTrunc16to8:
 		v.Op = OpCopy
 		return true
diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go
index 8b41d62..0d63586 100644
--- a/src/cmd/compile/internal/ssa/rewriteS390X.go
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -819,6 +819,9 @@
 	case OpSubPtr:
 		v.Op = OpS390XSUB
 		return true
+	case OpTailCall:
+		v.Op = OpS390XCALLtail
+		return true
 	case OpTrunc:
 		return rewriteValueS390X_OpTrunc(v)
 	case OpTrunc16to8:
diff --git a/src/cmd/compile/internal/ssa/rewriteWasm.go b/src/cmd/compile/internal/ssa/rewriteWasm.go
index 5dab09f..defd40d 100644
--- a/src/cmd/compile/internal/ssa/rewriteWasm.go
+++ b/src/cmd/compile/internal/ssa/rewriteWasm.go
@@ -556,6 +556,9 @@
 	case OpSubPtr:
 		v.Op = OpWasmI64Sub
 		return true
+	case OpTailCall:
+		v.Op = OpWasmLoweredTailCall
+		return true
 	case OpTrunc:
 		v.Op = OpWasmF64Trunc
 		return true
diff --git a/src/cmd/compile/internal/ssa/testdata/inline-dump.go b/src/cmd/compile/internal/ssa/testdata/inline-dump.go
new file mode 100644
index 0000000..97893b6
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/testdata/inline-dump.go
@@ -0,0 +1,17 @@
+package foo
+
+func f(m, n int) int {
+	a := g(n)
+	b := g(m)
+	return a + b
+}
+
+func g(x int) int {
+	y := h(x + 1)
+	z := h(x - 1)
+	return y + z
+}
+
+func h(x int) int {
+	return x * x
+}
diff --git a/src/cmd/compile/internal/ssa/testdata/sayhi.go b/src/cmd/compile/internal/ssa/testdata/sayhi.go
new file mode 100644
index 0000000..680e1eb
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/testdata/sayhi.go
@@ -0,0 +1,12 @@
+package foo
+
+import (
+	"fmt"
+	"sync"
+)
+
+func sayhi(n int, wg *sync.WaitGroup) {
+	fmt.Println("hi", n)
+	fmt.Println("hi", n)
+	wg.Done()
+}
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 630e481..630143c 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -351,11 +351,13 @@
 // invalidateRecursively marks a value as invalid (unused)
 // and after decrementing reference counts on its Args,
 // also recursively invalidates any of those whose use
-// count goes to zero.
+// count goes to zero.  It returns whether any of the
+// invalidated values was marked with IsStmt.
 //
 // BEWARE of doing this *before* you've applied intended
 // updates to SSA.
-func (v *Value) invalidateRecursively() {
+func (v *Value) invalidateRecursively() bool {
+	lostStmt := v.Pos.IsStmt() == src.PosIsStmt
 	if v.InCache {
 		v.Block.Func.unCache(v)
 	}
@@ -364,7 +366,8 @@
 	for _, a := range v.Args {
 		a.Uses--
 		if a.Uses == 0 {
-			a.invalidateRecursively()
+			lost := a.invalidateRecursively()
+			lostStmt = lost || lostStmt
 		}
 	}
 
@@ -375,6 +378,7 @@
 
 	v.AuxInt = 0
 	v.Aux = nil
+	return lostStmt
 }
 
 // copyOf is called from rewrite rules.
diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go
index d751096..52f060b 100644
--- a/src/cmd/compile/internal/ssa/writebarrier.go
+++ b/src/cmd/compile/internal/ssa/writebarrier.go
@@ -544,7 +544,7 @@
 		v = v.Args[0]
 	}
 	switch v.Op {
-	case OpSP, OpLocalAddr, OpSelectNAddr:
+	case OpSP, OpLocalAddr, OpSelectNAddr, OpGetCallerSP:
 		return true
 	}
 	return false
diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go
index 6d8c53e..c54a734 100644
--- a/src/cmd/compile/internal/ssagen/abi.go
+++ b/src/cmd/compile/internal/ssagen/abi.go
@@ -382,18 +382,16 @@
 	}
 
 	var tail ir.Node
+	call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
+	call.Args = ir.ParamNames(tfn.Type())
+	call.IsDDD = tfn.Type().IsVariadic()
+	tail = call
 	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
-		}
+		tail = ir.NewTailCallStmt(base.Pos, call)
+	} else if tfn.Type().NumResults() > 0 {
+		n := ir.NewReturnStmt(base.Pos, nil)
+		n.Results = []ir.Node{call}
+		tail = n
 	}
 	fn.Body.Append(tail)
 
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 1d5a872..82d232f 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -96,6 +96,7 @@
 	ir.Syms.AssertE2I2 = typecheck.LookupRuntimeFunc("assertE2I2")
 	ir.Syms.AssertI2I = typecheck.LookupRuntimeFunc("assertI2I")
 	ir.Syms.AssertI2I2 = typecheck.LookupRuntimeFunc("assertI2I2")
+	ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignment")
 	ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc")
 	ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack")
 	ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn")
@@ -366,6 +367,7 @@
 	if fn.Pragma&ir.CgoUnsafeArgs != 0 {
 		s.cgoUnsafeArgs = true
 	}
+	s.checkPtrEnabled = ir.ShouldCheckPtr(fn, 1)
 
 	fe := ssafn{
 		curfn: fn,
@@ -709,6 +711,31 @@
 	return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, s.reflectType(typ))[0]
 }
 
+func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value) {
+	if !n.Type().IsPtr() {
+		s.Fatalf("expected pointer type: %v", n.Type())
+	}
+	elem := n.Type().Elem()
+	if count != nil {
+		if !elem.IsArray() {
+			s.Fatalf("expected array type: %v", elem)
+		}
+		elem = elem.Elem()
+	}
+	size := elem.Size()
+	// Casting from larger type to smaller one is ok, so for smallest type, do nothing.
+	if elem.Alignment() == 1 && (size == 0 || size == 1 || count == nil) {
+		return
+	}
+	if count == nil {
+		count = s.constInt(types.Types[types.TUINTPTR], 1)
+	}
+	if count.Type.Size() != s.config.PtrSize {
+		s.Fatalf("expected count fit to an uintptr size, have: %d, want: %d", count.Type.Size(), s.config.PtrSize)
+	}
+	s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, s.reflectType(elem), count)
+}
+
 // reflectType returns an SSA value representing a pointer to typ's
 // reflection type descriptor.
 func (s *state) reflectType(typ *types.Type) *ssa.Value {
@@ -861,10 +888,11 @@
 	// 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
+	cgoUnsafeArgs   bool
+	hasdefer        bool // whether the function contains a defer statement
+	softFloat       bool
+	hasOpenDefers   bool // whether we are doing open-coded defers
+	checkPtrEnabled bool // whether to insert checkptr instrumentation
 
 	// 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
@@ -1668,9 +1696,11 @@
 
 	case ir.OTAILCALL:
 		n := n.(*ir.TailCallStmt)
-		b := s.exit()
-		b.Kind = ssa.BlockRetJmp // override BlockRet
-		b.Aux = callTargetLSym(n.Target)
+		s.callResult(n.Call, callTail)
+		call := s.mem()
+		b := s.endBlock()
+		b.Kind = ssa.BlockRetJmp // could use BlockExit. BlockRetJmp is mostly for clarity.
+		b.SetControl(call)
 
 	case ir.OCONTINUE, ir.OBREAK:
 		n := n.(*ir.BranchStmt)
@@ -2323,8 +2353,181 @@
 	return x
 }
 
+func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value {
+	if ft.IsBoolean() && tt.IsKind(types.TUINT8) {
+		// Bool -> uint8 is generated internally when indexing into runtime.staticbyte.
+		return s.newValue1(ssa.OpCopy, tt, v)
+	}
+	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, tt, v)
+	}
+
+	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, v, ft, tt)
+				}
+				if tt.Size() == 8 {
+					return s.uint32Tofloat64(n, v, 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, v, ft, tt)
+				}
+				if ft.Size() == 8 {
+					return s.float64ToUint32(n, v, 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 v
+				}
+				return s.newValueOrSfCall1(op2, tt, v)
+			}
+			if op2 == ssa.OpCopy {
+				return s.newValueOrSfCall1(op1, tt, v)
+			}
+			return s.newValueOrSfCall1(op2, tt, s.newValueOrSfCall1(op1, types.Types[it], v))
+		}
+		// 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, v, ft, tt)
+			}
+			if tt.Size() == 8 {
+				return s.uint64Tofloat64(n, v, 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, v, ft, tt)
+		}
+		if ft.Size() == 8 {
+			return s.float64ToUint64(n, v, 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, v)),
+			s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, v)))
+	}
+
+	s.Fatalf("unhandled OCONV %s -> %s", ft.Kind(), tt.Kind())
+	return nil
+}
+
 // 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 {
+	return s.exprCheckPtr(n, true)
+}
+
+func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
 	if ir.HasUniquePos(n) {
 		// ONAMEs and named OLITERALs have the line number
 		// of the decl, not the use. See issue 14742.
@@ -2472,6 +2675,9 @@
 
 		// unsafe.Pointer <--> *T
 		if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
+			if s.checkPtrEnabled && checkPtrOK && to.IsPtr() && from.IsUnsafePtr() {
+				s.checkPtrAlignment(n, v, nil)
+			}
 			return v
 		}
 
@@ -2510,174 +2716,7 @@
 	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
+		return s.conv(n, x, n.X.Type(), n.Type())
 
 	case ir.ODOTTYPE:
 		n := n.(*ir.TypeAssertExpr)
@@ -3079,7 +3118,8 @@
 
 	case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR:
 		n := n.(*ir.SliceExpr)
-		v := s.expr(n.X)
+		check := s.checkPtrEnabled && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr()
+		v := s.exprCheckPtr(n.X, !check)
 		var i, j, k *ssa.Value
 		if n.Low != nil {
 			i = s.expr(n.Low)
@@ -3091,8 +3131,9 @@
 			k = s.expr(n.Max)
 		}
 		p, l, c := s.slice(v, i, j, k, n.Bounded())
-		if n.CheckPtrCall != nil {
-			s.stmt(n.CheckPtrCall)
+		if check {
+			// Emit checkptr instrumentation after bound check to prevent false positive, see #46938.
+			s.checkPtrAlignment(n.X.(*ir.ConvExpr), v, s.conv(n.Max, k, k.Type, types.Types[types.TUINTPTR]))
 		}
 		return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c)
 
@@ -3606,6 +3647,7 @@
 	callDefer
 	callDeferStack
 	callGo
+	callTail
 )
 
 type sfRtCallDef struct {
@@ -4173,12 +4215,12 @@
 		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)
+		sys.ARM64, sys.ARM, sys.PPC64, sys.RISCV64, 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)
+		sys.PPC64, sys.RISCV64, 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])
@@ -4872,13 +4914,13 @@
 		}
 	}
 
-	if k != callNormal && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) {
+	if k != callNormal && k != callTail && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) {
 		s.Fatalf("go/defer call with arguments: %v", n)
 	}
 
 	switch n.Op() {
 	case ir.OCALLFUNC:
-		if k == callNormal && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC {
+		if (k == callNormal || k == callTail) && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC {
 			fn := fn.(*ir.Name)
 			callee = fn
 			if buildcfg.Experiment.RegabiArgs {
@@ -4932,7 +4974,7 @@
 	stksize := params.ArgWidth() // includes receiver, args, and results
 
 	res := n.X.Type().Results()
-	if k == callNormal {
+	if k == callNormal || k == callTail {
 		for _, p := range params.OutParams() {
 			ACResults = append(ACResults, p.Type)
 		}
@@ -4979,7 +5021,7 @@
 		// These are written in SP-offset order.
 		argStart := base.Ctxt.FixedFrameSize()
 		// Defer/go args.
-		if k != callNormal {
+		if k != callNormal && k != callTail {
 			// Write closure (arg to newproc/deferproc).
 			ACArgs = append(ACArgs, types.Types[types.TUINTPTR]) // not argExtra
 			callArgs = append(callArgs, closure)
@@ -5029,6 +5071,10 @@
 		case callee != nil:
 			aux := ssa.StaticAuxCall(callTargetLSym(callee), params)
 			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+			if k == callTail {
+				call.Op = ssa.OpTailLECall
+				stksize = 0 // Tail call does not use stack. We reuse caller's frame.
+			}
 		default:
 			s.Fatalf("bad call type %v %v", n.Op(), n)
 		}
@@ -5265,12 +5311,6 @@
 			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?
 }
@@ -6663,7 +6703,8 @@
 	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 {
+	gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name]
+	if gatherPrintInfo {
 		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)
@@ -6774,7 +6815,7 @@
 				valueToProgAfter[v.ID] = s.pp.Next
 			}
 
-			if f.PrintOrHtmlSSA {
+			if gatherPrintInfo {
 				for ; x != s.pp.Next; x = x.Link {
 					progToValue[x] = v
 				}
@@ -6804,7 +6845,7 @@
 		x := s.pp.Next
 		s.SetPos(b.Pos)
 		Arch.SSAGenBlock(&s, b, next)
-		if f.PrintOrHtmlSSA {
+		if gatherPrintInfo {
 			for ; x != s.pp.Next; x = x.Link {
 				progToBlock[x] = b
 			}
@@ -6983,6 +7024,54 @@
 		buf.WriteString("</code>")
 		f.HTMLWriter.WriteColumn("genssa", "genssa", "ssa-prog", buf.String())
 	}
+	if ssa.GenssaDump[f.Name] {
+		fi := f.DumpFileForPhase("genssa")
+		if fi != nil {
+
+			// inliningDiffers if any filename changes or if any line number except the innermost (index 0) changes.
+			inliningDiffers := func(a, b []src.Pos) bool {
+				if len(a) != len(b) {
+					return true
+				}
+				for i := range a {
+					if a[i].Filename() != b[i].Filename() {
+						return true
+					}
+					if i > 0 && a[i].Line() != b[i].Line() {
+						return true
+					}
+				}
+				return false
+			}
+
+			var allPosOld []src.Pos
+			var allPos []src.Pos
+
+			for p := pp.Text; p != nil; p = p.Link {
+				if p.Pos.IsKnown() {
+					allPos = p.AllPos(allPos)
+					if inliningDiffers(allPos, allPosOld) {
+						for i := len(allPos) - 1; i >= 0; i-- {
+							pos := allPos[i]
+							fmt.Fprintf(fi, "# %s:%d\n", pos.Filename(), pos.Line())
+						}
+						allPos, allPosOld = allPosOld, allPos // swap, not copy, so that they do not share slice storage.
+					}
+				}
+
+				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
+				}
+				fmt.Fprintf(fi, " %-6s\t%.5d %s\t%s\n", s, p.Pc, ssa.StmtString(p.Pos), p.InstructionString())
+			}
+			fi.Close()
+		}
+	}
 
 	defframe(&s, e, f)
 
@@ -7360,6 +7449,14 @@
 	return p
 }
 
+// TailCall returns a new tail call instruction for the SSA value v.
+// It is like Call, but for a tail call.
+func (s *State) TailCall(v *ssa.Value) *obj.Prog {
+	p := s.Call(v)
+	p.As = obj.ARET
+	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).
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index e89796c..82cb06b 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -276,7 +276,9 @@
 }
 
 // tokstring returns the English word for selected punctuation tokens
-// for more readable error messages.
+// for more readable error messages. Use tokstring (not tok.String())
+// for user-facing (error) messages; use tok.String() for debugging
+// output.
 func tokstring(tok token) string {
 	switch tok {
 	case _Comma:
@@ -1839,7 +1841,7 @@
 }
 
 // ParameterDecl = [ IdentifierList ] [ "..." ] Type .
-func (p *parser) paramDeclOrNil(name *Name) *Field {
+func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
 	if trace {
 		defer p.trace("paramDecl")()
 	}
@@ -1893,8 +1895,8 @@
 		return f
 	}
 
-	p.syntaxError("expecting )")
-	p.advance(_Comma, _Rparen)
+	p.syntaxError("expecting " + tokstring(follow))
+	p.advance(_Comma, follow)
 	return nil
 }
 
@@ -1908,9 +1910,10 @@
 		defer p.trace("paramList")()
 	}
 
-	var named int // number of parameters that have an explicit name and type/bound
-	p.list(_Comma, close, func() bool {
-		par := p.paramDeclOrNil(name)
+	var named int // number of parameters that have an explicit name and type
+	var typed int // number of parameters that have an explicit type
+	end := p.list(_Comma, close, func() bool {
+		par := p.paramDeclOrNil(name, close)
 		name = nil // 1st name was consumed if present
 		if par != nil {
 			if debug && par.Name == nil && par.Type == nil {
@@ -1919,6 +1922,9 @@
 			if par.Name != nil && par.Type != nil {
 				named++
 			}
+			if par.Type != nil {
+				typed++
+			}
 			list = append(list, par)
 		}
 		return false
@@ -1939,10 +1945,11 @@
 		}
 	} else if named != len(list) {
 		// some named => all must have names and types
-		var pos Pos // left-most error position (or unknown)
-		var typ Expr
+		var pos Pos  // left-most error position (or unknown)
+		var typ Expr // current type (from right to left)
 		for i := len(list) - 1; i >= 0; i-- {
-			if par := list[i]; par.Type != nil {
+			par := list[i]
+			if par.Type != nil {
 				typ = par.Type
 				if par.Name == nil {
 					pos = typ.Pos()
@@ -1961,7 +1968,12 @@
 		if pos.IsKnown() {
 			var msg string
 			if requireNames {
-				msg = "type parameters must be named"
+				if named == typed {
+					pos = end // position error at closing ]
+					msg = "missing type constraint"
+				} else {
+					msg = "type parameters must be named"
+				}
 			} else {
 				msg = "mixed named and unnamed parameters"
 			}
@@ -2201,7 +2213,7 @@
 	if p.tok != _Semi {
 		// accept potential varDecl but complain
 		if p.got(_Var) {
-			p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", keyword.String()))
+			p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", tokstring(keyword)))
 		}
 		init = p.simpleStmt(nil, keyword)
 		// If we have a range clause, we are done (can only happen for keyword == _For).
diff --git a/src/cmd/compile/internal/syntax/testdata/issue43527.go2 b/src/cmd/compile/internal/syntax/testdata/issue43527.go2
new file mode 100644
index 0000000..dd2c9b1
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/issue43527.go2
@@ -0,0 +1,23 @@
+// 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
+
+type (
+        // 0 and 1-element []-lists are syntactically valid
+        _[A, B /* ERROR missing type constraint */ ] int
+        _[A, /* ERROR type parameters must be named */ interface{}] int
+        _[A, B, C /* ERROR missing type constraint */ ] int
+        _[A B, C /* ERROR missing type constraint */ ] int
+        _[A B, /* ERROR type parameters must be named */ interface{}] int
+        _[A B, /* ERROR type parameters must be named */ interface{}, C D] int
+        _[A B, /* ERROR type parameters must be named */ interface{}, C, D] int
+        _[A B, /* ERROR type parameters must be named */ interface{}, C, interface{}] int
+        _[A B, C interface{}, D, /* ERROR type parameters must be named */ interface{}] int
+)
+
+// function type parameters use the same parsing routine - just have a couple of tests
+
+func _[A, B /* ERROR missing type constraint */ ]() {}
+func _[A, /* ERROR type parameters must be named */ interface{}]() {}
diff --git a/src/cmd/compile/internal/syntax/testdata/tparams.go2 b/src/cmd/compile/internal/syntax/testdata/tparams.go2
index 42031c3..80e155b 100644
--- a/src/cmd/compile/internal/syntax/testdata/tparams.go2
+++ b/src/cmd/compile/internal/syntax/testdata/tparams.go2
@@ -4,8 +4,8 @@
 
 package p
 
-type t[ /* ERROR type parameters must be named */ a, b] struct{}
-type t[a t, b t, /* ERROR type parameters must be named */ c] struct{}
+type t[a, b /* ERROR missing type constraint */ ] struct{}
+type t[a t, b t, c /* ERROR missing type constraint */ ] struct{}
 type t struct {
 	t [n]byte
 	t[a]
@@ -18,5 +18,7 @@
 }
 
 func f[ /* ERROR empty type parameter list */ ]()
-func f[ /* ERROR type parameters must be named */ a, b]()
-func f[a t, b t, /* ERROR type parameters must be named */ c]()
+func f[a, b /* ERROR missing type constraint */ ]()
+func f[a t, b t, c /* ERROR missing type constraint */ ]()
+
+func f[a b,  /* ERROR expecting ] */ 0] ()
diff --git a/src/cmd/compile/internal/typecheck/crawler.go b/src/cmd/compile/internal/typecheck/crawler.go
index 9e523c3d..3f212aa 100644
--- a/src/cmd/compile/internal/typecheck/crawler.go
+++ b/src/cmd/compile/internal/typecheck/crawler.go
@@ -44,12 +44,13 @@
 	p.markType(n.Type())
 }
 
-// markType recursively visits types reachable from t to identify
-// functions whose inline bodies may be needed.
+// markType recursively visits types reachable from t to identify functions whose
+// inline bodies may be needed. For instantiated generic types, it visits the base
+// generic type, which has the relevant methods.
 func (p *crawler) markType(t *types.Type) {
-	if t.IsInstantiatedGeneric() {
-		// Re-instantiated types don't add anything new, so don't follow them.
-		return
+	if t.OrigSym() != nil {
+		// Convert to the base generic type.
+		t = t.OrigSym().Def.Type()
 	}
 	if p.marked[t] {
 		return
@@ -92,6 +93,9 @@
 		p.markType(t.Elem())
 
 	case types.TSTRUCT:
+		if t.IsFuncArgStruct() {
+			break
+		}
 		for _, f := range t.FieldSlice() {
 			if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
 				p.markType(f.Type)
@@ -129,9 +133,9 @@
 		t = t.Elem()
 	}
 
-	if t.IsInstantiatedGeneric() {
-		// Re-instantiated types don't add anything new, so don't follow them.
-		return
+	if t.OrigSym() != nil {
+		// Convert to the base generic type.
+		t = t.OrigSym().Def.Type()
 	}
 
 	if p.embedded[t] {
@@ -185,6 +189,15 @@
 
 	var doFlood func(n ir.Node)
 	doFlood = func(n ir.Node) {
+		t := n.Type()
+		if t != nil && (t.HasTParam() || t.IsFullyInstantiated()) {
+			// Ensure that we call markType() on any base generic type
+			// that is written to the export file (even if not explicitly
+			// marked for export), so we will call markInlBody on its
+			// methods, and the methods will be available for
+			// instantiation if needed.
+			p.markType(t)
+		}
 		switch n.Op() {
 		case ir.OMETHEXPR, ir.ODOTMETH:
 			p.markInlBody(ir.MethodExprName(n))
@@ -198,9 +211,6 @@
 			case ir.PEXTERN:
 				Export(n)
 			}
-			p.checkGenericType(n.Type())
-		case ir.OTYPE:
-			p.checkGenericType(n.Type())
 		case ir.OMETHVALUE:
 			// Okay, because we don't yet inline indirect
 			// calls to method values.
@@ -216,16 +226,3 @@
 	// because after inlining they might be callable.
 	ir.VisitList(fn.Inl.Body, doFlood)
 }
-
-// checkGenerictype ensures that we call markType() on any base generic type that
-// is written to the export file (even if not explicitly marked
-// for export), so its methods will be available for inlining if needed.
-func (p *crawler) checkGenericType(t *types.Type) {
-	if t != nil && t.HasTParam() {
-		if t.OrigSym() != nil {
-			// Convert to the base generic type.
-			t = t.OrigSym().Def.Type()
-		}
-		p.markType(t)
-	}
-}
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index f001017..489306e 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -63,8 +63,9 @@
 //     }
 //
 //     type Func struct {
-//         Tag       byte // 'F'
+//         Tag       byte // 'F' or 'G'
 //         Pos       Pos
+//         TypeParams []typeOff  // only present if Tag == 'G'
 //         Signature Signature
 //     }
 //
@@ -75,8 +76,9 @@
 //     }
 //
 //     type Type struct {
-//         Tag        byte // 'T'
+//         Tag        byte // 'T' or 'U'
 //         Pos        Pos
+//         TypeParams []typeOff  // only present if Tag == 'U'
 //         Underlying typeOff
 //
 //         Methods []struct{  // omitted if Underlying is an interface type
@@ -93,6 +95,12 @@
 //         Type typeOff
 //     }
 //
+//     // "Automatic" declaration of each typeparam
+//     type TypeParam struct {
+//         Tag  byte // 'P'
+//         Pos  Pos
+//         Bound typeOff
+//     }
 //
 // typeOff means a uvarint that either indicates a predeclared type,
 // or an offset into the Data section. If the uvarint is less than
@@ -104,7 +112,7 @@
 // (*exportWriter).value for details.
 //
 //
-// There are nine kinds of type descriptors, distinguished by an itag:
+// There are twelve kinds of type descriptors, distinguished by an itag:
 //
 //     type DefinedType struct {
 //         Tag     itag // definedType
@@ -172,8 +180,30 @@
 //         }
 //     }
 //
+//     // Reference to a type param declaration
+//     type TypeParamType struct {
+//         Tag     itag // typeParamType
+//         Name    stringOff
+//         PkgPath stringOff
+//     }
 //
-//  TODO(danscales): fill in doc for 'type TypeParamType' and 'type InstType'
+//     // Instantiation of a generic type (like List[T2] or List[int])
+//     type InstanceType struct {
+//         Tag     itag // instanceType
+//         Pos     pos
+//         TypeArgs []typeOff
+//         BaseType typeOff
+//     }
+//
+//     type UnionType struct {
+//         Tag     itag // interfaceType
+//         Terms   []struct {
+//             tilde bool
+//             Type  typeOff
+//         }
+//     }
+//
+//
 //
 //     type Signature struct {
 //         Params   []Param
@@ -255,7 +285,7 @@
 	structType
 	interfaceType
 	typeParamType
-	instType
+	instanceType // Instantiation of a generic type
 	unionType
 )
 
@@ -893,7 +923,7 @@
 		if strings.Index(s.Name, "[") < 0 {
 			base.Fatalf("incorrect name for instantiated type")
 		}
-		w.startType(instType)
+		w.startType(instanceType)
 		w.pos(t.Pos())
 		// Export the type arguments for the instantiated type. The
 		// instantiated type could be in a method header (e.g. "func (v
@@ -1456,10 +1486,23 @@
 	}
 }
 
-// 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 isNonEmptyAssign(n ir.Node) bool {
+	switch n.Op() {
+	case ir.OAS:
+		if n.(*ir.AssignStmt).Y != nil {
+			return true
+		}
+	case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
+		return true
+	}
+	return false
+}
+
+// Caution: stmt will emit more than one node for statement nodes n that have a
+// non-empty n.Ninit and where n is not a non-empty assignment or a node with 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()) {
+	if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) && n.Op() != ir.ORANGE {
 		// can't use stmtList here since we don't want the final OEND
 		for _, n := range n.Init() {
 			w.stmt(n)
@@ -1495,8 +1538,10 @@
 		if n.Y != nil {
 			w.op(ir.OAS)
 			w.pos(n.Pos())
+			w.stmtList(n.Init())
 			w.expr(n.X)
 			w.expr(n.Y)
+			w.bool(n.Def)
 		}
 
 	case ir.OASOP:
@@ -1517,8 +1562,10 @@
 			w.op(ir.OAS2)
 		}
 		w.pos(n.Pos())
+		w.stmtList(n.Init())
 		w.exprList(n.Lhs)
 		w.exprList(n.Rhs)
+		w.bool(n.Def)
 
 	case ir.ORETURN:
 		n := n.(*ir.ReturnStmt)
@@ -1556,6 +1603,7 @@
 		n := n.(*ir.RangeStmt)
 		w.op(ir.ORANGE)
 		w.pos(n.Pos())
+		w.stmtList(n.Init())
 		w.exprsOrNil(n.Key, n.Value)
 		w.expr(n.X)
 		w.stmtList(n.Body)
@@ -2065,8 +2113,10 @@
 		n := n.(*ir.AssignListStmt)
 		w.op(ir.OSELRECV2)
 		w.pos(n.Pos())
+		w.stmtList(n.Init())
 		w.exprList(n.Lhs)
 		w.exprList(n.Rhs)
+		w.bool(n.Def)
 
 	default:
 		base.Fatalf("cannot export %v (%d) node\n"+
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index 8bc098c..ec4057a 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -316,16 +316,12 @@
 		return n
 
 	case 'T', 'U':
-		var rparams []*types.Type
-		if tag == 'U' {
-			rparams = r.typeList()
-		}
-
 		// Types can be recursive. We need to setup a stub
 		// declaration before recursing.
 		n := importtype(pos, sym)
 		t := n.Type()
 		if tag == 'U' {
+			rparams := r.typeList()
 			t.SetRParams(rparams)
 		}
 
@@ -825,7 +821,7 @@
 		}
 		return n.Type()
 
-	case instType:
+	case instanceType:
 		if r.p.exportVersion < iexportVersionGenerics {
 			base.Fatalf("unexpected instantiation type")
 		}
@@ -1170,10 +1166,26 @@
 		if n.Op() == ir.OBLOCK {
 			n := n.(*ir.BlockStmt)
 			list = append(list, n.List...)
-		} else {
-			list = append(list, n)
+			continue
 		}
-
+		if len(list) > 0 {
+			// check for an optional label that can only immediately
+			// precede a for/range/select/switch statement.
+			if last := list[len(list)-1]; last.Op() == ir.OLABEL {
+				label := last.(*ir.LabelStmt).Label
+				switch n.Op() {
+				case ir.OFOR:
+					n.(*ir.ForStmt).Label = label
+				case ir.ORANGE:
+					n.(*ir.RangeStmt).Label = label
+				case ir.OSELECT:
+					n.(*ir.SelectStmt).Label = label
+				case ir.OSWITCH:
+					n.(*ir.SwitchStmt).Label = label
+				}
+			}
+		}
+		list = append(list, n)
 	}
 	return list
 }
@@ -1503,7 +1515,7 @@
 		if go117ExportTypes {
 			n.SetOp(op)
 		}
-		*n.PtrInit() = init
+		n.SetInit(init)
 		n.IsDDD = r.bool()
 		if go117ExportTypes {
 			n.SetType(r.exoticType())
@@ -1607,7 +1619,12 @@
 	// 	unreachable - never exported
 
 	case ir.OAS:
-		return ir.NewAssignStmt(r.pos(), r.expr(), r.expr())
+		pos := r.pos()
+		init := r.stmtList()
+		n := ir.NewAssignStmt(pos, r.expr(), r.expr())
+		n.SetInit(init)
+		n.Def = r.bool()
+		return n
 
 	case ir.OASOP:
 		n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil)
@@ -1624,7 +1641,12 @@
 			// unreachable - mapped to case OAS2 by exporter
 			goto error
 		}
-		return ir.NewAssignListStmt(r.pos(), op, r.exprList(), r.exprList())
+		pos := r.pos()
+		init := r.stmtList()
+		n := ir.NewAssignListStmt(pos, op, r.exprList(), r.exprList())
+		n.SetInit(init)
+		n.Def = r.bool()
+		return n
 
 	case ir.ORETURN:
 		return ir.NewReturnStmt(r.pos(), r.exprList())
@@ -1638,26 +1660,28 @@
 	case ir.OIF:
 		pos, init := r.pos(), r.stmtList()
 		n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList())
-		*n.PtrInit() = init
+		n.SetInit(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
+		n.SetInit(init)
 		return n
 
 	case ir.ORANGE:
-		pos := r.pos()
+		pos, init := r.pos(), r.stmtList()
 		k, v := r.exprsOrNil()
-		return ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
+		n := ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
+		n.SetInit(init)
+		return n
 
 	case ir.OSELECT:
 		pos := r.pos()
 		init := r.stmtList()
 		n := ir.NewSelectStmt(pos, r.commList())
-		*n.PtrInit() = init
+		n.SetInit(init)
 		return n
 
 	case ir.OSWITCH:
@@ -1665,7 +1689,7 @@
 		init := r.stmtList()
 		x, _ := r.exprsOrNil()
 		n := ir.NewSwitchStmt(pos, x, r.caseList(x))
-		*n.PtrInit() = init
+		n.SetInit(init)
 		return n
 
 	// case OCASE:
@@ -1709,7 +1733,12 @@
 		return n
 
 	case ir.OSELRECV2:
-		return ir.NewAssignListStmt(r.pos(), ir.OSELRECV2, r.exprList(), r.exprList())
+		pos := r.pos()
+		init := r.stmtList()
+		n := ir.NewAssignListStmt(pos, ir.OSELRECV2, r.exprList(), r.exprList())
+		n.SetInit(init)
+		n.Def = r.bool()
+		return n
 
 	default:
 		base.Fatalf("cannot import %v (%d) node\n"+
diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go
index c322d49..9a02c17 100644
--- a/src/cmd/compile/internal/typecheck/stmt.go
+++ b/src/cmd/compile/internal/typecheck/stmt.go
@@ -395,10 +395,11 @@
 			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
+				selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
+				selrecv.Def = def
+				selrecv.SetTypecheck(1)
+				selrecv.SetInit(n.Init())
+				ncase.Comm = selrecv
 			}
 			switch n.Op() {
 			default:
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
index 34f2087..d4af4e1 100644
--- a/src/cmd/compile/internal/typecheck/subr.go
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -352,7 +352,10 @@
 	if types.Identical(src, dst) {
 		return ir.OCONVNOP, ""
 	}
+	return Assignop1(src, dst)
+}
 
+func Assignop1(src, dst *types.Type) (ir.Op, string) {
 	// 2. src and dst have identical underlying types and
 	//   a. either src or dst is not a named type, or
 	//   b. both are empty interface types, or
@@ -1019,10 +1022,11 @@
 	SubstForwFunc func(*types.Type) *types.Type
 }
 
-// Typ computes the type obtained by substituting any type parameter in t with the
-// corresponding type argument in subst. If t contains no type parameters, the
-// result is t; otherwise the result is a new type. It deals with recursive types
-// by using TFORW types and finding partially or fully created types via sym.Def.
+// Typ computes the type obtained by substituting any type parameter or shape in t
+// that appears in subst.Tparams with the corresponding type argument in subst.Targs.
+// If t contains no type parameters, the result is t; otherwise the result is a new
+// type. It deals with recursive types by using TFORW types and finding partially or
+// fully created types via sym.Def.
 func (ts *Tsubster) Typ(t *types.Type) *types.Type {
 	// Defer the CheckSize calls until we have fully-defined
 	// (possibly-recursive) top-level type.
@@ -1033,14 +1037,14 @@
 }
 
 func (ts *Tsubster) typ1(t *types.Type) *types.Type {
-	if !t.HasTParam() && t.Kind() != types.TFUNC {
+	if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TFUNC {
 		// Note: function types need to be copied regardless, as the
 		// types of closures may contain declarations that need
 		// to be copied. See #45738.
 		return t
 	}
 
-	if t.IsTypeParam() {
+	if t.IsTypeParam() || t.IsShape() {
 		for i, tp := range ts.Tparams {
 			if tp == t {
 				return ts.Targs[i]
@@ -1072,14 +1076,14 @@
 	var targsChanged bool
 	var forw *types.Type
 
-	if t.Sym() != nil && t.HasTParam() {
+	if t.Sym() != nil && (t.HasTParam() || t.HasShape()) {
 		// Need to test for t.HasTParam() again because of special TFUNC case above.
 		// Translate the type params for this type according to
 		// the tparam/targs mapping from subst.
 		neededTargs = make([]*types.Type, len(t.RParams()))
 		for i, rparam := range t.RParams() {
 			neededTargs[i] = ts.typ1(rparam)
-			if !types.Identical(neededTargs[i], rparam) {
+			if !types.IdenticalStrict(neededTargs[i], rparam) {
 				targsChanged = true
 			}
 		}
@@ -1286,7 +1290,7 @@
 // fields, set force to true.
 func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
 	if t.NumFields() == 0 {
-		if t.HasTParam() {
+		if t.HasTParam() || t.HasShape() {
 			// For an empty struct, we need to return a new type,
 			// since it may now be fully instantiated (HasTParam
 			// becomes false).
@@ -1312,6 +1316,7 @@
 			// the type param, not the instantiated type).
 			newfields[i] = types.NewField(f.Pos, f.Sym, t2)
 			newfields[i].Embedded = f.Embedded
+			newfields[i].Note = f.Note
 			if f.IsDDD() {
 				newfields[i].SetIsDDD(true)
 			}
@@ -1387,19 +1392,20 @@
 	return sym.Name[0:strings.Index(sym.Name, "[")]
 }
 
-// Shapify takes a concrete type and returns a GCshape type that can
+// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
 // be used in place of the input type and still generate identical code.
 // No methods are added - all methods calls directly on a shape should
 // be done by converting to an interface using the dictionary.
 //
-// TODO: this could take the generic function and base its decisions
-// on how that generic function uses this type argument. For instance,
-// if it doesn't use it as a function argument/return value, then
-// we don't need to distinguish int64 and float64 (because they only
-// differ in how they get passed as arguments). For now, we only
-// unify two different types if they are identical in every possible way.
-func Shapify(t *types.Type) *types.Type {
-	assert(!t.HasShape())
+// For now, we only consider two types to have the same shape, if they have exactly
+// the same underlying type or they are both pointer types.
+//
+//  Shape types are also distinguished by the index of the type in a type param/arg
+//  list. We need to do this so we can distinguish and substitute properly for two
+//  type params in the same function that have the same shape for a particular
+//  instantiation.
+func Shapify(t *types.Type, index int) *types.Type {
+	assert(!t.IsShape())
 	// Map all types with the same underlying type to the same shape.
 	u := t.Underlying()
 
@@ -1409,22 +1415,35 @@
 		u = types.Types[types.TUINT8].PtrTo()
 	}
 
-	if s := shaped[u]; s != nil {
+	if shapeMap == nil {
+		shapeMap = map[int]map[*types.Type]*types.Type{}
+	}
+	submap := shapeMap[index]
+	if submap == nil {
+		submap = map[*types.Type]*types.Type{}
+		shapeMap[index] = submap
+	}
+	if s := submap[u]; s != nil {
 		return s
 	}
 
-	sym := shapePkg.Lookup(u.LinkString())
+	nm := fmt.Sprintf("%s_%d", u.LinkString(), index)
+	sym := types.ShapePkg.Lookup(nm)
+	if sym.Def != nil {
+		// Use any existing type with the same name
+		submap[u] = sym.Def.Type()
+		return submap[u]
+	}
 	name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym)
 	s := types.NewNamed(name)
+	sym.Def = name
 	s.SetUnderlying(u)
 	s.SetIsShape(true)
 	s.SetHasShape(true)
 	name.SetType(s)
 	name.SetTypecheck(1)
-	shaped[u] = s
+	submap[u] = s
 	return s
 }
 
-var shaped = map[*types.Type]*types.Type{}
-
-var shapePkg = types.NewPkg(".shape", ".shape")
+var shapeMap map[int]map[*types.Type]*types.Type
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 404af5b..42970f6 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -879,6 +879,7 @@
 
 	case ir.OTAILCALL:
 		n := n.(*ir.TailCallStmt)
+		n.Call = typecheck(n.Call, ctxStmt|ctxExpr).(*ir.CallExpr)
 		return n
 
 	case ir.OCHECKNIL:
diff --git a/src/cmd/compile/internal/types/identity.go b/src/cmd/compile/internal/types/identity.go
index 2e9e2f4..dce7d29 100644
--- a/src/cmd/compile/internal/types/identity.go
+++ b/src/cmd/compile/internal/types/identity.go
@@ -4,19 +4,30 @@
 
 package types
 
+const (
+	identIgnoreTags = 1 << iota
+	identStrict
+)
+
 // Identical reports whether t1 and t2 are identical types, following the spec rules.
 // Receiver parameter types are ignored. Named (defined) types are only equal if they
 // are pointer-equal - i.e. there must be a unique types.Type for each specific named
 // type. Also, a type containing a shape type is considered identical to another type
 // (shape or not) if their underlying types are the same, or they are both pointers.
 func Identical(t1, t2 *Type) bool {
-	return identical(t1, t2, true, nil)
+	return identical(t1, t2, 0, nil)
 }
 
 // IdenticalIgnoreTags is like Identical, but it ignores struct tags
 // for struct identity.
 func IdenticalIgnoreTags(t1, t2 *Type) bool {
-	return identical(t1, t2, false, nil)
+	return identical(t1, t2, identIgnoreTags, nil)
+}
+
+// IdenticalStrict is like Identical, but matches types exactly, without the
+// exception for shapes.
+func IdenticalStrict(t1, t2 *Type) bool {
+	return identical(t1, t2, identStrict, nil)
 }
 
 type typePair struct {
@@ -24,7 +35,7 @@
 	t2 *Type
 }
 
-func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
+func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool {
 	if t1 == t2 {
 		return true
 	}
@@ -32,7 +43,7 @@
 		return false
 	}
 	if t1.sym != nil || t2.sym != nil {
-		if t1.HasShape() || t2.HasShape() {
+		if flags&identStrict == 0 && (t1.HasShape() || t2.HasShape()) {
 			switch t1.kind {
 			case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64, TBOOL, TSTRING, TPTR, TUNSAFEPTR:
 				return true
@@ -78,7 +89,7 @@
 		}
 		for i, f1 := range t1.AllMethods().Slice() {
 			f2 := t2.AllMethods().Index(i)
-			if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+			if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, flags, assumedEqual) {
 				return false
 			}
 		}
@@ -90,10 +101,10 @@
 		}
 		for i, f1 := range t1.FieldSlice() {
 			f2 := t2.Field(i)
-			if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+			if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, flags, assumedEqual) {
 				return false
 			}
-			if cmpTags && f1.Note != f2.Note {
+			if (flags&identIgnoreTags) == 0 && f1.Note != f2.Note {
 				return false
 			}
 		}
@@ -111,7 +122,7 @@
 			}
 			for i, f1 := range fs1 {
 				f2 := fs2[i]
-				if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+				if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, flags, assumedEqual) {
 					return false
 				}
 			}
@@ -129,10 +140,10 @@
 		}
 
 	case TMAP:
-		if !identical(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
+		if !identical(t1.Key(), t2.Key(), flags, assumedEqual) {
 			return false
 		}
 	}
 
-	return identical(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
+	return identical(t1.Elem(), t2.Elem(), flags, assumedEqual)
 }
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index eb70f7b..392c54b 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -1706,6 +1706,10 @@
 	t := newType(TFORW)
 	t.sym = obj.Sym()
 	t.nod = obj
+	if t.sym.Pkg == ShapePkg {
+		t.SetIsShape(true)
+		t.SetHasShape(true)
+	}
 	return t
 }
 
@@ -2182,3 +2186,5 @@
 )
 
 var SimType [NTYPE]Kind
+
+var ShapePkg = NewPkg(".shape", ".shape")
diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go
index b2938b8..6914e6c 100644
--- a/src/cmd/compile/internal/types2/api.go
+++ b/src/cmd/compile/internal/types2/api.go
@@ -108,6 +108,11 @@
 // A Config specifies the configuration for type checking.
 // The zero value for Config is a ready-to-use default configuration.
 type Config struct {
+	// Environment is the environment used for resolving global
+	// identifiers. If nil, the type checker will initialize this
+	// field with a newly created environment.
+	Environment *Environment
+
 	// GoVersion describes the accepted Go language version. The string
 	// must follow the format "go%d.%d" (e.g. "go1.12") or ist must be
 	// empty; an empty string indicates the latest language version.
diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go
index 039a6c0..cd5a613 100644
--- a/src/cmd/compile/internal/types2/api_test.go
+++ b/src/cmd/compile/internal/types2/api_test.go
@@ -145,6 +145,7 @@
 		{`package f7b; var _            = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
 
 		{`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341
+		{`package g1; var(j int32; s int; n = 1.0<<s == j)`, `1.0`, `int32`, `1`},        // issue #48422
 	}
 
 	for _, test := range tests {
@@ -1645,6 +1646,48 @@
 	}
 }
 
+func TestIdenticalUnions(t *testing.T) {
+	tname := NewTypeName(nopos, nil, "myInt", nil)
+	myInt := NewNamed(tname, Typ[Int], nil)
+	tmap := map[string]*Term{
+		"int":     NewTerm(false, Typ[Int]),
+		"~int":    NewTerm(true, Typ[Int]),
+		"string":  NewTerm(false, Typ[String]),
+		"~string": NewTerm(true, Typ[String]),
+		"myInt":   NewTerm(false, myInt),
+	}
+	makeUnion := func(s string) *Union {
+		parts := strings.Split(s, "|")
+		var terms []*Term
+		for _, p := range parts {
+			term := tmap[p]
+			if term == nil {
+				t.Fatalf("missing term %q", p)
+			}
+			terms = append(terms, term)
+		}
+		return NewUnion(terms)
+	}
+	for _, test := range []struct {
+		x, y string
+		want bool
+	}{
+		// These tests are just sanity checks. The tests for type sets and
+		// interfaces provide much more test coverage.
+		{"int|~int", "~int", true},
+		{"myInt|~int", "~int", true},
+		{"int|string", "string|int", true},
+		{"int|int|string", "string|int", true},
+		{"myInt|string", "int|string", false},
+	} {
+		x := makeUnion(test.x)
+		y := makeUnion(test.y)
+		if got := Identical(x, y); got != test.want {
+			t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got)
+		}
+	}
+}
+
 func TestIssue15305(t *testing.T) {
 	const src = "package p; func f() int16; var _ = f(undef)"
 	f, err := parseSrc("issue15305.go", src)
@@ -1871,7 +1914,7 @@
 
 	// type T should have one type parameter
 	T := pkg.Scope().Lookup("T").Type().(*Named)
-	if n := T.TParams().Len(); n != 1 {
+	if n := T.TypeParams().Len(); n != 1 {
 		t.Fatalf("expected 1 type parameter; found %d", n)
 	}
 
diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go
index 6184fc2..29d63cf 100644
--- a/src/cmd/compile/internal/types2/assignments.go
+++ b/src/cmd/compile/internal/types2/assignments.go
@@ -68,7 +68,7 @@
 	// x.typ is typed
 
 	// A generic (non-instantiated) function value cannot be assigned to a variable.
-	if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 {
+	if sig := asSignature(x.typ); sig != nil && sig.TypeParams().Len() > 0 {
 		check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context)
 	}
 
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index e3844d5..3b8d858 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -826,7 +826,7 @@
 		// type param is placed in the current package so export/import
 		// works as expected.
 		tpar := NewTypeName(nopos, check.pkg, "<type parameter>", nil)
-		ptyp := check.NewTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect
+		ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect
 		ptyp.index = tp.index
 
 		return ptyp
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
index 5bf1787..ba3bb47 100644
--- a/src/cmd/compile/internal/types2/call.go
+++ b/src/cmd/compile/internal/types2/call.go
@@ -30,7 +30,7 @@
 
 	// check number of type arguments (got) vs number of type parameters (want)
 	sig := x.typ.(*Signature)
-	got, want := len(targs), sig.TParams().Len()
+	got, want := len(targs), sig.TypeParams().Len()
 	if !useConstraintTypeInference && got != want || got > want {
 		check.errorf(xlist[got-1], "got %d type arguments but want %d", got, want)
 		x.mode = invalid
@@ -41,7 +41,7 @@
 	// if we don't have enough type arguments, try type inference
 	inferred := false
 	if got < want {
-		targs = check.infer(inst.Pos(), sig.TParams().list(), targs, nil, nil, true)
+		targs = check.infer(inst.Pos(), sig.TypeParams().list(), targs, nil, nil, true)
 		if targs == nil {
 			// error was already reported
 			x.mode = invalid
@@ -61,7 +61,7 @@
 
 	// instantiate function signature
 	res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
-	assert(res.TParams().Len() == 0) // signature is not generic anymore
+	assert(res.TypeParams().Len() == 0) // signature is not generic anymore
 	if inferred {
 		check.recordInferred(inst, targs, res)
 	}
@@ -166,7 +166,7 @@
 		assert(len(targs) == len(xlist))
 
 		// check number of type arguments (got) vs number of type parameters (want)
-		got, want := len(targs), sig.TParams().Len()
+		got, want := len(targs), sig.TypeParams().Len()
 		if got > want {
 			check.errorf(xlist[want], "got %d type arguments but want %d", got, want)
 			check.use(call.ArgList...)
@@ -200,7 +200,7 @@
 
 	// if type inference failed, a parametrized result must be invalidated
 	// (operands cannot have a parametrized type)
-	if x.mode == value && sig.TParams().Len() > 0 && isParameterized(sig.TParams().list(), x.typ) {
+	if x.mode == value && sig.TypeParams().Len() > 0 && isParameterized(sig.TypeParams().list(), x.typ) {
 		x.mode = invalid
 	}
 
@@ -328,7 +328,7 @@
 	}
 
 	// infer type arguments and instantiate signature if necessary
-	if sig.TParams().Len() > 0 {
+	if sig.TypeParams().Len() > 0 {
 		if !check.allowVersion(check.pkg, 1, 18) {
 			if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil {
 				check.softErrorf(iexpr.Pos(), "function instantiation requires go1.18 or later")
@@ -338,21 +338,21 @@
 		}
 		// TODO(gri) provide position information for targs so we can feed
 		//           it to the instantiate call for better error reporting
-		targs := check.infer(call.Pos(), sig.TParams().list(), targs, sigParams, args, true)
+		targs := check.infer(call.Pos(), sig.TypeParams().list(), targs, sigParams, args, true)
 		if targs == nil {
 			return // error already reported
 		}
 
 		// compute result signature
 		rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
-		assert(rsig.TParams().Len() == 0) // signature is not generic anymore
+		assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
 		check.recordInferred(call, targs, rsig)
 
 		// Optimization: Only if the parameter list was adjusted do we
 		// need to compute it from the adjusted list; otherwise we can
 		// simply use the result signature's parameter list.
 		if adjusted {
-			sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TParams().list(), targs), nil).(*Tuple)
+			sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple)
 		} else {
 			sigParams = rsig.params
 		}
@@ -535,7 +535,7 @@
 		// the signature accordingly.
 		// TODO(gri) factor this code out
 		sig := m.typ.(*Signature)
-		if sig.RParams().Len() > 0 {
+		if sig.RecvTypeParams().Len() > 0 {
 			// For inference to work, we must use the receiver type
 			// matching the receiver in the actual method declaration.
 			// If the method is embedded, the matching receiver is the
@@ -564,7 +564,7 @@
 			// the receiver type arguments here, the receiver must be be otherwise invalid
 			// and an error has been reported elsewhere.
 			arg := operand{mode: variable, expr: x.expr, typ: recv}
-			targs := check.infer(m.pos, sig.RParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */)
+			targs := check.infer(m.pos, sig.RecvTypeParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */)
 			//check.dump("### inferred targs = %s", targs)
 			if targs == nil {
 				// We may reach here if there were other errors (see issue #40056).
@@ -574,7 +574,7 @@
 			// (If we modify m, some tests will fail; possibly because the m is in use.)
 			// TODO(gri) investigate and provide a correct explanation here
 			copy := *m
-			copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RParams().list(), targs), nil)
+			copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RecvTypeParams().list(), targs), nil)
 			obj = &copy
 		}
 		// TODO(gri) we also need to do substitution for parameterized interface methods
diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go
index 4226b4d..24a05e6 100644
--- a/src/cmd/compile/internal/types2/check.go
+++ b/src/cmd/compile/internal/types2/check.go
@@ -86,7 +86,6 @@
 	nextID  uint64                 // unique Id for type parameters (first valid Id is 1)
 	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
-	typMap  map[string]*Named      // maps an instantiated named type hash to a *Named type
 
 	// pkgPathMap maps package names to the set of distinct import paths we've
 	// seen for that name, anywhere in the import graph. It is used for
@@ -171,6 +170,11 @@
 		conf = new(Config)
 	}
 
+	// make sure we have an environment
+	if conf.Environment == nil {
+		conf.Environment = NewEnvironment()
+	}
+
 	// make sure we have an info struct
 	if info == nil {
 		info = new(Info)
@@ -188,7 +192,6 @@
 		version: version,
 		objMap:  make(map[Object]*declInfo),
 		impMap:  make(map[importKey]*Package),
-		typMap:  make(map[string]*Named),
 	}
 }
 
diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go
index cd97080..26e0505 100644
--- a/src/cmd/compile/internal/types2/decl.go
+++ b/src/cmd/compile/internal/types2/decl.go
@@ -317,7 +317,7 @@
 		}
 
 	case *Named:
-		t.expand(check.typMap)
+		t.resolve(check.conf.Environment)
 
 		// don't touch the type if it is from a different package or the Universe scope
 		// (doing so would lead to a race condition - was issue #35049)
@@ -592,13 +592,13 @@
 	named.underlying = under(named)
 
 	// If the RHS is a type parameter, it must be from this type declaration.
-	if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.TParams().list(), tpar) < 0 {
+	if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.TypeParams().list(), tpar) < 0 {
 		check.errorf(tdecl.Type, "cannot use function type parameter %s as RHS in type declaration", tpar)
 		named.underlying = Typ[Invalid]
 	}
 }
 
-func (check *Checker) collectTypeParams(dst **TParamList, list []*syntax.Field) {
+func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Field) {
 	tparams := make([]*TypeParam, len(list))
 
 	// Declare type parameters up-front.
@@ -648,7 +648,7 @@
 	//           constraints to make sure we don't rely on them if they
 	//           are not properly set yet.
 	tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
-	tpar := check.NewTypeParam(tname, Typ[Invalid])          // assigns type to tname as a side-effect
+	tpar := check.newTypeParam(tname, Typ[Invalid])          // assigns type to tname as a side-effect
 	check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
 	return tpar
 }
@@ -715,7 +715,7 @@
 		}
 
 		if base != nil {
-			base.load() // TODO(mdempsky): Probably unnecessary.
+			base.resolve(nil) // TODO(mdempsky): Probably unnecessary.
 			base.methods = append(base.methods, m)
 		}
 	}
diff --git a/src/cmd/compile/internal/types2/environment.go b/src/cmd/compile/internal/types2/environment.go
new file mode 100644
index 0000000..fe9a309
--- /dev/null
+++ b/src/cmd/compile/internal/types2/environment.go
@@ -0,0 +1,81 @@
+// 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 types2
+
+import (
+	"bytes"
+	"strings"
+	"sync"
+)
+
+// An Environment is an opaque type checking environment. It may be used to
+// share identical type instances across type-checked packages or calls to
+// Instantiate.
+//
+// It is safe for concurrent use.
+type Environment struct {
+	mu      sync.Mutex
+	typeMap map[string]*Named // type hash -> instance
+	nextID  int               // next unique ID
+	seen    map[*Named]int    // assigned unique IDs
+}
+
+// NewEnvironment creates a new Environment.
+func NewEnvironment() *Environment {
+	return &Environment{
+		typeMap: make(map[string]*Named),
+		seen:    make(map[*Named]int),
+	}
+}
+
+// TypeHash returns a string representation of typ, which can be used as an exact
+// type hash: types that are identical produce identical string representations.
+// If typ is a *Named type and targs is not empty, typ is printed as if it were
+// instantiated with targs. The result is guaranteed to not contain blanks (" ").
+func (env *Environment) TypeHash(typ Type, targs []Type) string {
+	assert(env != nil)
+	assert(typ != nil)
+	var buf bytes.Buffer
+
+	h := newTypeHasher(&buf, env)
+	if named, _ := typ.(*Named); named != nil && len(targs) > 0 {
+		// Don't use WriteType because we need to use the provided targs
+		// and not any targs that might already be with the *Named type.
+		h.typePrefix(named)
+		h.typeName(named.obj)
+		h.typeList(targs)
+	} else {
+		assert(targs == nil)
+		h.typ(typ)
+	}
+
+	return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4
+}
+
+// typeForHash returns the recorded type for the type hash h, if it exists.
+// If no type exists for h and n is non-nil, n is recorded for h.
+func (env *Environment) typeForHash(h string, n *Named) *Named {
+	env.mu.Lock()
+	defer env.mu.Unlock()
+	if existing := env.typeMap[h]; existing != nil {
+		return existing
+	}
+	if n != nil {
+		env.typeMap[h] = n
+	}
+	return n
+}
+
+// idForType returns a unique ID for the pointer n.
+func (env *Environment) idForType(n *Named) int {
+	env.mu.Lock()
+	defer env.mu.Unlock()
+	id, ok := env.seen[n]
+	if !ok {
+		id = env.nextID
+		env.seen[n] = id
+		env.nextID++
+	}
+	return id
+}
diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go
index a682732..ea43fab 100644
--- a/src/cmd/compile/internal/types2/errors.go
+++ b/src/cmd/compile/internal/types2/errors.go
@@ -246,7 +246,7 @@
 	var b bytes.Buffer
 	for _, r := range s {
 		// strip #'s and subscript digits
-		if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
+		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
 			b.WriteRune(r)
 		}
 	}
diff --git a/src/cmd/compile/internal/types2/errors_test.go b/src/cmd/compile/internal/types2/errors_test.go
index e1f0e83..72a2ce3 100644
--- a/src/cmd/compile/internal/types2/errors_test.go
+++ b/src/cmd/compile/internal/types2/errors_test.go
@@ -35,7 +35,6 @@
 		{"foo", "foo"},
 		{"foo₀", "foo"},
 		{"foo(T₀)", "foo(T)"},
-		{"#foo(T₀)", "foo(T)"},
 	} {
 		got := stripAnnotations(test.in)
 		if got != test.want {
diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go
index febfd21..848a70d 100644
--- a/src/cmd/compile/internal/types2/index.go
+++ b/src/cmd/compile/internal/types2/index.go
@@ -34,7 +34,7 @@
 		return false
 
 	case value:
-		if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 {
+		if sig := asSignature(x.typ); sig != nil && sig.TypeParams().Len() > 0 {
 			// function instantiation
 			return true
 		}
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go
index bb7270b..c2a8155 100644
--- a/src/cmd/compile/internal/types2/infer.go
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -562,7 +562,7 @@
 		w.typ(t.elem)
 
 	case *Named:
-		for _, tpar := range t.TArgs().list() {
+		for _, tpar := range t.TypeArgs().list() {
 			w.typ(tpar)
 		}
 
diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go
index c882699..7a92799 100644
--- a/src/cmd/compile/internal/types2/instantiate.go
+++ b/src/cmd/compile/internal/types2/instantiate.go
@@ -13,21 +13,6 @@
 	"fmt"
 )
 
-// An Environment is an opaque type checking environment. It may be used to
-// share identical type instances across type checked packages or calls to
-// Instantiate.
-type Environment struct {
-	// For now, Environment just hides a Checker.
-	// Eventually, we strive to remove the need for a checker.
-	check *Checker
-}
-
-// NewEnvironment returns a new Environment, initialized with the given
-// Checker, or nil.
-func NewEnvironment(check *Checker) *Environment {
-	return &Environment{check}
-}
-
 // Instantiate instantiates the type typ with the given type arguments targs.
 // typ must be a *Named or a *Signature type, and its number of type parameters
 // must match the number of provided type arguments. The result is a new,
@@ -46,22 +31,18 @@
 // TODO(rfindley): change this function to also return an error if lengths of
 // tparams and targs do not match.
 func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, error) {
-	var check *Checker
-	if env != nil {
-		check = env.check
-	}
-	inst := check.instance(nopos, typ, targs)
+	inst := (*Checker)(nil).instance(nopos, typ, targs, env)
 
 	var err error
 	if validate {
 		var tparams []*TypeParam
 		switch t := typ.(type) {
 		case *Named:
-			tparams = t.TParams().list()
+			tparams = t.TypeParams().list()
 		case *Signature:
-			tparams = t.TParams().list()
+			tparams = t.TypeParams().list()
 		}
-		if i, err := check.verify(nopos, tparams, targs); err != nil {
+		if i, err := (*Checker)(nil).verify(nopos, tparams, targs); err != nil {
 			return inst, ArgumentError{i, err}
 		}
 	}
@@ -90,7 +71,7 @@
 		}()
 	}
 
-	inst := check.instance(pos, typ, targs)
+	inst := check.instance(pos, typ, targs, check.conf.Environment)
 
 	assert(len(posList) <= len(targs))
 	check.later(func() {
@@ -99,9 +80,9 @@
 		var tparams []*TypeParam
 		switch t := typ.(type) {
 		case *Named:
-			tparams = t.TParams().list()
+			tparams = t.TypeParams().list()
 		case *Signature:
-			tparams = t.TParams().list()
+			tparams = t.TypeParams().list()
 		}
 		// Avoid duplicate errors; instantiate will have complained if tparams
 		// and targs do not have the same length.
@@ -122,35 +103,40 @@
 // instance creates a type or function instance using the given original type
 // typ and arguments targs. For Named types the resulting instance will be
 // unexpanded.
-func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) Type {
+func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Environment) Type {
 	switch t := typ.(type) {
 	case *Named:
-		h := typeHash(t, targs)
-		if check != nil {
-			// typ may already have been instantiated with identical type arguments.
-			// In that case, re-use the existing instance.
-			if named := check.typMap[h]; named != nil {
+		var h string
+		if env != nil {
+			h = env.TypeHash(t, targs)
+			// typ may already have been instantiated with identical type arguments. In
+			// that case, re-use the existing instance.
+			if named := env.typeForHash(h, nil); named != nil {
 				return named
 			}
 		}
 		tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
-		named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded
+		named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved
 		named.targs = NewTypeList(targs)
-		named.instPos = &pos
-		if check != nil {
-			check.typMap[h] = named
+		named.resolver = func(env *Environment, n *Named) (*TypeParamList, Type, []*Func) {
+			return expandNamed(env, n, pos)
+		}
+		if env != nil {
+			// It's possible that we've lost a race to add named to the environment.
+			// In this case, use whichever instance is recorded in the environment.
+			named = env.typeForHash(h, named)
 		}
 		return named
 
 	case *Signature:
-		tparams := t.TParams()
+		tparams := t.TypeParams()
 		if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
 			return Typ[Invalid]
 		}
 		if tparams.Len() == 0 {
 			return typ // nothing to do (minor optimization)
 		}
-		sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), nil).(*Signature)
+		sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), env).(*Signature)
 		// If the signature doesn't use its type parameters, subst
 		// will not make a copy. In that case, make a copy now (so
 		// we can set tparams to nil w/o causing side-effects).
diff --git a/src/cmd/compile/internal/types2/instantiate_test.go b/src/cmd/compile/internal/types2/instantiate_test.go
new file mode 100644
index 0000000..69a2649
--- /dev/null
+++ b/src/cmd/compile/internal/types2/instantiate_test.go
@@ -0,0 +1,62 @@
+// 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 types2_test
+
+import (
+	. "cmd/compile/internal/types2"
+	"testing"
+)
+
+func TestInstantiateEquality(t *testing.T) {
+	const src = genericPkg + "p; type T[P any] int"
+	pkg, err := pkgFor(".", src, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	T := pkg.Scope().Lookup("T").Type().(*Named)
+	// Instantiating the same type twice should result in pointer-equivalent
+	// instances.
+	env := NewEnvironment()
+	res1, err := Instantiate(env, T, []Type{Typ[Int]}, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	res2, err := Instantiate(env, T, []Type{Typ[Int]}, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if res1 != res2 {
+		t.Errorf("first instance (%s) not pointer-equivalent to second instance (%s)", res1, res2)
+	}
+}
+func TestInstantiateNonEquality(t *testing.T) {
+	const src = genericPkg + "p; type T[P any] int"
+	pkg1, err := pkgFor(".", src, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	pkg2, err := pkgFor(".", src, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	// We consider T1 and T2 to be distinct types, so their instances should not
+	// be deduplicated by the environment.
+	T1 := pkg1.Scope().Lookup("T").Type().(*Named)
+	T2 := pkg2.Scope().Lookup("T").Type().(*Named)
+	env := NewEnvironment()
+	res1, err := Instantiate(env, T1, []Type{Typ[Int]}, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	res2, err := Instantiate(env, T2, []Type{Typ[Int]}, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if res1 == res2 {
+		t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2)
+	}
+	if Identical(res1, res2) {
+		t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2)
+	}
+}
diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go
index e57158d..340df51 100644
--- a/src/cmd/compile/internal/types2/interface.go
+++ b/src/cmd/compile/internal/types2/interface.go
@@ -11,6 +11,7 @@
 
 // An Interface represents an interface type.
 type Interface struct {
+	check     *Checker      // for error reporting; nil once type set is computed
 	obj       *TypeName     // corresponding declared object; or nil (for better error messages)
 	methods   []*Func       // ordered list of explicitly declared methods
 	embeddeds []Type        // ordered list of explicitly embedded elements
@@ -21,7 +22,7 @@
 }
 
 // typeSet returns the type set for interface t.
-func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(nil, nopos, t) }
+func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, nopos, t) }
 
 // emptyInterface represents the empty interface
 var emptyInterface = Interface{complete: true, tset: &topTypeSet}
@@ -198,7 +199,7 @@
 	}
 
 	// All methods and embedded elements for this interface are collected;
-	// i.e., this interface is may be used in a type set computation.
+	// i.e., this interface may be used in a type set computation.
 	ityp.complete = true
 
 	if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
@@ -214,7 +215,15 @@
 	// Compute type set with a non-nil *Checker as soon as possible
 	// to report any errors. Subsequent uses of type sets will use
 	// this computed type set and won't need to pass in a *Checker.
-	check.later(func() { computeInterfaceTypeSet(check, iface.Pos(), ityp) })
+	//
+	// Pin the checker to the interface type in the interim, in case the type set
+	// must be used before delayed funcs are processed (see issue #48234).
+	// TODO(rfindley): clean up use of *Checker with computeInterfaceTypeSet
+	ityp.check = check
+	check.later(func() {
+		computeInterfaceTypeSet(check, iface.Pos(), ityp)
+		ityp.check = nil
+	})
 }
 
 func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go
index d0718e5..0e7a2b7 100644
--- a/src/cmd/compile/internal/types2/lookup.go
+++ b/src/cmd/compile/internal/types2/lookup.go
@@ -122,7 +122,7 @@
 				seen[named] = true
 
 				// look for a matching attached method
-				named.load()
+				named.resolve(nil)
 				if i, m := lookupMethod(named.methods, pkg, name); m != nil {
 					// potential match
 					// caution: method may not have a proper signature yet
@@ -321,10 +321,10 @@
 			// both methods must have the same number of type parameters
 			ftyp := f.typ.(*Signature)
 			mtyp := m.typ.(*Signature)
-			if ftyp.TParams().Len() != mtyp.TParams().Len() {
+			if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
 				return m, f
 			}
-			if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 {
+			if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 {
 				panic("method with type parameters")
 			}
 
@@ -334,7 +334,7 @@
 			// TODO(gri) is this always correct? what about type bounds?
 			// (Alternative is to rename/subst type parameters and compare.)
 			u := newUnifier(true)
-			u.x.init(ftyp.TParams().list())
+			u.x.init(ftyp.TypeParams().list())
 			if !u.unify(ftyp, mtyp) {
 				return m, f
 			}
@@ -373,10 +373,10 @@
 		// both methods must have the same number of type parameters
 		ftyp := f.typ.(*Signature)
 		mtyp := m.typ.(*Signature)
-		if ftyp.TParams().Len() != mtyp.TParams().Len() {
+		if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
 			return m, f
 		}
-		if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 {
+		if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 {
 			panic("method with type parameters")
 		}
 
@@ -387,17 +387,17 @@
 		// In order to compare the signatures, substitute the receiver
 		// type parameters of ftyp with V's instantiation type arguments.
 		// This lazily instantiates the signature of method f.
-		if Vn != nil && Vn.TParams().Len() > 0 {
+		if Vn != nil && Vn.TypeParams().Len() > 0 {
 			// Be careful: The number of type arguments may not match
 			// the number of receiver parameters. If so, an error was
 			// reported earlier but the length discrepancy is still
 			// here. Exit early in this case to prevent an assertion
 			// failure in makeSubstMap.
 			// TODO(gri) Can we avoid this check by fixing the lengths?
-			if len(ftyp.RParams().list()) != Vn.targs.Len() {
+			if len(ftyp.RecvTypeParams().list()) != Vn.targs.Len() {
 				return
 			}
-			ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs.list()), nil).(*Signature)
+			ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RecvTypeParams().list(), Vn.targs.list()), nil).(*Signature)
 		}
 
 		// If the methods have type parameters we don't care whether they
@@ -406,7 +406,7 @@
 		// TODO(gri) is this always correct? what about type bounds?
 		// (Alternative is to rename/subst type parameters and compare.)
 		u := newUnifier(true)
-		if ftyp.TParams().Len() > 0 {
+		if ftyp.TypeParams().Len() > 0 {
 			// We reach here only if we accept method type parameters.
 			// In this case, unification must consider any receiver
 			// and method type parameters as "free" type parameters.
@@ -416,9 +416,9 @@
 			// unimplemented call so that we test this code if we
 			// enable method type parameters.
 			unimplemented()
-			u.x.init(append(ftyp.RParams().list(), ftyp.TParams().list()...))
+			u.x.init(append(ftyp.RecvTypeParams().list(), ftyp.TypeParams().list()...))
 		} else {
-			u.x.init(ftyp.RParams().list())
+			u.x.init(ftyp.RecvTypeParams().list())
 		}
 		if !u.unify(ftyp, mtyp) {
 			return m, f
diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go
index a76e69f..c844012 100644
--- a/src/cmd/compile/internal/types2/named.go
+++ b/src/cmd/compile/internal/types2/named.go
@@ -12,18 +12,18 @@
 // A Named represents a named (defined) type.
 type Named struct {
 	check      *Checker
-	info       typeInfo    // for cycle detection
-	obj        *TypeName   // corresponding declared object for declared types; placeholder for instantiated types
-	orig       *Named      // original, uninstantiated type
-	fromRHS    Type        // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
-	underlying Type        // possibly a *Named during setup; never a *Named once set up completely
-	instPos    *syntax.Pos // position information for lazy instantiation, or nil
-	tparams    *TParamList // type parameters, or nil
-	targs      *TypeList   // type arguments (after instantiation), or nil
-	methods    []*Func     // methods declared for this type (not the method set of this type); signatures are type-checked lazily
+	info       typeInfo       // for cycle detection
+	obj        *TypeName      // corresponding declared object for declared types; placeholder for instantiated types
+	orig       *Named         // original, uninstantiated type
+	fromRHS    Type           // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
+	underlying Type           // possibly a *Named during setup; never a *Named once set up completely
+	tparams    *TypeParamList // type parameters, or nil
+	targs      *TypeList      // type arguments (after instantiation), or nil
+	methods    []*Func        // methods declared for this type (not the method set of this type); signatures are type-checked lazily
 
-	resolve func(*Named) ([]*TypeParam, Type, []*Func)
-	once    sync.Once
+	// resolver may be provided to lazily resolve type parameters, underlying, and methods.
+	resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func)
+	once     sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
 }
 
 // NewNamed returns a new named type for the given type name, underlying type, and associated methods.
@@ -36,49 +36,28 @@
 	return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
 }
 
-func (t *Named) load() *Named {
-	// If t is an instantiated type, it derives its methods and tparams from its
-	// base type. Since we expect type parameters and methods to be set after a
-	// call to load, we must load the base and copy here.
-	//
-	// underlying is set when t is expanded.
-	//
-	// By convention, a type instance is loaded iff its tparams are set.
-	if t.targs.Len() > 0 && t.tparams == nil {
-		t.orig.load()
-		t.tparams = t.orig.tparams
-		t.methods = t.orig.methods
-	}
-	if t.resolve == nil {
+func (t *Named) resolve(env *Environment) *Named {
+	if t.resolver == nil {
 		return t
 	}
 
 	t.once.Do(func() {
-		// TODO(mdempsky): Since we're passing t to resolve anyway
+		// TODO(mdempsky): Since we're passing t to the resolver anyway
 		// (necessary because types2 expects the receiver type for methods
 		// on defined interface types to be the Named rather than the
 		// underlying Interface), maybe it should just handle calling
-		// SetTParams, SetUnderlying, and AddMethod instead?  Those
-		// methods would need to support reentrant calls though.  It would
+		// SetTypeParams, SetUnderlying, and AddMethod instead?  Those
+		// methods would need to support reentrant calls though. It would
 		// also make the API more future-proof towards further extensions
-		// (like SetTParams).
-
-		tparams, underlying, methods := t.resolve(t)
-
-		switch underlying.(type) {
-		case nil, *Named:
-			panic("invalid underlying type")
-		}
-
-		t.tparams = bindTParams(tparams)
-		t.underlying = underlying
-		t.methods = methods
+		// (like SetTypeParams).
+		t.tparams, t.underlying, t.methods = t.resolver(env, t)
+		t.fromRHS = t.underlying // for cycle detection
 	})
 	return t
 }
 
 // newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
-func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TParamList, methods []*Func) *Named {
+func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParamList, methods []*Func) *Named {
 	typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
 	if typ.orig == nil {
 		typ.orig = typ
@@ -119,21 +98,21 @@
 // TODO(gri) Come up with a better representation and API to distinguish
 //           between parameterized instantiated and non-instantiated types.
 
-// TParams returns the type parameters of the named type t, or nil.
+// TypeParams returns the type parameters of the named type t, or nil.
 // The result is non-nil for an (originally) parameterized type even if it is instantiated.
-func (t *Named) TParams() *TParamList { return t.load().tparams }
+func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
 
-// SetTParams sets the type parameters of the named type t.
-func (t *Named) SetTParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) }
+// SetTypeParams sets the type parameters of the named type t.
+func (t *Named) SetTypeParams(tparams []*TypeParam) { t.resolve(nil).tparams = bindTParams(tparams) }
 
-// TArgs returns the type arguments used to instantiate the named type t.
-func (t *Named) TArgs() *TypeList { return t.targs }
+// TypeArgs returns the type arguments used to instantiate the named type t.
+func (t *Named) TypeArgs() *TypeList { return t.targs }
 
 // NumMethods returns the number of explicit methods whose receiver is named type t.
-func (t *Named) NumMethods() int { return len(t.load().methods) }
+func (t *Named) NumMethods() int { return len(t.resolve(nil).methods) }
 
 // Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
-func (t *Named) Method(i int) *Func { return t.load().methods[i] }
+func (t *Named) Method(i int) *Func { return t.resolve(nil).methods[i] }
 
 // SetUnderlying sets the underlying type and marks t as complete.
 func (t *Named) SetUnderlying(underlying Type) {
@@ -143,18 +122,18 @@
 	if _, ok := underlying.(*Named); ok {
 		panic("underlying type must not be *Named")
 	}
-	t.load().underlying = underlying
+	t.resolve(nil).underlying = underlying
 }
 
 // AddMethod adds method m unless it is already in the method list.
 func (t *Named) AddMethod(m *Func) {
-	t.load()
+	t.resolve(nil)
 	if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
 		t.methods = append(t.methods, m)
 	}
 }
 
-func (t *Named) Underlying() Type { return t.load().expand(nil).underlying }
+func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
 func (t *Named) String() string   { return TypeString(t, nil) }
 
 // ----------------------------------------------------------------------------
@@ -240,37 +219,36 @@
 	}
 }
 
-// expand ensures that the underlying type of n is instantiated.
+// expandNamed ensures that the underlying type of n is instantiated.
 // The underlying type will be Typ[Invalid] if there was an error.
-func (n *Named) expand(typMap map[string]*Named) *Named {
-	if n.instPos != nil {
-		// n must be loaded before instantiation, in order to have accurate
-		// tparams. This is done implicitly by the call to n.TParams, but making it
-		// explicit is harmless: load is idempotent.
-		n.load()
-		var u Type
-		if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) {
-			if typMap == nil {
-				if n.check != nil {
-					typMap = n.check.typMap
-				} else {
-					// If we're instantiating lazily, we might be outside the scope of a
-					// type-checking pass. In that case we won't have a pre-existing
-					// typMap, but don't want to create a duplicate of the current instance
-					// in the process of expansion.
-					h := typeHash(n.orig, n.targs.list())
-					typMap = map[string]*Named{h: n}
-				}
+func expandNamed(env *Environment, n *Named, instPos syntax.Pos) (*TypeParamList, Type, []*Func) {
+	n.orig.resolve(env)
+
+	var u Type
+	if n.check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) {
+		// TODO(rfindley): handling an optional Checker and Environment here (and
+		// in subst) feels overly complicated. Can we simplify?
+		if env == nil {
+			if n.check != nil {
+				env = n.check.conf.Environment
+			} else {
+				// If we're instantiating lazily, we might be outside the scope of a
+				// type-checking pass. In that case we won't have a pre-existing
+				// environment, but don't want to create a duplicate of the current
+				// instance in the process of expansion.
+				env = NewEnvironment()
 			}
-			u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), typMap)
-		} else {
-			u = Typ[Invalid]
+			h := env.TypeHash(n.orig, n.targs.list())
+			// add the instance to the environment to avoid infinite recursion.
+			// addInstance may return a different, existing instance, but we
+			// shouldn't return that instance from expand.
+			env.typeForHash(h, n)
 		}
-		n.underlying = u
-		n.fromRHS = u
-		n.instPos = nil
+		u = n.check.subst(instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env)
+	} else {
+		u = Typ[Invalid]
 	}
-	return n
+	return n.orig.tparams, u, n.orig.methods
 }
 
 // safeUnderlying returns the underlying of typ without expanding instances, to
@@ -279,7 +257,7 @@
 // TODO(rfindley): eliminate this function or give it a better name.
 func safeUnderlying(typ Type) Type {
 	if t, _ := typ.(*Named); t != nil {
-		return t.load().underlying
+		return t.resolve(nil).underlying
 	}
 	return typ.Underlying()
 }
diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go
index a3f5f91..540cb3f 100644
--- a/src/cmd/compile/internal/types2/object.go
+++ b/src/cmd/compile/internal/types2/object.go
@@ -278,9 +278,21 @@
 
 // NewTypeNameLazy returns a new defined type like NewTypeName, but it
 // lazily calls resolve to finish constructing the Named object.
-func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, resolve func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
+func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
 	obj := NewTypeName(pos, pkg, name, nil)
-	NewNamed(obj, nil, nil).resolve = resolve
+
+	resolve := func(_ *Environment, t *Named) (*TypeParamList, Type, []*Func) {
+		tparams, underlying, methods := load(t)
+
+		switch underlying.(type) {
+		case nil, *Named:
+			panic(fmt.Sprintf("invalid underlying type %T", t.underlying))
+		}
+
+		return bindTParams(tparams), underlying, methods
+	}
+
+	NewNamed(obj, nil, nil).resolver = resolve
 	return obj
 }
 
@@ -475,8 +487,8 @@
 		if _, ok := typ.(*Basic); ok {
 			return
 		}
-		if named, _ := typ.(*Named); named != nil && named.TParams().Len() > 0 {
-			newTypeWriter(buf, qf).tParamList(named.TParams().list())
+		if named, _ := typ.(*Named); named != nil && named.TypeParams().Len() > 0 {
+			newTypeWriter(buf, qf).tParamList(named.TypeParams().list())
 		}
 		if tname.IsAlias() {
 			buf.WriteString(" =")
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index 3ccafef..74ad3da 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -21,7 +21,7 @@
 func isGeneric(typ Type) bool {
 	// A parameterized type is only instantiated if it doesn't have an instantiation already.
 	named, _ := typ.(*Named)
-	return named != nil && named.obj != nil && named.targs == nil && named.TParams() != nil
+	return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil
 }
 
 func is(typ Type, what BasicInfo) bool {
@@ -220,11 +220,18 @@
 		// parameter names.
 		if y, ok := y.(*Signature); ok {
 			return x.variadic == y.variadic &&
-				identicalTParams(x.TParams().list(), y.TParams().list(), cmpTags, p) &&
+				identicalTParams(x.TypeParams().list(), y.TypeParams().list(), cmpTags, p) &&
 				identical(x.params, y.params, cmpTags, p) &&
 				identical(x.results, y.results, cmpTags, p)
 		}
 
+	case *Union:
+		if y, _ := y.(*Union); y != nil {
+			xset := computeUnionTypeSet(nil, nopos, x)
+			yset := computeUnionTypeSet(nil, nopos, y)
+			return xset.terms.equal(yset.terms)
+		}
+
 	case *Interface:
 		// Two interface types are identical if they describe the same type sets.
 		// With the existing implementation restriction, this simplifies to:
@@ -302,11 +309,8 @@
 		// Two named types are identical if their type names originate
 		// in the same type declaration.
 		if y, ok := y.(*Named); ok {
-			x.expand(nil)
-			y.expand(nil)
-
-			xargs := x.TArgs().list()
-			yargs := y.TArgs().list()
+			xargs := x.TypeArgs().list()
+			yargs := y.TypeArgs().list()
 
 			if len(xargs) != len(yargs) {
 				return false
diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go
index a7d0db6..e3186f5 100644
--- a/src/cmd/compile/internal/types2/signature.go
+++ b/src/cmd/compile/internal/types2/signature.go
@@ -19,13 +19,13 @@
 	// and store it in the Func Object) because when type-checking a function
 	// literal we call the general type checker which returns a general Type.
 	// We then unpack the *Signature and use the scope for the literal body.
-	rparams  *TParamList // receiver type parameters from left to right, or nil
-	tparams  *TParamList // type parameters from left to right, or nil
-	scope    *Scope      // function scope, present for package-local signatures
-	recv     *Var        // nil if not a method
-	params   *Tuple      // (incoming) parameters from left to right; or nil
-	results  *Tuple      // (outgoing) results from left to right; or nil
-	variadic bool        // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+	rparams  *TypeParamList // receiver type parameters from left to right, or nil
+	tparams  *TypeParamList // type parameters from left to right, or nil
+	scope    *Scope         // function scope, present for package-local signatures
+	recv     *Var           // nil if not a method
+	params   *Tuple         // (incoming) parameters from left to right; or nil
+	results  *Tuple         // (outgoing) results from left to right; or nil
+	variadic bool           // true if the last parameter's type is of the form ...T (or string, for append built-in only)
 }
 
 // NewSignature returns a new function type for the given receiver, parameters,
@@ -53,17 +53,17 @@
 // contain methods whose receiver type is a different interface.
 func (s *Signature) Recv() *Var { return s.recv }
 
-// TParams returns the type parameters of signature s, or nil.
-func (s *Signature) TParams() *TParamList { return s.tparams }
+// TypeParams returns the type parameters of signature s, or nil.
+func (s *Signature) TypeParams() *TypeParamList { return s.tparams }
 
-// SetTParams sets the type parameters of signature s.
-func (s *Signature) SetTParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) }
+// SetTypeParams sets the type parameters of signature s.
+func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) }
 
-// RParams returns the receiver type parameters of signature s, or nil.
-func (s *Signature) RParams() *TParamList { return s.rparams }
+// RecvTypeParams returns the receiver type parameters of signature s, or nil.
+func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams }
 
-// SetRParams sets the receiver type params of signature s.
-func (s *Signature) SetRParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) }
+// SetRecvTypeParams sets the receiver type params of signature s.
+func (s *Signature) SetRecvTypeParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) }
 
 // Params returns the parameters of signature s, or nil.
 func (s *Signature) Params() *Tuple { return s.params }
@@ -133,19 +133,19 @@
 				//       again when we type-check the signature.
 				// TODO(gri) maybe the receiver should be marked as invalid instead?
 				if recv, _ := check.genericType(rname, false).(*Named); recv != nil {
-					recvTParams = recv.TParams().list()
+					recvTParams = recv.TypeParams().list()
 				}
 			}
 			// provide type parameter bounds
 			// - only do this if we have the right number (otherwise an error is reported elsewhere)
-			if sig.RParams().Len() == len(recvTParams) {
+			if sig.RecvTypeParams().Len() == len(recvTParams) {
 				// We have a list of *TypeNames but we need a list of Types.
-				list := make([]Type, sig.RParams().Len())
-				for i, t := range sig.RParams().list() {
+				list := make([]Type, sig.RecvTypeParams().Len())
+				for i, t := range sig.RecvTypeParams().list() {
 					list[i] = t
 				}
 				smap := makeSubstMap(recvTParams, list)
-				for i, tpar := range sig.RParams().list() {
+				for i, tpar := range sig.RecvTypeParams().list() {
 					bound := recvTParams[i].bound
 					// bound is (possibly) parameterized in the context of the
 					// receiver type declaration. Substitute parameters for the
@@ -210,10 +210,10 @@
 			var err string
 			switch T := rtyp.(type) {
 			case *Named:
-				T.expand(nil)
+				T.resolve(check.conf.Environment)
 				// The receiver type may be an instantiated type referred to
 				// by an alias (which cannot have receiver parameters for now).
-				if T.TArgs() != nil && sig.RParams() == nil {
+				if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
 					check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ)
 					break
 				}
diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go
index 5be369d..a7f1185 100644
--- a/src/cmd/compile/internal/types2/sizeof_test.go
+++ b/src/cmd/compile/internal/types2/sizeof_test.go
@@ -28,10 +28,10 @@
 		{Tuple{}, 12, 24},
 		{Signature{}, 28, 56},
 		{Union{}, 16, 32},
-		{Interface{}, 40, 80},
+		{Interface{}, 44, 88},
 		{Map{}, 16, 32},
 		{Chan{}, 12, 24},
-		{Named{}, 72, 136},
+		{Named{}, 68, 128},
 		{TypeParam{}, 28, 48},
 		{term{}, 12, 24},
 		{top{}, 0, 0},
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index c67538d..87c1d78 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -6,10 +6,7 @@
 
 package types2
 
-import (
-	"bytes"
-	"cmd/compile/internal/syntax"
-)
+import "cmd/compile/internal/syntax"
 
 type substMap map[*TypeParam]Type
 
@@ -40,8 +37,8 @@
 // incoming type. If a substitution took place, the result type is different
 // from the incoming type.
 //
-// If the given typMap is non-nil, it is used in lieu of check.typMap.
-func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, typMap map[string]*Named) Type {
+// If the given environment is non-nil, it is used in lieu of check.env.
+func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, env *Environment) Type {
 	if smap.empty() {
 		return typ
 	}
@@ -61,27 +58,27 @@
 
 	if check != nil {
 		subst.check = check
-		if typMap == nil {
-			typMap = check.typMap
+		if env == nil {
+			env = check.conf.Environment
 		}
 	}
-	if typMap == nil {
+	if env == nil {
 		// If we don't have a *Checker and its global type map,
 		// use a local version. Besides avoiding duplicate work,
 		// the type map prevents infinite recursive substitution
 		// for recursive types (example: type T[P any] *T[P]).
-		typMap = make(map[string]*Named)
+		env = NewEnvironment()
 	}
-	subst.typMap = typMap
+	subst.env = env
 
 	return subst.typ(typ)
 }
 
 type subster struct {
-	pos    syntax.Pos
-	smap   substMap
-	check  *Checker // nil if called via Instantiate
-	typMap map[string]*Named
+	pos   syntax.Pos
+	smap  substMap
+	check *Checker // nil if called via Instantiate
+	env   *Environment
 }
 
 func (subst *subster) typ(typ Type) Type {
@@ -182,13 +179,19 @@
 			}
 		}
 
-		if t.TParams().Len() == 0 {
+		// subst is called by expandNamed, so in this function we need to be
+		// careful not to call any methods that would cause t to be expanded: doing
+		// so would result in deadlock.
+		//
+		// So we call t.orig.TypeParams() rather than t.TypeParams() here and
+		// below.
+		if t.orig.TypeParams().Len() == 0 {
 			dump(">>> %s is not parameterized", t)
 			return t // type is not parameterized
 		}
 
 		var newTArgs []Type
-		assert(t.targs.Len() == t.TParams().Len())
+		assert(t.targs.Len() == t.orig.TypeParams().Len())
 
 		// already instantiated
 		dump(">>> %s already instantiated", t)
@@ -201,7 +204,7 @@
 			if new_targ != targ {
 				dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
 				if newTArgs == nil {
-					newTArgs = make([]Type, t.TParams().Len())
+					newTArgs = make([]Type, t.orig.TypeParams().Len())
 					copy(newTArgs, t.targs.list())
 				}
 				newTArgs[i] = new_targ
@@ -214,32 +217,29 @@
 		}
 
 		// before creating a new named type, check if we have this one already
-		h := typeHash(t, newTArgs)
+		h := subst.env.TypeHash(t.orig, newTArgs)
 		dump(">>> new type hash: %s", h)
-		if named, found := subst.typMap[h]; found {
+		if named := subst.env.typeForHash(h, nil); named != nil {
 			dump(">>> found %s", named)
 			return named
 		}
 
-		// Create a new named type and populate typMap to avoid endless recursion.
-		// The position used here is irrelevant because validation only occurs on t
-		// (we don't call validType on named), but we use subst.pos to help with
-		// debugging.
-		tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
-		t.load()
-		// It's ok to provide a nil *Checker because the newly created type
-		// doesn't need to be (lazily) expanded; it's expanded below.
-		named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available
-		named.targs = NewTypeList(newTArgs)
-		subst.typMap[h] = named
-		t.expand(subst.typMap) // must happen after typMap update to avoid infinite recursion
-
-		// do the substitution
-		dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTArgs)
-		named.underlying = subst.typOrNil(t.underlying)
-		dump(">>> underlying: %v", named.underlying)
+		t.orig.resolve(subst.env)
+		// Create a new instance and populate the environment to avoid endless
+		// recursion. The position used here is irrelevant because validation only
+		// occurs on t (we don't call validType on named), but we use subst.pos to
+		// help with debugging.
+		named := subst.check.instance(subst.pos, t.orig, newTArgs, subst.env).(*Named)
+		// TODO(rfindley): we probably don't need to resolve here. Investigate if
+		// this can be removed.
+		named.resolve(subst.env)
 		assert(named.underlying != nil)
-		named.fromRHS = named.underlying // for consistency, though no cycle detection is necessary
+
+		// Note that if we were to expose substitution more generally (not just in
+		// the context of a declaration), we'd have to substitute in
+		// named.underlying as well.
+		//
+		// But this is unnecessary for now.
 
 		return named
 
@@ -253,35 +253,6 @@
 	return typ
 }
 
-// typeHash returns a string representation of typ, which can be used as an exact
-// type hash: types that are identical produce identical string representations.
-// If typ is a *Named type and targs is not empty, typ is printed as if it were
-// instantiated with targs.
-func typeHash(typ Type, targs []Type) string {
-	assert(typ != nil)
-	var buf bytes.Buffer
-
-	h := newTypeHasher(&buf)
-	if named, _ := typ.(*Named); named != nil && len(targs) > 0 {
-		// Don't use WriteType because we need to use the provided targs
-		// and not any targs that might already be with the *Named type.
-		h.typeName(named.obj)
-		h.typeList(targs)
-	} else {
-		assert(targs == nil)
-		h.typ(typ)
-	}
-
-	if debug {
-		// there should be no instance markers in type hashes
-		for _, b := range buf.Bytes() {
-			assert(b != instanceMarker)
-		}
-	}
-
-	return buf.String()
-}
-
 // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid].
 // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_))
 // where an array/slice element is accessed before it is set up.
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2
new file mode 100644
index 0000000..e4bcee5
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2
@@ -0,0 +1,16 @@
+// 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
+
+const L = 10
+
+type (
+	_        [L]struct{}
+	_        [A /* ERROR undeclared name A for array length */ ]struct{}
+	_        [B /* ERROR not an expression */ ]struct{}
+	_[A any] struct{}
+
+	B int
+)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2
new file mode 100644
index 0000000..4c4fc2f
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2
@@ -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 p
+
+type Fooer[t any] interface {
+	foo(Barer[t])
+}
+type Barer[t any] interface {
+	bar(Bazer[t])
+}
+type Bazer[t any] interface {
+	Fooer[t]
+	baz(t)
+}
+
+type Int int
+
+func (n Int) baz(int) {}
+func (n Int) foo(b Barer[int]) { b.bar(n) }
+
+type F[t any] interface { f(G[t]) }
+type G[t any] interface { g(H[t]) }
+type H[t any] interface { F[t] }
+
+type T struct{}
+func (n T) f(b G[T]) { b.g(n) }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2
index 56e9094..2c4b661 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2
@@ -5,4 +5,4 @@
 package p
 
 // don't crash
-func T /* ERROR missing */ [P /* ERROR named */ ] m /* ERROR m */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */
+func T /* ERROR missing */ [P] /* ERROR missing */ m /* ERROR unexpected */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2
new file mode 100644
index 0000000..e069930
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2
@@ -0,0 +1,10 @@
+// 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 _ = interface{
+	m()
+	m /* ERROR "duplicate method" */ ()
+}(nil)
diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go
index ca5ecdc..400d6f7 100644
--- a/src/cmd/compile/internal/types2/type.go
+++ b/src/cmd/compile/internal/types2/type.go
@@ -115,7 +115,7 @@
 func asNamed(t Type) *Named {
 	e, _ := t.(*Named)
 	if e != nil {
-		e.expand(nil)
+		e.resolve(nil)
 	}
 	return e
 }
diff --git a/src/cmd/compile/internal/types2/typelists.go b/src/cmd/compile/internal/types2/typelists.go
index f313ea3..ababe85 100644
--- a/src/cmd/compile/internal/types2/typelists.go
+++ b/src/cmd/compile/internal/types2/typelists.go
@@ -6,20 +6,20 @@
 
 import "bytes"
 
-// TParamList holds a list of type parameters.
-type TParamList struct{ tparams []*TypeParam }
+// TypeParamList holds a list of type parameters.
+type TypeParamList struct{ tparams []*TypeParam }
 
 // Len returns the number of type parameters in the list.
 // It is safe to call on a nil receiver.
-func (l *TParamList) Len() int { return len(l.list()) }
+func (l *TypeParamList) Len() int { return len(l.list()) }
 
 // At returns the i'th type parameter in the list.
-func (l *TParamList) At(i int) *TypeParam { return l.tparams[i] }
+func (l *TypeParamList) At(i int) *TypeParam { return l.tparams[i] }
 
 // list is for internal use where we expect a []*TypeParam.
 // TODO(rfindley): list should probably be eliminated: we can pass around a
-// TParamList instead.
-func (l *TParamList) list() []*TypeParam {
+// TypeParamList instead.
+func (l *TypeParamList) list() []*TypeParam {
 	if l == nil {
 		return nil
 	}
@@ -66,7 +66,7 @@
 // ----------------------------------------------------------------------------
 // Implementation
 
-func bindTParams(list []*TypeParam) *TParamList {
+func bindTParams(list []*TypeParam) *TypeParamList {
 	if len(list) == 0 {
 		return nil
 	}
@@ -76,5 +76,5 @@
 		}
 		typ.index = i
 	}
-	return &TParamList{tparams: list}
+	return &TypeParamList{tparams: list}
 }
diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go
index 445337f..505596f 100644
--- a/src/cmd/compile/internal/types2/typeparam.go
+++ b/src/cmd/compile/internal/types2/typeparam.go
@@ -29,18 +29,22 @@
 func (t *TypeParam) Obj() *TypeName { return t.obj }
 
 // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named
-// or Signature type by calling SetTParams. Setting a type parameter on more
+// or Signature type by calling SetTypeParams. Setting a type parameter on more
 // than one type will result in a panic.
 //
-// The bound argument can be nil, and set later via SetBound.
-func (check *Checker) NewTypeParam(obj *TypeName, bound Type) *TypeParam {
+// The constraint argument can be nil, and set later via SetConstraint.
+func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
+	return (*Checker)(nil).newTypeParam(obj, constraint)
+}
+
+func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
 	// Always increment lastID, even if it is not used.
 	id := nextID()
 	if check != nil {
 		check.nextID++
 		id = check.nextID
 	}
-	typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: bound}
+	typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint}
 	if obj.typ == nil {
 		obj.typ = typ
 	}
diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go
index 6083955..bdafcf8 100644
--- a/src/cmd/compile/internal/types2/typestring.go
+++ b/src/cmd/compile/internal/types2/typestring.go
@@ -8,7 +8,7 @@
 
 import (
 	"bytes"
-	"fmt"
+	"strconv"
 	"unicode/utf8"
 )
 
@@ -63,32 +63,45 @@
 	newTypeWriter(buf, qf).signature(sig)
 }
 
-// instanceMarker is the prefix for an instantiated type in unexpanded form.
-const instanceMarker = '#'
-
 type typeWriter struct {
 	buf  *bytes.Buffer
 	seen map[Type]bool
 	qf   Qualifier
-	hash bool
+	env  *Environment // if non-nil, we are type hashing
 }
 
 func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
-	return &typeWriter{buf, make(map[Type]bool), qf, false}
+	return &typeWriter{buf, make(map[Type]bool), qf, nil}
 }
 
-func newTypeHasher(buf *bytes.Buffer) *typeWriter {
-	return &typeWriter{buf, make(map[Type]bool), nil, true}
+func newTypeHasher(buf *bytes.Buffer, env *Environment) *typeWriter {
+	assert(env != nil)
+	return &typeWriter{buf, make(map[Type]bool), nil, env}
 }
 
-func (w *typeWriter) byte(b byte)                               { w.buf.WriteByte(b) }
-func (w *typeWriter) string(s string)                           { w.buf.WriteString(s) }
-func (w *typeWriter) writef(format string, args ...interface{}) { fmt.Fprintf(w.buf, format, args...) }
+func (w *typeWriter) byte(b byte) {
+	if w.env != nil {
+		if b == ' ' {
+			b = '#'
+		}
+		w.buf.WriteByte(b)
+		return
+	}
+	w.buf.WriteByte(b)
+	if b == ',' || b == ';' {
+		w.buf.WriteByte(' ')
+	}
+}
+
+func (w *typeWriter) string(s string) {
+	w.buf.WriteString(s)
+}
+
 func (w *typeWriter) error(msg string) {
-	if w.hash {
+	if w.env != nil {
 		panic(msg)
 	}
-	w.string("<" + msg + ">")
+	w.buf.WriteString("<" + msg + ">")
 }
 
 func (w *typeWriter) typ(typ Type) {
@@ -115,7 +128,9 @@
 		w.string(t.name)
 
 	case *Array:
-		w.writef("[%d]", t.len)
+		w.byte('[')
+		w.string(strconv.FormatInt(t.len, 10))
+		w.byte(']')
 		w.typ(t.elem)
 
 	case *Slice:
@@ -126,7 +141,7 @@
 		w.string("struct{")
 		for i, f := range t.fields {
 			if i > 0 {
-				w.string("; ")
+				w.byte(';')
 			}
 			// This doesn't do the right thing for embedded type
 			// aliases where we should print the alias name, not
@@ -137,7 +152,11 @@
 			}
 			w.typ(f.typ)
 			if tag := t.Tag(i); tag != "" {
-				w.writef(" %q", tag)
+				w.byte(' ')
+				// TODO(gri) If tag contains blanks, replacing them with '#'
+				//           in Environment.TypeHash may produce another tag
+				//           accidentally.
+				w.string(strconv.Quote(tag))
 			}
 		}
 		w.byte('}')
@@ -175,7 +194,7 @@
 		first := true
 		for _, m := range t.methods {
 			if !first {
-				w.string("; ")
+				w.byte(';')
 			}
 			first = false
 			w.string(m.name)
@@ -183,7 +202,7 @@
 		}
 		for _, typ := range t.embeddeds {
 			if !first {
-				w.string("; ")
+				w.byte(';')
 			}
 			first = false
 			w.typ(typ)
@@ -223,20 +242,14 @@
 		}
 
 	case *Named:
-		// Instance markers indicate unexpanded instantiated
-		// types. Write them to aid debugging, but don't write
-		// them when we need an instance hash: whether a type
-		// is fully expanded or not doesn't matter for identity.
-		if !w.hash && t.instPos != nil {
-			w.byte(instanceMarker)
-		}
+		w.typePrefix(t)
 		w.typeName(t.obj)
 		if t.targs != nil {
 			// instantiated type
 			w.typeList(t.targs.list())
-		} else if !w.hash && t.TParams().Len() != 0 { // For type hashing, don't need to format the TParams
+		} else if w.env == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TParams
 			// parameterized type
-			w.tParamList(t.TParams().list())
+			w.tParamList(t.TypeParams().list())
 		}
 
 	case *TypeParam:
@@ -263,11 +276,20 @@
 	}
 }
 
+// If w.env is non-nil, typePrefix writes a unique prefix for the named type t
+// based on the types already observed by w.env. If w.env is nil, it does
+// nothing.
+func (w *typeWriter) typePrefix(t *Named) {
+	if w.env != nil {
+		w.string(strconv.Itoa(w.env.idForType(t)))
+	}
+}
+
 func (w *typeWriter) typeList(list []Type) {
 	w.byte('[')
 	for i, typ := range list {
 		if i > 0 {
-			w.string(", ")
+			w.byte(',')
 		}
 		w.typ(typ)
 	}
@@ -291,7 +313,7 @@
 				w.byte(' ')
 				w.typ(prev)
 			}
-			w.string(", ")
+			w.byte(',')
 		}
 		prev = tpar.bound
 		w.typ(tpar)
@@ -308,31 +330,6 @@
 		writePackage(w.buf, obj.pkg, w.qf)
 	}
 	w.string(obj.name)
-
-	if w.hash {
-		// For local defined types, use the (original!) TypeName's scope
-		// numbers to disambiguate.
-		if typ, _ := obj.typ.(*Named); typ != nil {
-			// TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes
-			//           and whether the loop can iterate more than twice.
-			//           (It seems somehow connected to instance types.)
-			for typ.orig != typ {
-				typ = typ.orig
-			}
-			w.writeScopeNumbers(typ.obj.parent)
-		}
-	}
-}
-
-// writeScopeNumbers writes the number sequence for this scope to buf
-// in the form ".i.j.k" where i, j, k, etc. stand for scope numbers.
-// If a scope is nil or has no parent (such as a package scope), nothing
-// is written.
-func (w *typeWriter) writeScopeNumbers(s *Scope) {
-	if s != nil && s.number > 0 {
-		w.writeScopeNumbers(s.parent)
-		w.writef(".%d", s.number)
-	}
 }
 
 func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
@@ -340,10 +337,10 @@
 	if tup != nil {
 		for i, v := range tup.vars {
 			if i > 0 {
-				w.string(", ")
+				w.byte(',')
 			}
 			// parameter names are ignored for type identity and thus type hashes
-			if !w.hash && v.name != "" {
+			if w.env == nil && v.name != "" {
 				w.string(v.name)
 				w.byte(' ')
 			}
@@ -371,8 +368,8 @@
 }
 
 func (w *typeWriter) signature(sig *Signature) {
-	if sig.TParams().Len() != 0 {
-		w.tParamList(sig.TParams().list())
+	if sig.TypeParams().Len() != 0 {
+		w.tParamList(sig.TypeParams().list())
 	}
 
 	w.tuple(sig.params, sig.variadic)
@@ -384,7 +381,7 @@
 	}
 
 	w.byte(' ')
-	if n == 1 && (w.hash || sig.results.vars[0].name == "") {
+	if n == 1 && (w.env != nil || sig.results.vars[0].name == "") {
 		// single unnamed result (if type hashing, name must be ignored)
 		w.typ(sig.results.vars[0].typ)
 		return
diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go
index f3db3bb..5aacb94 100644
--- a/src/cmd/compile/internal/types2/typexpr.go
+++ b/src/cmd/compile/internal/types2/typexpr.go
@@ -428,6 +428,14 @@
 // and returns the constant length >= 0, or a value < 0
 // to indicate an error (and thus an unknown length).
 func (check *Checker) arrayLength(e syntax.Expr) int64 {
+	// If e is an undeclared identifier, the array declaration might be an
+	// attempt at a parameterized type declaration with missing constraint.
+	// Provide a better error message than just "undeclared name: X".
+	if name, _ := e.(*syntax.Name); name != nil && check.lookup(name.Value) == nil {
+		check.errorf(name, "undeclared name %s for array length", name.Value)
+		return -1
+	}
+
 	var x operand
 	check.expr(&x, e)
 	if x.mode != constant_ {
@@ -436,6 +444,7 @@
 		}
 		return -1
 	}
+
 	if isUntyped(x.typ) || isInteger(x.typ) {
 		if val := constant.ToInt(x.val); val.Kind() == constant.Int {
 			if representableConst(val, check, Typ[Int], nil) {
@@ -447,6 +456,7 @@
 			}
 		}
 	}
+
 	check.errorf(&x, "array length %s must be integer", &x)
 	return -1
 }
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
index a1e5b36..bb69f0d 100644
--- a/src/cmd/compile/internal/types2/unify.go
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -428,9 +428,6 @@
 
 	case *Named:
 		if y, ok := y.(*Named); ok {
-			x.expand(nil)
-			y.expand(nil)
-
 			xargs := x.targs.list()
 			yargs := y.targs.list()
 
diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go
index a615b4c..af3ab97 100644
--- a/src/cmd/compile/internal/types2/universe.go
+++ b/src/cmd/compile/internal/types2/universe.go
@@ -88,7 +88,7 @@
 		res := NewVar(nopos, nil, "", Typ[String])
 		sig := NewSignature(nil, nil, NewTuple(res), false)
 		err := NewFunc(nopos, nil, "Error", sig)
-		ityp := &Interface{obj, []*Func{err}, nil, nil, true, nil}
+		ityp := &Interface{nil, obj, []*Func{err}, nil, nil, true, nil}
 		computeInterfaceTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset
 		typ := NewNamed(obj, ityp, nil)
 		sig.recv = NewVar(nopos, nil, "", typ)
@@ -99,7 +99,7 @@
 	{
 		obj := NewTypeName(nopos, nil, "comparable", nil)
 		obj.setColor(black)
-		ityp := &Interface{obj, nil, nil, nil, true, &_TypeSet{true, nil, allTermlist}}
+		ityp := &Interface{nil, obj, nil, nil, nil, true, &_TypeSet{true, nil, allTermlist}}
 		NewNamed(obj, ityp, nil)
 		def(obj)
 	}
diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go
index d701d54..5d69fc3 100644
--- a/src/cmd/compile/internal/walk/convert.go
+++ b/src/cmd/compile/internal/walk/convert.go
@@ -25,9 +25,6 @@
 		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)
 		}
@@ -414,41 +411,6 @@
 	return n
 }
 
-func walkCheckPtrAlignment(n *ir.ConvExpr, init *ir.Nodes, se *ir.SliceExpr) ir.Node {
-	if !n.Type().IsPtr() {
-		base.Fatalf("expected pointer type: %v", n.Type())
-	}
-	elem := n.Type().Elem()
-	var count ir.Node
-	if se != nil {
-		count = se.Max
-	}
-	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)
-	checkPtrCall := mkcall("checkptrAlignment", nil, init, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]), reflectdata.TypePtr(elem), typecheck.Conv(count, types.Types[types.TUINTPTR]))
-	if se != nil {
-		se.CheckPtrCall = checkPtrCall
-	} else {
-		init.Append(checkPtrCall)
-	}
-	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
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
index ed2d685..e5bf6cf 100644
--- a/src/cmd/compile/internal/walk/expr.go
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -807,15 +807,7 @@
 
 // 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.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].
@@ -823,9 +815,6 @@
 	}
 	n.High = walkExpr(n.High, init)
 	n.Max = walkExpr(n.Max, init)
-	if checkSlice {
-		n.X = walkCheckPtrAlignment(n.X.(*ir.ConvExpr), init, n)
-	}
 
 	if n.Op().IsSlice3() {
 		if n.Max != nil && n.Max.Op() == ir.OCAP && ir.SameSafeExpr(n.X, n.Max.(*ir.UnaryExpr).X) {
diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go
index 4de8858..7ac1f75 100644
--- a/src/cmd/compile/internal/walk/order.go
+++ b/src/cmd/compile/internal/walk/order.go
@@ -941,6 +941,12 @@
 					if colas {
 						if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n {
 							init = init[1:]
+
+							// iimport may have added a default initialization assignment,
+							// due to how it handles ODCL statements.
+							if len(init) > 0 && init[0].Op() == ir.OAS && init[0].(*ir.AssignStmt).X == n {
+								init = init[1:]
+							}
 						}
 						dcl := typecheck.Stmt(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
 						ncas.PtrInit().Append(dcl)
diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go
index 4581bca..f09e916 100644
--- a/src/cmd/compile/internal/walk/stmt.go
+++ b/src/cmd/compile/internal/walk/stmt.go
@@ -136,6 +136,14 @@
 
 	case ir.OTAILCALL:
 		n := n.(*ir.TailCallStmt)
+
+		var init ir.Nodes
+		n.Call.X = walkExpr(n.Call.X, &init)
+
+		if len(init) > 0 {
+			init.Append(n)
+			return ir.NewBlockStmt(n.Pos(), init)
+		}
 		return n
 
 	case ir.OINLMARK:
diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go
index 0b2ca3f..765051c 100644
--- a/src/cmd/compile/internal/wasm/ssa.go
+++ b/src/cmd/compile/internal/wasm/ssa.go
@@ -88,13 +88,7 @@
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
 
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.ARET)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
-
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 
 	case ssa.BlockDefer:
 		p := s.Prog(wasm.AGet)
@@ -122,7 +116,7 @@
 
 func ssaGenValue(s *ssagen.State, v *ssa.Value) {
 	switch v.Op {
-	case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall:
+	case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall, ssa.OpWasmLoweredTailCall:
 		s.PrepareCall(v)
 		if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == ir.Syms.Deferreturn {
 			// The runtime needs to inject jumps to
@@ -141,6 +135,9 @@
 			p := s.Prog(obj.ACALL)
 			p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
 			p.Pos = v.Pos
+			if v.Op == ssa.OpWasmLoweredTailCall {
+				p.As = obj.ARET
+			}
 		} else {
 			getValue64(s, v.Args[0])
 			p := s.Prog(obj.ACALL)
diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go
index a06fdbc..32e29f3 100644
--- a/src/cmd/compile/internal/x86/ssa.go
+++ b/src/cmd/compile/internal/x86/ssa.go
@@ -752,6 +752,8 @@
 
 	case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter:
 		s.Call(v)
+	case ssa.Op386CALLtail:
+		s.TailCall(v)
 	case ssa.Op386NEGL,
 		ssa.Op386BSWAPL,
 		ssa.Op386NOTL:
@@ -892,14 +894,9 @@
 			p.To.Type = obj.TYPE_BRANCH
 			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
 		}
-	case ssa.BlockExit:
+	case ssa.BlockExit, ssa.BlockRetJmp:
 	case ssa.BlockRet:
 		s.Prog(obj.ARET)
-	case ssa.BlockRetJmp:
-		p := s.Prog(obj.AJMP)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = b.Aux.(*obj.LSym)
 
 	case ssa.Block386EQF:
 		s.CombJump(b, next, &eqfJumps)
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 33a329e..39f016e 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -32,6 +32,7 @@
 	goos             string
 	goarm            string
 	go386            string
+	goamd64          string
 	gomips           string
 	gomips64         string
 	goppc64          string
@@ -145,6 +146,12 @@
 	}
 	go386 = b
 
+	b = os.Getenv("GOAMD64")
+	if b == "" {
+		b = "v1"
+	}
+	goamd64 = b
+
 	b = os.Getenv("GOMIPS")
 	if b == "" {
 		b = "hardfloat"
@@ -217,6 +224,7 @@
 
 	// For tools being invoked but also for os.ExpandEnv.
 	os.Setenv("GO386", go386)
+	os.Setenv("GOAMD64", goamd64)
 	os.Setenv("GOARCH", goarch)
 	os.Setenv("GOARM", goarm)
 	os.Setenv("GOHOSTARCH", gohostarch)
@@ -1181,6 +1189,9 @@
 	if goarch == "386" {
 		xprintf(format, "GO386", go386)
 	}
+	if goarch == "amd64" {
+		xprintf(format, "GOAMD64", goamd64)
+	}
 	if goarch == "mips" || goarch == "mipsle" {
 		xprintf(format, "GOMIPS", gomips)
 	}
diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go
index 54e935a..fdc1d25 100644
--- a/src/cmd/dist/buildruntime.go
+++ b/src/cmd/dist/buildruntime.go
@@ -60,6 +60,7 @@
 	fmt.Fprintf(&buf, "import \"runtime\"\n")
 	fmt.Fprintln(&buf)
 	fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
+	fmt.Fprintf(&buf, "const defaultGOAMD64 = `%s`\n", goamd64)
 	fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
 	fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
 	fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index a104b5c..dd4e96e 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -984,11 +984,6 @@
 		// linkmode=internal fails on dragonfly since errno is a TLS relocation.
 		return false
 	}
-	if gohostarch == "ppc64le" {
-		// linkmode=internal fails on ppc64le because cmd/link doesn't
-		// handle the TOC correctly (issue 15409).
-		return false
-	}
 	if goos == "android" {
 		return false
 	}
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index 05a118d..26be677 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -4,12 +4,15 @@
 
 require (
 	github.com/google/pprof v0.0.0-20210827144239-02619b876842
-	github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect
 	golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1
-	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
-	golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4
-	golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect
+	golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a
 	golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
 	golang.org/x/tools v0.1.6-0.20210904010709-360456621443
+)
+
+require (
+	github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect
+	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
+	golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 )
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index eebb44c..19bb1ee 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -9,8 +9,8 @@
 golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4 h1:7Qds88gNaRx0Dz/1wOwXlR7asekh1B1u26wEwN6FcEI=
-golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a h1:55PVa91KndtPGH2lus5l2gDZqoO/x+Oa5CV0lVf8Ij8=
+golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c=
 golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 18afaf1..7452269 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -705,14 +705,17 @@
 //
 // - All arguments must refer to packages in the same module at the same version.
 //
+// - Package path arguments must refer to main packages. Pattern arguments
+// will only match main packages.
+//
 // - No module is considered the "main" module. If the module containing
 // packages named on the command line has a go.mod file, it must not contain
 // directives (replace and exclude) that would cause it to be interpreted
 // differently than if it were the main module. The module must not require
 // a higher version of itself.
 //
-// - Package path arguments must refer to main packages. Pattern arguments
-// will only match main packages.
+// - Vendor directories are not used in any module. (Vendor directories are not
+// included in the module zip files downloaded by 'go install'.)
 //
 // If the arguments don't have version suffixes, "go install" may run in
 // module-aware mode or GOPATH mode, depending on the GO111MODULE environment
@@ -1989,6 +1992,10 @@
 // 	GO386
 // 		For GOARCH=386, how to implement floating point instructions.
 // 		Valid values are sse2 (default), softfloat.
+// 	GOAMD64
+// 		For GOARCH=amd64, the microarchitecture level for which to compile.
+// 		Valid values are v1 (default), v2, v3, v4.
+// 		See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels.
 // 	GOMIPS
 // 		For GOARCH=mips{,le}, whether to use floating point instructions.
 // 		Valid values are hardfloat (default), softfloat.
diff --git a/src/cmd/go/internal/base/tool.go b/src/cmd/go/internal/base/tool.go
index d0da65e..f927016 100644
--- a/src/cmd/go/internal/base/tool.go
+++ b/src/cmd/go/internal/base/tool.go
@@ -36,7 +36,7 @@
 	}
 	// Give a nice message if there is no tool with that name.
 	if _, err := os.Stat(toolPath); err != nil {
-		fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
+		fmt.Fprintf(os.Stderr, "go: no such tool %q\n", toolName)
 		SetExitStatus(2)
 		Exit()
 	}
diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go
index 307527c..a81ca7d 100644
--- a/src/cmd/go/internal/bug/bug.go
+++ b/src/cmd/go/internal/bug/bug.go
@@ -40,7 +40,7 @@
 
 func runBug(ctx context.Context, cmd *base.Command, args []string) {
 	if len(args) > 0 {
-		base.Fatalf("go bug: bug takes no arguments")
+		base.Fatalf("go: bug takes no arguments")
 	}
 	var buf bytes.Buffer
 	buf.WriteString(bugHeader)
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index ac2c70f..dd0e8cb 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -267,6 +267,7 @@
 	// Used in envcmd.MkEnv and build ID computations.
 	GOARM    = envOr("GOARM", fmt.Sprint(buildcfg.GOARM))
 	GO386    = envOr("GO386", buildcfg.GO386)
+	GOAMD64  = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
 	GOMIPS   = envOr("GOMIPS", buildcfg.GOMIPS)
 	GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64)
 	GOPPC64  = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
@@ -293,6 +294,8 @@
 		return "GOARM", GOARM
 	case "386":
 		return "GO386", GO386
+	case "amd64":
+		return "GOAMD64", GOAMD64
 	case "mips", "mipsle":
 		return "GOMIPS", GOMIPS
 	case "mips64", "mips64le":
diff --git a/src/cmd/go/internal/clean/clean.go b/src/cmd/go/internal/clean/clean.go
index 13d6961..518473c 100644
--- a/src/cmd/go/internal/clean/clean.go
+++ b/src/cmd/go/internal/clean/clean.go
@@ -148,7 +148,7 @@
 						// This also mimics what os.RemoveAll(dir) would do.
 						if err := os.RemoveAll(d); err != nil && !printedErrors {
 							printedErrors = true
-							base.Errorf("go clean -cache: %v", err)
+							base.Errorf("go: %v", err)
 						}
 					}
 				}
@@ -161,7 +161,7 @@
 			if !cfg.BuildN {
 				if err := os.RemoveAll(logFile); err != nil && !printedErrors {
 					printedErrors = true
-					base.Errorf("go clean -cache: %v", err)
+					base.Errorf("go: %v", err)
 				}
 			}
 		}
@@ -191,7 +191,7 @@
 			}
 			if err != nil {
 				if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) {
-					base.Errorf("go clean -testcache: %v", err)
+					base.Errorf("go: %v", err)
 				}
 			}
 		}
@@ -199,14 +199,14 @@
 
 	if cleanModcache {
 		if cfg.GOMODCACHE == "" {
-			base.Fatalf("go clean -modcache: no module cache")
+			base.Fatalf("go: cannot clean -modcache without a module cache")
 		}
 		if cfg.BuildN || cfg.BuildX {
 			b.Showcmd("", "rm -rf %s", cfg.GOMODCACHE)
 		}
 		if !cfg.BuildN {
 			if err := modfetch.RemoveAll(cfg.GOMODCACHE); err != nil {
-				base.Errorf("go clean -modcache: %v", err)
+				base.Errorf("go: %v", err)
 			}
 		}
 	}
@@ -261,7 +261,7 @@
 	}
 	dirs, err := os.ReadDir(p.Dir)
 	if err != nil {
-		base.Errorf("go clean %s: %v", p.Dir, err)
+		base.Errorf("go: %s: %v", p.Dir, err)
 		return
 	}
 
@@ -350,7 +350,7 @@
 					}
 				}
 				if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil {
-					base.Errorf("go clean: %v", err)
+					base.Errorf("go: %v", err)
 				}
 			}
 			continue
@@ -402,5 +402,5 @@
 			return
 		}
 	}
-	base.Errorf("go clean: %v", err)
+	base.Errorf("go: %v", err)
 }
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index d23d539..1eb7734 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -193,13 +193,13 @@
 
 func runEnv(ctx context.Context, cmd *base.Command, args []string) {
 	if *envJson && *envU {
-		base.Fatalf("go env: cannot use -json with -u")
+		base.Fatalf("go: cannot use -json with -u")
 	}
 	if *envJson && *envW {
-		base.Fatalf("go env: cannot use -json with -w")
+		base.Fatalf("go: cannot use -json with -w")
 	}
 	if *envU && *envW {
-		base.Fatalf("go env: cannot use -u with -w")
+		base.Fatalf("go: cannot use -u with -w")
 	}
 
 	// Handle 'go env -w' and 'go env -u' before calling buildcfg.Check,
@@ -277,7 +277,7 @@
 func runEnvW(args []string) {
 	// Process and sanity-check command line.
 	if len(args) == 0 {
-		base.Fatalf("go env -w: no KEY=VALUE arguments given")
+		base.Fatalf("go: no KEY=VALUE arguments given")
 	}
 	osEnv := make(map[string]string)
 	for _, e := range cfg.OrigEnv {
@@ -289,14 +289,14 @@
 	for _, arg := range args {
 		i := strings.Index(arg, "=")
 		if i < 0 {
-			base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
+			base.Fatalf("go: arguments must be KEY=VALUE: invalid argument: %s", arg)
 		}
 		key, val := arg[:i], arg[i+1:]
 		if err := checkEnvWrite(key, val); err != nil {
-			base.Fatalf("go env -w: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		if _, ok := add[key]; ok {
-			base.Fatalf("go env -w: multiple values for key: %s", key)
+			base.Fatalf("go: multiple values for key: %s", key)
 		}
 		add[key] = val
 		if osVal := osEnv[key]; osVal != "" && osVal != val {
@@ -305,13 +305,13 @@
 	}
 
 	if err := checkBuildConfig(add, nil); err != nil {
-		base.Fatalf("go env -w: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 
 	gotmp, okGOTMP := add["GOTMPDIR"]
 	if okGOTMP {
 		if !filepath.IsAbs(gotmp) && gotmp != "" {
-			base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
+			base.Fatalf("go: GOTMPDIR must be an absolute path")
 		}
 	}
 
@@ -321,18 +321,18 @@
 func runEnvU(args []string) {
 	// Process and sanity-check command line.
 	if len(args) == 0 {
-		base.Fatalf("go env -u: no arguments given")
+		base.Fatalf("go: 'go env -u' requires an argument")
 	}
 	del := make(map[string]bool)
 	for _, arg := range args {
 		if err := checkEnvWrite(arg, ""); err != nil {
-			base.Fatalf("go env -u: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		del[arg] = true
 	}
 
 	if err := checkBuildConfig(nil, del); err != nil {
-		base.Fatalf("go env -u: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 
 	updateEnvFile(nil, del)
@@ -416,7 +416,7 @@
 	enc := json.NewEncoder(os.Stdout)
 	enc.SetIndent("", "\t")
 	if err := enc.Encode(m); err != nil {
-		base.Fatalf("go env -json: %s", err)
+		base.Fatalf("go: %s", err)
 	}
 }
 
@@ -494,11 +494,11 @@
 func updateEnvFile(add map[string]string, del map[string]bool) {
 	file, err := cfg.EnvFile()
 	if file == "" {
-		base.Fatalf("go env: cannot find go env config: %v", err)
+		base.Fatalf("go: cannot find go env config: %v", err)
 	}
 	data, err := os.ReadFile(file)
 	if err != nil && (!os.IsNotExist(err) || len(add) == 0) {
-		base.Fatalf("go env: reading go env config: %v", err)
+		base.Fatalf("go: reading go env config: %v", err)
 	}
 
 	lines := strings.SplitAfter(string(data), "\n")
@@ -556,7 +556,7 @@
 		os.MkdirAll(filepath.Dir(file), 0777)
 		err = os.WriteFile(file, data, 0666)
 		if err != nil {
-			base.Fatalf("go env: writing go env config: %v", err)
+			base.Fatalf("go: writing go env config: %v", err)
 		}
 	}
 }
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index 075594b..b79d3ba 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -114,16 +114,16 @@
 func runGet(ctx context.Context, cmd *base.Command, args []string) {
 	if cfg.ModulesEnabled {
 		// Should not happen: main.go should install the separate module-enabled get code.
-		base.Fatalf("go get: modules not implemented")
+		base.Fatalf("go: modules not implemented")
 	}
 
 	work.BuildInit()
 
 	if *getF && !*getU {
-		base.Fatalf("go get: cannot use -f flag without -u")
+		base.Fatalf("go: cannot use -f flag without -u")
 	}
 	if *getInsecure {
-		base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead")
+		base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead")
 	}
 
 	// Disable any prompting for passwords by Git itself.
@@ -214,11 +214,11 @@
 		// if the argument has no slash or refers to an existing file.
 		if strings.HasSuffix(arg, ".go") {
 			if !strings.Contains(arg, "/") {
-				base.Errorf("go get %s: arguments must be package or module paths", arg)
+				base.Errorf("go: %s: arguments must be package or module paths", arg)
 				continue
 			}
 			if fi, err := os.Stat(arg); err == nil && !fi.IsDir() {
-				base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg)
+				base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", arg)
 			}
 		}
 	}
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index 490ff1f..749dcf1 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -592,6 +592,10 @@
 	GO386
 		For GOARCH=386, how to implement floating point instructions.
 		Valid values are sse2 (default), softfloat.
+	GOAMD64
+		For GOARCH=amd64, the microarchitecture level for which to compile.
+		Valid values are v1 (default), v2, v3, v4.
+		See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels.
 	GOMIPS
 		For GOARCH=mips{,le}, whether to use floating point instructions.
 		Valid values are hardfloat (default), softfloat.
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index 704d61e..821e622 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -427,12 +427,12 @@
 		}
 
 		if modload.Init(); !modload.Enabled() {
-			base.Fatalf("go list -m: not using modules")
+			base.Fatalf("go: list -m cannot be used with GO111MODULE=off")
 		}
 
 		modload.LoadModFile(ctx) // Sets cfg.BuildMod as a side-effect.
 		if cfg.BuildMod == "vendor" {
-			const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)"
+			const actionDisabledFormat = "go: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)"
 
 			if *listVersions {
 				base.Fatalf(actionDisabledFormat, "determine available versions")
@@ -471,11 +471,11 @@
 		if !*listE {
 			for _, m := range mods {
 				if m.Error != nil {
-					base.Errorf("go list -m: %v", m.Error.Err)
+					base.Errorf("go: %v", m.Error.Err)
 				}
 			}
 			if err != nil {
-				base.Errorf("go list -m: %v", err)
+				base.Errorf("go: %v", err)
 			}
 			base.ExitIfErrors()
 		}
@@ -711,7 +711,7 @@
 			}
 			rmods, err := modload.ListModules(ctx, args, mode)
 			if err != nil && !*listE {
-				base.Errorf("go list -retracted: %v", err)
+				base.Errorf("go: %v", err)
 			}
 			for i, arg := range args {
 				rmod := rmods[i]
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index 38e15cc..317053d 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -2684,10 +2684,7 @@
 		if fi.IsDir() {
 			base.Fatalf("%s is a directory, should be a Go file", file)
 		}
-		dir1, _ := filepath.Split(file)
-		if dir1 == "" {
-			dir1 = "./"
-		}
+		dir1 := filepath.Dir(file)
 		if dir == "" {
 			dir = dir1
 		} else if dir != dir1 {
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go
index ff56d05..0f64785 100644
--- a/src/cmd/go/internal/modcmd/download.go
+++ b/src/cmd/go/internal/modcmd/download.go
@@ -87,7 +87,7 @@
 	// Check whether modules are enabled and whether we're in a module.
 	modload.ForceUseModules = true
 	if !modload.HasModRoot() && len(args) == 0 {
-		base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
+		base.Fatalf("go: no modules specified (see 'go help mod download')")
 	}
 	haveExplicitArgs := len(args) > 0
 	if !haveExplicitArgs {
@@ -106,7 +106,7 @@
 		for _, arg := range args {
 			switch arg {
 			case mainModule.Path, targetAtUpgrade, targetAtPatch:
-				os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n")
+				os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n")
 			}
 		}
 	}
@@ -192,7 +192,7 @@
 		for _, m := range mods {
 			b, err := json.MarshalIndent(m, "", "\t")
 			if err != nil {
-				base.Fatalf("go mod download: %v", err)
+				base.Fatalf("go: %v", err)
 			}
 			os.Stdout.Write(append(b, '\n'))
 			if m.Error != "" {
@@ -202,7 +202,7 @@
 	} else {
 		for _, m := range mods {
 			if m.Error != "" {
-				base.Errorf("go mod download: %v", m.Error)
+				base.Errorf("go: %v", m.Error)
 			}
 		}
 		base.ExitIfErrors()
@@ -222,6 +222,6 @@
 	// (after we've written the checksums for the modules that were downloaded
 	// successfully).
 	if infosErr != nil {
-		base.Errorf("go mod download: %v", infosErr)
+		base.Errorf("go: %v", infosErr)
 	}
 }
diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go
index bb3d521..e5182a9 100644
--- a/src/cmd/go/internal/modcmd/edit.go
+++ b/src/cmd/go/internal/modcmd/edit.go
@@ -171,15 +171,15 @@
 			len(edits) > 0
 
 	if !anyFlags {
-		base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').")
+		base.Fatalf("go: no flags specified (see 'go help mod edit').")
 	}
 
 	if *editJSON && *editPrint {
-		base.Fatalf("go mod edit: cannot use both -json and -print")
+		base.Fatalf("go: cannot use both -json and -print")
 	}
 
 	if len(args) > 1 {
-		base.Fatalf("go mod edit: too many arguments")
+		base.Fatalf("go: too many arguments")
 	}
 	var gomod string
 	if len(args) == 1 {
@@ -190,7 +190,7 @@
 
 	if *editModule != "" {
 		if err := module.CheckImportPath(*editModule); err != nil {
-			base.Fatalf("go mod: invalid -module: %v", err)
+			base.Fatalf("go: invalid -module: %v", err)
 		}
 	}
 
@@ -264,15 +264,15 @@
 func parsePathVersion(flag, arg string) (path, version string) {
 	i := strings.Index(arg, "@")
 	if i < 0 {
-		base.Fatalf("go mod: -%s=%s: need path@version", flag, arg)
+		base.Fatalf("go: -%s=%s: need path@version", flag, arg)
 	}
 	path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
 	if err := module.CheckImportPath(path); err != nil {
-		base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
+		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
 	}
 
 	if !allowedVersionArg(version) {
-		base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version)
+		base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version)
 	}
 
 	return path, version
@@ -281,11 +281,11 @@
 // parsePath parses -flag=arg expecting arg to be path (not path@version).
 func parsePath(flag, arg string) (path string) {
 	if strings.Contains(arg, "@") {
-		base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg)
+		base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg)
 	}
 	path = arg
 	if err := module.CheckImportPath(path); err != nil {
-		base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
+		base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
 	}
 	return path
 }
@@ -350,7 +350,7 @@
 	path, version := parsePathVersion("require", arg)
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.AddRequire(path, version); err != nil {
-			base.Fatalf("go mod: -require=%s: %v", arg, err)
+			base.Fatalf("go: -require=%s: %v", arg, err)
 		}
 	})
 }
@@ -360,7 +360,7 @@
 	path := parsePath("droprequire", arg)
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.DropRequire(path); err != nil {
-			base.Fatalf("go mod: -droprequire=%s: %v", arg, err)
+			base.Fatalf("go: -droprequire=%s: %v", arg, err)
 		}
 	})
 }
@@ -370,7 +370,7 @@
 	path, version := parsePathVersion("exclude", arg)
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.AddExclude(path, version); err != nil {
-			base.Fatalf("go mod: -exclude=%s: %v", arg, err)
+			base.Fatalf("go: -exclude=%s: %v", arg, err)
 		}
 	})
 }
@@ -380,7 +380,7 @@
 	path, version := parsePathVersion("dropexclude", arg)
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.DropExclude(path, version); err != nil {
-			base.Fatalf("go mod: -dropexclude=%s: %v", arg, err)
+			base.Fatalf("go: -dropexclude=%s: %v", arg, err)
 		}
 	})
 }
@@ -389,27 +389,27 @@
 func flagReplace(arg string) {
 	var i int
 	if i = strings.Index(arg, "="); i < 0 {
-		base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
+		base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
 	}
 	old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
 	if strings.HasPrefix(new, ">") {
-		base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg)
+		base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg)
 	}
 	oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
 	if err != nil {
-		base.Fatalf("go mod: -replace=%s: %v", arg, err)
+		base.Fatalf("go: -replace=%s: %v", arg, err)
 	}
 	newPath, newVersion, err := parsePathVersionOptional("new", new, true)
 	if err != nil {
-		base.Fatalf("go mod: -replace=%s: %v", arg, err)
+		base.Fatalf("go: -replace=%s: %v", arg, err)
 	}
 	if newPath == new && !modfile.IsDirectoryPath(new) {
-		base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg)
+		base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg)
 	}
 
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
-			base.Fatalf("go mod: -replace=%s: %v", arg, err)
+			base.Fatalf("go: -replace=%s: %v", arg, err)
 		}
 	})
 }
@@ -418,11 +418,11 @@
 func flagDropReplace(arg string) {
 	path, version, err := parsePathVersionOptional("old", arg, true)
 	if err != nil {
-		base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
+		base.Fatalf("go: -dropreplace=%s: %v", arg, err)
 	}
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.DropReplace(path, version); err != nil {
-			base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
+			base.Fatalf("go: -dropreplace=%s: %v", arg, err)
 		}
 	})
 }
@@ -431,11 +431,11 @@
 func flagRetract(arg string) {
 	vi, err := parseVersionInterval(arg)
 	if err != nil {
-		base.Fatalf("go mod: -retract=%s: %v", arg, err)
+		base.Fatalf("go: -retract=%s: %v", arg, err)
 	}
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.AddRetract(vi, ""); err != nil {
-			base.Fatalf("go mod: -retract=%s: %v", arg, err)
+			base.Fatalf("go: -retract=%s: %v", arg, err)
 		}
 	})
 }
@@ -444,11 +444,11 @@
 func flagDropRetract(arg string) {
 	vi, err := parseVersionInterval(arg)
 	if err != nil {
-		base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
+		base.Fatalf("go: -dropretract=%s: %v", arg, err)
 	}
 	edits = append(edits, func(f *modfile.File) {
 		if err := f.DropRetract(vi); err != nil {
-			base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
+			base.Fatalf("go: -dropretract=%s: %v", arg, err)
 		}
 	})
 }
diff --git a/src/cmd/go/internal/modcmd/editwork.go b/src/cmd/go/internal/modcmd/editwork.go
index 29895b1..235c655 100644
--- a/src/cmd/go/internal/modcmd/editwork.go
+++ b/src/cmd/go/internal/modcmd/editwork.go
@@ -118,15 +118,15 @@
 			len(workedits) > 0
 
 	if !anyFlags {
-		base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').")
+		base.Fatalf("go: no flags specified (see 'go help mod edit').")
 	}
 
 	if *editworkJSON && *editworkPrint {
-		base.Fatalf("go mod edit: cannot use both -json and -print")
+		base.Fatalf("go: cannot use both -json and -print")
 	}
 
 	if len(args) > 1 {
-		base.Fatalf("go mod edit: too many arguments")
+		base.Fatalf("go: 'go mod editwork' accepts at most one argument")
 	}
 	var gowork string
 	if len(args) == 1 {
@@ -199,7 +199,7 @@
 		}
 		f.AddDirectory(modload.ToDirectoryPath(arg), modulePath)
 		if err := f.AddDirectory(modload.ToDirectoryPath(arg), ""); err != nil {
-			base.Fatalf("go mod: -directory=%s: %v", arg, err)
+			base.Fatalf("go: -directory=%s: %v", arg, err)
 		}
 	})
 }
@@ -208,7 +208,7 @@
 func flagEditworkDropDirectory(arg string) {
 	workedits = append(workedits, func(f *modfile.WorkFile) {
 		if err := f.DropDirectory(modload.ToDirectoryPath(arg)); err != nil {
-			base.Fatalf("go mod: -dropdirectory=%s: %v", arg, err)
+			base.Fatalf("go: -dropdirectory=%s: %v", arg, err)
 		}
 	})
 }
@@ -217,27 +217,27 @@
 func flagEditworkReplace(arg string) {
 	var i int
 	if i = strings.Index(arg, "="); i < 0 {
-		base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
+		base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
 	}
 	old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
 	if strings.HasPrefix(new, ">") {
-		base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg)
+		base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg)
 	}
 	oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
 	if err != nil {
-		base.Fatalf("go mod: -replace=%s: %v", arg, err)
+		base.Fatalf("go: -replace=%s: %v", arg, err)
 	}
 	newPath, newVersion, err := parsePathVersionOptional("new", new, true)
 	if err != nil {
-		base.Fatalf("go mod: -replace=%s: %v", arg, err)
+		base.Fatalf("go: -replace=%s: %v", arg, err)
 	}
 	if newPath == new && !modfile.IsDirectoryPath(new) {
-		base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg)
+		base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg)
 	}
 
 	workedits = append(workedits, func(f *modfile.WorkFile) {
 		if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
-			base.Fatalf("go mod: -replace=%s: %v", arg, err)
+			base.Fatalf("go: -replace=%s: %v", arg, err)
 		}
 	})
 }
@@ -246,11 +246,11 @@
 func flagEditworkDropReplace(arg string) {
 	path, version, err := parsePathVersionOptional("old", arg, true)
 	if err != nil {
-		base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
+		base.Fatalf("go: -dropreplace=%s: %v", arg, err)
 	}
 	workedits = append(workedits, func(f *modfile.WorkFile) {
 		if err := f.DropReplace(path, version); err != nil {
-			base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
+			base.Fatalf("go: -dropreplace=%s: %v", arg, err)
 		}
 	})
 }
diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go
index 2cbabae..9b6aa1f 100644
--- a/src/cmd/go/internal/modcmd/graph.go
+++ b/src/cmd/go/internal/modcmd/graph.go
@@ -49,7 +49,7 @@
 	modload.InitWorkfile()
 
 	if len(args) > 0 {
-		base.Fatalf("go mod graph: graph takes no arguments")
+		base.Fatalf("go: 'go mod graph' accepts no arguments")
 	}
 	modload.ForceUseModules = true
 	modload.RootMode = modload.NeedRoot
diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go
index 958c306..bc4620a 100644
--- a/src/cmd/go/internal/modcmd/init.go
+++ b/src/cmd/go/internal/modcmd/init.go
@@ -39,7 +39,7 @@
 
 func runInit(ctx context.Context, cmd *base.Command, args []string) {
 	if len(args) > 1 {
-		base.Fatalf("go mod init: too many arguments")
+		base.Fatalf("go: 'go mod init' accepts at most one argument")
 	}
 	var modPath string
 	if len(args) == 1 {
diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go
index fe25507..57d303a 100644
--- a/src/cmd/go/internal/modcmd/tidy.go
+++ b/src/cmd/go/internal/modcmd/tidy.go
@@ -95,7 +95,7 @@
 
 func runTidy(ctx context.Context, cmd *base.Command, args []string) {
 	if len(args) > 0 {
-		base.Fatalf("go mod tidy: no arguments allowed")
+		base.Fatalf("go: 'go mod tidy' accepts no arguments")
 	}
 
 	// Tidy aims to make 'go test' reproducible for any package in 'all', so we
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
index 1effcea..92348b8 100644
--- a/src/cmd/go/internal/modcmd/vendor.go
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -59,7 +59,7 @@
 
 func runVendor(ctx context.Context, cmd *base.Command, args []string) {
 	if len(args) != 0 {
-		base.Fatalf("go mod vendor: vendor takes no arguments")
+		base.Fatalf("go: 'go mod vendor' accepts no arguments")
 	}
 	modload.ForceUseModules = true
 	modload.RootMode = modload.NeedRoot
@@ -76,7 +76,7 @@
 
 	vdir := filepath.Join(modload.VendorDir())
 	if err := os.RemoveAll(vdir); err != nil {
-		base.Fatalf("go mod vendor: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 
 	modpkgs := make(map[module.Version][]string)
@@ -178,11 +178,11 @@
 	}
 
 	if err := os.MkdirAll(vdir, 0777); err != nil {
-		base.Fatalf("go mod vendor: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 
 	if err := os.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
-		base.Fatalf("go mod vendor: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 }
 
@@ -250,7 +250,7 @@
 	embedPatterns := str.StringList(bp.EmbedPatterns, bp.TestEmbedPatterns, bp.XTestEmbedPatterns)
 	embeds, err := load.ResolveEmbed(bp.Dir, embedPatterns)
 	if err != nil {
-		base.Fatalf("go mod vendor: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 	for _, embed := range embeds {
 		embedDst := filepath.Join(dst, embed)
@@ -261,21 +261,21 @@
 		// Copy the file as is done by copyDir below.
 		r, err := os.Open(filepath.Join(src, embed))
 		if err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		if err := os.MkdirAll(filepath.Dir(embedDst), 0777); err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		w, err := os.Create(embedDst)
 		if err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		if _, err := io.Copy(w, r); err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		r.Close()
 		if err := w.Close(); err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 	}
 }
@@ -354,7 +354,7 @@
 	if strings.HasSuffix(info.Name(), ".go") {
 		f, err := fsys.Open(filepath.Join(dir, info.Name()))
 		if err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		defer f.Close()
 
@@ -376,10 +376,10 @@
 func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, copiedFiles map[string]bool) {
 	files, err := os.ReadDir(src)
 	if err != nil {
-		base.Fatalf("go mod vendor: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 	if err := os.MkdirAll(dst, 0777); err != nil {
-		base.Fatalf("go mod vendor: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 	for _, file := range files {
 		if file.IsDir() || !file.Type().IsRegular() || !match(src, file) {
@@ -388,20 +388,20 @@
 		copiedFiles[file.Name()] = true
 		r, err := os.Open(filepath.Join(src, file.Name()))
 		if err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		dstPath := filepath.Join(dst, file.Name())
 		copiedFiles[dstPath] = true
 		w, err := os.Create(dstPath)
 		if err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		if _, err := io.Copy(w, r); err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		r.Close()
 		if err := w.Close(); err != nil {
-			base.Fatalf("go mod vendor: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 	}
 }
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
index 14c4d76..3f0c005 100644
--- a/src/cmd/go/internal/modcmd/verify.go
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -47,7 +47,7 @@
 
 	if len(args) != 0 {
 		// NOTE(rsc): Could take a module pattern.
-		base.Fatalf("go mod verify: verify takes no arguments")
+		base.Fatalf("go: verify takes no arguments")
 	}
 	modload.ForceUseModules = true
 	modload.RootMode = modload.NeedRoot
diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go
index eef5fa5..ed5e9d7 100644
--- a/src/cmd/go/internal/modcmd/why.go
+++ b/src/cmd/go/internal/modcmd/why.go
@@ -80,13 +80,13 @@
 	if *whyM {
 		for _, arg := range args {
 			if strings.Contains(arg, "@") {
-				base.Fatalf("go mod why: module query not allowed")
+				base.Fatalf("go: %s: 'go mod why' requires a module path, not a version query", arg)
 			}
 		}
 
 		mods, err := modload.ListModules(ctx, args, 0)
 		if err != nil {
-			base.Fatalf("go mod why: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 
 		byModule := make(map[module.Version][]string)
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index 37912ce..db07624 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -263,19 +263,19 @@
 	case "", "upgrade", "patch":
 		// ok
 	default:
-		base.Fatalf("go get: unknown upgrade flag -u=%s", getU.rawVersion)
+		base.Fatalf("go: unknown upgrade flag -u=%s", getU.rawVersion)
 	}
 	if *getF {
-		fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n")
+		fmt.Fprintf(os.Stderr, "go: -f flag is a no-op when using modules\n")
 	}
 	if *getFix {
-		fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n")
+		fmt.Fprintf(os.Stderr, "go: -fix flag is a no-op when using modules\n")
 	}
 	if *getM {
-		base.Fatalf("go get: -m flag is no longer supported; consider -d to skip building packages")
+		base.Fatalf("go: -m flag is no longer supported; consider -d to skip building packages")
 	}
 	if *getInsecure {
-		base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead")
+		base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead")
 	}
 
 	// Do not allow any updating of go.mod until we've applied
@@ -397,7 +397,7 @@
 			}
 		}
 		if haveExternalExe {
-			fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.")
+			fmt.Fprint(os.Stderr, "go: installing executables with 'go get' in module mode is deprecated.")
 			var altMsg string
 			if modload.HasModRoot() {
 				altMsg = `
@@ -442,7 +442,7 @@
 	for _, arg := range search.CleanPatterns(rawArgs) {
 		q, err := newQuery(arg)
 		if err != nil {
-			base.Errorf("go get: %v", err)
+			base.Errorf("go: %v", err)
 			continue
 		}
 
@@ -457,11 +457,11 @@
 		// if the argument has no version and either has no slash or refers to an existing file.
 		if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" {
 			if !strings.Contains(q.raw, "/") {
-				base.Errorf("go get %s: arguments must be package or module paths", q.raw)
+				base.Errorf("go: %s: arguments must be package or module paths", q.raw)
 				continue
 			}
 			if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() {
-				base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", q.raw)
+				base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", q.raw)
 				continue
 			}
 		}
@@ -743,7 +743,7 @@
 
 			if len(match.Pkgs) == 0 {
 				if q.raw == "" || q.raw == "." {
-					return errSet(fmt.Errorf("no package in current directory"))
+					return errSet(fmt.Errorf("no package to get in current directory"))
 				}
 				if !q.isWildcard() {
 					modload.MustHaveModRoot()
@@ -1342,7 +1342,7 @@
 	var tentative []module.Version
 	for _, cs := range upgrades {
 		if cs.err != nil {
-			base.Errorf("go get: %v", cs.err)
+			base.Errorf("go: %v", cs.err)
 			continue
 		}
 
@@ -1735,13 +1735,13 @@
 	})
 	for _, c := range sortedChanges {
 		if c.old == "" {
-			fmt.Fprintf(os.Stderr, "go get: added %s %s\n", c.path, c.new)
+			fmt.Fprintf(os.Stderr, "go: added %s %s\n", c.path, c.new)
 		} else if c.new == "none" || c.new == "" {
-			fmt.Fprintf(os.Stderr, "go get: removed %s %s\n", c.path, c.old)
+			fmt.Fprintf(os.Stderr, "go: removed %s %s\n", c.path, c.old)
 		} else if semver.Compare(c.new, c.old) > 0 {
-			fmt.Fprintf(os.Stderr, "go get: upgraded %s %s => %s\n", c.path, c.old, c.new)
+			fmt.Fprintf(os.Stderr, "go: upgraded %s %s => %s\n", c.path, c.old, c.new)
 		} else {
-			fmt.Fprintf(os.Stderr, "go get: downgraded %s %s => %s\n", c.path, c.old, c.new)
+			fmt.Fprintf(os.Stderr, "go: downgraded %s %s => %s\n", c.path, c.old, c.new)
 		}
 	}
 
@@ -1800,7 +1800,7 @@
 	if err != nil {
 		var constraint *modload.ConstraintError
 		if !errors.As(err, &constraint) {
-			base.Errorf("go get: %v", err)
+			base.Errorf("go: %v", err)
 			return false
 		}
 
@@ -1812,7 +1812,7 @@
 			return rv.reason.ResolvedString(module.Version{Path: m.Path, Version: rv.version})
 		}
 		for _, c := range constraint.Conflicts {
-			base.Errorf("go get: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint))
+			base.Errorf("go: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint))
 		}
 		return false
 	}
diff --git a/src/cmd/go/internal/modget/query.go b/src/cmd/go/internal/modget/query.go
index 7604190..d7341e7 100644
--- a/src/cmd/go/internal/modget/query.go
+++ b/src/cmd/go/internal/modget/query.go
@@ -284,21 +284,21 @@
 	patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:;)\"`]|$)")
 	if patternRE.MatchString(errStr) {
 		if q.rawVersion == "" {
-			base.Errorf("go get: %s", errStr)
+			base.Errorf("go: %s", errStr)
 			return
 		}
 
 		versionRE := regexp.MustCompile("(?m)(?:[ @(\"`]|^)" + regexp.QuoteMeta(q.version) + "(?:[ :;)\"`]|$)")
 		if versionRE.MatchString(errStr) {
-			base.Errorf("go get: %s", errStr)
+			base.Errorf("go: %s", errStr)
 			return
 		}
 	}
 
 	if qs := q.String(); qs != "" {
-		base.Errorf("go get %s: %s", qs, errStr)
+		base.Errorf("go: %s: %s", qs, errStr)
 	} else {
-		base.Errorf("go get: %s", errStr)
+		base.Errorf("go: %s", errStr)
 	}
 }
 
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
index 777e29a..da9e640 100644
--- a/src/cmd/go/internal/modload/buildlist.go
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -591,7 +591,7 @@
 // 	   selected at the same version or is upgraded by the dependencies of a
 // 	   root.
 //
-// If any module that provided a package has been upgraded above its previous,
+// If any module that provided a package has been upgraded above its previous
 // version, the caller may need to reload and recompute the package graph.
 //
 // To ensure that the loading process eventually converges, the caller should
@@ -980,17 +980,37 @@
 	return true
 }
 
-// tidyUnprunedRoots returns a minimal set of root requirements that maintains the
-// selected version of every module that provided a package in pkgs, and
-// includes the selected version of every such module in direct as a root.
+// tidyUnprunedRoots returns a minimal set of root requirements that maintains
+// the selected version of every module that provided or lexically could have
+// provided a package in pkgs, and includes the selected version of every such
+// module in direct as a root.
 func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
 	var (
+		// keep is a set of of modules that provide packages or are needed to
+		// disambiguate imports.
 		keep     []module.Version
 		keptPath = map[string]bool{}
-	)
-	var (
-		rootPaths   []string // module paths that should be included as roots
+
+		// rootPaths is a list of module paths that provide packages directly
+		// imported from the main module. They should be included as roots.
+		rootPaths   []string
 		inRootPaths = map[string]bool{}
+
+		// altMods is a set of paths of modules that lexically could have provided
+		// imported packages. It may be okay to remove these from the list of
+		// explicit requirements if that removes them from the module graph. If they
+		// are present in the module graph reachable from rootPaths, they must not
+		// be at a lower version. That could cause a missing sum error or a new
+		// import ambiguity.
+		//
+		// For example, suppose a developer rewrites imports from example.com/m to
+		// example.com/m/v2, then runs 'go mod tidy'. Tidy may delete the
+		// requirement on example.com/m if there is no other transitive requirement
+		// on it. However, if example.com/m were downgraded to a version not in
+		// go.sum, when package example.com/m/v2/p is loaded, we'd get an error
+		// trying to disambiguate the import, since we can't check example.com/m
+		// without its sum. See #47738.
+		altMods = map[string]string{}
 	)
 	for _, pkg := range pkgs {
 		if !pkg.fromExternalModule() {
@@ -1004,12 +1024,44 @@
 				inRootPaths[m.Path] = true
 			}
 		}
+		for _, m := range pkg.altMods {
+			altMods[m.Path] = m.Version
+		}
 	}
 
-	min, err := mvs.Req(mainModule, rootPaths, &mvsReqs{roots: keep})
+	// Construct a build list with a minimal set of roots.
+	// This may remove or downgrade modules in altMods.
+	reqs := &mvsReqs{roots: keep}
+	min, err := mvs.Req(mainModule, rootPaths, reqs)
 	if err != nil {
 		return nil, err
 	}
+	buildList, err := mvs.BuildList([]module.Version{mainModule}, reqs)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check if modules in altMods were downgraded but not removed.
+	// If so, add them to roots, which will retain an "// indirect" requirement
+	// in go.mod. See comment on altMods above.
+	keptAltMod := false
+	for _, m := range buildList {
+		if v, ok := altMods[m.Path]; ok && semver.Compare(m.Version, v) < 0 {
+			keep = append(keep, module.Version{Path: m.Path, Version: v})
+			keptAltMod = true
+		}
+	}
+	if keptAltMod {
+		// We must run mvs.Req again instead of simply adding altMods to min.
+		// It's possible that a requirement in altMods makes some other
+		// explicit indirect requirement unnecessary.
+		reqs.roots = keep
+		min, err = mvs.Req(mainModule, rootPaths, reqs)
+		if err != nil {
+			return nil, err
+		}
+	}
+
 	return newRequirements(unpruned, min, direct), nil
 }
 
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index de47974..e64677a 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -243,20 +243,24 @@
 //
 // If the package is not present in any module selected from the requirement
 // graph, importFromModules returns an *ImportMissingError.
-func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, err error) {
+//
+// If the package is present in exactly one module, importFromModules will
+// return the module, its root directory, and a list of other modules that
+// lexically could have provided the package but did not.
+func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, altMods []module.Version, err error) {
 	if strings.Contains(path, "@") {
-		return module.Version{}, "", fmt.Errorf("import path should not have @version")
+		return module.Version{}, "", nil, fmt.Errorf("import path should not have @version")
 	}
 	if build.IsLocalImport(path) {
-		return module.Version{}, "", fmt.Errorf("relative import not supported")
+		return module.Version{}, "", nil, fmt.Errorf("relative import not supported")
 	}
 	if path == "C" {
 		// There's no directory for import "C".
-		return module.Version{}, "", nil
+		return module.Version{}, "", nil, nil
 	}
 	// Before any further lookup, check that the path is valid.
 	if err := module.CheckImportPath(path); err != nil {
-		return module.Version{}, "", &invalidImportError{importPath: path, err: err}
+		return module.Version{}, "", nil, &invalidImportError{importPath: path, err: err}
 	}
 
 	// Is the package in the standard library?
@@ -265,14 +269,14 @@
 		for _, mainModule := range MainModules.Versions() {
 			if MainModules.InGorootSrc(mainModule) {
 				if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil {
-					return module.Version{}, dir, err
+					return module.Version{}, dir, nil, err
 				} else if ok {
-					return mainModule, dir, nil
+					return mainModule, dir, nil, nil
 				}
 			}
 		}
 		dir := filepath.Join(cfg.GOROOT, "src", path)
-		return module.Version{}, dir, nil
+		return module.Version{}, dir, nil, nil
 	}
 
 	// -mod=vendor is special.
@@ -283,19 +287,19 @@
 		mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true)
 		vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false)
 		if mainOK && vendorOK {
-			return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}}
+			return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}}
 		}
 		// Prefer to return main directory if there is one,
 		// Note that we're not checking that the package exists.
 		// We'll leave that for load.
 		if !vendorOK && mainDir != "" {
-			return mainModule, mainDir, nil
+			return mainModule, mainDir, nil, nil
 		}
 		if mainErr != nil {
-			return module.Version{}, "", mainErr
+			return module.Version{}, "", nil, mainErr
 		}
 		readVendorList(mainModule)
-		return vendorPkgModule[path], vendorDir, nil
+		return vendorPkgModule[path], vendorDir, nil, nil
 	}
 
 	// Check each module on the build list.
@@ -316,7 +320,7 @@
 	// already non-nil, then we attempt to load the package using the full
 	// requirements in mg.
 	for {
-		var sumErrMods []module.Version
+		var sumErrMods, altMods []module.Version
 		for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) {
 			var (
 				v  string
@@ -350,13 +354,15 @@
 				// continue the loop and find the package in some other module,
 				// we need to look at this module to make sure the import is
 				// not ambiguous.
-				return module.Version{}, "", err
+				return module.Version{}, "", nil, err
 			}
 			if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
-				return module.Version{}, "", err
+				return module.Version{}, "", nil, err
 			} else if ok {
 				mods = append(mods, m)
 				dirs = append(dirs, dir)
+			} else {
+				altMods = append(altMods, m)
 			}
 		}
 
@@ -369,7 +375,7 @@
 				mods[i], mods[j] = mods[j], mods[i]
 				dirs[i], dirs[j] = dirs[j], dirs[i]
 			}
-			return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
+			return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
 		}
 
 		if len(sumErrMods) > 0 {
@@ -377,7 +383,7 @@
 				j := len(sumErrMods) - 1 - i
 				sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i]
 			}
-			return module.Version{}, "", &ImportMissingSumError{
+			return module.Version{}, "", nil, &ImportMissingSumError{
 				importPath: path,
 				mods:       sumErrMods,
 				found:      len(mods) > 0,
@@ -385,7 +391,7 @@
 		}
 
 		if len(mods) == 1 {
-			return mods[0], dirs[0], nil
+			return mods[0], dirs[0], altMods, nil
 		}
 
 		if mg != nil {
@@ -395,7 +401,7 @@
 			if !HasModRoot() {
 				queryErr = ErrNoModRoot
 			}
-			return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
+			return module.Version{}, "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
 		}
 
 		// So far we've checked the root dependencies.
@@ -406,7 +412,7 @@
 			// the module graph, so we can't return an ImportMissingError here — one
 			// of the missing modules might actually contain the package in question,
 			// in which case we shouldn't go looking for it in some new dependency.
-			return module.Version{}, "", err
+			return module.Version{}, "", nil, err
 		}
 	}
 }
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 20c007a..48f268c 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -862,6 +862,7 @@
 	imports     []*loadPkg     // packages imported by this one
 	testImports []string       // test-only imports, saved for use by pkg.test.
 	inStd       bool
+	altMods     []module.Version // modules that could have contained the package but did not
 
 	// Populated by (*loader).pkgTest:
 	testOnce sync.Once
@@ -970,7 +971,7 @@
 		ld.GoVersion = MainModules.GoVersion()
 
 		if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 {
-			ld.errorf("go mod tidy: go.mod file indicates go %s, but maximum supported version is %s\n", ld.GoVersion, LatestGoVersion())
+			ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion())
 			base.ExitIfErrors()
 		}
 	}
@@ -1142,7 +1143,7 @@
 			// If that is not the case, there is a bug in the loading loop above.
 			for _, m := range rs.rootModules {
 				if v, ok := ld.requirements.rootSelected(m.Path); !ok || v != m.Version {
-					ld.errorf("go mod tidy: internal error: a requirement on %v is needed but was not added during package loading\n", m)
+					ld.errorf("go: internal error: a requirement on %v is needed but was not added during package loading\n", m)
 					base.ExitIfErrors()
 				}
 			}
@@ -1184,8 +1185,7 @@
 }
 
 // updateRequirements ensures that ld.requirements is consistent with the
-// information gained from ld.pkgs and includes the modules in add as roots at
-// at least the given versions.
+// information gained from ld.pkgs.
 //
 // In particular:
 //
@@ -1343,7 +1343,7 @@
 				//
 				// In some sense, we can think of this as ‘upgraded the module providing
 				// pkg.path from "none" to a version higher than "none"’.
-				if _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
+				if _, _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
 					changed = true
 					break
 				}
@@ -1554,7 +1554,7 @@
 			// If the main module is tidy and the package is in "all" — or if we're
 			// lucky — we can identify all of its imports without actually loading the
 			// full module graph.
-			m, _, err := importFromModules(ctx, path, ld.requirements, nil)
+			m, _, _, err := importFromModules(ctx, path, ld.requirements, nil)
 			if err != nil {
 				var missing *ImportMissingError
 				if errors.As(err, &missing) && ld.ResolveMissingImports {
@@ -1659,7 +1659,7 @@
 		}
 	}
 
-	pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
+	pkg.mod, pkg.dir, pkg.altMods, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
 	if pkg.dir == "" {
 		return
 	}
@@ -1884,7 +1884,7 @@
 
 	mg, err := rs.Graph(ctx)
 	if err != nil {
-		ld.errorf("go mod tidy: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err)
+		ld.errorf("go: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err)
 		suggestFixes()
 		return
 	}
@@ -1918,7 +1918,7 @@
 
 		pkg := pkg
 		ld.work.Add(func() {
-			mod, _, err := importFromModules(ctx, pkg.path, rs, mg)
+			mod, _, _, err := importFromModules(ctx, pkg.path, rs, mg)
 			if mod != pkg.mod {
 				mismatches := <-mismatchMu
 				mismatches[pkg] = mismatch{mod: mod, err: err}
diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go
index 931fdce..11e2c81 100644
--- a/src/cmd/go/internal/run/run.go
+++ b/src/cmd/go/internal/run/run.go
@@ -103,7 +103,7 @@
 			if strings.HasSuffix(file, "_test.go") {
 				// GoFilesPackage is going to assign this to TestGoFiles.
 				// Reject since it won't be part of the build.
-				base.Fatalf("go run: cannot run *_test.go files (%s)", file)
+				base.Fatalf("go: cannot run *_test.go files (%s)", file)
 			}
 		}
 		p = load.GoFilesPackage(ctx, pkgOpts, files)
@@ -114,26 +114,26 @@
 			var err error
 			pkgs, err = load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args[:1])
 			if err != nil {
-				base.Fatalf("go run: %v", err)
+				base.Fatalf("go: %v", err)
 			}
 		} else {
 			pkgs = load.PackagesAndErrors(ctx, pkgOpts, args[:1])
 		}
 
 		if len(pkgs) == 0 {
-			base.Fatalf("go run: no packages loaded from %s", arg)
+			base.Fatalf("go: no packages loaded from %s", arg)
 		}
 		if len(pkgs) > 1 {
 			var names []string
 			for _, p := range pkgs {
 				names = append(names, p.ImportPath)
 			}
-			base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t"))
+			base.Fatalf("go: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t"))
 		}
 		p = pkgs[0]
 		i++
 	} else {
-		base.Fatalf("go run: no go files listed")
+		base.Fatalf("go: no go files listed")
 	}
 	cmdArgs := args[i:]
 	load.CheckPackageErrors([]*load.Package{p})
@@ -154,7 +154,7 @@
 			if !cfg.BuildContext.CgoEnabled {
 				hint = " (cgo is disabled)"
 			}
-			base.Fatalf("go run: no suitable source files%s", hint)
+			base.Fatalf("go: no suitable source files%s", hint)
 		}
 		p.Internal.ExeName = src[:len(src)-len(".go")]
 	} else {
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index 173e8a2..8f5d57e 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -710,7 +710,7 @@
 	b.Init()
 
 	if cfg.BuildI {
-		fmt.Fprint(os.Stderr, "go test: -i flag is deprecated\n")
+		fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
 		cfg.BuildV = testV
 
 		deps := make(map[string]bool)
diff --git a/src/cmd/go/internal/test/testflag.go b/src/cmd/go/internal/test/testflag.go
index aca0dbd..cb35438 100644
--- a/src/cmd/go/internal/test/testflag.go
+++ b/src/cmd/go/internal/test/testflag.go
@@ -387,7 +387,7 @@
 		if !testC {
 			buildFlag = "-i"
 		}
-		fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
+		fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
 		exitWithUsage()
 	}
 
diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go
index 95c90ea..4fe4c2b 100644
--- a/src/cmd/go/internal/tool/tool.go
+++ b/src/cmd/go/internal/tool/tool.go
@@ -61,7 +61,7 @@
 		switch {
 		case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
 		default:
-			fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
+			fmt.Fprintf(os.Stderr, "go: bad tool name %q\n", toolName)
 			base.SetExitStatus(2)
 			return
 		}
@@ -117,14 +117,14 @@
 func listTools() {
 	f, err := os.Open(base.ToolDir)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
+		fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err)
 		base.SetExitStatus(2)
 		return
 	}
 	defer f.Close()
 	names, err := f.Readdirnames(-1)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
+		fmt.Fprintf(os.Stderr, "go: can't read tool directory: %s\n", err)
 		base.SetExitStatus(2)
 		return
 	}
diff --git a/src/cmd/go/internal/version/version.go b/src/cmd/go/internal/version/version.go
index 58cbd32..e885933 100644
--- a/src/cmd/go/internal/version/version.go
+++ b/src/cmd/go/internal/version/version.go
@@ -62,8 +62,14 @@
 		// a reasonable use case. For example, imagine GOFLAGS=-v to
 		// turn "verbose mode" on for all Go commands, which should not
 		// break "go version".
-		if (!base.InGOFLAGS("-m") && *versionM) || (!base.InGOFLAGS("-v") && *versionV) {
-			fmt.Fprintf(os.Stderr, "go version: flags can only be used with arguments\n")
+		var argOnlyFlag string
+		if !base.InGOFLAGS("-m") && *versionM {
+			argOnlyFlag = "-m"
+		} else if !base.InGOFLAGS("-v") && *versionV {
+			argOnlyFlag = "-v"
+		}
+		if argOnlyFlag != "" {
+			fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag)
 			base.SetExitStatus(2)
 			return
 		}
diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go
index 1d419dd..88b3c57 100644
--- a/src/cmd/go/internal/vet/vet.go
+++ b/src/cmd/go/internal/vet/vet.go
@@ -103,7 +103,7 @@
 			continue
 		}
 		if len(ptest.GoFiles) == 0 && len(ptest.CgoFiles) == 0 && pxtest == nil {
-			base.Errorf("go vet %s: no Go files in %s", p.ImportPath, p.Dir)
+			base.Errorf("go: can't vet %s: no Go files in %s", p.ImportPath, p.Dir)
 			continue
 		}
 		if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 {
diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go
index b5b3c46..3551a59 100644
--- a/src/cmd/go/internal/vet/vetflag.go
+++ b/src/cmd/go/internal/vet/vetflag.go
@@ -82,7 +82,7 @@
 	vetcmd := exec.Command(tool, "-flags")
 	vetcmd.Stdout = out
 	if err := vetcmd.Run(); err != nil {
-		fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err)
+		fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err)
 		base.SetExitStatus(2)
 		base.Exit()
 	}
@@ -92,7 +92,7 @@
 		Usage string
 	}
 	if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
-		fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err)
+		fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err)
 		base.SetExitStatus(2)
 		base.Exit()
 	}
diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go
index 69940cb..6f5ac13 100644
--- a/src/cmd/go/internal/work/action.go
+++ b/src/cmd/go/internal/work/action.go
@@ -294,14 +294,14 @@
 	}
 
 	if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil {
-		fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
+		fmt.Fprintf(os.Stderr, "go: %v\n", err)
 		base.SetExitStatus(2)
 		base.Exit()
 	}
 
 	for _, tag := range cfg.BuildContext.BuildTags {
 		if strings.Contains(tag, ",") {
-			fmt.Fprintf(os.Stderr, "cmd/go: -tags space-separated list contains comma\n")
+			fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n")
 			base.SetExitStatus(2)
 			base.Exit()
 		}
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index c51dd39..e5d7f4a 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -406,7 +406,7 @@
 	depMode := ModeBuild
 	if cfg.BuildI {
 		depMode = ModeInstall
-		fmt.Fprint(os.Stderr, "go build: -i flag is deprecated\n")
+		fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
 	}
 
 	pkgs = omitTestOnly(pkgsFilter(pkgs))
@@ -425,7 +425,7 @@
 			strings.HasSuffix(cfg.BuildO, "/") ||
 			strings.HasSuffix(cfg.BuildO, string(os.PathSeparator)) {
 			if !explicitO {
-				base.Fatalf("go build: build output %q already exists and is a directory", cfg.BuildO)
+				base.Fatalf("go: build output %q already exists and is a directory", cfg.BuildO)
 			}
 			a := &Action{Mode: "go build"}
 			for _, p := range pkgs {
@@ -440,13 +440,13 @@
 				a.Deps = append(a.Deps, b.AutoAction(ModeInstall, depMode, p))
 			}
 			if len(a.Deps) == 0 {
-				base.Fatalf("go build: no main packages to build")
+				base.Fatalf("go: no main packages to build")
 			}
 			b.Do(ctx, a)
 			return
 		}
 		if len(pkgs) > 1 {
-			base.Fatalf("go build: cannot write multiple packages to non-directory %s", cfg.BuildO)
+			base.Fatalf("go: cannot write multiple packages to non-directory %s", cfg.BuildO)
 		} else if len(pkgs) == 0 {
 			base.Fatalf("no packages to build")
 		}
@@ -496,14 +496,17 @@
 
 - All arguments must refer to packages in the same module at the same version.
 
+- Package path arguments must refer to main packages. Pattern arguments
+will only match main packages.
+
 - No module is considered the "main" module. If the module containing
 packages named on the command line has a go.mod file, it must not contain
 directives (replace and exclude) that would cause it to be interpreted
 differently than if it were the main module. The module must not require
 a higher version of itself.
 
-- Package path arguments must refer to main packages. Pattern arguments
-will only match main packages.
+- Vendor directories are not used in any module. (Vendor directories are not
+included in the module zip files downloaded by 'go install'.)
 
 If the arguments don't have version suffixes, "go install" may run in
 module-aware mode or GOPATH mode, depending on the GO111MODULE environment
@@ -590,7 +593,7 @@
 	for _, arg := range args {
 		if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) {
 			if cfg.BuildI {
-				fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n")
+				fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
 			}
 			installOutsideModule(ctx, args)
 			return
@@ -618,7 +621,7 @@
 				latestArgs[i] = args[i] + "@latest"
 			}
 			hint := strings.Join(latestArgs, " ")
-			base.Fatalf("go install: version is required when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint)
+			base.Fatalf("go: 'go install' requires a version when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint)
 		}
 	}
 	load.CheckPackageErrors(pkgs)
@@ -631,7 +634,7 @@
 			}
 		}
 		if !allGoroot {
-			fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n")
+			fmt.Fprintf(os.Stderr, "go: -i flag is deprecated\n")
 		}
 	}
 
@@ -677,14 +680,14 @@
 			case p.Name != "main" && p.Module != nil:
 				// Non-executables have no target (except the cache) when building with modules.
 			case p.Internal.GobinSubdir:
-				base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName)
+				base.Errorf("go: cannot install cross-compiled binaries when GOBIN is set")
 			case p.Internal.CmdlineFiles:
-				base.Errorf("go %s: no install location for .go files listed on command line (GOBIN not set)", cfg.CmdName)
+				base.Errorf("go: no install location for .go files listed on command line (GOBIN not set)")
 			case p.ConflictDir != "":
-				base.Errorf("go %s: no install location for %s: hidden by %s", cfg.CmdName, p.Dir, p.ConflictDir)
+				base.Errorf("go: no install location for %s: hidden by %s", p.Dir, p.ConflictDir)
 			default:
-				base.Errorf("go %s: no install location for directory %s outside GOPATH\n"+
-					"\tFor more details see: 'go help gopath'", cfg.CmdName, p.Dir)
+				base.Errorf("go: no install location for directory %s outside GOPATH\n"+
+					"\tFor more details see: 'go help gopath'", p.Dir)
 			}
 		}
 	}
@@ -779,7 +782,7 @@
 	pkgOpts := load.PackageOpts{MainOnly: true}
 	pkgs, err := load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args)
 	if err != nil {
-		base.Fatalf("go install: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 	load.CheckPackageErrors(pkgs)
 	patterns := make([]string, len(args))
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index f7fae9f..f82028a 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -2963,18 +2963,24 @@
 	linkobj := str.StringList(ofile, outObj, mkAbsFiles(p.Dir, p.SysoFiles))
 	dynobj := objdir + "_cgo_.o"
 
-	// we need to use -pie for Linux/ARM to get accurate imported sym
 	ldflags := cgoLDFLAGS
 	if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" {
-		// -static -pie doesn't make sense, and causes link errors.
-		// Issue 26197.
-		n := make([]string, 0, len(ldflags))
-		for _, flag := range ldflags {
-			if flag != "-static" {
-				n = append(n, flag)
-			}
+		if !str.Contains(ldflags, "-no-pie") {
+			// we need to use -pie for Linux/ARM to get accurate imported sym (added in https://golang.org/cl/5989058)
+			// this seems to be outdated, but we don't want to break existing builds depending on this (Issue 45940)
+			ldflags = append(ldflags, "-pie")
 		}
-		ldflags = append(n, "-pie")
+		if str.Contains(ldflags, "-pie") && str.Contains(ldflags, "-static") {
+			// -static -pie doesn't make sense, and causes link errors.
+			// Issue 26197.
+			n := make([]string, 0, len(ldflags)-1)
+			for _, flag := range ldflags {
+				if flag != "-static" {
+					n = append(n, flag)
+				}
+			}
+			ldflags = n
+		}
 	}
 	if err := b.gccld(a, p, objdir, dynobj, ldflags, linkobj); err != nil {
 		return err
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index 1cce5d4..800800f 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -379,6 +379,11 @@
 		args = append(args, "-D", "GO386_"+cfg.GO386)
 	}
 
+	if cfg.Goarch == "amd64" {
+		// Define GOAMD64_value from cfg.GOAMD64.
+		args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64)
+	}
+
 	if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
 		// Define GOMIPS_value from cfg.GOMIPS.
 		args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index 8674bbd..2a605e7 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -13,7 +13,6 @@
 	"cmd/go/internal/modload"
 	"cmd/internal/str"
 	"cmd/internal/sys"
-	"flag"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -33,7 +32,7 @@
 	if cfg.BuildPkgdir != "" && !filepath.IsAbs(cfg.BuildPkgdir) {
 		p, err := filepath.Abs(cfg.BuildPkgdir)
 		if err != nil {
-			fmt.Fprintf(os.Stderr, "go %s: evaluating -pkgdir: %v\n", flag.Args()[0], err)
+			fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err)
 			base.SetExitStatus(2)
 			base.Exit()
 		}
@@ -49,14 +48,14 @@
 		value := cfg.Getenv(key)
 		args, err := str.SplitQuotedFields(value)
 		if err != nil {
-			base.Fatalf("go %s: %s environment variable could not be parsed: %v", flag.Args()[0], key, err)
+			base.Fatalf("go: %s environment variable could not be parsed: %v", key, err)
 		}
 		if len(args) == 0 {
 			continue
 		}
 		path := args[0]
 		if !filepath.IsAbs(path) && path != filepath.Base(path) {
-			base.Fatalf("go %s: %s environment variable is relative; must be absolute path: %s\n", flag.Args()[0], key, path)
+			base.Fatalf("go: %s environment variable is relative; must be absolute path: %s\n", key, path)
 		}
 	}
 }
@@ -74,7 +73,7 @@
 		return
 	}
 	if cfg.BuildRace && cfg.BuildMSan {
-		fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously\n", flag.Args()[0])
+		fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n")
 		base.SetExitStatus(2)
 		base.Exit()
 	}
@@ -85,7 +84,7 @@
 	}
 	if cfg.BuildRace {
 		if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
-			fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n", flag.Args()[0])
+			fmt.Fprintf(os.Stderr, "go: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n")
 			base.SetExitStatus(2)
 			base.Exit()
 		}
@@ -103,9 +102,9 @@
 
 	if !cfg.BuildContext.CgoEnabled {
 		if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch {
-			fmt.Fprintf(os.Stderr, "go %s: %s requires cgo\n", flag.Args()[0], modeFlag)
+			fmt.Fprintf(os.Stderr, "go: %s requires cgo\n", modeFlag)
 		} else {
-			fmt.Fprintf(os.Stderr, "go %s: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0], modeFlag)
+			fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag)
 		}
 
 		base.SetExitStatus(2)
diff --git a/src/cmd/go/testdata/script/build_i_deprecate.txt b/src/cmd/go/testdata/script/build_i_deprecate.txt
index 71356e5..5c17995 100644
--- a/src/cmd/go/testdata/script/build_i_deprecate.txt
+++ b/src/cmd/go/testdata/script/build_i_deprecate.txt
@@ -2,13 +2,13 @@
 # TODO(golang.org/issue/41696): remove the -i flag after Go 1.16, and this test.
 
 go build -n -i
-stderr '^go build: -i flag is deprecated$'
+stderr '^go: -i flag is deprecated$'
 
 go install -n -i
-stderr '^go install: -i flag is deprecated$'
+stderr '^go: -i flag is deprecated$'
 
 go test -n -i
-stderr '^go test: -i flag is deprecated$'
+stderr '^go: -i flag is deprecated$'
 
 
 # 'go clean -i' should not print a deprecation warning.
diff --git a/src/cmd/go/testdata/script/env_unset.txt b/src/cmd/go/testdata/script/env_unset.txt
index 4e0f249..22bc845 100644
--- a/src/cmd/go/testdata/script/env_unset.txt
+++ b/src/cmd/go/testdata/script/env_unset.txt
@@ -12,13 +12,13 @@
 go env -u GOEXPERIMENT
 
 ! go env
-stderr '^cmd/go: unsupported GOOS/GOARCH pair bados/badarch$'
+stderr '^go: unsupported GOOS/GOARCH pair bados/badarch$'
 
 ! go env -u GOOS
-stderr '^go env -u: unsupported GOOS/GOARCH pair \w+/badarch$'
+stderr '^go: unsupported GOOS/GOARCH pair \w+/badarch$'
 
 ! go env -u GOARCH
-stderr '^go env -u: unsupported GOOS/GOARCH pair bados/\w+$'
+stderr '^go: unsupported GOOS/GOARCH pair bados/\w+$'
 
 go env -u GOOS GOARCH
 
diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt
index b5e9739..132947c 100644
--- a/src/cmd/go/testdata/script/env_write.txt
+++ b/src/cmd/go/testdata/script/env_write.txt
@@ -30,9 +30,9 @@
 
 # checking errors
 ! go env -w
-stderr 'go env -w: no KEY=VALUE arguments given'
+stderr 'go: no KEY=VALUE arguments given'
 ! go env -u
-stderr 'go env -u: no arguments given'
+stderr 'go: ''go env -u'' requires an argument'
 
 # go env -w changes default setting
 env root=
@@ -111,7 +111,7 @@
 
 # go env -w rejects invalid GOTMPDIR values
 ! go env -w GOTMPDIR=x
-stderr 'go env -w: GOTMPDIR must be an absolute path'
+stderr 'go: GOTMPDIR must be an absolute path'
 
 # go env -w should accept absolute GOTMPDIR value
 # and should not create it
@@ -134,24 +134,24 @@
 go env -w CC=clang
 [!windows] ! go env -w CC=./clang
 [!windows] ! go env -w CC=bin/clang
-[!windows] stderr 'go env -w: CC entry is relative; must be absolute path'
+[!windows] stderr 'go: CC entry is relative; must be absolute path'
 
 [windows] go env -w CC=$WORK\bin\clang
 [windows] ! go env -w CC=.\clang
 [windows] ! go env -w CC=bin\clang
-[windows] stderr 'go env -w: CC entry is relative; must be absolute path'
+[windows] stderr 'go: CC entry is relative; must be absolute path'
 
 # go env -w rejects relative CXX values
 [!windows] go env -w CC=/usr/bin/cpp
 go env -w CXX=cpp
 [!windows] ! go env -w CXX=./cpp
 [!windows] ! go env -w CXX=bin/cpp
-[!windows] stderr 'go env -w: CXX entry is relative; must be absolute path'
+[!windows] stderr 'go: CXX entry is relative; must be absolute path'
 
 [windows] go env -w CXX=$WORK\bin\cpp
 [windows] ! go env -w CXX=.\cpp
 [windows] ! go env -w CXX=bin\cpp
-[windows] stderr 'go env -w: CXX entry is relative; must be absolute path'
+[windows] stderr 'go: CXX entry is relative; must be absolute path'
 
 # go env -w/-u checks validity of GOOS/ARCH combinations
 env GOOS=
@@ -176,9 +176,9 @@
 
 # go env -w should reject relative paths in GOMODCACHE environment.
 ! go env -w GOMODCACHE=~/test
-stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "~/test"'
+stderr 'go: GOMODCACHE entry is relative; must be absolute path: "~/test"'
 ! go env -w GOMODCACHE=./test
-stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "./test"'
+stderr 'go: GOMODCACHE entry is relative; must be absolute path: "./test"'
 
 # go env -w checks validity of GOEXPERIMENT
 env GOEXPERIMENT=
diff --git a/src/cmd/go/testdata/script/get_go_file.txt b/src/cmd/go/testdata/script/get_go_file.txt
index bed8720..f6d0de4 100644
--- a/src/cmd/go/testdata/script/get_go_file.txt
+++ b/src/cmd/go/testdata/script/get_go_file.txt
@@ -9,15 +9,15 @@
 
 # argument has .go suffix, is a file and exists
 ! go get -d test.go
-stderr 'go get test.go: arguments must be package or module paths'
+stderr 'go: test.go: arguments must be package or module paths'
 
 # argument has .go suffix, doesn't exist and has no slashes
 ! go get -d test_missing.go
-stderr 'go get test_missing.go: arguments must be package or module paths'
+stderr 'go: test_missing.go: arguments must be package or module paths'
 
 # argument has .go suffix, is a file and exists in sub-directory
 ! go get -d test/test.go
-stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments'
+stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments'
 
 # argument has .go suffix, doesn't exist and has slashes
 ! go get -d test/test_missing.go
@@ -27,19 +27,19 @@
 # argument has .go suffix, is a symlink and exists
 [symlink] symlink test_sym.go -> test.go
 [symlink] ! go get -d test_sym.go
-[symlink] stderr 'go get test_sym.go: arguments must be package or module paths'
+[symlink] stderr 'go: test_sym.go: arguments must be package or module paths'
 [symlink] rm test_sym.go
 
 # argument has .go suffix, is a symlink and exists in sub-directory
 [symlink] symlink test/test_sym.go -> test.go
 [symlink] ! go get -d test/test_sym.go
-[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
+[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
 [symlink] rm test_sym.go
 
 # argument has .go suffix, is a directory and exists
 mkdir test_dir.go
 ! go get -d test_dir.go
-stderr 'go get test_dir.go: arguments must be package or module paths'
+stderr 'go: test_dir.go: arguments must be package or module paths'
 rm test_dir.go
 
 # argument has .go suffix, is a directory and exists in sub-directory
diff --git a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt
index 2517664..00bf32f 100644
--- a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt
+++ b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt
@@ -3,11 +3,11 @@
 
 # GOPATH: Fetch with insecure, should error
 ! go get -insecure test
-stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead'
+stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead'
 
 # Modules: Set up
 env GO111MODULE=on
 
 # Modules: Fetch with insecure, should error
 ! go get -insecure test
-stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead'
+stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead'
diff --git a/src/cmd/go/testdata/script/gopath_install.txt b/src/cmd/go/testdata/script/gopath_install.txt
index 4b42fc5..6c572ea 100644
--- a/src/cmd/go/testdata/script/gopath_install.txt
+++ b/src/cmd/go/testdata/script/gopath_install.txt
@@ -26,7 +26,7 @@
 env GOPATH= # reset to default ($HOME/go, which does not exist)
 env GOBIN=
 ! go install go-cmd-test/helloworld.go
-stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$'
+stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$'
 
 # With $GOBIN set, should install there.
 env GOBIN=$WORK/bin1
diff --git a/src/cmd/go/testdata/script/gopath_local.txt b/src/cmd/go/testdata/script/gopath_local.txt
index 7ee1f83..54beaca 100644
--- a/src/cmd/go/testdata/script/gopath_local.txt
+++ b/src/cmd/go/testdata/script/gopath_local.txt
@@ -22,7 +22,7 @@
 # Explicit source files listed on the command line should not install without
 # GOBIN set, since individual source files aren't part of the containing GOPATH.
 ! go install testdata/local/easy.go
-stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$'
+stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$'
 
 [windows] stop  # Windows does not allow the ridiculous directory name we're about to use.
 
@@ -58,7 +58,7 @@
 # Explicit source files listed on the command line should not install without
 # GOBIN set, since individual source files aren't part of the containing GOPATH.
 ! go install testdata/$BAD_DIR_NAME/easy.go
-stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$'
+stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$'
 
 -- testdata/local/easy.go --
 package main
diff --git a/src/cmd/go/testdata/script/govcs.txt b/src/cmd/go/testdata/script/govcs.txt
index 4180d7d..e8dd791 100644
--- a/src/cmd/go/testdata/script/govcs.txt
+++ b/src/cmd/go/testdata/script/govcs.txt
@@ -5,40 +5,40 @@
 # GOVCS stops go get
 env GOVCS='*:none'
 ! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
 env GOPRIVATE='github.com/google'
 ! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
 
 # public pattern works
 env GOPRIVATE='github.com/google'
 env GOVCS='public:all,private:none'
 ! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
 
 # private pattern works
 env GOPRIVATE='hubgit.com/google'
 env GOVCS='private:all,public:none'
 ! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
 
 # other patterns work (for more patterns, see TestGOVCS)
 env GOPRIVATE=
 env GOVCS='github.com:svn|hg'
 ! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
 env GOVCS='github.com/google/go-cmp/inner:git,github.com:svn|hg'
 ! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
 
 # bad patterns are reported (for more bad patterns, see TestGOVCSErrors)
 env GOVCS='git'
 ! go get github.com/google/go-cmp
-stderr '^go get github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$'
+stderr '^go: github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$'
 
 env GOVCS=github.com:hg,github.com:git
 ! go get github.com/google/go-cmp
-stderr '^go get github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$'
+stderr '^go: github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$'
 
 # bad GOVCS patterns do not stop commands that do not need to check VCS
 go list
@@ -50,19 +50,19 @@
 env GOPRIVATE=
 env GOVCS=
 ! go get rsc.io/nonexist.svn/hello
-stderr '^go get rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$'
 
 # fossil is disallowed by default
 env GOPRIVATE=
 env GOVCS=
 ! go get rsc.io/nonexist.fossil/hello
-stderr '^go get rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$'
 
 # bzr is disallowed by default
 env GOPRIVATE=
 env GOVCS=
 ! go get rsc.io/nonexist.bzr/hello
-stderr '^go get rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$'
 
 # git is OK by default
 env GOVCS=
@@ -77,12 +77,12 @@
 # git can be disallowed
 env GOVCS=public:hg
 ! go get rsc.io/nonexist.git/hello
-stderr '^go get rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$'
 
 # hg can be disallowed
 env GOVCS=public:git
 ! go get rsc.io/nonexist.hg/hello
-stderr '^go get rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$'
 
 # Repeat in GOPATH mode. Error texts slightly different.
 
diff --git a/src/cmd/go/testdata/script/list_shadow.txt b/src/cmd/go/testdata/script/list_shadow.txt
index 7b24d93..660508d 100644
--- a/src/cmd/go/testdata/script/list_shadow.txt
+++ b/src/cmd/go/testdata/script/list_shadow.txt
@@ -15,7 +15,7 @@
 
 # The error for go install should mention the conflicting directory.
 ! go install -n ./shadow/root2/src/foo
-stderr 'go install: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo'
+stderr 'go: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo'
 
 -- shadow/root1/src/foo/foo.go --
 package foo
diff --git a/src/cmd/go/testdata/script/mod_all.txt b/src/cmd/go/testdata/script/mod_all.txt
index 6fa2d83..b71a920 100644
--- a/src/cmd/go/testdata/script/mod_all.txt
+++ b/src/cmd/go/testdata/script/mod_all.txt
@@ -202,9 +202,9 @@
 go mod edit -go=1.17 w/go.mod
 go mod edit -go=1.17 x/go.mod
 go mod edit -go=1.17
-cp go.mod go.mod.orig
+cmp go.mod go.mod.beforetidy
 go mod tidy
-cmp go.mod go.mod.orig
+cmp go.mod go.mod.aftertidy
 
 # With lazy loading, 'go list all' with neither -mod=vendor nor -test should
 # match -mod=vendor without -test in 1.15.
@@ -466,3 +466,66 @@
 go 1.15
 -- x/x.go --
 package x
+-- go.mod.beforetidy --
+module example.com/main
+
+// Note: this go.mod file initially specifies go 1.15,
+// but includes some redundant roots so that it
+// also already obeys the 1.17 lazy loading invariants.
+go 1.17
+
+require (
+	example.com/a v0.1.0
+	example.com/b v0.1.0 // indirect
+	example.com/q v0.1.0
+	example.com/r v0.1.0 // indirect
+	example.com/t v0.1.0
+	example.com/u v0.1.0 // indirect
+)
+
+replace (
+	example.com/a v0.1.0 => ./a
+	example.com/b v0.1.0 => ./b
+	example.com/c v0.1.0 => ./c
+	example.com/d v0.1.0 => ./d
+	example.com/q v0.1.0 => ./q
+	example.com/r v0.1.0 => ./r
+	example.com/s v0.1.0 => ./s
+	example.com/t v0.1.0 => ./t
+	example.com/u v0.1.0 => ./u
+	example.com/w v0.1.0 => ./w
+	example.com/x v0.1.0 => ./x
+)
+-- go.mod.aftertidy --
+module example.com/main
+
+// Note: this go.mod file initially specifies go 1.15,
+// but includes some redundant roots so that it
+// also already obeys the 1.17 lazy loading invariants.
+go 1.17
+
+require (
+	example.com/a v0.1.0
+	example.com/q v0.1.0
+	example.com/t v0.1.0
+)
+
+require (
+	example.com/b v0.1.0 // indirect
+	example.com/r v0.1.0 // indirect
+	example.com/u v0.1.0 // indirect
+)
+
+replace (
+	example.com/a v0.1.0 => ./a
+	example.com/b v0.1.0 => ./b
+	example.com/c v0.1.0 => ./c
+	example.com/d v0.1.0 => ./d
+	example.com/q v0.1.0 => ./q
+	example.com/r v0.1.0 => ./r
+	example.com/s v0.1.0 => ./s
+	example.com/t v0.1.0 => ./t
+	example.com/u v0.1.0 => ./u
+	example.com/w v0.1.0 => ./w
+	example.com/x v0.1.0 => ./x
+)
diff --git a/src/cmd/go/testdata/script/mod_bad_domain.txt b/src/cmd/go/testdata/script/mod_bad_domain.txt
index 7a270d0..ed6a8c6 100644
--- a/src/cmd/go/testdata/script/mod_bad_domain.txt
+++ b/src/cmd/go/testdata/script/mod_bad_domain.txt
@@ -2,7 +2,7 @@
 
 # explicit get should report errors about bad names
 ! go get appengine
-stderr '^go get: malformed module path "appengine": missing dot in first path element$'
+stderr '^go: malformed module path "appengine": missing dot in first path element$'
 ! go get x/y.z
 stderr 'malformed module path "x/y.z": missing dot in first path element'
 
diff --git a/src/cmd/go/testdata/script/mod_dot.txt b/src/cmd/go/testdata/script/mod_dot.txt
index ca8d5c6..cb60e98 100644
--- a/src/cmd/go/testdata/script/mod_dot.txt
+++ b/src/cmd/go/testdata/script/mod_dot.txt
@@ -5,11 +5,11 @@
 # to resolve an external module.
 cd dir
 ! go get
-stderr '^go get: no package in current directory$'
+stderr '^go: no package to get in current directory$'
 ! go get .
-stderr '^go get \.: no package in current directory$'
+stderr '^go: .: no package to get in current directory$'
 ! go get ./subdir
-stderr '^go get: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$'
+stderr '^go: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$'
 ! go list
 ! stderr 'cannot find module providing package'
 stderr '^no Go files in '$WORK'[/\\]gopath[/\\]src[/\\]dir$'
diff --git a/src/cmd/go/testdata/script/mod_download.txt b/src/cmd/go/testdata/script/mod_download.txt
index c2b72b2..89e58a2 100644
--- a/src/cmd/go/testdata/script/mod_download.txt
+++ b/src/cmd/go/testdata/script/mod_download.txt
@@ -93,19 +93,19 @@
 
 # download reports errors encountered when locating modules
 ! go mod download bad/path
-stderr '^go mod download: module bad/path: not a known dependency$'
+stderr '^go: module bad/path: not a known dependency$'
 ! go mod download bad/path@latest
-stderr '^go mod download: bad/path@latest: malformed module path "bad/path": missing dot in first path element$'
+stderr '^go: bad/path@latest: malformed module path "bad/path": missing dot in first path element$'
 ! go mod download rsc.io/quote@v1.999.999
-stderr '^go mod download: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$'
+stderr '^go: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$'
 ! go mod download -json bad/path
 stdout '^\t"Error": "module bad/path: not a known dependency"'
 
 # download main module produces a warning or error
 go mod download m
-stderr '^go mod download: skipping argument m that resolves to the main module\n'
+stderr '^go: skipping download of m that resolves to the main module\n'
 ! go mod download m@latest
-stderr '^go mod download: m@latest: malformed module path "m": missing dot in first path element$'
+stderr '^go: m@latest: malformed module path "m": missing dot in first path element$'
 
 # download without arguments updates go.mod and go.sum after loading the
 # build list, but does not save sums for downloaded zips.
diff --git a/src/cmd/go/testdata/script/mod_edit.txt b/src/cmd/go/testdata/script/mod_edit.txt
index 5aa5ca1..ebc032a 100644
--- a/src/cmd/go/testdata/script/mod_edit.txt
+++ b/src/cmd/go/testdata/script/mod_edit.txt
@@ -23,18 +23,18 @@
 
 # -exclude and -retract reject invalid versions.
 ! go mod edit -exclude=example.com/m@bad
-stderr '^go mod: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$'
+stderr '^go: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$'
 ! go mod edit -retract=bad
-stderr '^go mod: -retract=bad: version "bad" invalid: must be of the form v1.2.3$'
+stderr '^go: -retract=bad: version "bad" invalid: must be of the form v1.2.3$'
 
 ! go mod edit -exclude=example.com/m@v2.0.0
-stderr '^go mod: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$'
+stderr '^go: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$'
 
 ! go mod edit -exclude=example.com/m/v2@v1.0.0
-stderr '^go mod: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$'
+stderr '^go: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$'
 
 ! go mod edit -exclude=gopkg.in/example.v1@v2.0.0
-stderr '^go mod: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$'
+stderr '^go: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$'
 
 cmpenv go.mod $WORK/go.mod.edit2
 
diff --git a/src/cmd/go/testdata/script/mod_get_changes.txt b/src/cmd/go/testdata/script/mod_get_changes.txt
index 3287b2a..2829111 100644
--- a/src/cmd/go/testdata/script/mod_get_changes.txt
+++ b/src/cmd/go/testdata/script/mod_get_changes.txt
@@ -4,8 +4,8 @@
 go list -m all
 ! stdout golang.org/x/text
 go get -d rsc.io/quote@v1.5.2
-stderr '^go get: added rsc.io/quote v1.5.2$'
-stderr '^go get: upgraded rsc.io/sampler v1.0.0 => v1.3.0$'
+stderr '^go: added rsc.io/quote v1.5.2$'
+stderr '^go: upgraded rsc.io/sampler v1.0.0 => v1.3.0$'
 ! stderr '^go get.*golang.org/x/text'
 go list -m all
 stdout golang.org/x/text
@@ -15,8 +15,8 @@
 # and for changed explicit dependencies. 'go get' does not print messages
 # for changed indirect dependencies.
 go get -d rsc.io/sampler@none
-stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.3.0$'
-stderr '^go get: removed rsc.io/sampler v1.3.0$'
+stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.3.0$'
+stderr '^go: removed rsc.io/sampler v1.3.0$'
 ! stderr '^go get.*golang.org/x/text'
 cmp go.mod go.mod.downgrade
 
@@ -24,8 +24,8 @@
 # for explicit dependencies removed as a consequence.
 cp go.mod.usequote go.mod
 go get -d rsc.io/quote@v1.5.1
-stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.5.1$'
-stderr '^go get: removed usequote v0.0.0$'
+stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.5.1$'
+stderr '^go: removed usequote v0.0.0$'
 
 -- go.mod --
 module m
diff --git a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt
index 63cd27a..e8142af 100644
--- a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt
+++ b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt
@@ -4,7 +4,7 @@
 
 # 'go get' outside a module with an executable prints a deprecation message.
 go get example.com/cmd/a
-stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$'
+stderr '^go: installing executables with ''go get'' in module mode is deprecated.$'
 stderr 'Use ''go install pkg@version'' instead.'
 
 cp go.mod.orig go.mod
@@ -18,7 +18,7 @@
 # 'go get' inside a module with an executable prints a different
 # deprecation message.
 go get example.com/cmd/a
-stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$'
+stderr '^go: installing executables with ''go get'' in module mode is deprecated.$'
 stderr 'To adjust and download dependencies of the current module, use ''go get -d'''
 cp go.mod.orig go.mod
 
diff --git a/src/cmd/go/testdata/script/mod_get_downgrade.txt b/src/cmd/go/testdata/script/mod_get_downgrade.txt
index c26c5e1..685bde7 100644
--- a/src/cmd/go/testdata/script/mod_get_downgrade.txt
+++ b/src/cmd/go/testdata/script/mod_get_downgrade.txt
@@ -20,8 +20,8 @@
 stdout 'rsc.io/sampler v1.3.0'
 
 ! go get -d rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none
-stderr -count=1 '^go get:'
-stderr '^go get: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$'
+! stderr add|remove|upgrad|downgrad
+stderr '^go: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$'
 
 go list -m all
 stdout 'rsc.io/quote v1.5.1'
diff --git a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt
index 5b768fa..2068cae 100644
--- a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt
+++ b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt
@@ -5,7 +5,7 @@
 # rather than a "matched no packages" warning.
 
 ! go get -d example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/...
-stderr '^go get: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$'
+stderr '^go: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$'
 ! stderr 'matched no packages'
 cmp go.mod.orig go.mod
 
diff --git a/src/cmd/go/testdata/script/mod_get_go_file.txt b/src/cmd/go/testdata/script/mod_get_go_file.txt
index 0c7b5dc..35a77a9 100644
--- a/src/cmd/go/testdata/script/mod_get_go_file.txt
+++ b/src/cmd/go/testdata/script/mod_get_go_file.txt
@@ -17,7 +17,7 @@
 
 # argument has .go suffix, is a file and exists
 ! go get test.go
-stderr 'go get test.go: arguments must be package or module paths'
+stderr 'go: test.go: arguments must be package or module paths'
 
 # argument has .go suffix, doesn't exist and has no slashes
 ! go get test_missing.go
@@ -25,7 +25,7 @@
 
 # argument has .go suffix, is a file and exists in sub-directory
 ! go get test/test.go
-stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments'
+stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments'
 
 # argument has .go suffix, doesn't exist and has slashes
 ! go get test/test_missing.go
@@ -35,19 +35,19 @@
 # argument has .go suffix, is a symlink and exists
 [symlink] symlink test_sym.go -> test.go
 [symlink] ! go get test_sym.go
-[symlink] stderr 'go get test_sym.go: arguments must be package or module paths'
+[symlink] stderr 'go: test_sym.go: arguments must be package or module paths'
 [symlink] rm test_sym.go
 
 # argument has .go suffix, is a symlink and exists in sub-directory
 [symlink] symlink test/test_sym.go -> test.go
 [symlink] ! go get test/test_sym.go
-[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
+[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
 [symlink] rm test_sym.go
 
 # argument has .go suffix, is a directory and exists
 mkdir test_dir.go
 ! go get test_dir.go
-stderr 'go get test_dir.go: arguments must be package or module paths'
+stderr 'go: test_dir.go: arguments must be package or module paths'
 rm test_dir.go
 
 # argument has .go suffix, is a directory and exists in sub-directory
diff --git a/src/cmd/go/testdata/script/mod_get_main.txt b/src/cmd/go/testdata/script/mod_get_main.txt
index 50b2fee..5c9b762 100644
--- a/src/cmd/go/testdata/script/mod_get_main.txt
+++ b/src/cmd/go/testdata/script/mod_get_main.txt
@@ -3,13 +3,13 @@
 
 # relative and absolute paths must be within the main module.
 ! go get -d ..
-stderr '^go get: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+stderr '^go: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
 ! go get -d $WORK
-stderr '^go get: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+stderr '^go: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
 ! go get -d ../...
-stderr '^go get: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+stderr '^go: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
 ! go get -d $WORK/...
-stderr '^go get: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+stderr '^go: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
 
 # @patch and @latest within the main module refer to the current version.
 # The main module won't be upgraded, but missing dependencies will be added.
@@ -31,15 +31,15 @@
 
 # The main module cannot be updated to a specific version.
 ! go get -d rsc.io@v0.1.0
-stderr '^go get: can''t request version "v0.1.0" of the main module \(rsc.io\)$'
+stderr '^go: can''t request version "v0.1.0" of the main module \(rsc.io\)$'
 
 # A package in the main module can't be upgraded either.
 ! go get -d rsc.io/x@v0.1.0
-stderr '^go get: package rsc.io/x is in the main module, so can''t request version v0.1.0$'
+stderr '^go: package rsc.io/x is in the main module, so can''t request version v0.1.0$'
 
 # Nor can a pattern matching packages in the main module.
 ! go get -d rsc.io/x/...@latest
-stderr '^go get: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$'
+stderr '^go: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$'
 
 -- go.mod.orig --
 module rsc.io
diff --git a/src/cmd/go/testdata/script/mod_get_newcycle.txt b/src/cmd/go/testdata/script/mod_get_newcycle.txt
index f71620c..18dc650 100644
--- a/src/cmd/go/testdata/script/mod_get_newcycle.txt
+++ b/src/cmd/go/testdata/script/mod_get_newcycle.txt
@@ -11,4 +11,4 @@
 cmp stderr stderr-expected
 
 -- stderr-expected --
-go get: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0
+go: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0
diff --git a/src/cmd/go/testdata/script/mod_get_nopkgs.txt b/src/cmd/go/testdata/script/mod_get_nopkgs.txt
index 078e71a..2711f93 100644
--- a/src/cmd/go/testdata/script/mod_get_nopkgs.txt
+++ b/src/cmd/go/testdata/script/mod_get_nopkgs.txt
@@ -16,7 +16,7 @@
 
 ! go get -d example.net/emptysubdir/subdir/...
 ! stderr 'matched no packages'
-stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z'
+stderr '^go: example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z'
 
 # It doesn't make sense to 'go get' a path in the standard library,
 # since the standard library necessarily can't have unresolved imports.
@@ -27,7 +27,7 @@
 # which isn't ideal either.
 
 ! go get -d builtin/...  # in GOROOT/src, but contains no packages
-stderr '^go get builtin/...: malformed module path "builtin": missing dot in first path element$'
+stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$'
 
 -- go.mod --
 module example.net/emptysubdir
diff --git a/src/cmd/go/testdata/script/mod_get_patch.txt b/src/cmd/go/testdata/script/mod_get_patch.txt
index 053ef62..5957a36 100644
--- a/src/cmd/go/testdata/script/mod_get_patch.txt
+++ b/src/cmd/go/testdata/script/mod_get_patch.txt
@@ -8,7 +8,7 @@
 # at the start of 'go get', not the version after applying other changes.
 
 ! go get -d example.net/a@v0.2.0 example.net/b@patch
-stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
+stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
 cmp go.mod go.mod.orig
 
 
@@ -19,7 +19,7 @@
 # TODO(#42360): Reconsider the change in defaults.
 
 ! go get -d -u=patch example.net/a@v0.2.0 example.net/b
-stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
+stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
 cmp go.mod go.mod.orig
 
 
@@ -38,7 +38,7 @@
 
 cp go.mod.orig go.mod
 ! go get -u=patch all
-stderr '^go get: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$'
+stderr '^go: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$'
 cmp go.mod go.mod.orig
 
 
diff --git a/src/cmd/go/testdata/script/mod_get_patchcycle.txt b/src/cmd/go/testdata/script/mod_get_patchcycle.txt
index d1db56f..6600109 100644
--- a/src/cmd/go/testdata/script/mod_get_patchcycle.txt
+++ b/src/cmd/go/testdata/script/mod_get_patchcycle.txt
@@ -6,7 +6,7 @@
 # (It used to print v0.1.1 but then silently upgrade to v0.2.0.)
 
 ! go get example.net/a@patch
-stderr '^go get: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$'  # TODO: A mention of b v0.1.0 would be nice.
+stderr '^go: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$'  # TODO: A mention of b v0.1.0 would be nice.
 
 -- go.mod --
 module example
diff --git a/src/cmd/go/testdata/script/mod_get_patchmod.txt b/src/cmd/go/testdata/script/mod_get_patchmod.txt
index e39d13a..bc1859e 100644
--- a/src/cmd/go/testdata/script/mod_get_patchmod.txt
+++ b/src/cmd/go/testdata/script/mod_get_patchmod.txt
@@ -16,7 +16,7 @@
 # not upgraded to the latest patch of the new transitive dependency.
 
 ! go get -d example.net/pkgremoved@patch example.net/other@v0.1.0
-stderr '^go get: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$'
+stderr '^go: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$'
 cmp go.mod.orig go.mod
 
 
diff --git a/src/cmd/go/testdata/script/mod_get_patterns.txt b/src/cmd/go/testdata/script/mod_get_patterns.txt
index aee4374..3b5ab43 100644
--- a/src/cmd/go/testdata/script/mod_get_patterns.txt
+++ b/src/cmd/go/testdata/script/mod_get_patterns.txt
@@ -10,11 +10,11 @@
 
 cp go.mod.orig go.mod
 ! go get -d rsc.io/quote/x...
-stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...'
+stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...'
 ! grep 'require rsc.io/quote' go.mod
 
 ! go get -d rsc.io/quote/x/...
-stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...'
+stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...'
 ! grep 'require rsc.io/quote' go.mod
 
 # If a pattern matches no packages within a module, the module should not
diff --git a/src/cmd/go/testdata/script/mod_get_pkgtags.txt b/src/cmd/go/testdata/script/mod_get_pkgtags.txt
index 0c79ec7..2e2ab72 100644
--- a/src/cmd/go/testdata/script/mod_get_pkgtags.txt
+++ b/src/cmd/go/testdata/script/mod_get_pkgtags.txt
@@ -59,7 +59,7 @@
 # but fail for a non-package subdirectory of a module.
 
 ! go get -d example.net/missing/subdir@v0.1.0
-stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
+stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
 
 go get -d example.net/missing@v0.1.0
 
@@ -68,7 +68,7 @@
 # module is already present in the build list.
 
 ! go get -d example.net/missing/subdir@v0.1.0
-stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
+stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
 
 
 -- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_get_private_vcs.txt b/src/cmd/go/testdata/script/mod_get_private_vcs.txt
index 75c776a..c8862f4 100644
--- a/src/cmd/go/testdata/script/mod_get_private_vcs.txt
+++ b/src/cmd/go/testdata/script/mod_get_private_vcs.txt
@@ -13,7 +13,7 @@
 # Fetching a nonexistent commit should return an "unknown revision"
 # error message.
 ! go get github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b
-stderr '^go get: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$'
+stderr '^go: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$'
 ! stdout .
 
 ! go get github.com/golang/nonexist@master
diff --git a/src/cmd/go/testdata/script/mod_get_replaced.txt b/src/cmd/go/testdata/script/mod_get_replaced.txt
index d97f3f1..ab21bd5 100644
--- a/src/cmd/go/testdata/script/mod_get_replaced.txt
+++ b/src/cmd/go/testdata/script/mod_get_replaced.txt
@@ -82,7 +82,7 @@
 ! go list example
 stderr '^package example is not in GOROOT \(.*\)$'
 ! go get -d example
-stderr '^go get: malformed module path "example": missing dot in first path element$'
+stderr '^go: malformed module path "example": missing dot in first path element$'
 
 go mod edit -replace example@v0.1.0=./example
 
diff --git a/src/cmd/go/testdata/script/mod_get_split.txt b/src/cmd/go/testdata/script/mod_get_split.txt
index f4e7661..2fb88ab 100644
--- a/src/cmd/go/testdata/script/mod_get_split.txt
+++ b/src/cmd/go/testdata/script/mod_get_split.txt
@@ -55,7 +55,7 @@
 # TODO(#27899): Should we instead upgrade or downgrade to an arbirary version?
 
 ! go get -d example.net/split/nested/...@v0.1.0
-stderr '^go get: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$'
+stderr '^go: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$'
 
 cmp go.mod go.mod.orig
 
diff --git a/src/cmd/go/testdata/script/mod_get_svn.txt b/src/cmd/go/testdata/script/mod_get_svn.txt
index 3817fce..4d6b94ae 100644
--- a/src/cmd/go/testdata/script/mod_get_svn.txt
+++ b/src/cmd/go/testdata/script/mod_get_svn.txt
@@ -27,7 +27,7 @@
 # reasonable message instead of a panic.
 ! go get -d vcs-test.golang.org/svn/nonexistent.svn
 ! stderr panic
-stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"'
+stderr 'go: vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"'
 
 -- go.mod --
 module golang/go/issues/28943/main
diff --git a/src/cmd/go/testdata/script/mod_get_wild.txt b/src/cmd/go/testdata/script/mod_get_wild.txt
index 78c645c..b88f268 100644
--- a/src/cmd/go/testdata/script/mod_get_wild.txt
+++ b/src/cmd/go/testdata/script/mod_get_wild.txt
@@ -12,7 +12,7 @@
 # from attempting to resolve a new module whose path is a prefix of the pattern.
 
 ! go get -d -u=patch example.../b@upgrade
-stderr '^go get: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$'
+stderr '^go: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$'
 
 
 # Patching . causes a patch to example.net/a, which introduces a new match
diff --git a/src/cmd/go/testdata/script/mod_getmode_vendor.txt b/src/cmd/go/testdata/script/mod_getmode_vendor.txt
index 00070c0..a4e23ac 100644
--- a/src/cmd/go/testdata/script/mod_getmode_vendor.txt
+++ b/src/cmd/go/testdata/script/mod_getmode_vendor.txt
@@ -11,16 +11,16 @@
 stdout '^golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text[\\/]language$'
 
 ! go list -mod=vendor -m rsc.io/quote@latest
-stderr 'go list -m: rsc.io/quote@latest: cannot query module due to -mod=vendor'
+stderr 'go: rsc.io/quote@latest: cannot query module due to -mod=vendor'
 ! go get -mod=vendor -u
 stderr 'flag provided but not defined: -mod'
 
 # Since we don't have a complete module graph, 'go list -m' queries
 # that require the complete graph should fail with a useful error.
 ! go list -mod=vendor -m all
-stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
 ! go list -mod=vendor -m ...
-stderr 'go list -m: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+stderr 'go: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
 
 -- go.mod --
 module x
diff --git a/src/cmd/go/testdata/script/mod_gonoproxy.txt b/src/cmd/go/testdata/script/mod_gonoproxy.txt
index 2047869..1909400 100644
--- a/src/cmd/go/testdata/script/mod_gonoproxy.txt
+++ b/src/cmd/go/testdata/script/mod_gonoproxy.txt
@@ -27,13 +27,13 @@
 # When GOPROXY is not empty but contains no entries, an error should be reported.
 env GOPROXY=','
 ! go get -d golang.org/x/text
-stderr '^go get golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$'
+stderr '^go: golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$'
 
 # When GOPROXY=off, fetching modules not matched by GONOPROXY fails.
 env GONOPROXY=*/fortune
 env GOPROXY=off
 ! go get -d golang.org/x/text
-stderr '^go get golang.org/x/text: module lookup disabled by GOPROXY=off$'
+stderr '^go: golang.org/x/text: module lookup disabled by GOPROXY=off$'
 
 # GONOPROXY bypasses proxy
 [!net] skip
diff --git a/src/cmd/go/testdata/script/mod_install_pkg_version.txt b/src/cmd/go/testdata/script/mod_install_pkg_version.txt
index fd02392..1ee68e7 100644
--- a/src/cmd/go/testdata/script/mod_install_pkg_version.txt
+++ b/src/cmd/go/testdata/script/mod_install_pkg_version.txt
@@ -16,7 +16,7 @@
 cd m
 cp go.mod go.mod.orig
 ! go list -m all
-stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$'
+stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$'
 go install example.com/cmd/a@latest
 cmp go.mod go.mod.orig
 exists $GOPATH/bin/a$GOEXE
@@ -81,15 +81,15 @@
 # 'go install pkg@version' reports errors for meta packages, std packages,
 # and directories.
 ! go install std@v1.0.0
-stderr '^go install: std@v1.0.0: argument must be a package path, not a meta-package$'
+stderr '^go: std@v1.0.0: argument must be a package path, not a meta-package$'
 ! go install fmt@v1.0.0
-stderr '^go install: fmt@v1.0.0: argument must not be a package in the standard library$'
+stderr '^go: fmt@v1.0.0: argument must not be a package in the standard library$'
 ! go install example.com//cmd/a@v1.0.0
-stderr '^go install: example.com//cmd/a@v1.0.0: argument must be a clean package path$'
+stderr '^go: example.com//cmd/a@v1.0.0: argument must be a clean package path$'
 ! go install example.com/cmd/a@v1.0.0 ./x@v1.0.0
-stderr '^go install: ./x@v1.0.0: argument must be a package path, not a relative path$'
+stderr '^go: ./x@v1.0.0: argument must be a package path, not a relative path$'
 ! go install example.com/cmd/a@v1.0.0 $GOPATH/src/x@v1.0.0
-stderr '^go install: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$'
+stderr '^go: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$'
 ! go install example.com/cmd/a@v1.0.0 cmd/...@v1.0.0
 stderr '^package cmd/go not provided by module example.com/cmd@v1.0.0$'
 
@@ -106,7 +106,7 @@
 env GO111MODULE=auto
 
 ! go install example.com/cmd/a@v1.0.0 example.com/cmd/b@latest
-stderr '^go install: example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$'
+stderr '^go: example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$'
 
 
 # 'go install pkg@version' should report an error if the arguments are in
@@ -137,7 +137,7 @@
 
 # If a wildcard matches no packages, we should see a warning.
 ! go install example.com/cmd/nomatch...@v1.0.0
-stderr '^go install: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$'
+stderr '^go: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$'
 go install example.com/cmd/a@v1.0.0 example.com/cmd/nomatch...@v1.0.0
 stderr '^go: warning: "example.com/cmd/nomatch\.\.\." matched no packages$'
 
@@ -159,7 +159,7 @@
 # 'go install pkg@version' should report an error if the module requires a
 # higher version of itself.
 ! go install example.com/cmd/a@v1.0.0-newerself
-stderr '^go install: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$'
+stderr '^go: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$'
 
 
 # 'go install pkg@version' will only match a retracted version if it's
@@ -192,12 +192,12 @@
 
 func main() {}
 -- replace-err --
-go install: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace):
+go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace):
 	The go.mod file for the module providing named packages contains one or
 	more replace directives. It must not contain directives that would cause
 	it to be interpreted differently than if it were the main module.
 -- exclude-err --
-go install: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude):
+go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude):
 	The go.mod file for the module providing named packages contains one or
 	more exclude directives. It must not contain directives that would cause
 	it to be interpreted differently than if it were the main module.
diff --git a/src/cmd/go/testdata/script/mod_invalid_path.txt b/src/cmd/go/testdata/script/mod_invalid_path.txt
index 333a3ff..766e9c0 100644
--- a/src/cmd/go/testdata/script/mod_invalid_path.txt
+++ b/src/cmd/go/testdata/script/mod_invalid_path.txt
@@ -29,7 +29,7 @@
 go list ./use
 stdout '^example.com/dotname/use$'
 ! go list -m example.com/dotname/.dot@latest
-stderr '^go list -m: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$'
+stderr '^go: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$'
 go get -d example.com/dotname/.dot
 go get -d example.com/dotname/use
 go mod tidy
diff --git a/src/cmd/go/testdata/script/mod_invalid_path_plus.txt b/src/cmd/go/testdata/script/mod_invalid_path_plus.txt
index 51dbf93..6a29eb8 100644
--- a/src/cmd/go/testdata/script/mod_invalid_path_plus.txt
+++ b/src/cmd/go/testdata/script/mod_invalid_path_plus.txt
@@ -9,7 +9,7 @@
 
 # 'go list -m' rejects module paths with pluses.
 ! go list -versions -m 'example.net/bad++'
-stderr '^go list -m: malformed module path "example.net/bad\+\+": invalid char ''\+''$'
+stderr '^go: malformed module path "example.net/bad\+\+": invalid char ''\+''$'
 
 # 'go get' accepts package paths with pluses.
 cp go.mod.orig go.mod
diff --git a/src/cmd/go/testdata/script/mod_invalid_version.txt b/src/cmd/go/testdata/script/mod_invalid_version.txt
index 6846a79..31b25f7 100644
--- a/src/cmd/go/testdata/script/mod_invalid_version.txt
+++ b/src/cmd/go/testdata/script/mod_invalid_version.txt
@@ -19,7 +19,7 @@
 go mod edit -require golang.org/x/text@14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3'
+stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3'
 cd ..
 go list -m golang.org/x/text
 stdout 'golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c'
@@ -30,7 +30,7 @@
 go mod edit -require golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c'
@@ -47,7 +47,7 @@
 go mod edit -require golang.org/x/text@v2.1.1-0.20170915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2'
+stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2'
 cd ..
 ! go list -m golang.org/x/text
 stderr $WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2'
@@ -57,7 +57,7 @@
 go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)'
@@ -67,7 +67,7 @@
 go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)'
@@ -77,7 +77,7 @@
 go mod edit -require golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
@@ -87,7 +87,7 @@
 go mod edit -replace golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c=golang.org/x/text@14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
 cd ..
 go list -m golang.org/x/text
 stdout 'golang.org/x/text v0.1.1-0.20190915032832-14c0d48ead0c => golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c'
@@ -97,7 +97,7 @@
 go mod edit -require golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found'
@@ -109,7 +109,7 @@
 go mod edit -require golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1'
@@ -120,7 +120,7 @@
 go mod edit -require golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number'
@@ -130,7 +130,7 @@
 go mod edit -replace golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c=golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number'
 cd ..
 go list -m golang.org/x/text
 stdout 'golang.org/x/text v0.0.0-0.20170915032832-14c0d48ead0c => golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c'
@@ -153,7 +153,7 @@
 go mod edit -require golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)'
@@ -163,7 +163,7 @@
 go mod edit -require golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag'
@@ -173,7 +173,7 @@
 go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c+incompatible
 cd outside
 ! go list -m golang.org/x/text
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible'
 cd ..
 ! go list -m golang.org/x/text
 stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible'
@@ -194,7 +194,7 @@
 go mod edit -require github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d+incompatible
 cd outside
 ! go list -m github.com/pierrec/lz4
-stderr 'go list -m: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
+stderr 'go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
 cd ..
 ! go list -m github.com/pierrec/lz4
 stderr 'github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
@@ -222,7 +222,7 @@
 # not resolve to a pseudo-version with a different major version.
 cp go.mod.orig go.mod
 ! go get -d github.com/pierrec/lz4@v2.0.8
-stderr 'go get: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2'
+stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2'
 
 # An invalid +incompatible suffix for a canonical version should error out,
 # not resolve to a pseudo-version.
diff --git a/src/cmd/go/testdata/script/mod_list.txt b/src/cmd/go/testdata/script/mod_list.txt
index 239c7ca..06316cc 100644
--- a/src/cmd/go/testdata/script/mod_list.txt
+++ b/src/cmd/go/testdata/script/mod_list.txt
@@ -39,8 +39,8 @@
 stdout '^module rsc.io/quote/buggy: not a known dependency$'
 
 ! go list -m nonexist rsc.io/quote/buggy
-stderr '^go list -m: module nonexist: not a known dependency'
-stderr '^go list -m: module rsc.io/quote/buggy: not a known dependency'
+stderr '^go: module nonexist: not a known dependency'
+stderr '^go: module rsc.io/quote/buggy: not a known dependency'
 
 # Module loader does not interfere with list -e (golang.org/issue/24149).
 go list -e -f '{{.Error.Err}}' database
diff --git a/src/cmd/go/testdata/script/mod_list_sums.txt b/src/cmd/go/testdata/script/mod_list_sums.txt
index 86c528f..6c2f57c 100644
--- a/src/cmd/go/testdata/script/mod_list_sums.txt
+++ b/src/cmd/go/testdata/script/mod_list_sums.txt
@@ -29,4 +29,4 @@
 #
 # TODO(#41297): This should not be an error either.
 ! go list -m -mod=readonly -versions rsc.io/sampler
-stderr '^go list -m: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$'
+stderr '^go: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$'
diff --git a/src/cmd/go/testdata/script/mod_list_update_nolatest.txt b/src/cmd/go/testdata/script/mod_list_update_nolatest.txt
index c6bbbb0..7eebe26 100644
--- a/src/cmd/go/testdata/script/mod_list_update_nolatest.txt
+++ b/src/cmd/go/testdata/script/mod_list_update_nolatest.txt
@@ -26,7 +26,7 @@
 # If proxy returns an invalid response, we should see an error.
 env GOPROXY=$testproxy/invalid
 ! go list -m -u example.com/nolatest
-stderr '^go list -m: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$'
+stderr '^go: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$'
 
 -- go.mod --
 module m
diff --git a/src/cmd/go/testdata/script/mod_load_badchain.txt b/src/cmd/go/testdata/script/mod_load_badchain.txt
index eb464ab..0c4e5e1 100644
--- a/src/cmd/go/testdata/script/mod_load_badchain.txt
+++ b/src/cmd/go/testdata/script/mod_load_badchain.txt
@@ -69,17 +69,17 @@
 
 func Test(t *testing.T) {}
 -- update-main-expected --
-go get: example.com/badchain/c@v1.1.0: parsing go.mod:
+go: example.com/badchain/c@v1.1.0: parsing go.mod:
 	module declares its path as: badchain.example.com/c
 	        but was required as: example.com/badchain/c
 -- update-a-expected --
-go get: example.com/badchain/a@v1.1.0 requires
+go: example.com/badchain/a@v1.1.0 requires
 	example.com/badchain/b@v1.1.0 requires
 	example.com/badchain/c@v1.1.0: parsing go.mod:
 	module declares its path as: badchain.example.com/c
 	        but was required as: example.com/badchain/c
 -- list-expected --
-go list -m: example.com/badchain/a@v1.1.0 requires
+go: example.com/badchain/a@v1.1.0 requires
 	example.com/badchain/b@v1.1.0 requires
 	example.com/badchain/c@v1.1.0: parsing go.mod:
 	module declares its path as: badchain.example.com/c
diff --git a/src/cmd/go/testdata/script/mod_outside.txt b/src/cmd/go/testdata/script/mod_outside.txt
index 3b45594..6da6314 100644
--- a/src/cmd/go/testdata/script/mod_outside.txt
+++ b/src/cmd/go/testdata/script/mod_outside.txt
@@ -135,12 +135,12 @@
 # 'go get -u all' upgrades the transitive import graph of the main module,
 # which is empty.
 ! go get -u all
-stderr '^go get: cannot match "all": go.mod file not found in current directory or any parent directory; see ''go help modules''$'
+stderr '^go: cannot match "all": go.mod file not found in current directory or any parent directory; see ''go help modules''$'
 
 # 'go get' should check the proposed module graph for consistency,
 # even though we won't write it anywhere.
 ! go get -d example.com/printversion@v1.0.0 example.com/version@none
-stderr '^go get: example.com/printversion@v1.0.0 requires example.com/version@v1.0.0, not example.com/version@none$'
+stderr '^go: example.com/printversion@v1.0.0 requires example.com/version@v1.0.0, not example.com/version@none$'
 
 # 'go get -d' should download and extract the source code needed to build the requested version.
 rm -r $GOPATH/pkg/mod/example.com
@@ -196,7 +196,7 @@
 
 # 'go install' should fail if a package argument must be resolved to a module.
 ! go install example.com/printversion
-stderr '^go install: version is required when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$'
+stderr '^go: ''go install'' requires a version when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$'
 
 # 'go install' should fail if a source file imports a package that must be
 # resolved to a module.
diff --git a/src/cmd/go/testdata/script/mod_prefer_compatible.txt b/src/cmd/go/testdata/script/mod_prefer_compatible.txt
index 1b408c3..8e88997 100644
--- a/src/cmd/go/testdata/script/mod_prefer_compatible.txt
+++ b/src/cmd/go/testdata/script/mod_prefer_compatible.txt
@@ -24,7 +24,7 @@
 stdout '^github.com/russross/blackfriday v1\.'
 
 ! go list -m github.com/russross/blackfriday@patch
-stderr '^go list -m: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$'
+stderr '^go: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$'
 
 # If we're fetching directly from version control, ignored +incompatible
 # versions should also be omitted by 'go list'.
diff --git a/src/cmd/go/testdata/script/mod_proxy_invalid.txt b/src/cmd/go/testdata/script/mod_proxy_invalid.txt
index 6427cc1..63980b8 100644
--- a/src/cmd/go/testdata/script/mod_proxy_invalid.txt
+++ b/src/cmd/go/testdata/script/mod_proxy_invalid.txt
@@ -2,7 +2,7 @@
 env GOPROXY=$GOPROXY/invalid
 
 ! go list -m rsc.io/quote@latest
-stderr '^go list -m: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$'
+stderr '^go: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$'
 
 ! go list -m rsc.io/quote@1.5.2
-stderr '^go list -m: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$'
+stderr '^go: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$'
diff --git a/src/cmd/go/testdata/script/mod_query.txt b/src/cmd/go/testdata/script/mod_query.txt
index a75f86e..37587325 100644
--- a/src/cmd/go/testdata/script/mod_query.txt
+++ b/src/cmd/go/testdata/script/mod_query.txt
@@ -25,7 +25,7 @@
 stdout 'rsc.io/quote v1.5.2$'
 
 ! go list -m rsc.io/quote@>v1.5.3
-stderr 'go list -m: module rsc.io/quote: no matching versions for query ">v1.5.3"'
+stderr 'go: module rsc.io/quote: no matching versions for query ">v1.5.3"'
 
 go list -m -e -f '{{.Error.Err}}' rsc.io/quote@>v1.5.3
 stdout 'no matching versions for query ">v1.5.3"'
diff --git a/src/cmd/go/testdata/script/mod_query_empty.txt b/src/cmd/go/testdata/script/mod_query_empty.txt
index f8b6e3e..af08711 100644
--- a/src/cmd/go/testdata/script/mod_query_empty.txt
+++ b/src/cmd/go/testdata/script/mod_query_empty.txt
@@ -8,7 +8,7 @@
 env GOPROXY=file:///$WORK/badproxy
 cp go.mod.orig go.mod
 ! go get -d example.com/join/subpkg
-stderr 'go get: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*'
+stderr 'go: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*'
 
 # If @v/list is empty, the 'go' command should still try to resolve
 # other module paths.
@@ -40,7 +40,7 @@
 chmod 0000 $WORK/gatekeeper/example.com/join/subpkg/@latest
 cp go.mod.orig go.mod
 ! go get -d example.com/join/subpkg
-stderr 'go get: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)'
+stderr 'go: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)'
 
 -- go.mod.orig --
 module example.com/othermodule
diff --git a/src/cmd/go/testdata/script/mod_query_exclude.txt b/src/cmd/go/testdata/script/mod_query_exclude.txt
index b001969..8eae42d 100644
--- a/src/cmd/go/testdata/script/mod_query_exclude.txt
+++ b/src/cmd/go/testdata/script/mod_query_exclude.txt
@@ -19,7 +19,7 @@
 # get excluded version
 cp go.exclude.mod go.exclude.mod.orig
 ! go get -modfile=go.exclude.mod -d rsc.io/quote@v1.5.0
-stderr '^go get: rsc.io/quote@v1.5.0: excluded by go.mod$'
+stderr '^go: rsc.io/quote@v1.5.0: excluded by go.mod$'
 
 # get non-excluded version
 cp go.exclude.mod.orig go.exclude.mod
diff --git a/src/cmd/go/testdata/script/mod_query_main.txt b/src/cmd/go/testdata/script/mod_query_main.txt
index 39e5841..2a2fa42 100644
--- a/src/cmd/go/testdata/script/mod_query_main.txt
+++ b/src/cmd/go/testdata/script/mod_query_main.txt
@@ -6,9 +6,9 @@
 # 'go mod download' will not download @upgrade or @patch, since they always
 # resolve to the main module.
 go mod download rsc.io/quote@upgrade
-stderr '^go mod download: skipping argument rsc.io/quote@upgrade that resolves to the main module$'
+stderr '^go: skipping download of rsc.io/quote@upgrade that resolves to the main module$'
 go mod download rsc.io/quote@patch
-stderr '^go mod download: skipping argument rsc.io/quote@patch that resolves to the main module$'
+stderr '^go: skipping download of rsc.io/quote@patch that resolves to the main module$'
 
 # 'go list -m' can show a version of the main module.
 go list -m rsc.io/quote@5d9f230b
@@ -31,11 +31,11 @@
 # 'go get' will not attempt to upgrade the main module to any specific version.
 # See also: mod_get_main.txt.
 ! go get rsc.io/quote@5d9f230b
-stderr '^go get: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$'
+stderr '^go: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$'
 ! go get rsc.io/quote@v1.5.2
-stderr '^go get: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$'
+stderr '^go: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$'
 ! go get rsc.io/quote@latest
-stderr '^go get: can''t request version "latest" of the main module \(rsc.io/quote\)$'
+stderr '^go: can''t request version "latest" of the main module \(rsc.io/quote\)$'
 
 -- go.mod --
 module rsc.io/quote
diff --git a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt
index d24f37b..df752d9 100644
--- a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt
+++ b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt
@@ -35,7 +35,7 @@
 # A mismatched gopkg.in path should not be able to replace a different major version.
 cd ../3-to-gomod-4
 ! go list -m gopkg.in/src-d/go-git.v3
-stderr '^go list -m: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$'
+stderr '^go: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$'
 
 -- 4-to-4/go.mod --
 module golang.org/issue/34254
diff --git a/src/cmd/go/testdata/script/mod_retention.txt b/src/cmd/go/testdata/script/mod_retention.txt
index 481c10d..9d30026 100644
--- a/src/cmd/go/testdata/script/mod_retention.txt
+++ b/src/cmd/go/testdata/script/mod_retention.txt
@@ -83,14 +83,14 @@
 package x
 import _ "rsc.io/quote"
 -- go.mod.crlf --
-module m

-

-go 1.14

-

-require (

-	rsc.io/quote v1.5.2

-	rsc.io/testonly v1.0.0 // indirect

-)

+module m
+
+go 1.14
+
+require (
+	rsc.io/quote v1.5.2
+	rsc.io/testonly v1.0.0 // indirect
+)
 -- go.mod.unsorted --
 module m
 
@@ -141,10 +141,10 @@
 
 go $goversion
 
+require rsc.io/quote v1.5.2
+
 require (
-	rsc.io/quote v1.5.2
+	golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
 	rsc.io/sampler v1.3.0 // indirect
 	rsc.io/testonly v1.0.0 // indirect
 )
-
-require golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
diff --git a/src/cmd/go/testdata/script/mod_retract_fix_version.txt b/src/cmd/go/testdata/script/mod_retract_fix_version.txt
index e45758b..9ae49f5 100644
--- a/src/cmd/go/testdata/script/mod_retract_fix_version.txt
+++ b/src/cmd/go/testdata/script/mod_retract_fix_version.txt
@@ -15,7 +15,7 @@
 # If a retracted version doesn't match the module's major version suffx,
 # an error should be reported.
 ! go mod edit -retract=v3.0.1
-stderr '^go mod: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$'
+stderr '^go: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$'
 cp go.mod.mismatch-v2 go.mod
 ! go list -m all
 stderr 'go.mod:3: retract rsc.io/quote/v2: version "v3.0.1" invalid: should be v2, not v3$'
diff --git a/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt
index eb00e84..d1a5e12 100644
--- a/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt
+++ b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt
@@ -29,7 +29,7 @@
 stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.1-0.20201009173747-713affd19d7b$'
 
 ! go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371
-stderr '^go get: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$'
+stderr '^go: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$'
 
 -- retract-pseudo.sh --
 #!/bin/bash
diff --git a/src/cmd/go/testdata/script/mod_run_nonmain.txt b/src/cmd/go/testdata/script/mod_run_nonmain.txt
index 036755d..8435fc0 100644
--- a/src/cmd/go/testdata/script/mod_run_nonmain.txt
+++ b/src/cmd/go/testdata/script/mod_run_nonmain.txt
@@ -7,7 +7,7 @@
 
 ! go run ./...
 stderr '^go: warning: "\./\.\.\." matched only non-main packages$'
-stderr '^go run: no packages loaded from \./\.\.\.$'
+stderr '^go: no packages loaded from \./\.\.\.$'
 
 -- go.mod --
 module example.net/nonmain
diff --git a/src/cmd/go/testdata/script/mod_run_pkg_version.txt b/src/cmd/go/testdata/script/mod_run_pkg_version.txt
index e921fab..c3a218d 100644
--- a/src/cmd/go/testdata/script/mod_run_pkg_version.txt
+++ b/src/cmd/go/testdata/script/mod_run_pkg_version.txt
@@ -21,7 +21,7 @@
 cd m
 cp go.mod go.mod.orig
 ! go list -m all
-stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$'
+stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$'
 go run example.com/cmd/a@v1.0.0
 stdout '^a@v1.0.0$'
 cmp go.mod go.mod.orig
@@ -92,12 +92,12 @@
 
 func main() {}
 -- replace-err --
-go run: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace):
+go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace):
 	The go.mod file for the module providing named packages contains one or
 	more replace directives. It must not contain directives that would cause
 	it to be interpreted differently than if it were the main module.
 -- exclude-err --
-go run: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude):
+go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude):
 	The go.mod file for the module providing named packages contains one or
 	more exclude directives. It must not contain directives that would cause
 	it to be interpreted differently than if it were the main module.
diff --git a/src/cmd/go/testdata/script/mod_sum_readonly.txt b/src/cmd/go/testdata/script/mod_sum_readonly.txt
index 113f13e..57c5bbe 100644
--- a/src/cmd/go/testdata/script/mod_sum_readonly.txt
+++ b/src/cmd/go/testdata/script/mod_sum_readonly.txt
@@ -4,7 +4,7 @@
 # When a sum is needed to load the build list, we get an error for the
 # specific module. The .mod file is not downloaded, and go.sum is not written.
 ! go list -m all
-stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$'
+stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$'
 ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod
 ! exists go.sum
 
@@ -12,7 +12,7 @@
 # we should see the same error.
 cp go.sum.h2only go.sum
 ! go list -m all
-stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$'
+stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$'
 ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod
 cmp go.sum go.sum.h2only
 rm go.sum
@@ -21,7 +21,7 @@
 cp go.mod go.mod.orig
 go mod edit -replace rsc.io/quote@v1.5.2=rsc.io/quote@v1.5.1
 ! go list -m all
-stderr '^go list -m: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$'
+stderr '^go: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$'
 ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.mod
 ! exists go.sum
 cp go.mod.orig go.mod
diff --git a/src/cmd/go/testdata/script/mod_sumdb.txt b/src/cmd/go/testdata/script/mod_sumdb.txt
index fa3483c..dd791be 100644
--- a/src/cmd/go/testdata/script/mod_sumdb.txt
+++ b/src/cmd/go/testdata/script/mod_sumdb.txt
@@ -9,7 +9,7 @@
 cp go.mod.orig go.mod
 env GOSUMDB=$sumdb' '$proxy/sumdb-wrong
 ! go get -d rsc.io/quote
-stderr 'go get: rsc.io/quote@v1.5.2: verifying module: checksum mismatch'
+stderr 'go: rsc.io/quote@v1.5.2: verifying module: checksum mismatch'
 stderr 'downloaded: h1:3fEy'
 stderr 'localhost.localdev/sumdb: h1:wrong'
 stderr 'SECURITY ERROR\nThis download does NOT match the one reported by the checksum server.'
diff --git a/src/cmd/go/testdata/script/mod_sumdb_file_path.txt b/src/cmd/go/testdata/script/mod_sumdb_file_path.txt
index 22fcbf3..575c7c4 100644
--- a/src/cmd/go/testdata/script/mod_sumdb_file_path.txt
+++ b/src/cmd/go/testdata/script/mod_sumdb_file_path.txt
@@ -13,7 +13,7 @@
 [windows] env GOPROXY=file:///$WORK/sumproxy,https://proxy.golang.org
 [!windows] env GOPROXY=file://$WORK/sumproxy,https://proxy.golang.org
 ! go get -d golang.org/x/text@v0.3.2
-stderr '^go get: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)'
+stderr '^go: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)'
 
 # If the proxy does not claim to support the database,
 # checksum verification should fall through to the next proxy,
diff --git a/src/cmd/go/testdata/script/mod_tidy_compat.txt b/src/cmd/go/testdata/script/mod_tidy_compat.txt
index 29cae17..18b297d 100644
--- a/src/cmd/go/testdata/script/mod_tidy_compat.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_compat.txt
@@ -50,7 +50,7 @@
 
 go mod edit -go=1.16
 ! go list -m all
-stderr '^go list -m: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$'
+stderr '^go: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$'
 
 
 -- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt
index c544cb7..a45de5a 100644
--- a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt
@@ -62,7 +62,7 @@
 
 go mod edit -go=1.16
 ! go list -m all
-stderr '^go list -m: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n'
+stderr '^go: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n'
 
 
 -- go.mod --
@@ -72,10 +72,9 @@
 
 replace example.net/indirect v0.1.0 => ./indirect
 
-require (
-	example.net/ambiguous/nested v0.1.0 // indirect
-	example.net/indirect v0.1.0
-)
+require example.net/indirect v0.1.0
+
+require example.net/ambiguous/nested v0.1.0 // indirect
 -- all-m.txt --
 example.com/m
 example.net/ambiguous v0.1.0
diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt
index ea9e42e..11313f1 100644
--- a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt
@@ -97,10 +97,9 @@
 	example.net/requireincompatible v0.1.0 => ./requireincompatible
 )
 
-require (
-	example.com/retract/incompatible v1.0.0 // indirect
-	example.net/lazy v0.1.0
-)
+require example.net/lazy v0.1.0
+
+require example.com/retract/incompatible v1.0.0 // indirect
 -- incompatible.go --
 package incompatible
 
diff --git a/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt
new file mode 100644
index 0000000..8b508c7
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt
@@ -0,0 +1,58 @@
+# Verifies golang.org/issue/47738.
+
+# In this test, the user has rewritten their imports to use rsc.io/quote/v3,
+# but their go.mod still requires rsc.io/quote@v1.5.2, and they indirectly
+# require rsc.io/quote@v1.5.1 but don't import anything from it.
+go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all
+stdout '^rsc.io/quote@v1.5.2$'
+! stdout 'rsc.io/quote/v3'
+go list -e all
+! stdout '^rsc.io/quote$'
+
+# 'go mod tidy' should preserve the requirement on rsc.io/quote but mark it
+# indirect. This prevents a downgrade to v1.5.1, which could introduce
+# an ambiguity.
+go mod tidy
+go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all
+stdout '^rsc.io/quote@v1.5.2 indirect$'
+stdout '^rsc.io/quote/v3@v3.0.0$'
+
+-- go.mod --
+module use
+
+go 1.16
+
+require (
+	old-indirect v0.0.0
+	rsc.io/quote v1.5.2
+)
+
+replace old-indirect v0.0.0 => ./old-indirect
+-- go.sum --
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+rsc.io/quote v1.5.1/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
+rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0=
+rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
+rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+-- use.go --
+package use
+
+import (
+	_ "old-indirect/empty"
+
+	_ "rsc.io/quote/v3"
+)
+-- old-indirect/empty/empty.go --
+package empty
+-- old-indirect/go.mod --
+module old-indirect
+
+go 1.16
+
+require rsc.io/quote v1.5.1
+-- old-indirect/go.sum --
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/src/cmd/go/testdata/script/mod_tidy_too_new.txt b/src/cmd/go/testdata/script/mod_tidy_too_new.txt
index b9c53b5..8c34a99 100644
--- a/src/cmd/go/testdata/script/mod_tidy_too_new.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_too_new.txt
@@ -9,7 +9,7 @@
 # would look like.
 
 ! go mod tidy
-stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$'
+stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$'
 cmp go.mod go.mod.orig
 
 
@@ -18,7 +18,7 @@
 
 cp go.mod.orig go.mod
 go mod tidy -e
-stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$'
+stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$'
 cmp go.mod go.mod.tidy
 
 
diff --git a/src/cmd/go/testdata/script/mod_upgrade_patch.txt b/src/cmd/go/testdata/script/mod_upgrade_patch.txt
index 8b34f8b..b8c178c 100644
--- a/src/cmd/go/testdata/script/mod_upgrade_patch.txt
+++ b/src/cmd/go/testdata/script/mod_upgrade_patch.txt
@@ -11,7 +11,7 @@
 
 # @patch should be rejected for modules not already in the build list.
 ! go get -d patch.example.com/depofdirectpatch@patch
-stderr '^go get: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$'
+stderr '^go: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$'
 cmp go.mod.orig go.mod
 
 # get -u=patch, with no arguments, should patch-update all dependencies
@@ -38,7 +38,7 @@
 go mod edit -droprequire=patch.example.com/direct
 cp go.mod go.mod.dropped
 ! go get -d all@patch
-stderr '^go get all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$'
+stderr '^go: all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$'
 cmp go.mod.dropped go.mod
 
 # Requesting the direct dependency with -u=patch but without an explicit version
@@ -86,7 +86,7 @@
 # Standard library packages cannot be upgraded explicitly.
 cp go.mod.orig go.mod
 ! go get cmd/vet@patch
-stderr 'go get: can''t request explicit version "patch" of standard library package cmd/vet$'
+stderr 'go: can''t request explicit version "patch" of standard library package cmd/vet$'
 
 # However, standard-library packages without explicit versions are fine.
 go get -d -u=patch -d cmd/go
diff --git a/src/cmd/go/testdata/script/mod_vendor.txt b/src/cmd/go/testdata/script/mod_vendor.txt
index 2622916..4eb80c2 100644
--- a/src/cmd/go/testdata/script/mod_vendor.txt
+++ b/src/cmd/go/testdata/script/mod_vendor.txt
@@ -40,15 +40,15 @@
 
 # -mod=vendor should cause 'go list' flags that look up versions to fail.
 ! go list -mod=vendor -versions -m x
-stderr '^go list -m: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$'
+stderr '^go: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$'
 ! go list -mod=vendor -u -m x
-stderr '^go list -m: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$'
+stderr '^go: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$'
 
 # 'go list -mod=vendor -m' on a transitive dependency that does not
 # provide vendored packages should give a helpful error rather than
 # 'not a known dependency'.
 ! go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m diamondright
-stderr 'go list -m: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+stderr 'go: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
 
 # 'go list -mod=mod' should report packages outside the import graph,
 # but 'go list -mod=vendor' should error out for them.
diff --git a/src/cmd/go/testdata/script/mod_vendor_auto.txt b/src/cmd/go/testdata/script/mod_vendor_auto.txt
index b0ea907..96db5c1 100644
--- a/src/cmd/go/testdata/script/mod_vendor_auto.txt
+++ b/src/cmd/go/testdata/script/mod_vendor_auto.txt
@@ -17,10 +17,10 @@
 stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$'
 
 ! go list -m all
-stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
 
 ! go list -m -f '{{.Dir}}' all
-stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
 
 # An explicit -mod=mod should force the vendor directory to be ignored.
 env GOFLAGS=-mod=mod
@@ -105,10 +105,10 @@
 # ...but 'go list -m' should continue to fail, this time without
 # referring to a -mod default that the user didn't set.
 ! go list -m all
-stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
 
 ! go list -m -f '{{.Dir}}' all
-stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
 
 
 # 'go mod init' should work if there is already a GOPATH-mode vendor directory
diff --git a/src/cmd/go/testdata/script/mod_vendor_embed.txt b/src/cmd/go/testdata/script/mod_vendor_embed.txt
index be11415..b14fd99 100644
--- a/src/cmd/go/testdata/script/mod_vendor_embed.txt
+++ b/src/cmd/go/testdata/script/mod_vendor_embed.txt
@@ -6,11 +6,11 @@
 
 cd broken_no_matching_files
 ! go mod vendor
-stderr 'go mod vendor: pattern foo.txt: no matching files found'
+stderr 'go: pattern foo.txt: no matching files found'
 
 cd ../broken_bad_pattern
 ! go mod vendor
-stderr 'go mod vendor: pattern ../foo.txt: invalid pattern syntax'
+stderr 'go: pattern ../foo.txt: invalid pattern syntax'
 
 # matchPotentialSourceFile prunes out tests and unbuilt code.
 # Make sure that they are vendored if they are embedded files.
diff --git a/src/cmd/go/testdata/script/run_dirs.txt b/src/cmd/go/testdata/script/run_dirs.txt
index 538a6ac..bd5cfbe 100644
--- a/src/cmd/go/testdata/script/run_dirs.txt
+++ b/src/cmd/go/testdata/script/run_dirs.txt
@@ -1,11 +1,21 @@
 cd rundir
 
 ! go run x.go sub/sub.go
-stderr 'named files must all be in one directory; have ./ and sub/'
+stderr 'named files must all be in one directory; have . and sub'
 ! go run sub/sub.go x.go
-stderr 'named files must all be in one directory; have sub/ and ./'
+stderr 'named files must all be in one directory; have sub and .'
+
+cd ../
+go run rundir/foo.go ./rundir/bar.go
+stderr 'hello world'
 
 -- rundir/sub/sub.go --
 package main
 -- rundir/x.go --
 package main
+-- rundir/foo.go --
+package main
+func main() { println(msg) }
+-- rundir/bar.go --
+package main
+const msg = "hello world"
diff --git a/src/cmd/go/testdata/script/run_wildcard.txt b/src/cmd/go/testdata/script/run_wildcard.txt
index 72036d1..3e7e7b7 100644
--- a/src/cmd/go/testdata/script/run_wildcard.txt
+++ b/src/cmd/go/testdata/script/run_wildcard.txt
@@ -4,4 +4,4 @@
 # go run x/... should not panic when directory x doesn't exist.
 
 ! go run nonexistent/...
-stderr '^go run: no packages loaded from nonexistent/...$'
+stderr '^go: no packages loaded from nonexistent/...$'
diff --git a/src/cmd/go/testdata/script/test_flag.txt b/src/cmd/go/testdata/script/test_flag.txt
index 0142b3f..d168cfe 100644
--- a/src/cmd/go/testdata/script/test_flag.txt
+++ b/src/cmd/go/testdata/script/test_flag.txt
@@ -9,13 +9,13 @@
 # However, it should be an error to use custom flags when -i or -c are used,
 # since we know for sure that no test binary will run at all.
 ! go test -i -custom
-stderr '^go test: unknown flag -custom cannot be used with -i$'
+stderr '^go: unknown flag -custom cannot be used with -i$'
 ! go test -c -custom
-stderr '^go test: unknown flag -custom cannot be used with -c$'
+stderr '^go: unknown flag -custom cannot be used with -c$'
 
 # The same should apply even if -c or -i come after a custom flag.
 ! go test -custom -c
-stderr '^go test: unknown flag -custom cannot be used with -c$'
+stderr '^go: unknown flag -custom cannot be used with -c$'
 
 -- go.mod --
 module m
diff --git a/src/cmd/go/testdata/script/test_race_install.txt b/src/cmd/go/testdata/script/test_race_install.txt
index 8b1f343..a1d47a7 100644
--- a/src/cmd/go/testdata/script/test_race_install.txt
+++ b/src/cmd/go/testdata/script/test_race_install.txt
@@ -15,4 +15,4 @@
 -- pkg/pkg.go --
 package p
 -- stderr.txt --
-go test: -i flag is deprecated
+go: -i flag is deprecated
diff --git a/src/cmd/go/testdata/script/work_prune.txt b/src/cmd/go/testdata/script/work_prune.txt
new file mode 100644
index 0000000..f0fb073
--- /dev/null
+++ b/src/cmd/go/testdata/script/work_prune.txt
@@ -0,0 +1,104 @@
+# This test makes sure workspace mode's handling of the module graph
+# is compatible with module pruning. The graph we load from either of
+# the workspace modules should be the same, even if their graphs
+# don't overlap.
+#
+# This is the module graph in the test:
+#
+#  example.com/a -> example.com/b v1.0.0 -> example.com/q v1.1.0
+#  example.com/p -> example.com/q v1.0.0
+#
+# If we didn't load the whole graph and didn't load the dependencies of b
+# when loading p, we would end up loading q v1.0.0, rather than v1.1.0,
+# which is selected by MVS.
+# TODO(#48331): We currently load the wrong version of q. Fix this.
+
+go list -m -f '{{.Version}}' example.com/q
+stdout '^v1.0.0$' # TODO(#48331): This should be 1.1.0. Fix this.
+
+-- go.work --
+go 1.18
+
+directory (
+	./a
+	./p
+)
+-- a/go.mod --
+module example.com/a
+
+go 1.18
+
+require example.com/b v1.0.0
+
+replace example.com/b v1.0.0 => ../b
+-- a/foo.go --
+package main
+
+import "example.com/b"
+
+func main() {
+	b.B()
+}
+-- b/go.mod --
+module example.com/b
+
+go 1.18
+
+require example.com/q v1.1.0
+
+replace example.com/q v1.0.0 => ../q1_0_0
+replace example.com/q v1.1.0 => ../q1_1_0
+-- b/b.go --
+package b
+
+func B() {
+}
+-- b/b_test.go --
+package b
+
+import "example.com/q"
+
+func TestB() {
+	q.PrintVersion()
+}
+-- p/go.mod --
+module example.com/p
+
+go 1.18
+
+require example.com/q v1.0.0
+
+replace example.com/q v1.0.0 => ../q1_0_0
+replace example.com/q v1.1.0 => ../q1_1_0
+-- p/main.go --
+package main
+
+import "example.com/q"
+
+func main() {
+	q.PrintVersion()
+}
+-- q1_0_0/go.mod --
+module example.com/q
+
+go 1.18
+-- q1_0_0/q.go --
+package q
+
+import "fmt"
+
+func PrintVersion() {
+	fmt.Println("version 1.0.0")
+}
+-- q1_1_0/go.mod --
+module example.com/q
+
+go 1.18
+-- q1_1_0/q.go --
+package q
+
+import "fmt"
+
+func PrintVersion() {
+	fmt.Println("version 1.1.0")
+}
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index 4e163db..be37641 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -50,6 +50,7 @@
 	Abbrev        int // Either DW_ABRV_AUTO[_LOCLIST] or DW_ABRV_PARAM[_LOCLIST]
 	IsReturnValue bool
 	IsInlFormal   bool
+	DictIndex     uint16 // index of the dictionary entry describing the type of this variable
 	StackOffset   int32
 	// This package can't use the ssa package, so it can't mention ssa.FuncDebug,
 	// so indirect through a closure.
@@ -97,6 +98,8 @@
 	Scopes        []Scope
 	InlCalls      InlCalls
 	UseBASEntries bool
+
+	dictIndexToOffset []int64
 }
 
 func EnableLogging(doit bool) {
@@ -315,6 +318,7 @@
 	DW_AT_go_runtime_type   = 0x2904
 
 	DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit
+	DW_AT_go_dict_index   = 0x2906 // Attribute for DW_TAG_typedef_type, index of the dictionary entry describing the real type of this type shape
 
 	DW_AT_internal_location = 253 // params and locals; not emitted
 )
@@ -325,8 +329,10 @@
 	DW_ABRV_COMPUNIT
 	DW_ABRV_COMPUNIT_TEXTLESS
 	DW_ABRV_FUNCTION
+	DW_ABRV_WRAPPER
 	DW_ABRV_FUNCTION_ABSTRACT
 	DW_ABRV_FUNCTION_CONCRETE
+	DW_ABRV_WRAPPER_CONCRETE
 	DW_ABRV_INLINED_SUBROUTINE
 	DW_ABRV_INLINED_SUBROUTINE_RANGES
 	DW_ABRV_VARIABLE
@@ -360,6 +366,7 @@
 	DW_ABRV_STRINGTYPE
 	DW_ABRV_STRUCTTYPE
 	DW_ABRV_TYPEDECL
+	DW_ABRV_DICT_INDEX
 	DW_NABRV
 )
 
@@ -455,6 +462,19 @@
 		},
 	},
 
+	/* WRAPPER */
+	{
+		DW_TAG_subprogram,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_low_pc, DW_FORM_addr},
+			{DW_AT_high_pc, DW_FORM_addr},
+			{DW_AT_frame_base, DW_FORM_block1},
+			{DW_AT_trampoline, DW_FORM_flag},
+		},
+	},
+
 	/* FUNCTION_ABSTRACT */
 	{
 		DW_TAG_subprogram,
@@ -478,6 +498,19 @@
 		},
 	},
 
+	/* WRAPPER_CONCRETE */
+	{
+		DW_TAG_subprogram,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_abstract_origin, DW_FORM_ref_addr},
+			{DW_AT_low_pc, DW_FORM_addr},
+			{DW_AT_high_pc, DW_FORM_addr},
+			{DW_AT_frame_base, DW_FORM_block1},
+			{DW_AT_trampoline, DW_FORM_flag},
+		},
+	},
+
 	/* INLINED_SUBROUTINE */
 	{
 		DW_TAG_inlined_subroutine,
@@ -854,6 +887,17 @@
 			{DW_AT_type, DW_FORM_ref_addr},
 		},
 	},
+
+	/* DICT_INDEX */
+	{
+		DW_TAG_typedef,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_go_dict_index, DW_FORM_udata},
+		},
+	},
 }
 
 // GetAbbrev returns the contents of the .debug_abbrev section.
@@ -1168,6 +1212,9 @@
 		sort.Sort(byChildIndex(pruned.Vars))
 		scopes[k] = pruned
 	}
+
+	s.dictIndexToOffset = putparamtypes(ctxt, s, scopes, fnabbrev)
+
 	var encbuf [20]byte
 	if putscope(ctxt, s, scopes, 0, fnabbrev, encbuf[:0]) < int32(len(scopes)) {
 		return errors.New("multiple toplevel scopes")
@@ -1329,11 +1376,14 @@
 // for the function (which holds location-independent attributes such
 // as name, type), then the remainder of the attributes are specific
 // to this instance (location, frame base, etc).
-func PutConcreteFunc(ctxt Context, s *FnState) error {
+func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error {
 	if logDwarf {
 		ctxt.Logf("PutConcreteFunc(%v)\n", s.Info)
 	}
 	abbrev := DW_ABRV_FUNCTION_CONCRETE
+	if isWrapper {
+		abbrev = DW_ABRV_WRAPPER_CONCRETE
+	}
 	Uleb128put(ctxt, s.Info, int64(abbrev))
 
 	// Abstract origin.
@@ -1346,6 +1396,10 @@
 	// cfa / frame base
 	putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
 
+	if isWrapper {
+		putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
+	}
+
 	// Scopes
 	if err := putPrunedScopes(ctxt, s, abbrev); err != nil {
 		return err
@@ -1368,11 +1422,14 @@
 // when its containing package was compiled (hence there is no need to
 // emit an abstract version for it to use as a base for inlined
 // routine records).
-func PutDefaultFunc(ctxt Context, s *FnState) error {
+func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error {
 	if logDwarf {
 		ctxt.Logf("PutDefaultFunc(%v)\n", s.Info)
 	}
 	abbrev := DW_ABRV_FUNCTION
+	if isWrapper {
+		abbrev = DW_ABRV_WRAPPER
+	}
 	Uleb128put(ctxt, s.Info, int64(abbrev))
 
 	// Expand '"".' to import path.
@@ -1385,13 +1442,16 @@
 	putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
 	putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
 	putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
-	ctxt.AddFileRef(s.Info, s.Filesym)
-
-	var ev int64
-	if s.External {
-		ev = 1
+	if isWrapper {
+		putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
+	} else {
+		ctxt.AddFileRef(s.Info, s.Filesym)
+		var ev int64
+		if s.External {
+			ev = 1
+		}
+		putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
 	}
-	putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
 
 	// Scopes
 	if err := putPrunedScopes(ctxt, s, abbrev); err != nil {
@@ -1410,6 +1470,47 @@
 	return nil
 }
 
+// putparamtypes writes typedef DIEs for any parametric types that are used by this function.
+func putparamtypes(ctxt Context, s *FnState, scopes []Scope, fnabbrev int) []int64 {
+	if fnabbrev == DW_ABRV_FUNCTION_CONCRETE {
+		return nil
+	}
+
+	maxDictIndex := uint16(0)
+
+	for i := range scopes {
+		for _, v := range scopes[i].Vars {
+			if v.DictIndex > maxDictIndex {
+				maxDictIndex = v.DictIndex
+			}
+		}
+	}
+
+	if maxDictIndex == 0 {
+		return nil
+	}
+
+	dictIndexToOffset := make([]int64, maxDictIndex)
+
+	for i := range scopes {
+		for _, v := range scopes[i].Vars {
+			if v.DictIndex == 0 || dictIndexToOffset[v.DictIndex-1] != 0 {
+				continue
+			}
+
+			dictIndexToOffset[v.DictIndex-1] = ctxt.CurrentOffset(s.Info)
+
+			Uleb128put(ctxt, s.Info, int64(DW_ABRV_DICT_INDEX))
+			n := fmt.Sprintf(".param%d", v.DictIndex-1)
+			putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
+			putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+			putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DictIndex-1), nil)
+		}
+	}
+
+	return dictIndexToOffset
+}
+
 func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev int, encbuf []byte) int32 {
 
 	if logDwarf {
@@ -1489,10 +1590,10 @@
 	// Determine whether to use a concrete variable or regular variable DIE.
 	concrete := true
 	switch fnabbrev {
-	case DW_ABRV_FUNCTION:
+	case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER:
 		concrete = false
 		break
-	case DW_ABRV_FUNCTION_CONCRETE:
+	case DW_ABRV_FUNCTION_CONCRETE, DW_ABRV_WRAPPER_CONCRETE:
 		// If we're emitting a concrete subprogram DIE and the variable
 		// in question is not part of the corresponding abstract function DIE,
 		// then use the default (non-concrete) abbrev for this param.
@@ -1583,7 +1684,12 @@
 			putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil)
 		}
 		putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil)
-		putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+		if v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0 {
+			// If the type of this variable is parametric use the entry emitted by putparamtypes
+			putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, s.dictIndexToOffset[v.DictIndex-1], s.Info)
+		} else {
+			putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+		}
 	}
 
 	if abbrevUsesLoclist(abbrev) {
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go
index e2858bd..20bf0eb 100644
--- a/src/cmd/internal/goobj/objfile.go
+++ b/src/cmd/internal/goobj/objfile.go
@@ -304,6 +304,7 @@
 const (
 	SymFlagUsedInIface = 1 << iota
 	SymFlagItab
+	SymFlagDict
 )
 
 // Returns the length of the name of the symbol.
@@ -333,6 +334,7 @@
 func (s *Sym) IsGoType() bool      { return s.Flag()&SymFlagGoType != 0 }
 func (s *Sym) UsedInIface() bool   { return s.Flag2()&SymFlagUsedInIface != 0 }
 func (s *Sym) IsItab() bool        { return s.Flag2()&SymFlagItab != 0 }
+func (s *Sym) IsDict() bool        { return s.Flag2()&SymFlagDict != 0 }
 
 func (s *Sym) SetName(x string, w *Writer) {
 	binary.LittleEndian.PutUint32(s[:], uint32(len(x)))
diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go
index bf75bb4..aa7c54d 100644
--- a/src/cmd/internal/obj/arm64/a.out.go
+++ b/src/cmd/internal/obj/arm64/a.out.go
@@ -1060,9 +1060,10 @@
 
 const (
 	// shift types
-	SHIFT_LL = 0 << 22
-	SHIFT_LR = 1 << 22
-	SHIFT_AR = 2 << 22
+	SHIFT_LL  = 0 << 22
+	SHIFT_LR  = 1 << 22
+	SHIFT_AR  = 2 << 22
+	SHIFT_ROR = 3 << 22
 )
 
 // Arrangement for ARM64 SIMD instructions
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 8db25cf..5d6caae 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -3407,6 +3407,9 @@
 		o4 = os[3]
 
 	case 13: /* addop $vcon, [R], R (64 bit literal); cmp $lcon,R -> addop $lcon,R, ZR */
+		if p.Reg == REGTMP {
+			c.ctxt.Diag("cannot use REGTMP as source: %v\n", p)
+		}
 		if p.To.Reg == REG_RSP && isADDSop(p.As) {
 			c.ctxt.Diag("illegal destination register: %v\n", p)
 		}
@@ -3724,6 +3727,9 @@
 		o1 |= (uint32(r&31) << 5) | uint32(rt&31)
 
 	case 28: /* logop $vcon, [R], R (64 bit literal) */
+		if p.Reg == REGTMP {
+			c.ctxt.Diag("cannot use REGTMP as source: %v\n", p)
+		}
 		o := uint32(0)
 		num := uint8(0)
 		cls := oclass(&p.From)
@@ -4354,6 +4360,9 @@
 
 		/* reloc ops */
 	case 64: /* movT R,addr -> adrp + add + movT R, (REGTMP) */
+		if p.From.Reg == REGTMP {
+			c.ctxt.Diag("cannot use REGTMP as source: %v\n", p)
+		}
 		o1 = ADR(1, 0, REGTMP)
 		o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31
 		rel := obj.Addrel(c.cursym)
@@ -4585,6 +4594,9 @@
 		//	add Rtmp, R, Rtmp
 		//	ldp (Rtmp), (R1, R2)
 		r := int(p.From.Reg)
+		if r == REGTMP {
+			c.ctxt.Diag("REGTMP used in large offset load: %v", p)
+		}
 		if r == obj.REG_NONE {
 			r = int(o.param)
 		}
@@ -4601,6 +4613,9 @@
 	case 76:
 		//	add $O, R, Rtmp or sub $O, R, Rtmp
 		//	stp (R1, R2), (Rtmp)
+		if p.From.Reg == REGTMP || p.From.Offset == REGTMP {
+			c.ctxt.Diag("cannot use REGTMP as source: %v", p)
+		}
 		r := int(p.To.Reg)
 		if r == obj.REG_NONE {
 			r = int(o.param)
@@ -4628,6 +4643,9 @@
 		//	add Rtmp, R, Rtmp
 		//	stp (R1, R2), (Rtmp)
 		r := int(p.To.Reg)
+		if r == REGTMP || p.From.Reg == REGTMP || p.From.Offset == REGTMP {
+			c.ctxt.Diag("REGTMP used in large offset store: %v", p)
+		}
 		if r == obj.REG_NONE {
 			r = int(o.param)
 		}
@@ -4933,6 +4951,9 @@
 		o1 |= (uint32(Q&1) << 30) | (uint32((r>>5)&7) << 16) | (uint32(r&0x1f) << 5) | uint32(rt&31)
 
 	case 87: /* stp (r,r), addr(SB) -> adrp + add + stp */
+		if p.From.Reg == REGTMP || p.From.Offset == REGTMP {
+			c.ctxt.Diag("cannot use REGTMP as source: %v", p)
+		}
 		o1 = ADR(1, 0, REGTMP)
 		o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31
 		rel := obj.Addrel(c.cursym)
diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go
index 6dd53ff..29e367a 100644
--- a/src/cmd/internal/obj/dwarf.go
+++ b/src/cmd/internal/obj/dwarf.go
@@ -378,9 +378,9 @@
 		if err != nil {
 			ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
 		}
-		err = dwarf.PutConcreteFunc(dwctxt, fnstate)
+		err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper())
 	} else {
-		err = dwarf.PutDefaultFunc(dwctxt, fnstate)
+		err = dwarf.PutDefaultFunc(dwctxt, fnstate, s.Wrapper())
 	}
 	if err != nil {
 		ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 01466ea..910e6ef 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -340,6 +340,9 @@
 	if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA {
 		flag2 |= goobj.SymFlagItab
 	}
+	if strings.HasPrefix(s.Name, w.ctxt.Pkgpath) && strings.HasPrefix(s.Name[len(w.ctxt.Pkgpath):], "..dict") {
+		flag2 |= goobj.SymFlagDict
+	}
 	name := s.Name
 	if strings.HasPrefix(name, "gofile..") {
 		name = filepath.ToSlash(name)
diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go
index e57beb3..dda24a0b 100644
--- a/src/cmd/internal/obj/ppc64/a.out.go
+++ b/src/cmd/internal/obj/ppc64/a.out.go
@@ -79,8 +79,10 @@
 	REG_R30
 	REG_R31
 
-	/* F0=4128 ... F31=4159 */
-	REG_F0
+	/* Align FPR and VSR vectors such that when masked with 0x3F they produce
+	   an equivalent VSX register. */
+	/* F0=4160 ... F31=4191 */
+	REG_F0 = obj.RBasePPC64 + iota + 32
 	REG_F1
 	REG_F2
 	REG_F3
@@ -113,7 +115,7 @@
 	REG_F30
 	REG_F31
 
-	/* V0=4160 ... V31=4191 */
+	/* V0=4192 ... V31=4223 */
 	REG_V0
 	REG_V1
 	REG_V2
@@ -147,7 +149,7 @@
 	REG_V30
 	REG_V31
 
-	/* VS0=4192 ... VS63=4255 */
+	/* VS0=4224 ... VS63=4287 */
 	REG_VS0
 	REG_VS1
 	REG_VS2
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index e642413..1d92c486 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -428,15 +428,13 @@
 	{as: ASTXSIWX, a1: C_VSREG, a6: C_SOREG, type_: 86, size: 4}, /* vsx scalar as integer store, xx1-form */
 
 	/* VSX move from VSR */
-	{as: AMFVSRD, a1: C_VSREG, a6: C_REG, type_: 88, size: 4}, /* vsx move from vsr, xx1-form */
+	{as: AMFVSRD, a1: C_VSREG, a6: C_REG, type_: 88, size: 4},
 	{as: AMFVSRD, a1: C_FREG, a6: C_REG, type_: 88, size: 4},
-	{as: AMFVSRD, a1: C_VREG, a6: C_REG, type_: 88, size: 4},
 
 	/* VSX move to VSR */
-	{as: AMTVSRD, a1: C_REG, a6: C_VSREG, type_: 88, size: 4}, /* vsx move to vsr, xx1-form */
-	{as: AMTVSRD, a1: C_REG, a2: C_REG, a6: C_VSREG, type_: 88, size: 4},
-	{as: AMTVSRD, a1: C_REG, a6: C_FREG, type_: 88, size: 4},
-	{as: AMTVSRD, a1: C_REG, a6: C_VREG, type_: 88, size: 4},
+	{as: AMTVSRD, a1: C_REG, a6: C_VSREG, type_: 104, size: 4},
+	{as: AMTVSRD, a1: C_REG, a6: C_FREG, type_: 104, size: 4},
+	{as: AMTVSRDD, a1: C_REG, a2: C_REG, a6: C_VSREG, type_: 104, size: 4},
 
 	/* VSX logical */
 	{as: AXXLAND, a1: C_VSREG, a2: C_VSREG, a6: C_VSREG, type_: 90, size: 4}, /* vsx and, xx3-form */
@@ -1036,13 +1034,14 @@
 	// c.ctxt.Logf("oplook %v %d %d %d %d\n", p, a1, a2, a3, a4, a5, a6)
 	ops := oprange[p.As&obj.AMask]
 	c1 := &xcmp[a1]
+	c2 := &xcmp[a2]
 	c3 := &xcmp[a3]
 	c4 := &xcmp[a4]
 	c5 := &xcmp[a5]
 	c6 := &xcmp[a6]
 	for i := range ops {
 		op := &ops[i]
-		if int(op.a2) == a2 && c1[op.a1] && c3[op.a3] && c4[op.a4] && c5[op.a5] && c6[op.a6] {
+		if c1[op.a1] && c2[op.a2] && c3[op.a3] && c4[op.a4] && c5[op.a5] && c6[op.a6] {
 			p.Optab = uint16(cap(optab) - cap(ops) + i + 1)
 			return op
 		}
@@ -1116,6 +1115,12 @@
 			return r0iszero != 0 /*TypeKind(100016)*/
 		}
 
+	case C_VSREG:
+		/* Allow any VR argument as a VSR operand. */
+		if b == C_VREG {
+			return true
+		}
+
 	case C_ANY:
 		return true
 	}
@@ -1594,7 +1599,6 @@
 			opset(AMTVRD, r0)
 			opset(AMTVSRWA, r0)
 			opset(AMTVSRWZ, r0)
-			opset(AMTVSRDD, r0)
 			opset(AMTVSRWS, r0)
 
 		case AXXLAND: /* xxland, xxlandc, xxleqv, xxlnand */
@@ -1977,6 +1981,7 @@
 			ACMPEQB,
 			AECIWX,
 			ACLRLSLWI,
+			AMTVSRDD,
 			obj.ANOP,
 			obj.ATEXT,
 			obj.AUNDEF,
@@ -2075,50 +2080,32 @@
 }
 
 /* XX1-form 3-register operands, 1 VSR operand */
-func AOP_XX1(op uint32, d uint32, a uint32, b uint32) uint32 {
-	/* For the XX-form encodings, we need the VSX register number to be exactly */
-	/* between 0-63, so we can properly set the rightmost bits. */
-	r := d - REG_VS0
+func AOP_XX1(op uint32, r uint32, a uint32, b uint32) uint32 {
 	return op | (r&31)<<21 | (a&31)<<16 | (b&31)<<11 | (r&32)>>5
 }
 
 /* XX2-form 3-register operands, 2 VSR operands */
-func AOP_XX2(op uint32, d uint32, a uint32, b uint32) uint32 {
-	xt := d - REG_VS0
-	xb := b - REG_VS0
+func AOP_XX2(op uint32, xt uint32, a uint32, xb uint32) uint32 {
 	return op | (xt&31)<<21 | (a&3)<<16 | (xb&31)<<11 | (xb&32)>>4 | (xt&32)>>5
 }
 
 /* XX3-form 3 VSR operands */
-func AOP_XX3(op uint32, d uint32, a uint32, b uint32) uint32 {
-	xt := d - REG_VS0
-	xa := a - REG_VS0
-	xb := b - REG_VS0
+func AOP_XX3(op uint32, xt uint32, xa uint32, xb uint32) uint32 {
 	return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5
 }
 
 /* XX3-form 3 VSR operands + immediate */
-func AOP_XX3I(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 {
-	xt := d - REG_VS0
-	xa := a - REG_VS0
-	xb := b - REG_VS0
+func AOP_XX3I(op uint32, xt uint32, xa uint32, xb uint32, c uint32) uint32 {
 	return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (c&3)<<8 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5
 }
 
 /* XX4-form, 4 VSR operands */
-func AOP_XX4(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 {
-	xt := d - REG_VS0
-	xa := a - REG_VS0
-	xb := b - REG_VS0
-	xc := c - REG_VS0
+func AOP_XX4(op uint32, xt uint32, xa uint32, xb uint32, xc uint32) uint32 {
 	return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (xc&31)<<6 | (xc&32)>>2 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5
 }
 
 /* DQ-form, VSR register, register + offset operands */
-func AOP_DQ(op uint32, d uint32, a uint32, b uint32) uint32 {
-	/* For the DQ-form encodings, we need the VSX register number to be exactly */
-	/* between 0-63, so we can properly set the SX bit. */
-	r := d - REG_VS0
+func AOP_DQ(op uint32, xt uint32, a uint32, b uint32) uint32 {
 	/* The EA for this instruction form is (RA) + DQ << 4, where DQ is a 12-bit signed integer. */
 	/* In order to match the output of the GNU objdump (and make the usage in Go asm easier), the */
 	/* instruction is called using the sign extended value (i.e. a valid offset would be -32752 or 32752, */
@@ -2126,7 +2113,7 @@
 	/* bits 0 to 3 in 'dq' need to be zero, otherwise this will generate an illegal instruction. */
 	/* If in doubt how this instruction form is encoded, refer to ISA 3.0b, pages 492 and 507. */
 	dq := b >> 4
-	return op | (r&31)<<21 | (a&31)<<16 | (dq&4095)<<4 | (r&32)>>2
+	return op | (xt&31)<<21 | (a&31)<<16 | (dq&4095)<<4 | (xt&32)>>2
 }
 
 /* Z23-form, 3-register operands + CY field */
@@ -3586,33 +3573,8 @@
 		/* 3-register operand order: (RB)(RA*1), XT */
 		o1 = AOP_XX1(c.oploadx(p.As), uint32(p.To.Reg), uint32(p.From.Index), uint32(p.From.Reg))
 
-	case 88: /* VSX instructions, XX1-form */
-		/* reg reg none OR reg reg reg */
-		/* 3-register operand order: RA, RB, XT */
-		/* 2-register operand order: XS, RA or RA, XT */
-		xt := int32(p.To.Reg)
-		xs := int32(p.From.Reg)
-		/* We need to treat the special case of extended mnemonics that may have a FREG/VREG as an argument */
-		if REG_V0 <= xt && xt <= REG_V31 {
-			/* Convert V0-V31 to VS32-VS63 */
-			xt = xt + 64
-			o1 = AOP_XX1(c.oprrr(p.As), uint32(xt), uint32(p.From.Reg), uint32(p.Reg))
-		} else if REG_F0 <= xt && xt <= REG_F31 {
-			/* Convert F0-F31 to VS0-VS31 */
-			xt = xt + 64
-			o1 = AOP_XX1(c.oprrr(p.As), uint32(xt), uint32(p.From.Reg), uint32(p.Reg))
-		} else if REG_VS0 <= xt && xt <= REG_VS63 {
-			o1 = AOP_XX1(c.oprrr(p.As), uint32(xt), uint32(p.From.Reg), uint32(p.Reg))
-		} else if REG_V0 <= xs && xs <= REG_V31 {
-			/* Likewise for XS */
-			xs = xs + 64
-			o1 = AOP_XX1(c.oprrr(p.As), uint32(xs), uint32(p.To.Reg), uint32(p.Reg))
-		} else if REG_F0 <= xs && xs <= REG_F31 {
-			xs = xs + 64
-			o1 = AOP_XX1(c.oprrr(p.As), uint32(xs), uint32(p.To.Reg), uint32(p.Reg))
-		} else if REG_VS0 <= xs && xs <= REG_VS63 {
-			o1 = AOP_XX1(c.oprrr(p.As), uint32(xs), uint32(p.To.Reg), uint32(p.Reg))
-		}
+	case 88: /* VSX mfvsr* instructions, XX1-form XS,RA */
+		o1 = AOP_XX1(c.oprrr(p.As), uint32(p.From.Reg), uint32(p.To.Reg), uint32(p.Reg))
 
 	case 89: /* VSX instructions, XX2-form */
 		/* reg none reg OR reg imm reg */
@@ -3743,6 +3705,9 @@
 		mb := uint32(c.regoff(&p.RestArgs[0].Addr))
 		me := uint32(c.regoff(&p.RestArgs[1].Addr))
 		o1 = OP_RLW(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.Reg), uint32(p.From.Reg), mb, me)
+
+	case 104: /* VSX mtvsr* instructions, XX1-form RA,RB,XT */
+		o1 = AOP_XX1(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg))
 	}
 
 	out[0] = o1
diff --git a/src/cmd/internal/obj/ppc64/asm_test.go b/src/cmd/internal/obj/ppc64/asm_test.go
index 70dabc2..b851d3c 100644
--- a/src/cmd/internal/obj/ppc64/asm_test.go
+++ b/src/cmd/internal/obj/ppc64/asm_test.go
@@ -107,3 +107,44 @@
 		t.Errorf("Invalid alignment not detected for PCALIGN\n")
 	}
 }
+
+// Verify register constants are correctly aligned. Much of the ppc64 assembler assumes masking out significant
+// bits will produce a valid register number:
+// REG_Rx & 31 == x
+// REG_Fx & 31 == x
+// REG_Vx & 31 == x
+// REG_VSx & 63 == x
+// REG_SPRx & 1023 == x
+// REG_CRx & 7 == x
+//
+// VR and FPR disjointly overlap VSR, interpreting as VSR registers should produce the correctly overlapped VSR.
+// REG_FPx & 63 == x
+// REG_Vx & 63 == x + 32
+func TestRegValueAlignment(t *testing.T) {
+	tstFunc := func(rstart, rend, msk, rout int) {
+		for i := rstart; i <= rend; i++ {
+			if i&msk != rout {
+				t.Errorf("%v is not aligned to 0x%X (expected %d, got %d)\n", rconv(i), msk, rout, rstart&msk)
+			}
+			rout++
+		}
+	}
+	var testType = []struct {
+		rstart int
+		rend   int
+		msk    int
+		rout   int
+	}{
+		{REG_VS0, REG_VS63, 63, 0},
+		{REG_R0, REG_R31, 31, 0},
+		{REG_F0, REG_F31, 31, 0},
+		{REG_V0, REG_V31, 31, 0},
+		{REG_V0, REG_V31, 63, 32},
+		{REG_F0, REG_F31, 63, 0},
+		{REG_SPR0, REG_SPR0 + 1023, 1023, 0},
+		{REG_CR0, REG_CR7, 7, 0},
+	}
+	for _, t := range testType {
+		tstFunc(t.rstart, t.rend, t.msk, t.rout)
+	}
+}
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index c2722b0..ee93fe0 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -294,9 +294,9 @@
 		//     BL (LR)
 		var sym *obj.LSym
 		if p.As == obj.ADUFFZERO {
-			sym = c.ctxt.Lookup("runtime.duffzero")
+			sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
 		} else {
-			sym = c.ctxt.Lookup("runtime.duffcopy")
+			sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
 		}
 		offset := p.To.Offset
 		p.As = AMOVD
@@ -687,7 +687,6 @@
 					q.From.Reg = REG_LR
 					q.To.Type = obj.TYPE_REG
 					q.To.Reg = REGTMP
-
 					prologueEnd = q
 
 					q = obj.Appendp(q, c.newprog)
@@ -787,14 +786,14 @@
 				q.From.Reg = REGG
 				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
 				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REG_R3
+				q.To.Reg = REG_R22
 
 				q = obj.Appendp(q, c.newprog)
 				q.As = ACMP
 				q.From.Type = obj.TYPE_REG
 				q.From.Reg = REG_R0
 				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REG_R3
+				q.To.Reg = REG_R22
 
 				q = obj.Appendp(q, c.newprog)
 				q.As = ABEQ
@@ -804,10 +803,10 @@
 				q = obj.Appendp(q, c.newprog)
 				q.As = AMOVD
 				q.From.Type = obj.TYPE_MEM
-				q.From.Reg = REG_R3
+				q.From.Reg = REG_R22
 				q.From.Offset = 0 // Panic.argp
 				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REG_R4
+				q.To.Reg = REG_R23
 
 				q = obj.Appendp(q, c.newprog)
 				q.As = AADD
@@ -815,14 +814,14 @@
 				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
 				q.Reg = REGSP
 				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REG_R5
+				q.To.Reg = REG_R24
 
 				q = obj.Appendp(q, c.newprog)
 				q.As = ACMP
 				q.From.Type = obj.TYPE_REG
-				q.From.Reg = REG_R4
+				q.From.Reg = REG_R23
 				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REG_R5
+				q.To.Reg = REG_R24
 
 				q = obj.Appendp(q, c.newprog)
 				q.As = ABNE
@@ -835,14 +834,14 @@
 				q.From.Offset = c.ctxt.FixedFrameSize()
 				q.Reg = REGSP
 				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REG_R6
+				q.To.Reg = REG_R25
 
 				q = obj.Appendp(q, c.newprog)
 				q.As = AMOVD
 				q.From.Type = obj.TYPE_REG
-				q.From.Reg = REG_R6
+				q.From.Reg = REG_R25
 				q.To.Type = obj.TYPE_MEM
-				q.To.Reg = REG_R3
+				q.To.Reg = REG_R22
 				q.To.Offset = 0 // Panic.argp
 
 				q = obj.Appendp(q, c.newprog)
@@ -1051,7 +1050,7 @@
 func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
 	p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
 
-	// MOVD	g_stackguard(g), R3
+	// MOVD	g_stackguard(g), R22
 	p = obj.Appendp(p, c.newprog)
 
 	p.As = AMOVD
@@ -1062,7 +1061,7 @@
 		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
 	}
 	p.To.Type = obj.TYPE_REG
-	p.To.Reg = REG_R3
+	p.To.Reg = REG_R22
 
 	// Mark the stack bound check and morestack call async nonpreemptible.
 	// If we get preempted here, when resumed the preemption request is
@@ -1078,7 +1077,7 @@
 
 		p.As = ACMPU
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = REG_R3
+		p.From.Reg = REG_R22
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = REGSP
 	} else {
@@ -1108,14 +1107,14 @@
 				p.From.Type = obj.TYPE_CONST
 				p.From.Offset = offset
 				p.To.Type = obj.TYPE_REG
-				p.To.Reg = REG_R4
+				p.To.Reg = REG_R23
 
 				p = obj.Appendp(p, c.newprog)
 				p.As = ACMPU
 				p.From.Type = obj.TYPE_REG
 				p.From.Reg = REGSP
 				p.To.Type = obj.TYPE_REG
-				p.To.Reg = REG_R4
+				p.To.Reg = REG_R23
 			}
 
 			p = obj.Appendp(p, c.newprog)
@@ -1134,14 +1133,14 @@
 		p.From.Offset = -offset
 		p.Reg = REGSP
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = REG_R4
+		p.To.Reg = REG_R23
 
 		p = obj.Appendp(p, c.newprog)
 		p.As = ACMPU
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = REG_R3
+		p.From.Reg = REG_R22
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = REG_R4
+		p.To.Reg = REG_R23
 	}
 
 	// q1: BLT	done
@@ -1151,17 +1150,25 @@
 	p.As = ABLT
 	p.To.Type = obj.TYPE_BRANCH
 
-	// MOVD	LR, R5
 	p = obj.Appendp(p, c.newprog)
+	p.As = obj.ANOP // zero-width place holder
 
+	if q != nil {
+		q.To.SetTarget(p)
+	}
+
+	// Spill the register args that could be clobbered by the
+	// morestack code.
+
+	spill := c.cursym.Func().SpillRegisterArgs(p, c.newprog)
+
+	// MOVD LR, R5
+	p = obj.Appendp(spill, c.newprog)
 	p.As = AMOVD
 	p.From.Type = obj.TYPE_REG
 	p.From.Reg = REG_LR
 	p.To.Type = obj.TYPE_REG
 	p.To.Reg = REG_R5
-	if q != nil {
-		q.To.SetTarget(p)
-	}
 
 	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
 
@@ -1181,8 +1188,7 @@
 		// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
 		// the caller's frame, but not used (0(SP) is caller's saved LR,
 		// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
-
-		// MOVD R12, 8(SP)
+		// MOVD R2, 8(SP)
 		p = obj.Appendp(p, c.newprog)
 		p.As = AMOVD
 		p.From.Type = obj.TYPE_REG
@@ -1249,7 +1255,8 @@
 		p.To.Reg = REG_R2
 	}
 
-	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
+	unspill := c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
+	p = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1)
 
 	// BR	start
 	p = obj.Appendp(p, c.newprog)
diff --git a/src/cmd/internal/obj/riscv/anames.go b/src/cmd/internal/obj/riscv/anames.go
index 6581bb3..d2a3674 100644
--- a/src/cmd/internal/obj/riscv/anames.go
+++ b/src/cmd/internal/obj/riscv/anames.go
@@ -236,6 +236,8 @@
 	"BLEZ",
 	"BLTZ",
 	"BNEZ",
+	"FABSD",
+	"FABSS",
 	"FNEGD",
 	"FNEGS",
 	"FNED",
diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go
index 1519dc1..a258367 100644
--- a/src/cmd/internal/obj/riscv/cpu.go
+++ b/src/cmd/internal/obj/riscv/cpu.go
@@ -590,6 +590,8 @@
 	ABLEZ
 	ABLTZ
 	ABNEZ
+	AFABSD
+	AFABSS
 	AFNEGD
 	AFNEGS
 	AFNED
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index fafde64..f0ea21d 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -1003,6 +1003,7 @@
 	wantImmI(ctxt, ins.as, ins.imm, 12)
 	wantIntReg(ctxt, ins.as, "rd", ins.rd)
 	wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
+	wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
 	wantNoneReg(ctxt, ins.as, "rs3", ins.rs3)
 }
 
@@ -1010,6 +1011,7 @@
 	wantImmI(ctxt, ins.as, ins.imm, 12)
 	wantFloatReg(ctxt, ins.as, "rd", ins.rd)
 	wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
+	wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
 	wantNoneReg(ctxt, ins.as, "rs3", ins.rs3)
 }
 
@@ -1017,6 +1019,7 @@
 	wantImmI(ctxt, ins.as, ins.imm, 12)
 	wantIntReg(ctxt, ins.as, "rd", ins.rd)
 	wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
+	wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
 	wantNoneReg(ctxt, ins.as, "rs3", ins.rs3)
 }
 
@@ -1024,6 +1027,7 @@
 	wantImmI(ctxt, ins.as, ins.imm, 12)
 	wantIntReg(ctxt, ins.as, "rd", ins.rd)
 	wantFloatReg(ctxt, ins.as, "rs1", ins.rs1)
+	wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
 	wantNoneReg(ctxt, ins.as, "rs3", ins.rs3)
 }
 
@@ -1567,7 +1571,7 @@
 func instructionsForOpImmediate(p *obj.Prog, as obj.As, rs int16) []*instruction {
 	// <opi> $imm, REG, TO
 	ins := instructionForProg(p)
-	ins.as, ins.rs1 = as, uint32(rs)
+	ins.as, ins.rs1, ins.rs2 = as, uint32(rs), obj.REG_NONE
 
 	low, high, err := Split32BitImmediate(ins.imm)
 	if err != nil {
@@ -1990,7 +1994,7 @@
 	case ASEQZ:
 		// SEQZ rs, rd -> SLTIU $1, rs, rd
 		ins.as = ASLTIU
-		ins.rs1 = uint32(p.From.Reg)
+		ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
 		ins.imm = 1
 
 	case ASNEZ:
@@ -1998,6 +2002,16 @@
 		ins.as = ASLTU
 		ins.rs1 = REG_ZERO
 
+	case AFABSS:
+		// FABSS rs, rd -> FSGNJXS rs, rs, rd
+		ins.as = AFSGNJXS
+		ins.rs1 = uint32(p.From.Reg)
+
+	case AFABSD:
+		// FABSD rs, rd -> FSGNJXD rs, rs, rd
+		ins.as = AFSGNJXD
+		ins.rs1 = uint32(p.From.Reg)
+
 	case AFNEGS:
 		// FNEGS rs, rd -> FSGNJNS rs, rs, rd
 		ins.as = AFSGNJNS
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index e8441a6..0c9dde7 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -7,6 +7,7 @@
 import (
 	"bytes"
 	"cmd/internal/objabi"
+	"cmd/internal/src"
 	"fmt"
 	"internal/buildcfg"
 	"io"
@@ -47,6 +48,10 @@
 	return pos.Filename()
 }
 
+func (p *Prog) AllPos(result []src.Pos) []src.Pos {
+	return p.Ctxt.AllPos(p.Pos, result)
+}
+
 var armCondCode = []string{
 	".EQ",
 	".NE",
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index 20f1d0b..4045c97 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -195,8 +195,7 @@
 
 	// Internally linking cgo is incomplete on some architectures.
 	// https://golang.org/issue/14449
-	// https://golang.org/issue/21961
-	if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
+	if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.RISCV64) {
 		return true, buildcfg.GOARCH + " does not support internal cgo"
 	}
 	if iscgo && (buildcfg.GOOS == "android" || buildcfg.GOOS == "dragonfly") {
@@ -209,12 +208,9 @@
 		// windows/arm64 internal linking is not implemented.
 		return true, buildcfg.GOOS + "/" + buildcfg.GOARCH + " does not support internal cgo"
 	}
-
-	// When the race flag is set, the LLVM tsan relocatable file is linked
-	// into the final binary, which means external linking is required because
-	// internal linking does not support it.
-	if *flagRace && ctxt.Arch.InFamily(sys.PPC64) {
-		return true, "race on " + buildcfg.GOARCH
+	if iscgo && ctxt.Arch == sys.ArchPPC64 {
+		// Big Endian PPC64 cgo internal linking is not implemented for aix or linux.
+		return true, buildcfg.GOOS + " does not support internal cgo"
 	}
 
 	// Some build modes require work the internal linker cannot do (yet).
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 70138d3..d72846a 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -67,20 +67,6 @@
 	dwmu *sync.Mutex
 }
 
-func newdwctxt(linkctxt *Link, forTypeGen bool) dwctxt {
-	d := dwctxt{
-		linkctxt: linkctxt,
-		ldr:      linkctxt.loader,
-		arch:     linkctxt.Arch,
-		tmap:     make(map[string]loader.Sym),
-		tdmap:    make(map[loader.Sym]loader.Sym),
-		rtmap:    make(map[loader.Sym]loader.Sym),
-	}
-	d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface")
-	d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface")
-	return d
-}
-
 // dwSym wraps a loader.Sym; this type is meant to obey the interface
 // rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and
 // DwAttr objects contain references to symbols via this type.
@@ -249,7 +235,7 @@
 // up all attrs in a single large table, then store indices into the
 // table in the DIE. This would allow us to common up storage for
 // attributes that are shared by many DIEs (ex: byte size of N).
-func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr {
+func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) {
 	a := new(dwarf.DWAttr)
 	a.Link = die.Attr
 	die.Attr = a
@@ -257,7 +243,6 @@
 	a.Cls = uint8(cls)
 	a.Value = value
 	a.Data = data
-	return a
 }
 
 // Each DIE (except the root ones) has at least 1 attribute: its
@@ -290,7 +275,7 @@
 // The compiler does create nameless DWARF DIEs (ex: concrete subprogram
 // instance).
 // FIXME: it would be more efficient to bulk-allocate DIEs.
-func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie {
+func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string) *dwarf.DWDie {
 	die := new(dwarf.DWDie)
 	die.Abbrev = abbrev
 	die.Link = parent.Child
@@ -298,10 +283,9 @@
 
 	newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)
 
-	// Sanity check: all DIEs created in the linker should have a non-empty
-	// name and be version zero.
-	if name == "" || version != 0 {
-		panic("nameless or version non-zero DWARF DIE")
+	// Sanity check: all DIEs created in the linker should be named.
+	if name == "" {
+		panic("nameless DWARF DIE")
 	}
 
 	var st sym.SymKind
@@ -321,7 +305,7 @@
 		// this also includes loose ends such as STRUCT_FIELD.
 		st = sym.SDWARFTYPE
 	}
-	ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, version)
+	ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, 0)
 	dsu := d.ldr.MakeSymbolUpdater(ds)
 	dsu.SetType(st)
 	d.ldr.SetAttrNotInSymbolTable(ds, true)
@@ -397,22 +381,20 @@
 	return r
 }
 
-func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) int64 {
-	var result int64
+func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) {
 	switch size {
 	default:
 		d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size)
 	case d.arch.PtrSize, 4:
 	}
-	result = sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size)
-	return result
+	sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size)
 }
 
-func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr {
+func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) {
 	if ref == 0 {
-		return nil
+		return
 	}
-	return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref))
+	newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref))
 }
 
 func (d *dwctxt) dtolsym(s dwarf.Sym) loader.Sym {
@@ -481,7 +463,7 @@
 	return symIdx
 }
 
-func (d *dwctxt) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, def *dwarf.DWDie) *dwarf.DWDie {
+func (d *dwctxt) dotypedef(parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie {
 	// Only emit typedefs for real names.
 	if strings.HasPrefix(name, "map[") {
 		return nil
@@ -513,7 +495,7 @@
 	// so that future lookups will find the typedef instead
 	// of the real definition. This hooks the typedef into any
 	// circular definition loops, so that gdb can understand them.
-	die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
+	die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name)
 
 	d.newrefattr(die, dwarf.DW_AT_type, tds)
 
@@ -558,7 +540,7 @@
 	var die, typedefdie *dwarf.DWDie
 	switch kind {
 	case objabi.KindBool:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
@@ -567,7 +549,7 @@
 		objabi.KindInt16,
 		objabi.KindInt32,
 		objabi.KindInt64:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
@@ -577,29 +559,29 @@
 		objabi.KindUint32,
 		objabi.KindUint64,
 		objabi.KindUintptr:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindFloat32,
 		objabi.KindFloat64:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindComplex64,
 		objabi.KindComplex128:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name)
 		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindArray:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
-		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name)
+		typedefdie = d.dotypedef(&dwtypes, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 		s := decodetypeArrayElem(d.ldr, d.arch, gotype)
 		d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
-		fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0)
+		fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range")
 
 		// use actual length not upper bound; correct for 0-length arrays.
 		newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(d.ldr, d.arch, gotype), 0)
@@ -607,7 +589,7 @@
 		d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
 
 	case objabi.KindChan:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name)
 		s := decodetypeChanElem(d.ldr, d.arch, gotype)
 		d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s))
 		// Save elem type for synthesizechantypes. We could synthesize here
@@ -615,9 +597,9 @@
 		d.newrefattr(die, dwarf.DW_AT_type, s)
 
 	case objabi.KindFunc:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
-		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		typedefdie = d.dotypedef(&dwtypes, name, die)
 		data := d.ldr.Data(gotype)
 		// FIXME: add caching or reuse reloc slice.
 		relocs := d.ldr.Relocs(gotype)
@@ -625,24 +607,24 @@
 		for i := 0; i < nfields; i++ {
 			s := decodetypeFuncInType(d.ldr, d.arch, gotype, &relocs, i)
 			sn := d.ldr.SymName(s)
-			fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0)
+			fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:])
 			d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
 		}
 
 		if decodetypeFuncDotdotdot(d.arch, data) {
-			d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
+			d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...")
 		}
 		nfields = decodetypeFuncOutCount(d.arch, data)
 		for i := 0; i < nfields; i++ {
 			s := decodetypeFuncOutType(d.ldr, d.arch, gotype, &relocs, i)
 			sn := d.ldr.SymName(s)
-			fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0)
+			fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:])
 			d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s)))
 		}
 
 	case objabi.KindInterface:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
-		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name)
+		typedefdie = d.dotypedef(&dwtypes, name, die)
 		data := d.ldr.Data(gotype)
 		nfields := int(decodetypeIfaceMethodCount(d.arch, data))
 		var s loader.Sym
@@ -654,7 +636,7 @@
 		d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
 
 	case objabi.KindMap:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name)
 		s := decodetypeMapKey(d.ldr, d.arch, gotype)
 		d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s))
 		s = decodetypeMapValue(d.ldr, d.arch, gotype)
@@ -664,26 +646,26 @@
 		d.newrefattr(die, dwarf.DW_AT_type, gotype)
 
 	case objabi.KindPtr:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0)
-		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name)
+		typedefdie = d.dotypedef(&dwtypes, name, die)
 		s := decodetypePtrElem(d.ldr, d.arch, gotype)
 		d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s))
 
 	case objabi.KindSlice:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
-		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name)
+		typedefdie = d.dotypedef(&dwtypes, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 		s := decodetypeArrayElem(d.ldr, d.arch, gotype)
 		elem := d.defgotype(s)
 		d.newrefattr(die, dwarf.DW_AT_go_elem, elem)
 
 	case objabi.KindString:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case objabi.KindStruct:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
-		typedefdie = d.dotypedef(&dwtypes, gotype, name, die)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name)
+		typedefdie = d.dotypedef(&dwtypes, name, die)
 		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 		nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype)
 		for i := 0; i < nfields; i++ {
@@ -693,7 +675,7 @@
 				sn := d.ldr.SymName(s)
 				f = sn[5:] // skip "type."
 			}
-			fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f, 0)
+			fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f)
 			d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
 			offsetAnon := decodetypeStructFieldOffsAnon(d.ldr, d.arch, gotype, i)
 			newmemberoffsetattr(fld, int32(offsetAnon>>1))
@@ -703,11 +685,11 @@
 		}
 
 	case objabi.KindUnsafePointer:
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name)
 
 	default:
 		d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
-		die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0)
+		die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name)
 		d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("<unspecified>"))
 	}
 
@@ -754,7 +736,7 @@
 		return die
 	}
 
-	pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
+	pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname)
 	d.newrefattr(pdie, dwarf.DW_AT_type, dwtype)
 
 	// The DWARF info synthesizes pointer types that don't exist at the
@@ -782,7 +764,7 @@
 		if src == except {
 			continue
 		}
-		c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0)
+		c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string))
 		for a := src.Attr; a != nil; a = a.Link {
 			newattr(c, a.Atr, int(a.Cls), a.Value, a.Data)
 		}
@@ -877,7 +859,7 @@
 	if s != 0 && d.ldr.SymType(s) == sym.SDWARFTYPE {
 		return s
 	}
-	die := d.newdie(&dwtypes, abbrev, name, 0)
+	die := d.newdie(&dwtypes, abbrev, name)
 	f(die)
 	return d.dtolsym(die.Sym)
 }
@@ -922,7 +904,7 @@
 				t = d.defptrto(keytype)
 			}
 			d.newrefattr(dwhk, dwarf.DW_AT_type, t)
-			fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+			fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size")
 			newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
 			d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
 		})
@@ -936,7 +918,7 @@
 				t = d.defptrto(valtype)
 			}
 			d.newrefattr(dwhv, dwarf.DW_AT_type, t)
-			fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+			fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size")
 			newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
 			d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
 		})
@@ -947,17 +929,17 @@
 			// bucket. "data" will be replaced with keys/values below.
 			d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
 
-			fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0)
+			fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys")
 			d.newrefattr(fld, dwarf.DW_AT_type, dwhks)
 			newmemberoffsetattr(fld, BucketSize)
-			fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0)
+			fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values")
 			d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
 			newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
-			fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
+			fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow")
 			d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym)))
 			newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
 			if d.arch.RegSize > d.arch.PtrSize {
-				fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0)
+				fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad")
 				d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
 				newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize))
 			}
@@ -1672,7 +1654,7 @@
 // newly created builtin type DIE 'typeDie'.
 func (d *dwctxt) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie {
 	// create type DIE
-	die := d.newdie(&dwtypes, abrv, tname, 0)
+	die := d.newdie(&dwtypes, abrv, tname)
 
 	// Look up type symbol.
 	gotype := d.lookupOrDiag("type." + tname)
@@ -1765,7 +1747,16 @@
 		return
 	}
 
-	d := newdwctxt(ctxt, true)
+	d := &dwctxt{
+		linkctxt: ctxt,
+		ldr:      ctxt.loader,
+		arch:     ctxt.Arch,
+		tmap:     make(map[string]loader.Sym),
+		tdmap:    make(map[loader.Sym]loader.Sym),
+		rtmap:    make(map[loader.Sym]loader.Sym),
+	}
+	d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface")
+	d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface")
 
 	if ctxt.HeadType == objabi.Haix {
 		// Initial map used to store package size for each DWARF section.
@@ -1776,7 +1767,7 @@
 	newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes")
 
 	// Unspecified type. There are no references to this in the symbol table.
-	d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0)
+	d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>")
 
 	// Some types that must exist to define other ones (uintptr in particular
 	// is needed for array size)
@@ -1841,7 +1832,7 @@
 			if len(unit.Textp) == 0 {
 				cuabrv = dwarf.DW_ABRV_COMPUNIT_TEXTLESS
 			}
-			unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg, 0)
+			unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg)
 			newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0)
 			// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
 			compDir := getCompilationDir()
@@ -1899,6 +1890,8 @@
 	// global variables. For each global of this sort, locate
 	// the corresponding compiler-generated DIE symbol and tack
 	// it onto the list associated with the unit.
+	// Also looks for dictionary symbols and generates DIE symbols for each
+	// type they reference.
 	for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ {
 		if !d.ldr.AttrReachable(idx) ||
 			d.ldr.AttrNotInSymbolTable(idx) ||
@@ -1912,9 +1905,21 @@
 		default:
 			continue
 		}
-		// Skip things with no type
+		// Skip things with no type, unless it's a dictionary
 		gt := d.ldr.SymGoType(idx)
 		if gt == 0 {
+			if t == sym.SRODATA {
+				if d.ldr.IsDict(idx) {
+					// This is a dictionary, make sure that all types referenced by this dictionary are reachable
+					relocs := d.ldr.Relocs(idx)
+					for i := 0; i < relocs.Count(); i++ {
+						reloc := relocs.At(i)
+						if reloc.Type() == objabi.R_USEIFACE {
+							d.defgotype(reloc.Sym())
+						}
+					}
+				}
+			}
 			continue
 		}
 		// Skip file local symbols (this includes static tmps, stack
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index 543dd5c..db90024 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -11,6 +11,7 @@
 	"debug/pe"
 	"errors"
 	"fmt"
+	"internal/buildcfg"
 	"internal/testenv"
 	"io"
 	"io/ioutil"
@@ -614,9 +615,6 @@
 	if runtime.GOOS == "plan9" {
 		t.Skip("skipping on plan9; no DWARF symbol table in executables")
 	}
-	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
-		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
-	}
 
 	t.Parallel()
 
@@ -851,9 +849,6 @@
 	if runtime.GOOS == "plan9" {
 		t.Skip("skipping on plan9; no DWARF symbol table in executables")
 	}
-	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
-		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
-	}
 
 	if wd, err := os.Getwd(); err == nil {
 		gopathdir := filepath.Join(wd, "testdata", "httptest")
@@ -869,9 +864,6 @@
 	if runtime.GOOS == "plan9" {
 		t.Skip("skipping on plan9; no DWARF symbol table in executables")
 	}
-	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
-		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
-	}
 	if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" {
 		t.Skip("skipping on not-amd64 not-386; location lists not supported")
 	}
@@ -890,9 +882,6 @@
 	if runtime.GOOS == "plan9" {
 		t.Skip("skipping on plan9; no DWARF symbol table in executables")
 	}
-	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
-		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
-	}
 	if wd, err := os.Getwd(); err == nil {
 		gopathdir := filepath.Join(wd, "testdata", "issue26237")
 		abstractOriginSanity(t, gopathdir, DefaultOpt)
@@ -1758,3 +1747,105 @@
 			expected, found)
 	}
 }
+
+func TestDictIndex(t *testing.T) {
+	// Check that variables with a parametric type have a dictionary index
+	// attribute and that types that are only referenced through dictionaries
+	// have DIEs.
+	testenv.MustHaveGoBuild(t)
+
+	if runtime.GOOS == "plan9" {
+		t.Skip("skipping on plan9; no DWARF symbol table in executables")
+	}
+	if buildcfg.Experiment.Unified {
+		t.Skip("GOEXPERIMENT=unified does not emit dictionaries yet")
+	}
+	t.Parallel()
+
+	const prog = `
+package main
+
+import "fmt"
+
+type CustomInt int
+
+func testfn[T any](arg T) {
+	var mapvar = make(map[int]T)
+	mapvar[0] = arg
+	fmt.Println(arg, mapvar)
+}
+
+func main() {
+	testfn(CustomInt(3))
+}
+`
+
+	dir := t.TempDir()
+	f := gobuild(t, dir, prog, NoOpt)
+	defer f.Close()
+
+	d, err := f.DWARF()
+	if err != nil {
+		t.Fatalf("error reading DWARF: %v", err)
+	}
+
+	rdr := d.Reader()
+	found := false
+	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+		if err != nil {
+			t.Fatalf("error reading DWARF: %v", err)
+		}
+		name, _ := entry.Val(dwarf.AttrName).(string)
+		if strings.HasPrefix(name, "main.testfn") {
+			found = true
+			break
+		}
+	}
+
+	if !found {
+		t.Fatalf("could not find main.testfn")
+	}
+
+	offs := []dwarf.Offset{}
+	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+		if err != nil {
+			t.Fatalf("error reading DWARF: %v", err)
+		}
+		if entry.Tag == 0 {
+			break
+		}
+		name, _ := entry.Val(dwarf.AttrName).(string)
+		switch name {
+		case "arg", "mapvar":
+			offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset))
+		}
+	}
+	if len(offs) != 2 {
+		t.Errorf("wrong number of variables found in main.testfn %d", len(offs))
+	}
+	for _, off := range offs {
+		rdr.Seek(off)
+		entry, err := rdr.Next()
+		if err != nil {
+			t.Fatalf("error reading DWARF: %v", err)
+		}
+		if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok {
+			t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index))
+		}
+	}
+
+	rdr.Seek(0)
+	ex := examiner{}
+	if err := ex.populate(rdr); err != nil {
+		t.Fatalf("error reading DWARF: %v", err)
+	}
+	for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} {
+		dies := ex.Named(typeName)
+		if len(dies) != 1 {
+			t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies))
+		}
+		if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 {
+			t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName)
+		}
+	}
+}
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index f144e00..b9a1da6 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -1209,6 +1209,15 @@
 	return r.Sym(li).IsItab()
 }
 
+// Returns whether this symbol is a dictionary symbol.
+func (l *Loader) IsDict(i Sym) bool {
+	if l.IsExternal(i) {
+		return false
+	}
+	r, li := l.toLocal(i)
+	return r.Sym(li).IsDict()
+}
+
 // Return whether this is a trampoline of a deferreturn call.
 func (l *Loader) IsDeferReturnTramp(i Sym) bool {
 	return l.deferReturnTramp[i]
diff --git a/src/cmd/vendor/golang.org/x/mod/modfile/rule.go b/src/cmd/vendor/golang.org/x/mod/modfile/rule.go
index d6a2d38..98211a4 100644
--- a/src/cmd/vendor/golang.org/x/mod/modfile/rule.go
+++ b/src/cmd/vendor/golang.org/x/mod/modfile/rule.go
@@ -1034,170 +1034,217 @@
 
 // SetRequireSeparateIndirect updates the requirements of f to contain the given
 // requirements. Comment contents (except for 'indirect' markings) are retained
-// from the first existing requirement for each module path, and block structure
-// is maintained as long as the indirect markings match.
+// from the first existing requirement for each module path. Like SetRequire,
+// SetRequireSeparateIndirect adds requirements for new paths in req,
+// updates the version and "// indirect" comment on existing requirements,
+// and deletes requirements on paths not in req. Existing duplicate requirements
+// are deleted.
 //
-// Any requirements on paths not already present in the file are added. Direct
-// requirements are added to the last block containing *any* other direct
-// requirement. Indirect requirements are added to the last block containing
-// *only* other indirect requirements. If no suitable block exists, a new one is
-// added, with the last block containing a direct dependency (if any)
-// immediately before the first block containing only indirect dependencies.
+// As its name suggests, SetRequireSeparateIndirect puts direct and indirect
+// requirements into two separate blocks, one containing only direct
+// requirements, and the other containing only indirect requirements.
+// SetRequireSeparateIndirect may move requirements between these two blocks
+// when their indirect markings change. However, SetRequireSeparateIndirect
+// won't move requirements from other blocks, especially blocks with comments.
 //
-// The Syntax field is ignored for requirements in the given blocks.
+// If the file initially has one uncommented block of requirements,
+// SetRequireSeparateIndirect will split it into a direct-only and indirect-only
+// block. This aids in the transition to separate blocks.
 func (f *File) SetRequireSeparateIndirect(req []*Require) {
-	type modKey struct {
-		path     string
-		indirect bool
-	}
-	need := make(map[modKey]string)
-	for _, r := range req {
-		need[modKey{r.Mod.Path, r.Indirect}] = r.Mod.Version
+	// hasComments returns whether a line or block has comments
+	// other than "indirect".
+	hasComments := func(c Comments) bool {
+		return len(c.Before) > 0 || len(c.After) > 0 || len(c.Suffix) > 1 ||
+			(len(c.Suffix) == 1 &&
+				strings.TrimSpace(strings.TrimPrefix(c.Suffix[0].Token, string(slashSlash))) != "indirect")
 	}
 
-	comments := make(map[string]Comments)
-	for _, r := range f.Require {
-		v, ok := need[modKey{r.Mod.Path, r.Indirect}]
-		if !ok {
-			if _, ok := need[modKey{r.Mod.Path, !r.Indirect}]; ok {
-				if _, dup := comments[r.Mod.Path]; !dup {
-					comments[r.Mod.Path] = r.Syntax.Comments
-				}
+	// moveReq adds r to block. If r was in another block, moveReq deletes
+	// it from that block and transfers its comments.
+	moveReq := func(r *Require, block *LineBlock) {
+		var line *Line
+		if r.Syntax == nil {
+			line = &Line{Token: []string{AutoQuote(r.Mod.Path), r.Mod.Version}}
+			r.Syntax = line
+			if r.Indirect {
+				r.setIndirect(true)
 			}
-			r.markRemoved()
-			continue
+		} else {
+			line = new(Line)
+			*line = *r.Syntax
+			if !line.InBlock && len(line.Token) > 0 && line.Token[0] == "require" {
+				line.Token = line.Token[1:]
+			}
+			r.Syntax.Token = nil // Cleanup will delete the old line.
+			r.Syntax = line
 		}
-		r.setVersion(v)
-		delete(need, modKey{r.Mod.Path, r.Indirect})
+		line.InBlock = true
+		block.Line = append(block.Line, line)
 	}
 
+	// Examine existing require lines and blocks.
 	var (
-		lastDirectOrMixedBlock Expr
-		firstIndirectOnlyBlock Expr
-		lastIndirectOnlyBlock  Expr
+		// We may insert new requirements into the last uncommented
+		// direct-only and indirect-only blocks. We may also move requirements
+		// to the opposite block if their indirect markings change.
+		lastDirectIndex   = -1
+		lastIndirectIndex = -1
+
+		// If there are no direct-only or indirect-only blocks, a new block may
+		// be inserted after the last require line or block.
+		lastRequireIndex = -1
+
+		// If there's only one require line or block, and it's uncommented,
+		// we'll move its requirements to the direct-only or indirect-only blocks.
+		requireLineOrBlockCount = 0
+
+		// Track the block each requirement belongs to (if any) so we can
+		// move them later.
+		lineToBlock = make(map[*Line]*LineBlock)
 	)
-	for _, stmt := range f.Syntax.Stmt {
+	for i, stmt := range f.Syntax.Stmt {
 		switch stmt := stmt.(type) {
 		case *Line:
 			if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
 				continue
 			}
-			if isIndirect(stmt) {
-				lastIndirectOnlyBlock = stmt
-			} else {
-				lastDirectOrMixedBlock = stmt
+			lastRequireIndex = i
+			requireLineOrBlockCount++
+			if !hasComments(stmt.Comments) {
+				if isIndirect(stmt) {
+					lastIndirectIndex = i
+				} else {
+					lastDirectIndex = i
+				}
 			}
+
 		case *LineBlock:
 			if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
 				continue
 			}
-			indirectOnly := true
+			lastRequireIndex = i
+			requireLineOrBlockCount++
+			allDirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments)
+			allIndirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments)
 			for _, line := range stmt.Line {
-				if len(line.Token) == 0 {
-					continue
-				}
-				if !isIndirect(line) {
-					indirectOnly = false
-					break
-				}
-			}
-			if indirectOnly {
-				lastIndirectOnlyBlock = stmt
-				if firstIndirectOnlyBlock == nil {
-					firstIndirectOnlyBlock = stmt
-				}
-			} else {
-				lastDirectOrMixedBlock = stmt
-			}
-		}
-	}
-
-	isOrContainsStmt := func(stmt Expr, target Expr) bool {
-		if stmt == target {
-			return true
-		}
-		if stmt, ok := stmt.(*LineBlock); ok {
-			if target, ok := target.(*Line); ok {
-				for _, line := range stmt.Line {
-					if line == target {
-						return true
-					}
-				}
-			}
-		}
-		return false
-	}
-
-	addRequire := func(path, vers string, indirect bool, comments Comments) {
-		var line *Line
-		if indirect {
-			if lastIndirectOnlyBlock != nil {
-				line = f.Syntax.addLine(lastIndirectOnlyBlock, "require", path, vers)
-			} else {
-				// Add a new require block after the last direct-only or mixed "require"
-				// block (if any).
-				//
-				// (f.Syntax.addLine would add the line to an existing "require" block if
-				// present, but here the existing "require" blocks are all direct-only, so
-				// we know we need to add a new block instead.)
-				line = &Line{Token: []string{"require", path, vers}}
-				lastIndirectOnlyBlock = line
-				firstIndirectOnlyBlock = line // only block implies first block
-				if lastDirectOrMixedBlock == nil {
-					f.Syntax.Stmt = append(f.Syntax.Stmt, line)
+				lineToBlock[line] = stmt
+				if hasComments(line.Comments) {
+					allDirect = false
+					allIndirect = false
+				} else if isIndirect(line) {
+					allDirect = false
 				} else {
-					for i, stmt := range f.Syntax.Stmt {
-						if isOrContainsStmt(stmt, lastDirectOrMixedBlock) {
-							f.Syntax.Stmt = append(f.Syntax.Stmt, nil)     // increase size
-							copy(f.Syntax.Stmt[i+2:], f.Syntax.Stmt[i+1:]) // shuffle elements up
-							f.Syntax.Stmt[i+1] = line
-							break
-						}
-					}
+					allIndirect = false
 				}
 			}
+			if allDirect {
+				lastDirectIndex = i
+			}
+			if allIndirect {
+				lastIndirectIndex = i
+			}
+		}
+	}
+
+	oneFlatUncommentedBlock := requireLineOrBlockCount == 1 &&
+		!hasComments(*f.Syntax.Stmt[lastRequireIndex].Comment())
+
+	// Create direct and indirect blocks if needed. Convert lines into blocks
+	// if needed. If we end up with an empty block or a one-line block,
+	// Cleanup will delete it or convert it to a line later.
+	insertBlock := func(i int) *LineBlock {
+		block := &LineBlock{Token: []string{"require"}}
+		f.Syntax.Stmt = append(f.Syntax.Stmt, nil)
+		copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:])
+		f.Syntax.Stmt[i] = block
+		return block
+	}
+
+	ensureBlock := func(i int) *LineBlock {
+		switch stmt := f.Syntax.Stmt[i].(type) {
+		case *LineBlock:
+			return stmt
+		case *Line:
+			block := &LineBlock{
+				Token: []string{"require"},
+				Line:  []*Line{stmt},
+			}
+			stmt.Token = stmt.Token[1:] // remove "require"
+			stmt.InBlock = true
+			f.Syntax.Stmt[i] = block
+			return block
+		default:
+			panic(fmt.Sprintf("unexpected statement: %v", stmt))
+		}
+	}
+
+	var lastDirectBlock *LineBlock
+	if lastDirectIndex < 0 {
+		if lastIndirectIndex >= 0 {
+			lastDirectIndex = lastIndirectIndex
+			lastIndirectIndex++
+		} else if lastRequireIndex >= 0 {
+			lastDirectIndex = lastRequireIndex + 1
 		} else {
-			if lastDirectOrMixedBlock != nil {
-				line = f.Syntax.addLine(lastDirectOrMixedBlock, "require", path, vers)
+			lastDirectIndex = len(f.Syntax.Stmt)
+		}
+		lastDirectBlock = insertBlock(lastDirectIndex)
+	} else {
+		lastDirectBlock = ensureBlock(lastDirectIndex)
+	}
+
+	var lastIndirectBlock *LineBlock
+	if lastIndirectIndex < 0 {
+		lastIndirectIndex = lastDirectIndex + 1
+		lastIndirectBlock = insertBlock(lastIndirectIndex)
+	} else {
+		lastIndirectBlock = ensureBlock(lastIndirectIndex)
+	}
+
+	// Delete requirements we don't want anymore.
+	// Update versions and indirect comments on requirements we want to keep.
+	// If a requirement is in last{Direct,Indirect}Block with the wrong
+	// indirect marking after this, or if the requirement is in an single
+	// uncommented mixed block (oneFlatUncommentedBlock), move it to the
+	// correct block.
+	//
+	// Some blocks may be empty after this. Cleanup will remove them.
+	need := make(map[string]*Require)
+	for _, r := range req {
+		need[r.Mod.Path] = r
+	}
+	have := make(map[string]*Require)
+	for _, r := range f.Require {
+		path := r.Mod.Path
+		if need[path] == nil || have[path] != nil {
+			// Requirement not needed, or duplicate requirement. Delete.
+			r.markRemoved()
+			continue
+		}
+		have[r.Mod.Path] = r
+		r.setVersion(need[path].Mod.Version)
+		r.setIndirect(need[path].Indirect)
+		if need[path].Indirect &&
+			(oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) {
+			moveReq(r, lastIndirectBlock)
+		} else if !need[path].Indirect &&
+			(oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) {
+			moveReq(r, lastDirectBlock)
+		}
+	}
+
+	// Add new requirements.
+	for path, r := range need {
+		if have[path] == nil {
+			if r.Indirect {
+				moveReq(r, lastIndirectBlock)
 			} else {
-				// Add a new require block before the first indirect block (if any).
-				//
-				// That way if the file initially contains only indirect lines,
-				// the direct lines still appear before it: we preserve existing
-				// structure, but only to the extent that that structure already
-				// reflects the direct/indirect split.
-				line = &Line{Token: []string{"require", path, vers}}
-				lastDirectOrMixedBlock = line
-				if firstIndirectOnlyBlock == nil {
-					f.Syntax.Stmt = append(f.Syntax.Stmt, line)
-				} else {
-					for i, stmt := range f.Syntax.Stmt {
-						if isOrContainsStmt(stmt, firstIndirectOnlyBlock) {
-							f.Syntax.Stmt = append(f.Syntax.Stmt, nil)   // increase size
-							copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) // shuffle elements up
-							f.Syntax.Stmt[i] = line
-							break
-						}
-					}
-				}
+				moveReq(r, lastDirectBlock)
 			}
+			f.Require = append(f.Require, r)
 		}
-
-		line.Comments.Before = commentsAdd(line.Comments.Before, comments.Before)
-		line.Comments.Suffix = commentsAdd(line.Comments.Suffix, comments.Suffix)
-
-		r := &Require{
-			Mod:      module.Version{Path: path, Version: vers},
-			Indirect: indirect,
-			Syntax:   line,
-		}
-		r.setIndirect(indirect)
-		f.Require = append(f.Require, r)
 	}
 
-	for k, vers := range need {
-		addRequire(k.path, vers, k.indirect, comments[k.path])
-	}
 	f.SortBlocks()
 }
 
diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt
index 966ba13..4ff07ab 100644
--- a/src/cmd/vendor/modules.txt
+++ b/src/cmd/vendor/modules.txt
@@ -28,7 +28,7 @@
 ## explicit; go 1.17
 golang.org/x/crypto/ed25519
 golang.org/x/crypto/ed25519/internal/edwards25519
-# golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4
+# golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a
 ## explicit; go 1.17
 golang.org/x/mod/internal/lazyregexp
 golang.org/x/mod/modfile
diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go
index 2668a5e..400da7e 100644
--- a/src/database/sql/convert_test.go
+++ b/src/database/sql/convert_test.go
@@ -51,9 +51,6 @@
 	scanbytes  []byte
 	scanraw    RawBytes
 	scanint    int
-	scanint8   int8
-	scanint16  int16
-	scanint32  int32
 	scanuint8  uint8
 	scanuint16 uint16
 	scanbool   bool
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go
index b25d820..e265796 100644
--- a/src/debug/elf/file.go
+++ b/src/debug/elf/file.go
@@ -494,7 +494,7 @@
 
 	data, err := symtabSection.Data()
 	if err != nil {
-		return nil, nil, errors.New("cannot load symbol section")
+		return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)
 	}
 	symtab := bytes.NewReader(data)
 	if symtab.Len()%Sym32Size != 0 {
@@ -503,7 +503,7 @@
 
 	strdata, err := f.stringTable(symtabSection.Link)
 	if err != nil {
-		return nil, nil, errors.New("cannot load string table section")
+		return nil, nil, fmt.Errorf("cannot load string table section: %w", err)
 	}
 
 	// The first entry is all zeros.
@@ -537,7 +537,7 @@
 
 	data, err := symtabSection.Data()
 	if err != nil {
-		return nil, nil, errors.New("cannot load symbol section")
+		return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)
 	}
 	symtab := bytes.NewReader(data)
 	if symtab.Len()%Sym64Size != 0 {
@@ -546,7 +546,7 @@
 
 	strdata, err := f.stringTable(symtabSection.Link)
 	if err != nil {
-		return nil, nil, errors.New("cannot load string table section")
+		return nil, nil, fmt.Errorf("cannot load string table section: %w", err)
 	}
 
 	// The first entry is all zeros.
diff --git a/src/embed/embed.go b/src/embed/embed.go
index 5dcd7f2..f87cc5b 100644
--- a/src/embed/embed.go
+++ b/src/embed/embed.go
@@ -291,6 +291,8 @@
 }
 
 // Open opens the named file for reading and returns it as an fs.File.
+//
+// The returned file implements io.Seeker when the file is not a directory.
 func (f FS) Open(name string) (fs.File, error) {
 	file := f.lookup(name)
 	if file == nil {
@@ -338,6 +340,10 @@
 	offset int64 // current read offset
 }
 
+var (
+	_ io.Seeker = (*openFile)(nil)
+)
+
 func (f *openFile) Close() error               { return nil }
 func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
 
diff --git a/src/go.mod b/src/go.mod
index a4a6c4f..69e2655 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -5,6 +5,9 @@
 require (
 	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
 	golang.org/x/net v0.0.0-20210825183410-e898025ed96a
+)
+
+require (
 	golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect
 	golang.org/x/text v0.3.7 // indirect
 )
diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go
index f9223e4..70d0912 100644
--- a/src/go/ast/ast.go
+++ b/src/go/ast/ast.go
@@ -344,9 +344,9 @@
 		Rbrack token.Pos // position of "]"
 	}
 
-	// A MultiIndexExpr node represents an expression followed by multiple
+	// An IndexListExpr node represents an expression followed by multiple
 	// indices.
-	MultiIndexExpr struct {
+	IndexListExpr struct {
 		X       Expr      // expression
 		Lbrack  token.Pos // position of "["
 		Indices []Expr    // index expressions
@@ -496,7 +496,7 @@
 func (x *ParenExpr) Pos() token.Pos      { return x.Lparen }
 func (x *SelectorExpr) Pos() token.Pos   { return x.X.Pos() }
 func (x *IndexExpr) Pos() token.Pos      { return x.X.Pos() }
-func (x *MultiIndexExpr) Pos() token.Pos { return x.X.Pos() }
+func (x *IndexListExpr) Pos() token.Pos  { return x.X.Pos() }
 func (x *SliceExpr) Pos() token.Pos      { return x.X.Pos() }
 func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() }
 func (x *CallExpr) Pos() token.Pos       { return x.Fun.Pos() }
@@ -530,7 +530,7 @@
 func (x *ParenExpr) End() token.Pos      { return x.Rparen + 1 }
 func (x *SelectorExpr) End() token.Pos   { return x.Sel.End() }
 func (x *IndexExpr) End() token.Pos      { return x.Rbrack + 1 }
-func (x *MultiIndexExpr) End() token.Pos { return x.Rbrack + 1 }
+func (x *IndexListExpr) End() token.Pos  { return x.Rbrack + 1 }
 func (x *SliceExpr) End() token.Pos      { return x.Rbrack + 1 }
 func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
 func (x *CallExpr) End() token.Pos       { return x.Rparen + 1 }
@@ -562,7 +562,7 @@
 func (*ParenExpr) exprNode()      {}
 func (*SelectorExpr) exprNode()   {}
 func (*IndexExpr) exprNode()      {}
-func (*MultiIndexExpr) exprNode() {}
+func (*IndexListExpr) exprNode()  {}
 func (*SliceExpr) exprNode()      {}
 func (*TypeAssertExpr) exprNode() {}
 func (*CallExpr) exprNode()       {}
diff --git a/src/go/ast/walk.go b/src/go/ast/walk.go
index 530735e..308662f 100644
--- a/src/go/ast/walk.go
+++ b/src/go/ast/walk.go
@@ -116,7 +116,7 @@
 		Walk(v, n.X)
 		Walk(v, n.Index)
 
-	case *MultiIndexExpr:
+	case *IndexListExpr:
 		Walk(v, n.X)
 		for _, index := range n.Indices {
 			Walk(v, index)
diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go
index 9f4345d..3a9ed79 100644
--- a/src/go/internal/gcimporter/gcimporter_test.go
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -238,9 +238,6 @@
 func sanitizeObjectString(s string) string {
 	var runes []rune
 	for _, r := range s {
-		if r == '#' {
-			continue // trim instance markers
-		}
 		if '₀' <= r && r < '₀'+10 {
 			continue // trim type parameter subscripts
 		}
diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go
index 1fe139d..f570aab 100644
--- a/src/go/internal/gcimporter/iimport.go
+++ b/src/go/internal/gcimporter/iimport.go
@@ -72,7 +72,7 @@
 	structType
 	interfaceType
 	typeParamType
-	instType
+	instanceType
 	unionType
 )
 
@@ -309,16 +309,17 @@
 		r.declare(types.NewFunc(pos, r.currPkg, name, sig))
 
 	case 'T', 'U':
-		var tparams []*types.TypeParam
-		if tag == 'U' {
-			tparams = r.tparamList()
-		}
 		// Types can be recursive. We need to setup a stub
 		// declaration before recursing.
 		obj := types.NewTypeName(pos, r.currPkg, name, nil)
 		named := types.NewNamed(obj, nil, nil)
-		named.SetTypeParams(tparams)
+		// Declare obj before calling r.tparamList, so the new type name is recognized
+		// if used in the constraint of one of its own typeparams (see #48280).
 		r.declare(obj)
+		if tag == 'U' {
+			tparams := r.tparamList()
+			named.SetTypeParams(tparams)
+		}
 
 		underlying := r.p.typAt(r.uint64(), named).Underlying()
 		named.SetUnderlying(underlying)
@@ -339,7 +340,7 @@
 					for i := range rparams {
 						rparams[i], _ = targs.At(i).(*types.TypeParam)
 					}
-					msig.SetRParams(rparams)
+					msig.SetRecvTypeParams(rparams)
 				}
 
 				named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
@@ -637,7 +638,7 @@
 		r.p.doDecl(pkg, name)
 		return r.p.tparamIndex[id]
 
-	case instType:
+	case instanceType:
 		if r.p.exportVersion < iexportVersionGenerics {
 			errorf("unexpected instantiation type")
 		}
@@ -652,7 +653,7 @@
 		baseType := r.typ()
 		// The imported instantiated type doesn't include any methods, so
 		// we must always use the methods of the base (orig) type.
-		// TODO provide a non-nil *Checker
+		// TODO provide a non-nil *Environment
 		t, _ := types.Instantiate(nil, baseType, targs, false)
 		return t
 
diff --git a/src/go/internal/typeparams/typeparams.go b/src/go/internal/typeparams/typeparams.go
index 9bf4f7b..3f84f2f 100644
--- a/src/go/internal/typeparams/typeparams.go
+++ b/src/go/internal/typeparams/typeparams.go
@@ -21,7 +21,7 @@
 			Rbrack: rbrack,
 		}
 	default:
-		return &ast.MultiIndexExpr{
+		return &ast.IndexListExpr{
 			X:       x,
 			Lbrack:  lbrack,
 			Indices: exprs,
@@ -30,25 +30,24 @@
 	}
 }
 
-// IndexExpr wraps an ast.IndexExpr or ast.MultiIndexExpr into the
-// MultiIndexExpr interface.
+// IndexExpr wraps an ast.IndexExpr or ast.IndexListExpr.
 //
 // Orig holds the original ast.Expr from which this IndexExpr was derived.
 type IndexExpr struct {
-	Orig ast.Expr // the wrapped expr, which may be distinct from MultiIndexExpr below.
-	*ast.MultiIndexExpr
+	Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below.
+	*ast.IndexListExpr
 }
 
 func UnpackIndexExpr(n ast.Node) *IndexExpr {
 	switch e := n.(type) {
 	case *ast.IndexExpr:
-		return &IndexExpr{e, &ast.MultiIndexExpr{
+		return &IndexExpr{e, &ast.IndexListExpr{
 			X:       e.X,
 			Lbrack:  e.Lbrack,
 			Indices: []ast.Expr{e.Index},
 			Rbrack:  e.Rbrack,
 		}}
-	case *ast.MultiIndexExpr:
+	case *ast.IndexListExpr:
 		return &IndexExpr{e, e}
 	}
 	return nil
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 5c0af8d..0495156 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -1570,7 +1570,7 @@
 		panic("unreachable")
 	case *ast.SelectorExpr:
 	case *ast.IndexExpr:
-	case *ast.MultiIndexExpr:
+	case *ast.IndexListExpr:
 	case *ast.SliceExpr:
 	case *ast.TypeAssertExpr:
 		// If t.Type == nil we have a type assertion of the form
@@ -1660,7 +1660,7 @@
 					return
 				}
 				// x is possibly a composite literal type
-			case *ast.IndexExpr, *ast.MultiIndexExpr:
+			case *ast.IndexExpr, *ast.IndexListExpr:
 				if p.exprLev < 0 {
 					return
 				}
diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go
index 9ce0115..053a8ef 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -873,7 +873,7 @@
 		p.expr0(x.Index, depth+1)
 		p.print(x.Rbrack, token.RBRACK)
 
-	case *ast.MultiIndexExpr:
+	case *ast.IndexListExpr:
 		// TODO(gri): as for IndexExpr, should treat [] like parentheses and undo
 		// one level of depth
 		p.expr1(x.X, token.HighestPrec, 1)
diff --git a/src/go/types/api.go b/src/go/types/api.go
index 5beeff5..ebc3a01 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -115,6 +115,11 @@
 // A Config specifies the configuration for type checking.
 // The zero value for Config is a ready-to-use default configuration.
 type Config struct {
+	// Environment is the environment used for resolving global
+	// identifiers. If nil, the type checker will initialize this
+	// field with a newly created environment.
+	Environment *Environment
+
 	// 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.
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 49c054b..d4f9bb6 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -152,6 +152,7 @@
 		{`package f7b; var _            = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
 
 		{`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341
+		{`package g1; var(j int32; s int; n = 1.0<<s == j)`, `1.0`, `int32`, `1`},        // issue #48422
 	}
 
 	for _, test := range tests {
@@ -1621,6 +1622,48 @@
 	}
 }
 
+func TestIdenticalUnions(t *testing.T) {
+	tname := NewTypeName(token.NoPos, nil, "myInt", nil)
+	myInt := NewNamed(tname, Typ[Int], nil)
+	tmap := map[string]*Term{
+		"int":     NewTerm(false, Typ[Int]),
+		"~int":    NewTerm(true, Typ[Int]),
+		"string":  NewTerm(false, Typ[String]),
+		"~string": NewTerm(true, Typ[String]),
+		"myInt":   NewTerm(false, myInt),
+	}
+	makeUnion := func(s string) *Union {
+		parts := strings.Split(s, "|")
+		var terms []*Term
+		for _, p := range parts {
+			term := tmap[p]
+			if term == nil {
+				t.Fatalf("missing term %q", p)
+			}
+			terms = append(terms, term)
+		}
+		return NewUnion(terms)
+	}
+	for _, test := range []struct {
+		x, y string
+		want bool
+	}{
+		// These tests are just sanity checks. The tests for type sets and
+		// interfaces provide much more test coverage.
+		{"int|~int", "~int", true},
+		{"myInt|~int", "~int", true},
+		{"int|string", "string|int", true},
+		{"int|int|string", "string|int", true},
+		{"myInt|string", "int|string", false},
+	} {
+		x := makeUnion(test.x)
+		y := makeUnion(test.y)
+		if got := Identical(x, y); got != test.want {
+			t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got)
+		}
+	}
+}
+
 func TestIssue15305(t *testing.T) {
 	const src = "package p; func f() int16; var _ = f(undef)"
 	fset := token.NewFileSet()
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 39cd67c..4d14e31 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -337,7 +337,7 @@
 	if sig.TypeParams().Len() > 0 {
 		if !check.allowVersion(check.pkg, 1, 18) {
 			switch call.Fun.(type) {
-			case *ast.IndexExpr, *ast.MultiIndexExpr:
+			case *ast.IndexExpr, *ast.IndexListExpr:
 				ix := typeparams.UnpackIndexExpr(call.Fun)
 				check.softErrorf(inNode(call.Fun, ix.Lbrack), _Todo, "function instantiation requires go1.18 or later")
 			default:
@@ -532,54 +532,6 @@
 	// methods may not have a fully set up signature yet
 	if m, _ := obj.(*Func); m != nil {
 		check.objDecl(m, nil)
-		// If m has a parameterized receiver type, infer the type arguments from
-		// the actual receiver provided and then substitute the type parameters in
-		// the signature accordingly.
-		// TODO(gri) factor this code out
-		sig := m.typ.(*Signature)
-		if sig.RParams().Len() > 0 {
-			// For inference to work, we must use the receiver type
-			// matching the receiver in the actual method declaration.
-			// If the method is embedded, the matching receiver is the
-			// embedded struct or interface that declared the method.
-			// Traverse the embedding to find that type (issue #44688).
-			recv := x.typ
-			for i := 0; i < len(index)-1; i++ {
-				// The embedded type is either a struct or a pointer to
-				// a struct except for the last one (which we don't need).
-				recv = asStruct(derefStructPtr(recv)).Field(index[i]).typ
-			}
-
-			// The method may have a pointer receiver, but the actually provided receiver
-			// may be a (hopefully addressable) non-pointer value, or vice versa. Here we
-			// only care about inferring receiver type parameters; to make the inference
-			// work, match up pointer-ness of receiver and argument.
-			if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(recv) {
-				if ptrRecv {
-					recv = NewPointer(recv)
-				} else {
-					recv = recv.(*Pointer).base
-				}
-			}
-			// Disable reporting of errors during inference below. If we're unable to infer
-			// the receiver type arguments here, the receiver must be be otherwise invalid
-			// and an error has been reported elsewhere.
-			arg := operand{mode: variable, expr: x.expr, typ: recv}
-			targs := check.infer(m, sig.RParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */)
-			if targs == nil {
-				// We may reach here if there were other errors (see issue #40056).
-				goto Error
-			}
-			// Don't modify m. Instead - for now - make a copy of m and use that instead.
-			// (If we modify m, some tests will fail; possibly because the m is in use.)
-			// TODO(gri) investigate and provide a correct explanation here
-			copy := *m
-			copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RParams().list(), targs), nil)
-			obj = &copy
-		}
-		// TODO(gri) we also need to do substitution for parameterized interface methods
-		//           (this breaks code in testdata/linalg.go2 at the moment)
-		//           12/20/2019: Is this TODO still correct?
 	}
 
 	if x.mode == typexpr {
diff --git a/src/go/types/check.go b/src/go/types/check.go
index 0383a58..63f4cbd 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -89,7 +89,6 @@
 	nextID  uint64                 // unique Id for type parameters (first valid Id is 1)
 	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
-	env     *Environment           // for deduplicating identical instances
 
 	// pkgPathMap maps package names to the set of distinct import paths we've
 	// seen for that name, anywhere in the import graph. It is used for
@@ -174,6 +173,11 @@
 		conf = new(Config)
 	}
 
+	// make sure we have an environment
+	if conf.Environment == nil {
+		conf.Environment = NewEnvironment()
+	}
+
 	// make sure we have an info struct
 	if info == nil {
 		info = new(Info)
@@ -192,7 +196,6 @@
 		version: version,
 		objMap:  make(map[Object]*declInfo),
 		impMap:  make(map[importKey]*Package),
-		env:     NewEnvironment(),
 	}
 }
 
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index d132d30..0fdcfa8 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -65,6 +65,12 @@
 		}()
 	}
 
+	// Funcs with m.instRecv set have not yet be completed. Complete them now
+	// so that they have a type when objDecl exits.
+	if m, _ := obj.(*Func); m != nil && m.instRecv != nil {
+		check.completeMethod(check.conf.Environment, m)
+	}
+
 	// Checking the declaration of obj means inferring its type
 	// (and possibly its value, for constants).
 	// An object's type (and thus the object) may be in one of
@@ -316,7 +322,7 @@
 		}
 
 	case *Named:
-		t.expand(check.env)
+		t.resolve(check.conf.Environment)
 		// don't touch the type if it is from a different package or the Universe scope
 		// (doing so would lead to a race condition - was issue #35049)
 		if t.obj.pkg != check.pkg {
@@ -615,7 +621,7 @@
 	if tdecl.TypeParams != nil {
 		check.openScope(tdecl, "type parameters")
 		defer check.closeScope()
-		named.tparams = check.collectTypeParams(tdecl.TypeParams)
+		check.collectTypeParams(&named.tparams, tdecl.TypeParams)
 	}
 
 	// determine underlying type of named
@@ -647,7 +653,7 @@
 	}
 }
 
-func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList {
+func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList) {
 	var tparams []*TypeParam
 	// Declare type parameters up-front, with empty interface as type bound.
 	// The scope of type parameters starts at the beginning of the type parameter
@@ -656,13 +662,28 @@
 		tparams = check.declareTypeParams(tparams, f.Names)
 	}
 
+	// Set the type parameters before collecting the type constraints because
+	// the parameterized type may be used by the constraints (issue #47887).
+	// Example: type T[P T[P]] interface{}
+	*dst = bindTParams(tparams)
+
 	index := 0
 	var bound Type
+	var bounds []Type
+	var posns []positioner // bound positions
 	for _, f := range list.List {
 		if f.Type == nil {
 			goto next
 		}
-		bound = check.boundType(f.Type)
+		// The predeclared identifier "any" is visible only as a type bound in a type parameter list.
+		// If we allow "any" for general use, this if-statement can be removed (issue #33232).
+		if name, _ := unparen(f.Type).(*ast.Ident); name != nil && name.Name == "any" && check.lookup("any") == universeAny {
+			bound = universeAny.Type()
+		} else {
+			bound = check.typ(f.Type)
+		}
+		bounds = append(bounds, bound)
+		posns = append(posns, f.Type)
 		for i := range f.Names {
 			tparams[index+i].bound = bound
 		}
@@ -671,13 +692,26 @@
 		index += len(f.Names)
 	}
 
-	return bindTParams(tparams)
+	check.later(func() {
+		for i, bound := range bounds {
+			u := under(bound)
+			if _, ok := u.(*Interface); !ok && u != Typ[Invalid] {
+				check.errorf(posns[i], _Todo, "%s is not an interface", bound)
+			}
+		}
+	})
 }
 
 func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam {
+	// Use Typ[Invalid] for the type constraint to ensure that a type
+	// is present even if the actual constraint has not been assigned
+	// yet.
+	// TODO(gri) Need to systematically review all uses of type parameter
+	//           constraints to make sure we don't rely on them if they
+	//           are not properly set yet.
 	for _, name := range names {
 		tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
-		tpar := check.newTypeParam(tname, &emptyInterface)       // assigns type to tpar as a side-effect
+		tpar := check.newTypeParam(tname, Typ[Invalid])          // assigns type to tpar as a side-effect
 		check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
 		tparams = append(tparams, tpar)
 	}
@@ -689,25 +723,6 @@
 	return tparams
 }
 
-// boundType type-checks the type expression e and returns its type, or Typ[Invalid].
-// The type must be an interface, including the predeclared type "any".
-func (check *Checker) boundType(e ast.Expr) Type {
-	// The predeclared identifier "any" is visible only as a type bound in a type parameter list.
-	// If we allow "any" for general use, this if-statement can be removed (issue #33232).
-	if name, _ := unparen(e).(*ast.Ident); name != nil && name.Name == "any" && check.lookup("any") == universeAny {
-		return universeAny.Type()
-	}
-
-	bound := check.typ(e)
-	check.later(func() {
-		u := under(bound)
-		if _, ok := u.(*Interface); !ok && u != Typ[Invalid] {
-			check.errorf(e, _Todo, "%s is not an interface", bound)
-		}
-	})
-	return bound
-}
-
 func (check *Checker) collectMethods(obj *TypeName) {
 	// get associated methods
 	// (Checker.collectObjects only collects methods with non-blank names;
@@ -764,7 +779,7 @@
 		}
 
 		if base != nil {
-			base.load() // TODO(mdempsky): Probably unnecessary.
+			base.resolve(nil) // TODO(mdempsky): Probably unnecessary.
 			base.methods = append(base.methods, m)
 		}
 	}
diff --git a/src/go/types/environment.go b/src/go/types/environment.go
index 93383ef..61fc3c5 100644
--- a/src/go/types/environment.go
+++ b/src/go/types/environment.go
@@ -50,13 +50,6 @@
 		h.typ(typ)
 	}
 
-	if debug {
-		// there should be no instance markers in type hashes
-		for _, b := range buf.Bytes() {
-			assert(b != instanceMarker)
-		}
-	}
-
 	return buf.String()
 }
 
diff --git a/src/go/types/errors.go b/src/go/types/errors.go
index 933de93..2d48fe1 100644
--- a/src/go/types/errors.go
+++ b/src/go/types/errors.go
@@ -265,7 +265,7 @@
 	var b strings.Builder
 	for _, r := range s {
 		// strip #'s and subscript digits
-		if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
+		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
 			b.WriteRune(r)
 		}
 	}
diff --git a/src/go/types/errors_test.go b/src/go/types/errors_test.go
index fdbe07c..942a9fd 100644
--- a/src/go/types/errors_test.go
+++ b/src/go/types/errors_test.go
@@ -15,7 +15,6 @@
 		{"foo", "foo"},
 		{"foo₀", "foo"},
 		{"foo(T₀)", "foo(T)"},
-		{"#foo(T₀)", "foo(T)"},
 	} {
 		got := stripAnnotations(test.in)
 		if got != test.want {
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 5ca4ede..007205a 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -1392,7 +1392,7 @@
 	case *ast.SelectorExpr:
 		check.selector(x, e)
 
-	case *ast.IndexExpr, *ast.MultiIndexExpr:
+	case *ast.IndexExpr, *ast.IndexListExpr:
 		ix := typeparams.UnpackIndexExpr(e)
 		if check.indexExpr(x, ix) {
 			check.funcInst(x, ix)
diff --git a/src/go/types/exprstring.go b/src/go/types/exprstring.go
index aee8a5b..06e7a9d 100644
--- a/src/go/types/exprstring.go
+++ b/src/go/types/exprstring.go
@@ -67,7 +67,7 @@
 		buf.WriteByte('.')
 		buf.WriteString(x.Sel.Name)
 
-	case *ast.IndexExpr, *ast.MultiIndexExpr:
+	case *ast.IndexExpr, *ast.IndexListExpr:
 		ix := typeparams.UnpackIndexExpr(x)
 		WriteExpr(buf, ix.X)
 		buf.WriteByte('[')
diff --git a/src/go/types/infer.go b/src/go/types/infer.go
index e641754..18c5119 100644
--- a/src/go/types/infer.go
+++ b/src/go/types/infer.go
@@ -8,6 +8,7 @@
 package types
 
 import (
+	"fmt"
 	"go/token"
 	"strings"
 )
@@ -27,6 +28,7 @@
 //
 // Constraint type inference is used after each step to expand the set of type arguments.
 //
+// TODO(rfindley): remove the report parameter: is no longer needed.
 func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand, report bool) (result []Type) {
 	if debug {
 		defer func() {
@@ -404,6 +406,34 @@
 		}
 	}
 
+	// The data structure of each (provided or inferred) type represents a graph, where
+	// each node corresponds to a type and each (directed) vertice points to a component
+	// type. The substitution process described above repeatedly replaces type parameter
+	// nodes in these graphs with the graphs of the types the type parameters stand for,
+	// which creates a new (possibly bigger) graph for each type.
+	// The substitution process will not stop if the replacement graph for a type parameter
+	// also contains that type parameter.
+	// For instance, for [A interface{ *A }], without any type argument provided for A,
+	// unification produces the type list [*A]. Substituting A in *A with the value for
+	// A will lead to infinite expansion by producing [**A], [****A], [********A], etc.,
+	// because the graph A -> *A has a cycle through A.
+	// Generally, cycles may occur across multiple type parameters and inferred types
+	// (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]).
+	// We eliminate cycles by walking the graphs for all type parameters. If a cycle
+	// through a type parameter is detected, cycleFinder nils out the respectice type
+	// which kills the cycle; this also means that the respective type could not be
+	// inferred.
+	//
+	// TODO(gri) If useful, we could report the respective cycle as an error. We don't
+	//           do this now because type inference will fail anyway, and furthermore,
+	//           constraints with cycles of this kind cannot currently be satisfied by
+	//           any user-suplied type. But should that change, reporting an error
+	//           would be wrong.
+	w := cycleFinder{tparams, types, make(map[Type]bool)}
+	for _, t := range tparams {
+		w.typ(t) // t != nil
+	}
+
 	// dirty tracks the indices of all types that may still contain type parameters.
 	// We know that nil type entries and entries corresponding to provided (non-nil)
 	// type arguments are clean, so exclude them from the start.
@@ -452,3 +482,98 @@
 
 	return
 }
+
+type cycleFinder struct {
+	tparams []*TypeParam
+	types   []Type
+	seen    map[Type]bool
+}
+
+func (w *cycleFinder) typ(typ Type) {
+	if w.seen[typ] {
+		// We have seen typ before. If it is one of the type parameters
+		// in tparams, iterative substitution will lead to infinite expansion.
+		// Nil out the corresponding type which effectively kills the cycle.
+		if tpar, _ := typ.(*TypeParam); tpar != nil {
+			if i := tparamIndex(w.tparams, tpar); i >= 0 {
+				// cycle through tpar
+				w.types[i] = nil
+			}
+		}
+		// If we don't have one of our type parameters, the cycle is due
+		// to an ordinary recursive type and we can just stop walking it.
+		return
+	}
+	w.seen[typ] = true
+	defer delete(w.seen, typ)
+
+	switch t := typ.(type) {
+	case *Basic, *top:
+		// nothing to do
+
+	case *Array:
+		w.typ(t.elem)
+
+	case *Slice:
+		w.typ(t.elem)
+
+	case *Struct:
+		w.varList(t.fields)
+
+	case *Pointer:
+		w.typ(t.base)
+
+	// case *Tuple:
+	//      This case should not occur because tuples only appear
+	//      in signatures where they are handled explicitly.
+
+	case *Signature:
+		// There are no "method types" so we should never see a recv.
+		assert(t.recv == nil)
+		if t.params != nil {
+			w.varList(t.params.vars)
+		}
+		if t.results != nil {
+			w.varList(t.results.vars)
+		}
+
+	case *Union:
+		for _, t := range t.terms {
+			w.typ(t.typ)
+		}
+
+	case *Interface:
+		for _, m := range t.methods {
+			w.typ(m.typ)
+		}
+		for _, t := range t.embeddeds {
+			w.typ(t)
+		}
+
+	case *Map:
+		w.typ(t.key)
+		w.typ(t.elem)
+
+	case *Chan:
+		w.typ(t.elem)
+
+	case *Named:
+		for _, tpar := range t.TypeArgs().list() {
+			w.typ(tpar)
+		}
+
+	case *TypeParam:
+		if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil {
+			w.typ(w.types[i])
+		}
+
+	default:
+		panic(fmt.Sprintf("unexpected %T", typ))
+	}
+}
+
+func (w *cycleFinder) varList(list []*Var) {
+	for _, v := range list {
+		w.typ(v.typ)
+	}
+}
diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go
index 0408778..b178d1e 100644
--- a/src/go/types/instantiate.go
+++ b/src/go/types/instantiate.go
@@ -71,7 +71,7 @@
 		}()
 	}
 
-	inst := check.instance(pos, typ, targs, check.env)
+	inst := check.instance(pos, typ, targs, check.conf.Environment)
 
 	assert(len(posList) <= len(targs))
 	check.later(func() {
@@ -116,9 +116,11 @@
 			}
 		}
 		tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
-		named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded
+		named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved
 		named.targs = NewTypeList(targs)
-		named.instPos = &pos
+		named.resolver = func(env *Environment, n *Named) (*TypeParamList, Type, []*Func) {
+			return expandNamed(env, n, pos)
+		}
 		if env != nil {
 			// It's possible that we've lost a race to add named to the environment.
 			// In this case, use whichever instance is recorded in the environment.
diff --git a/src/go/types/instantiate_test.go b/src/go/types/instantiate_test.go
index 0b09bfe..0c66acb 100644
--- a/src/go/types/instantiate_test.go
+++ b/src/go/types/instantiate_test.go
@@ -6,6 +6,7 @@
 
 import (
 	. "go/types"
+	"strings"
 	"testing"
 )
 
@@ -70,3 +71,84 @@
 		t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2)
 	}
 }
+
+func TestMethodInstantiation(t *testing.T) {
+	const prefix = genericPkg + `p
+
+type T[P any] struct{}
+
+var X T[int]
+
+`
+	tests := []struct {
+		decl string
+		want string
+	}{
+		{"func (r T[P]) m() P", "func (T[int]).m() int"},
+		{"func (r T[P]) m(P)", "func (T[int]).m(int)"},
+		{"func (r T[P]) m() func() P", "func (T[int]).m() func() int"},
+		{"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"},
+		{"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"},
+		{"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"},
+		{"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"},
+	}
+
+	for _, test := range tests {
+		src := prefix + test.decl
+		pkg, err := pkgFor(".", src, nil)
+		if err != nil {
+			t.Fatal(err)
+		}
+		typ := pkg.Scope().Lookup("X").Type().(*Named)
+		obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
+		m, _ := obj.(*Func)
+		if m == nil {
+			t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+		}
+		if got := ObjectString(m, RelativeTo(pkg)); got != test.want {
+			t.Errorf("instantiated %q, want %q", got, test.want)
+		}
+	}
+}
+
+func TestImmutableSignatures(t *testing.T) {
+	const src = genericPkg + `p
+
+type T[P any] struct{}
+
+func (T[P]) m() {}
+
+var _ T[int]
+`
+	pkg, err := pkgFor(".", src, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	typ := pkg.Scope().Lookup("T").Type().(*Named)
+	obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
+	if obj == nil {
+		t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+	}
+
+	// Verify that the original method is not mutated by instantiating T (this
+	// bug manifested when subst did not return a new signature).
+	want := "func (T[P]).m()"
+	if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want {
+		t.Errorf("instantiated %q, want %q", got, want)
+	}
+}
+
+// Copied from errors.go.
+func stripAnnotations(s string) string {
+	var b strings.Builder
+	for _, r := range s {
+		// strip #'s and subscript digits
+		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
+			b.WriteRune(r)
+		}
+	}
+	if b.Len() < len(s) {
+		return b.String()
+	}
+	return s
+}
diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go
index f5bdd31..a270159 100644
--- a/src/go/types/lookup.go
+++ b/src/go/types/lookup.go
@@ -6,8 +6,6 @@
 
 package types
 
-import "go/token"
-
 // Internal use of LookupFieldOrMethod: If the obj result is a method
 // associated with a concrete (non-interface) type, the method's signature
 // may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
@@ -124,7 +122,7 @@
 				seen[named] = true
 
 				// look for a matching attached method
-				named.load()
+				named.resolve(nil)
 				if i, m := lookupMethod(named.methods, pkg, name); m != nil {
 					// potential match
 					// caution: method may not have a proper signature yet
@@ -342,8 +340,6 @@
 	}
 
 	// A concrete type implements T if it implements all methods of T.
-	Vd, _ := deref(V)
-	Vn := asNamed(Vd)
 	for _, m := range T.typeSet().methods {
 		// TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
 		obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
@@ -378,33 +374,13 @@
 			panic("method with type parameters")
 		}
 
-		// If V is a (instantiated) generic type, its methods are still
-		// parameterized using the original (declaration) receiver type
-		// parameters (subst simply copies the existing method list, it
-		// does not instantiate the methods).
-		// In order to compare the signatures, substitute the receiver
-		// type parameters of ftyp with V's instantiation type arguments.
-		// This lazily instantiates the signature of method f.
-		if Vn != nil && Vn.TypeParams().Len() > 0 {
-			// Be careful: The number of type arguments may not match
-			// the number of receiver parameters. If so, an error was
-			// reported earlier but the length discrepancy is still
-			// here. Exit early in this case to prevent an assertion
-			// failure in makeSubstMap.
-			// TODO(gri) Can we avoid this check by fixing the lengths?
-			if len(ftyp.RParams().list()) != Vn.targs.Len() {
-				return
-			}
-			ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs.list()), nil).(*Signature)
-		}
-
 		// If the methods have type parameters we don't care whether they
 		// are the same or not, as long as they match up. Use unification
 		// to see if they can be made to match.
 		// TODO(gri) is this always correct? what about type bounds?
 		// (Alternative is to rename/subst type parameters and compare.)
 		u := newUnifier(true)
-		u.x.init(ftyp.RParams().list())
+		u.x.init(ftyp.RecvTypeParams().list())
 		if !u.unify(ftyp, mtyp) {
 			return m, f
 		}
diff --git a/src/go/types/named.go b/src/go/types/named.go
index 51c4a23..302e431 100644
--- a/src/go/types/named.go
+++ b/src/go/types/named.go
@@ -17,13 +17,13 @@
 	orig       *Named         // original, uninstantiated type
 	fromRHS    Type           // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
 	underlying Type           // possibly a *Named during setup; never a *Named once set up completely
-	instPos    *token.Pos     // position information for lazy instantiation, or nil
 	tparams    *TypeParamList // type parameters, or nil
 	targs      *TypeList      // type arguments (after instantiation), or nil
 	methods    []*Func        // methods declared for this type (not the method set of this type); signatures are type-checked lazily
 
-	resolve func(*Named) ([]*TypeParam, Type, []*Func)
-	once    sync.Once
+	// resolver may be provided to lazily resolve type parameters, underlying, and methods.
+	resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func)
+	once     sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
 }
 
 // NewNamed returns a new named type for the given type name, underlying type, and associated methods.
@@ -36,43 +36,22 @@
 	return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
 }
 
-func (t *Named) load() *Named {
-	// If t is an instantiated type, it derives its methods and tparams from its
-	// base type. Since we expect type parameters and methods to be set after a
-	// call to load, we must load the base and copy here.
-	//
-	// underlying is set when t is expanded.
-	//
-	// By convention, a type instance is loaded iff its tparams are set.
-	if t.targs.Len() > 0 && t.tparams == nil {
-		t.orig.load()
-		t.tparams = t.orig.tparams
-		t.methods = t.orig.methods
-	}
-	if t.resolve == nil {
+func (t *Named) resolve(env *Environment) *Named {
+	if t.resolver == nil {
 		return t
 	}
 
 	t.once.Do(func() {
-		// TODO(mdempsky): Since we're passing t to resolve anyway
+		// TODO(mdempsky): Since we're passing t to the resolver anyway
 		// (necessary because types2 expects the receiver type for methods
 		// on defined interface types to be the Named rather than the
 		// underlying Interface), maybe it should just handle calling
 		// SetTypeParams, SetUnderlying, and AddMethod instead?  Those
-		// methods would need to support reentrant calls though.  It would
+		// methods would need to support reentrant calls though. It would
 		// also make the API more future-proof towards further extensions
 		// (like SetTypeParams).
-
-		tparams, underlying, methods := t.resolve(t)
-
-		switch underlying.(type) {
-		case nil, *Named:
-			panic("invalid underlying type")
-		}
-
-		t.tparams = bindTParams(tparams)
-		t.underlying = underlying
-		t.methods = methods
+		t.tparams, t.underlying, t.methods = t.resolver(env, t)
+		t.fromRHS = t.underlying // for cycle detection
 	})
 	return t
 }
@@ -112,28 +91,28 @@
 	return t.orig.obj // for non-instances this is the same as t.obj
 }
 
-// _Orig returns the original generic type an instantiated type is derived from.
-// If t is not an instantiated type, the result is t.
-func (t *Named) _Orig() *Named { return t.orig }
+// Origin returns the parameterized type from which the named type t is
+// instantiated. If t is not an instantiated type, the result is t.
+func (t *Named) Origin() *Named { return t.orig }
 
 // TODO(gri) Come up with a better representation and API to distinguish
 //           between parameterized instantiated and non-instantiated types.
 
 // TypeParams returns the type parameters of the named type t, or nil.
 // The result is non-nil for an (originally) parameterized type even if it is instantiated.
-func (t *Named) TypeParams() *TypeParamList { return t.load().tparams }
+func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
 
 // SetTypeParams sets the type parameters of the named type t.
-func (t *Named) SetTypeParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) }
+func (t *Named) SetTypeParams(tparams []*TypeParam) { t.resolve(nil).tparams = bindTParams(tparams) }
 
 // TypeArgs returns the type arguments used to instantiate the named type t.
 func (t *Named) TypeArgs() *TypeList { return t.targs }
 
 // NumMethods returns the number of explicit methods whose receiver is named type t.
-func (t *Named) NumMethods() int { return len(t.load().methods) }
+func (t *Named) NumMethods() int { return len(t.resolve(nil).methods) }
 
 // Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
-func (t *Named) Method(i int) *Func { return t.load().methods[i] }
+func (t *Named) Method(i int) *Func { return t.resolve(nil).methods[i] }
 
 // SetUnderlying sets the underlying type and marks t as complete.
 func (t *Named) SetUnderlying(underlying Type) {
@@ -143,18 +122,18 @@
 	if _, ok := underlying.(*Named); ok {
 		panic("underlying type must not be *Named")
 	}
-	t.load().underlying = underlying
+	t.resolve(nil).underlying = underlying
 }
 
 // AddMethod adds method m unless it is already in the method list.
 func (t *Named) AddMethod(m *Func) {
-	t.load()
+	t.resolve(nil)
 	if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
 		t.methods = append(t.methods, m)
 	}
 }
 
-func (t *Named) Underlying() Type { return t.load().expand(nil).underlying }
+func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
 func (t *Named) String() string   { return TypeString(t, nil) }
 
 // ----------------------------------------------------------------------------
@@ -240,43 +219,105 @@
 	}
 }
 
-// expand ensures that the underlying type of n is instantiated.
-// The underlying type will be Typ[Invalid] if there was an error.
-func (n *Named) expand(env *Environment) *Named {
-	if n.instPos != nil {
-		// n must be loaded before instantiation, in order to have accurate
-		// tparams. This is done implicitly by the call to n.TypeParams, but making
-		// it explicit is harmless: load is idempotent.
-		n.load()
-		var u Type
-		if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) {
-			// TODO(rfindley): handling an optional Checker and Environment here (and
-			// in subst) feels overly complicated. Can we simplify?
-			if env == nil {
-				if n.check != nil {
-					env = n.check.env
-				} else {
-					// If we're instantiating lazily, we might be outside the scope of a
-					// type-checking pass. In that case we won't have a pre-existing
-					// environment, but don't want to create a duplicate of the current
-					// instance in the process of expansion.
-					env = NewEnvironment()
-				}
-				h := env.typeHash(n.orig, n.targs.list())
-				// add the instance to the environment to avoid infinite recursion.
-				// addInstance may return a different, existing instance, but we
-				// shouldn't return that instance from expand.
-				env.typeForHash(h, n)
-			}
-			u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TypeParams().list(), n.targs.list()), env)
-		} else {
-			u = Typ[Invalid]
-		}
-		n.underlying = u
-		n.fromRHS = u
-		n.instPos = nil
+// bestEnv returns the best available environment. In order of preference:
+// - the given env, if non-nil
+// - the Checker env, if check is non-nil
+// - a new environment
+func (check *Checker) bestEnv(env *Environment) *Environment {
+	if env != nil {
+		return env
 	}
-	return n
+	if check != nil {
+		assert(check.conf.Environment != nil)
+		return check.conf.Environment
+	}
+	return NewEnvironment()
+}
+
+// expandNamed ensures that the underlying type of n is instantiated.
+// The underlying type will be Typ[Invalid] if there was an error.
+func expandNamed(env *Environment, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) {
+	n.orig.resolve(env)
+
+	check := n.check
+
+	if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) {
+		// We must always have an env, to avoid infinite recursion.
+		env = check.bestEnv(env)
+		h := env.typeHash(n.orig, n.targs.list())
+		// ensure that an instance is recorded for h to avoid infinite recursion.
+		env.typeForHash(h, n)
+
+		smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
+		underlying = n.check.subst(instPos, n.orig.underlying, smap, env)
+
+		for i := 0; i < n.orig.NumMethods(); i++ {
+			origm := n.orig.Method(i)
+
+			// During type checking origm may not have a fully set up type, so defer
+			// instantiation of its signature until later.
+			m := NewFunc(origm.pos, origm.pkg, origm.name, nil)
+			m.hasPtrRecv = origm.hasPtrRecv
+			// Setting instRecv here allows us to complete later (we need the
+			// instRecv to get targs and the original method).
+			m.instRecv = n
+
+			methods = append(methods, m)
+		}
+	} else {
+		underlying = Typ[Invalid]
+	}
+
+	// Methods should not escape the type checker API without being completed. If
+	// we're in the context of a type checking pass, we need to defer this until
+	// later (not all methods may have types).
+	completeMethods := func() {
+		for _, m := range methods {
+			if m.instRecv != nil {
+				check.completeMethod(env, m)
+			}
+		}
+	}
+	if check != nil {
+		check.later(completeMethods)
+	} else {
+		completeMethods()
+	}
+
+	return n.orig.tparams, underlying, methods
+}
+
+func (check *Checker) completeMethod(env *Environment, m *Func) {
+	assert(m.instRecv != nil)
+	rtyp := m.instRecv
+	m.instRecv = nil
+	m.setColor(black)
+
+	assert(rtyp.TypeArgs().Len() > 0)
+
+	// Look up the original method.
+	_, orig := lookupMethod(rtyp.orig.methods, rtyp.obj.pkg, m.name)
+	assert(orig != nil)
+	if check != nil {
+		check.objDecl(orig, nil)
+	}
+	origSig := orig.typ.(*Signature)
+	if origSig.RecvTypeParams().Len() != rtyp.targs.Len() {
+		m.typ = origSig // or new(Signature), but we can't use Typ[Invalid]: Funcs must have Signature type
+		return          // error reported elsewhere
+	}
+
+	smap := makeSubstMap(origSig.RecvTypeParams().list(), rtyp.targs.list())
+	sig := check.subst(orig.pos, origSig, smap, env).(*Signature)
+	if sig == origSig {
+		// No substitution occurred, but we still need to create a copy to hold the
+		// instantiated receiver.
+		copy := *origSig
+		sig = &copy
+	}
+	sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
+
+	m.typ = sig
 }
 
 // safeUnderlying returns the underlying of typ without expanding instances, to
@@ -285,7 +326,7 @@
 // TODO(rfindley): eliminate this function or give it a better name.
 func safeUnderlying(typ Type) Type {
 	if t, _ := typ.(*Named); t != nil {
-		return t.load().underlying
+		return t.resolve(nil).underlying
 	}
 	return typ.Underlying()
 }
diff --git a/src/go/types/object.go b/src/go/types/object.go
index b25fffd..454b714 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -232,9 +232,21 @@
 
 // _NewTypeNameLazy returns a new defined type like NewTypeName, but it
 // lazily calls resolve to finish constructing the Named object.
-func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, resolve func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
+func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
 	obj := NewTypeName(pos, pkg, name, nil)
-	NewNamed(obj, nil, nil).resolve = resolve
+
+	resolve := func(_ *Environment, t *Named) (*TypeParamList, Type, []*Func) {
+		tparams, underlying, methods := load(t)
+
+		switch underlying.(type) {
+		case nil, *Named:
+			panic(fmt.Sprintf("invalid underlying type %T", t.underlying))
+		}
+
+		return bindTParams(tparams), underlying, methods
+	}
+
+	NewNamed(obj, nil, nil).resolver = resolve
 	return obj
 }
 
@@ -305,7 +317,8 @@
 // An abstract method may belong to many interfaces due to embedding.
 type Func struct {
 	object
-	hasPtrRecv bool // only valid for methods that don't have a type yet
+	instRecv   *Named // if non-nil, the receiver type for an incomplete instance method
+	hasPtrRecv bool   // only valid for methods that don't have a type yet
 }
 
 // NewFunc returns a new function with the given signature, representing
@@ -316,7 +329,7 @@
 	if sig != nil {
 		typ = sig
 	}
-	return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, false}
+	return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, nil, false}
 }
 
 // FullName returns the package- or receiver-type-qualified name of
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 73d2402..a5d4be9 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -6,6 +6,8 @@
 
 package types
 
+import "go/token"
+
 // isNamed reports whether typ has a name.
 // isNamed may be called with types that are not fully set up.
 func isNamed(typ Type) bool {
@@ -225,6 +227,13 @@
 				identical(x.results, y.results, cmpTags, p)
 		}
 
+	case *Union:
+		if y, _ := y.(*Union); y != nil {
+			xset := computeUnionTypeSet(nil, token.NoPos, x)
+			yset := computeUnionTypeSet(nil, token.NoPos, y)
+			return xset.terms.equal(yset.terms)
+		}
+
 	case *Interface:
 		// Two interface types are identical if they describe the same type sets.
 		// With the existing implementation restriction, this simplifies to:
@@ -302,9 +311,6 @@
 		// Two named types are identical if their type names originate
 		// in the same type declaration.
 		if y, ok := y.(*Named); ok {
-			x.expand(nil)
-			y.expand(nil)
-
 			xargs := x.TypeArgs().list()
 			yargs := y.TypeArgs().list()
 
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index b04a673..486c092 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -513,7 +513,7 @@
 
 	// unpack type parameters, if any
 	switch rtyp.(type) {
-	case *ast.IndexExpr, *ast.MultiIndexExpr:
+	case *ast.IndexExpr, *ast.IndexListExpr:
 		ix := typeparams.UnpackIndexExpr(rtyp)
 		rtyp = ix.X
 		if unpackParams {
diff --git a/src/go/types/signature.go b/src/go/types/signature.go
index 0561947..bf6c775 100644
--- a/src/go/types/signature.go
+++ b/src/go/types/signature.go
@@ -61,11 +61,11 @@
 // SetTypeParams sets the type parameters of signature s.
 func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) }
 
-// RParams returns the receiver type parameters of signature s, or nil.
-func (s *Signature) RParams() *TypeParamList { return s.rparams }
+// RecvTypeParams returns the receiver type parameters of signature s, or nil.
+func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams }
 
-// SetRParams sets the receiver type params of signature s.
-func (s *Signature) SetRParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) }
+// SetRecvTypeParams sets the receiver type params of signature s.
+func (s *Signature) SetRecvTypeParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) }
 
 // Params returns the parameters of signature s, or nil.
 func (s *Signature) Params() *Tuple { return s.params }
@@ -133,14 +133,14 @@
 			}
 			// provide type parameter bounds
 			// - only do this if we have the right number (otherwise an error is reported elsewhere)
-			if sig.RParams().Len() == len(recvTParams) {
+			if sig.RecvTypeParams().Len() == len(recvTParams) {
 				// We have a list of *TypeNames but we need a list of Types.
-				list := make([]Type, sig.RParams().Len())
-				for i, t := range sig.RParams().list() {
+				list := make([]Type, sig.RecvTypeParams().Len())
+				for i, t := range sig.RecvTypeParams().list() {
 					list[i] = t
 				}
 				smap := makeSubstMap(recvTParams, list)
-				for i, tpar := range sig.RParams().list() {
+				for i, tpar := range sig.RecvTypeParams().list() {
 					bound := recvTParams[i].bound
 					// bound is (possibly) parameterized in the context of the
 					// receiver type declaration. Substitute parameters for the
@@ -152,7 +152,7 @@
 	}
 
 	if ftyp.TypeParams != nil {
-		sig.tparams = check.collectTypeParams(ftyp.TypeParams)
+		check.collectTypeParams(&sig.tparams, ftyp.TypeParams)
 		// Always type-check method type parameters but complain that they are not allowed.
 		// (A separate check is needed when type-checking interface method signatures because
 		// they don't have a receiver specification.)
@@ -200,10 +200,10 @@
 			var err string
 			switch T := rtyp.(type) {
 			case *Named:
-				T.expand(nil)
+				T.resolve(check.conf.Environment)
 				// The receiver type may be an instantiated type referred to
 				// by an alias (which cannot have receiver parameters for now).
-				if T.TypeArgs() != nil && sig.RParams() == nil {
+				if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
 					check.errorf(atPos(recv.pos), _Todo, "cannot define methods on instantiated type %s", recv.typ)
 					break
 				}
@@ -326,7 +326,7 @@
 			new.X = X
 			return &new
 		}
-	case *ast.IndexExpr, *ast.MultiIndexExpr:
+	case *ast.IndexExpr, *ast.IndexListExpr:
 		ix := typeparams.UnpackIndexExpr(x)
 		var newIndexes []ast.Expr
 		for i, index := range ix.Indices {
diff --git a/src/go/types/sizeof_test.go b/src/go/types/sizeof_test.go
index f64f732..0e3c006 100644
--- a/src/go/types/sizeof_test.go
+++ b/src/go/types/sizeof_test.go
@@ -30,7 +30,7 @@
 		{Interface{}, 44, 88},
 		{Map{}, 16, 32},
 		{Chan{}, 12, 24},
-		{Named{}, 72, 136},
+		{Named{}, 68, 128},
 		{TypeParam{}, 28, 48},
 		{term{}, 12, 24},
 		{top{}, 0, 0},
@@ -40,7 +40,7 @@
 		{Const{}, 48, 88},
 		{TypeName{}, 40, 72},
 		{Var{}, 44, 80},
-		{Func{}, 44, 80},
+		{Func{}, 48, 88},
 		{Label{}, 44, 80},
 		{Builtin{}, 44, 80},
 		{Nil{}, 40, 72},
diff --git a/src/go/types/struct.go b/src/go/types/struct.go
index f6e6f2a..24a2435 100644
--- a/src/go/types/struct.go
+++ b/src/go/types/struct.go
@@ -176,7 +176,7 @@
 		return e.Sel
 	case *ast.IndexExpr:
 		return embeddedFieldIdent(e.X)
-	case *ast.MultiIndexExpr:
+	case *ast.IndexListExpr:
 		return embeddedFieldIdent(e.X)
 	}
 	return nil // invalid embedded field
diff --git a/src/go/types/subst.go b/src/go/types/subst.go
index 4f9d76d..16aafd6 100644
--- a/src/go/types/subst.go
+++ b/src/go/types/subst.go
@@ -8,9 +8,6 @@
 
 import "go/token"
 
-// TODO(rFindley) decide error codes for the errors in this file, and check
-//                if error spans can be improved
-
 type substMap map[*TypeParam]Type
 
 // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
@@ -55,25 +52,12 @@
 	}
 
 	// general case
-	var subst subster
-	subst.pos = pos
-	subst.smap = smap
-
-	if check != nil {
-		subst.check = check
-		if env == nil {
-			env = check.env
-		}
+	subst := subster{
+		pos:   pos,
+		smap:  smap,
+		check: check,
+		env:   check.bestEnv(env),
 	}
-	if env == nil {
-		// If we don't have a *Checker and its global type map,
-		// use a local version. Besides avoiding duplicate work,
-		// the type map prevents infinite recursive substitution
-		// for recursive types (example: type T[P any] *T[P]).
-		env = NewEnvironment()
-	}
-	subst.env = env
-
 	return subst.typ(typ)
 }
 
@@ -128,8 +112,7 @@
 		if recv != t.recv || params != t.params || results != t.results {
 			return &Signature{
 				rparams: t.rparams,
-				// TODO(rFindley) why can't we nil out tparams here, rather than in
-				//                instantiate above?
+				// TODO(rFindley) why can't we nil out tparams here, rather than in instantiate?
 				tparams:  t.tparams,
 				scope:    t.scope,
 				recv:     recv,
@@ -182,13 +165,19 @@
 			}
 		}
 
-		if t.TypeParams().Len() == 0 {
+		// subst is called by expandNamed, so in this function we need to be
+		// careful not to call any methods that would cause t to be expanded: doing
+		// so would result in deadlock.
+		//
+		// So we call t.orig.TypeParams() rather than t.TypeParams() here and
+		// below.
+		if t.orig.TypeParams().Len() == 0 {
 			dump(">>> %s is not parameterized", t)
 			return t // type is not parameterized
 		}
 
 		var newTArgs []Type
-		assert(t.targs.Len() == t.TypeParams().Len())
+		assert(t.targs.Len() == t.orig.TypeParams().Len())
 
 		// already instantiated
 		dump(">>> %s already instantiated", t)
@@ -201,7 +190,7 @@
 			if new_targ != targ {
 				dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
 				if newTArgs == nil {
-					newTArgs = make([]Type, t.TypeParams().Len())
+					newTArgs = make([]Type, t.orig.TypeParams().Len())
 					copy(newTArgs, t.targs.list())
 				}
 				newTArgs[i] = new_targ
@@ -221,27 +210,19 @@
 			return named
 		}
 
-		// Create a new named type and populate the environment to avoid endless
+		t.orig.resolve(subst.env)
+		// Create a new instance and populate the environment to avoid endless
 		// recursion. The position used here is irrelevant because validation only
 		// occurs on t (we don't call validType on named), but we use subst.pos to
 		// help with debugging.
-		tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
-		t.load()
-		// It's ok to provide a nil *Checker because the newly created type
-		// doesn't need to be (lazily) expanded; it's expanded below.
-		named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available
-		named.targs = NewTypeList(newTArgs)
-		subst.env.typeForHash(h, named)
-		t.expand(subst.env) // must happen after env update to avoid infinite recursion
+		t.orig.resolve(subst.env)
+		return subst.check.instance(subst.pos, t.orig, newTArgs, subst.env)
 
-		// do the substitution
-		dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTArgs)
-		named.underlying = subst.typOrNil(t.underlying)
-		dump(">>> underlying: %v", named.underlying)
-		assert(named.underlying != nil)
-		named.fromRHS = named.underlying // for consistency, though no cycle detection is necessary
-
-		return named
+		// Note that if we were to expose substitution more generally (not just in
+		// the context of a declaration), we'd have to substitute in
+		// named.underlying as well.
+		//
+		// But this is unnecessary for now.
 
 	case *TypeParam:
 		return subst.smap.lookup(t)
diff --git a/src/go/types/testdata/fixedbugs/issue43527.go2 b/src/go/types/testdata/fixedbugs/issue43527.go2
new file mode 100644
index 0000000..e4bcee5
--- /dev/null
+++ b/src/go/types/testdata/fixedbugs/issue43527.go2
@@ -0,0 +1,16 @@
+// 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
+
+const L = 10
+
+type (
+	_        [L]struct{}
+	_        [A /* ERROR undeclared name A for array length */ ]struct{}
+	_        [B /* ERROR not an expression */ ]struct{}
+	_[A any] struct{}
+
+	B int
+)
diff --git a/src/go/types/testdata/fixedbugs/issue45550.go2 b/src/go/types/testdata/fixedbugs/issue45550.go2
new file mode 100644
index 0000000..c3e9e34
--- /dev/null
+++ b/src/go/types/testdata/fixedbugs/issue45550.go2
@@ -0,0 +1,10 @@
+// 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
+
+type Builder[T interface{ struct{ Builder[T] } }] struct{}
+type myBuilder struct {
+	Builder[myBuilder /* ERROR myBuilder does not satisfy */]
+}
diff --git a/src/go/types/testdata/fixedbugs/issue47796.go2 b/src/go/types/testdata/fixedbugs/issue47796.go2
new file mode 100644
index 0000000..9c10683
--- /dev/null
+++ b/src/go/types/testdata/fixedbugs/issue47796.go2
@@ -0,0 +1,33 @@
+// 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
+
+// parameterized types with self-recursive constraints
+type (
+	T1[P T1[P]]                            interface{}
+	T2[P, Q T2[P, Q]]                      interface{}
+	T3[P T2[P, Q], Q interface{ ~string }] interface{}
+
+	T4a[P T4a[P]]                                                        interface{ ~int }
+	T4b[P T4b[int]]                                                      interface{ ~int }
+	T4c[P T4c[string /* ERROR string does not satisfy T4c\[string\] */]] interface{ ~int }
+
+	// mutually recursive constraints
+	T5[P T6[P]] interface{ int }
+	T6[P T5[P]] interface{ int }
+)
+
+// verify that constraints are checked as expected
+var (
+	_ T1[int]
+	_ T2[int, string]
+	_ T3[int, string]
+)
+
+// test case from issue
+
+type Eq[a Eq[a]] interface {
+	Equal(that a) bool
+}
diff --git a/src/go/types/testdata/fixedbugs/issue47887.go2 b/src/go/types/testdata/fixedbugs/issue47887.go2
new file mode 100644
index 0000000..4c4fc2f
--- /dev/null
+++ b/src/go/types/testdata/fixedbugs/issue47887.go2
@@ -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 p
+
+type Fooer[t any] interface {
+	foo(Barer[t])
+}
+type Barer[t any] interface {
+	bar(Bazer[t])
+}
+type Bazer[t any] interface {
+	Fooer[t]
+	baz(t)
+}
+
+type Int int
+
+func (n Int) baz(int) {}
+func (n Int) foo(b Barer[int]) { b.bar(n) }
+
+type F[t any] interface { f(G[t]) }
+type G[t any] interface { g(H[t]) }
+type H[t any] interface { F[t] }
+
+type T struct{}
+func (n T) f(b G[T]) { b.g(n) }
diff --git a/src/go/types/testdata/fixedbugs/issue48136.go2 b/src/go/types/testdata/fixedbugs/issue48136.go2
new file mode 100644
index 0000000..b87f84a
--- /dev/null
+++ b/src/go/types/testdata/fixedbugs/issue48136.go2
@@ -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 p
+
+func f1[P interface{ *P }]() {}
+func f2[P interface{ func(P) }]() {}
+func f3[P, Q interface{ func(Q) P }]() {}
+func f4[P interface{ *Q }, Q interface{ func(P) }]() {}
+func f5[P interface{ func(P) }]() {}
+func f6[P interface { *Tree[P] }, Q any ]() {}
+
+func _() {
+        f1 /* ERROR cannot infer P */ ()
+        f2 /* ERROR cannot infer P */ ()
+        f3 /* ERROR cannot infer P */ ()
+        f4 /* ERROR cannot infer P */ ()
+        f5 /* ERROR cannot infer P */ ()
+        f6 /* ERROR cannot infer P */ ()
+}
+
+type Tree[P any] struct {
+        left, right *Tree[P]
+        data P
+}
+
+// test case from issue
+
+func foo[Src interface { func() Src }]() Src {
+        return foo[Src]
+}
+
+func _() {
+        foo /* ERROR cannot infer Src */ ()
+}
diff --git a/src/go/types/type.go b/src/go/types/type.go
index b9634cf..31149cf 100644
--- a/src/go/types/type.go
+++ b/src/go/types/type.go
@@ -114,7 +114,7 @@
 func asNamed(t Type) *Named {
 	e, _ := t.(*Named)
 	if e != nil {
-		e.expand(nil)
+		e.resolve(nil)
 	}
 	return e
 }
diff --git a/src/go/types/typeparam.go b/src/go/types/typeparam.go
index a0f2a3a..150ad07 100644
--- a/src/go/types/typeparam.go
+++ b/src/go/types/typeparam.go
@@ -32,7 +32,7 @@
 // or Signature type by calling SetTypeParams. Setting a type parameter on more
 // than one type will result in a panic.
 //
-// The bound argument can be nil, and set later via SetConstraint.
+// The constraint argument can be nil, and set later via SetConstraint.
 func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
 	return (*Checker)(nil).newTypeParam(obj, constraint)
 }
diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go
index 7e971c0..eadc50a 100644
--- a/src/go/types/typestring.go
+++ b/src/go/types/typestring.go
@@ -65,9 +65,6 @@
 	newTypeWriter(buf, qf).signature(sig)
 }
 
-// instanceMarker is the prefix for an instantiated type in unexpanded form.
-const instanceMarker = '#'
-
 type typeWriter struct {
 	buf  *bytes.Buffer
 	seen map[Type]bool
@@ -226,13 +223,6 @@
 		}
 
 	case *Named:
-		// Instance markers indicate unexpanded instantiated
-		// types. Write them to aid debugging, but don't write
-		// them when we need an instance hash: whether a type
-		// is fully expanded or not doesn't matter for identity.
-		if w.env == nil && t.instPos != nil {
-			w.byte(instanceMarker)
-		}
 		w.typePrefix(t)
 		w.typeName(t.obj)
 		if t.targs != nil {
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index af56297..0143f53 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -258,7 +258,7 @@
 			check.errorf(&x, _NotAType, "%s is not a type", &x)
 		}
 
-	case *ast.IndexExpr, *ast.MultiIndexExpr:
+	case *ast.IndexExpr, *ast.IndexListExpr:
 		ix := typeparams.UnpackIndexExpr(e)
 		if !check.allowVersion(check.pkg, 1, 18) {
 			check.softErrorf(inNode(e, ix.Lbrack), _Todo, "type instantiation requires go1.18 or later")
@@ -412,6 +412,14 @@
 // and returns the constant length >= 0, or a value < 0
 // to indicate an error (and thus an unknown length).
 func (check *Checker) arrayLength(e ast.Expr) int64 {
+	// If e is an undeclared identifier, the array declaration might be an
+	// attempt at a parameterized type declaration with missing constraint.
+	// Provide a better error message than just "undeclared name: X".
+	if name, _ := e.(*ast.Ident); name != nil && check.lookup(name.Name) == nil {
+		check.errorf(name, _InvalidArrayLen, "undeclared name %s for array length", name.Name)
+		return -1
+	}
+
 	var x operand
 	check.expr(&x, e)
 	if x.mode != constant_ {
@@ -420,6 +428,7 @@
 		}
 		return -1
 	}
+
 	if isUntyped(x.typ) || isInteger(x.typ) {
 		if val := constant.ToInt(x.val); val.Kind() == constant.Int {
 			if representableConst(val, check, Typ[Int], nil) {
@@ -431,6 +440,7 @@
 			}
 		}
 	}
+
 	check.errorf(&x, _InvalidArrayLen, "array length %s must be integer", &x)
 	return -1
 }
diff --git a/src/go/types/unify.go b/src/go/types/unify.go
index ed769aa..6d10f71 100644
--- a/src/go/types/unify.go
+++ b/src/go/types/unify.go
@@ -425,9 +425,6 @@
 
 	case *Named:
 		if y, ok := y.(*Named); ok {
-			x.expand(nil)
-			y.expand(nil)
-
 			xargs := x.targs.list()
 			yargs := y.targs.list()
 
diff --git a/src/internal/abi/abi.go b/src/internal/abi/abi.go
index eadff24..46dc593 100644
--- a/src/internal/abi/abi.go
+++ b/src/internal/abi/abi.go
@@ -19,6 +19,14 @@
 // when it may not be safe to keep them only in the integer
 // register space otherwise.
 type RegArgs struct {
+	// Values in these slots should be precisely the bit-by-bit
+	// representation of how they would appear in a register.
+	//
+	// This means that on big endian arches, integer values should
+	// be in the top bits of the slot. Floats are usually just
+	// directly represented, but some architectures treat narrow
+	// width floating point values specially (e.g. they're promoted
+	// first, or they need to be NaN-boxed).
 	Ints   [IntArgRegs]uintptr  // untyped integer registers
 	Floats [FloatArgRegs]uint64 // untyped float registers
 
@@ -56,26 +64,6 @@
 	return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Ints[reg])) + offset)
 }
 
-// FloatRegArgAddr returns a pointer inside of r.Floats[reg] that is appropriately
-// offset for an argument of size argSize.
-//
-// argSize must be non-zero, fit in a register, and a power-of-two.
-//
-// This method is a helper for dealing with the endianness of different CPU
-// architectures, since sub-word-sized arguments in big endian architectures
-// need to be "aligned" to the upper edge of the register to be interpreted
-// by the CPU correctly.
-func (r *RegArgs) FloatRegArgAddr(reg int, argSize uintptr) unsafe.Pointer {
-	if argSize > EffectiveFloatRegSize || argSize == 0 || argSize&(argSize-1) != 0 {
-		panic("invalid argSize")
-	}
-	offset := uintptr(0)
-	if goarch.BigEndian {
-		offset = EffectiveFloatRegSize - argSize
-	}
-	return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Floats[reg])) + offset)
-}
-
 // IntArgRegBitmap is a bitmap large enough to hold one bit per
 // integer argument/return register.
 type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8
diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go
index 9fe7f21..68c10a2 100644
--- a/src/internal/buildcfg/cfg.go
+++ b/src/internal/buildcfg/cfg.go
@@ -25,6 +25,7 @@
 	GOARCH   = envOr("GOARCH", defaultGOARCH)
 	GOOS     = envOr("GOOS", defaultGOOS)
 	GO386    = envOr("GO386", defaultGO386)
+	GOAMD64  = goamd64()
 	GOARM    = goarm()
 	GOMIPS   = gomips()
 	GOMIPS64 = gomips64()
@@ -52,6 +53,21 @@
 	return value
 }
 
+func goamd64() int {
+	switch v := envOr("GOAMD64", defaultGOAMD64); v {
+	case "v1":
+		return 1
+	case "v2":
+		return 2
+	case "v3":
+		return 3
+	case "v4":
+		return 4
+	}
+	Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4")
+	return int(defaultGOAMD64[len("v")] - '0')
+}
+
 func goarm() int {
 	def := defaultGOARM
 	if GOOS == "android" && GOARCH == "arm" {
diff --git a/src/internal/buildcfg/cfg_test.go b/src/internal/buildcfg/cfg_test.go
new file mode 100644
index 0000000..9180441
--- /dev/null
+++ b/src/internal/buildcfg/cfg_test.go
@@ -0,0 +1,25 @@
+// 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 buildcfg
+
+import (
+	"os"
+	"testing"
+)
+
+func TestConfigFlags(t *testing.T) {
+	os.Setenv("GOAMD64", "v1")
+	if goamd64() != 1 {
+		t.Errorf("Wrong parsing of GOAMD64=v1")
+	}
+	os.Setenv("GOAMD64", "v4")
+	if goamd64() != 4 {
+		t.Errorf("Wrong parsing of GOAMD64=v4")
+	}
+	os.Setenv("GOAMD64", "1")
+	if goamd64() != 1 {
+		t.Errorf("Wrong parsing of GOAMD64=1")
+	}
+}
diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go
index 815994b..4cb3fbd 100644
--- a/src/internal/cfg/cfg.go
+++ b/src/internal/cfg/cfg.go
@@ -33,6 +33,7 @@
 	GCCGO
 	GO111MODULE
 	GO386
+	GOAMD64
 	GOARCH
 	GOARM
 	GOBIN
diff --git a/src/internal/poll/splice_linux_test.go b/src/internal/poll/splice_linux_test.go
index 280468c..8c43638 100644
--- a/src/internal/poll/splice_linux_test.go
+++ b/src/internal/poll/splice_linux_test.go
@@ -6,40 +6,48 @@
 
 import (
 	"internal/poll"
-	"internal/syscall/unix"
 	"runtime"
-	"syscall"
+	"sync"
+	"sync/atomic"
 	"testing"
 	"time"
 )
 
-// checkPipes returns true if all pipes are closed properly, false otherwise.
-func checkPipes(fds []int) bool {
-	for _, fd := range fds {
-		// Check if each pipe fd has been closed.
-		_, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0)
-		if errno == 0 {
-			return false
+var closeHook atomic.Value // func(fd int)
+
+func init() {
+	closeFunc := poll.CloseFunc
+	poll.CloseFunc = func(fd int) (err error) {
+		if v := closeHook.Load(); v != nil {
+			if hook := v.(func(int)); hook != nil {
+				hook(fd)
+			}
 		}
+		return closeFunc(fd)
 	}
-	return true
 }
 
 func TestSplicePipePool(t *testing.T) {
 	const N = 64
 	var (
-		p   *poll.SplicePipe
-		ps  []*poll.SplicePipe
-		fds []int
-		err error
+		p          *poll.SplicePipe
+		ps         []*poll.SplicePipe
+		allFDs     []int
+		pendingFDs sync.Map // fd → struct{}{}
+		err        error
 	)
+
+	closeHook.Store(func(fd int) { pendingFDs.Delete(fd) })
+	t.Cleanup(func() { closeHook.Store((func(int))(nil)) })
+
 	for i := 0; i < N; i++ {
 		p, _, err = poll.GetPipe()
 		if err != nil {
-			t.Skip("failed to create pipe, skip this test")
+			t.Skipf("failed to create pipe due to error(%v), skip this test", err)
 		}
 		_, pwfd := poll.GetPipeFds(p)
-		fds = append(fds, pwfd)
+		allFDs = append(allFDs, pwfd)
+		pendingFDs.Store(pwfd, struct{}{})
 		ps = append(ps, p)
 	}
 	for _, p = range ps {
@@ -62,12 +70,21 @@
 	for {
 		runtime.GC()
 		time.Sleep(10 * time.Millisecond)
-		if checkPipes(fds) {
+
+		// Detect whether all pipes are closed properly.
+		var leakedFDs []int
+		pendingFDs.Range(func(k, v interface{}) bool {
+			leakedFDs = append(leakedFDs, k.(int))
+			return true
+		})
+		if len(leakedFDs) == 0 {
 			break
 		}
+
 		select {
 		case <-expiredTime.C:
-			t.Fatal("at least one pipe is still open")
+			t.Logf("all descriptors: %v", allFDs)
+			t.Fatalf("leaked descriptors: %v", leakedFDs)
 		default:
 		}
 	}
diff --git a/src/log/log.go b/src/log/log.go
index b77af29..3172384 100644
--- a/src/log/log.go
+++ b/src/log/log.go
@@ -20,6 +20,7 @@
 	"os"
 	"runtime"
 	"sync"
+	"sync/atomic"
 	"time"
 )
 
@@ -50,11 +51,12 @@
 // the Writer's Write method. A Logger can be used simultaneously from
 // multiple goroutines; it guarantees to serialize access to the Writer.
 type Logger struct {
-	mu     sync.Mutex // ensures atomic writes; protects the following fields
-	prefix string     // prefix on each line to identify the logger (but see Lmsgprefix)
-	flag   int        // properties
-	out    io.Writer  // destination for output
-	buf    []byte     // for accumulating text to write
+	mu        sync.Mutex // ensures atomic writes; protects the following fields
+	prefix    string     // prefix on each line to identify the logger (but see Lmsgprefix)
+	flag      int        // properties
+	out       io.Writer  // destination for output
+	buf       []byte     // for accumulating text to write
+	isDiscard int32      // atomic boolean: whether out == io.Discard
 }
 
 // New creates a new Logger. The out variable sets the
@@ -63,7 +65,11 @@
 // after the log header if the Lmsgprefix flag is provided.
 // The flag argument defines the logging properties.
 func New(out io.Writer, prefix string, flag int) *Logger {
-	return &Logger{out: out, prefix: prefix, flag: flag}
+	l := &Logger{out: out, prefix: prefix, flag: flag}
+	if out == io.Discard {
+		l.isDiscard = 1
+	}
+	return l
 }
 
 // SetOutput sets the output destination for the logger.
@@ -71,6 +77,11 @@
 	l.mu.Lock()
 	defer l.mu.Unlock()
 	l.out = w
+	isDiscard := int32(0)
+	if w == io.Discard {
+		isDiscard = 1
+	}
+	atomic.StoreInt32(&l.isDiscard, isDiscard)
 }
 
 var std = New(os.Stderr, "", LstdFlags)
@@ -188,16 +199,29 @@
 // Printf calls l.Output to print to the logger.
 // Arguments are handled in the manner of fmt.Printf.
 func (l *Logger) Printf(format string, v ...interface{}) {
+	if atomic.LoadInt32(&l.isDiscard) != 0 {
+		return
+	}
 	l.Output(2, fmt.Sprintf(format, v...))
 }
 
 // Print calls l.Output to print to the logger.
 // Arguments are handled in the manner of fmt.Print.
-func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) }
+func (l *Logger) Print(v ...interface{}) {
+	if atomic.LoadInt32(&l.isDiscard) != 0 {
+		return
+	}
+	l.Output(2, fmt.Sprint(v...))
+}
 
 // Println calls l.Output to print to the logger.
 // Arguments are handled in the manner of fmt.Println.
-func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) }
+func (l *Logger) Println(v ...interface{}) {
+	if atomic.LoadInt32(&l.isDiscard) != 0 {
+		return
+	}
+	l.Output(2, fmt.Sprintln(v...))
+}
 
 // Fatal is equivalent to l.Print() followed by a call to os.Exit(1).
 func (l *Logger) Fatal(v ...interface{}) {
@@ -277,9 +301,7 @@
 
 // SetOutput sets the output destination for the standard logger.
 func SetOutput(w io.Writer) {
-	std.mu.Lock()
-	defer std.mu.Unlock()
-	std.out = w
+	std.SetOutput(w)
 }
 
 // Flags returns the output flags for the standard logger.
@@ -314,18 +336,27 @@
 // Print calls Output to print to the standard logger.
 // Arguments are handled in the manner of fmt.Print.
 func Print(v ...interface{}) {
+	if atomic.LoadInt32(&std.isDiscard) != 0 {
+		return
+	}
 	std.Output(2, fmt.Sprint(v...))
 }
 
 // Printf calls Output to print to the standard logger.
 // Arguments are handled in the manner of fmt.Printf.
 func Printf(format string, v ...interface{}) {
+	if atomic.LoadInt32(&std.isDiscard) != 0 {
+		return
+	}
 	std.Output(2, fmt.Sprintf(format, v...))
 }
 
 // Println calls Output to print to the standard logger.
 // Arguments are handled in the manner of fmt.Println.
 func Println(v ...interface{}) {
+	if atomic.LoadInt32(&std.isDiscard) != 0 {
+		return
+	}
 	std.Output(2, fmt.Sprintln(v...))
 }
 
diff --git a/src/log/log_test.go b/src/log/log_test.go
index 5be8e82..938ed42 100644
--- a/src/log/log_test.go
+++ b/src/log/log_test.go
@@ -9,6 +9,7 @@
 import (
 	"bytes"
 	"fmt"
+	"io"
 	"os"
 	"regexp"
 	"strings"
@@ -20,7 +21,7 @@
 	Rdate         = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
 	Rtime         = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
 	Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
-	Rline         = `(60|62):` // must update if the calls to l.Printf / l.Print below move
+	Rline         = `(61|63):` // must update if the calls to l.Printf / l.Print below move
 	Rlongfile     = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
 	Rshortfile    = `[A-Za-z0-9_\-]+\.go:` + Rline
 )
@@ -179,6 +180,17 @@
 	}
 }
 
+func TestDiscard(t *testing.T) {
+	l := New(io.Discard, "", 0)
+	s := strings.Repeat("a", 102400)
+	c := testing.AllocsPerRun(100, func() { l.Printf("%s", s) })
+	// One allocation for slice passed to Printf,
+	// but none for formatting of long string.
+	if c > 1 {
+		t.Errorf("got %v allocs, want at most 1", c)
+	}
+}
+
 func BenchmarkItoa(b *testing.B) {
 	dst := make([]byte, 0, 64)
 	for i := 0; i < b.N; i++ {
diff --git a/src/net/lookup.go b/src/net/lookup.go
index 02beaca..3c01530 100644
--- a/src/net/lookup.go
+++ b/src/net/lookup.go
@@ -442,7 +442,7 @@
 // The returned service names are validated to be properly
 // formatted presentation-format domain names. If the response contains
 // invalid names, those records are filtered out and an error
-// will be returned alongside the the remaining results, if any.
+// will be returned alongside the remaining results, if any.
 func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
 	return DefaultResolver.LookupSRV(context.Background(), service, proto, name)
 }
@@ -460,7 +460,7 @@
 // The returned service names are validated to be properly
 // formatted presentation-format domain names. If the response contains
 // invalid names, those records are filtered out and an error
-// will be returned alongside the the remaining results, if any.
+// will be returned alongside the remaining results, if any.
 func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
 	cname, addrs, err := r.lookupSRV(ctx, service, proto, name)
 	if err != nil {
@@ -490,7 +490,7 @@
 // The returned mail server names are validated to be properly
 // formatted presentation-format domain names. If the response contains
 // invalid names, those records are filtered out and an error
-// will be returned alongside the the remaining results, if any.
+// will be returned alongside the remaining results, if any.
 //
 // LookupMX uses context.Background internally; to specify the context, use
 // Resolver.LookupMX.
@@ -503,7 +503,7 @@
 // The returned mail server names are validated to be properly
 // formatted presentation-format domain names. If the response contains
 // invalid names, those records are filtered out and an error
-// will be returned alongside the the remaining results, if any.
+// will be returned alongside the remaining results, if any.
 func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
 	records, err := r.lookupMX(ctx, name)
 	if err != nil {
@@ -532,7 +532,7 @@
 // The returned name server names are validated to be properly
 // formatted presentation-format domain names. If the response contains
 // invalid names, those records are filtered out and an error
-// will be returned alongside the the remaining results, if any.
+// will be returned alongside the remaining results, if any.
 //
 // LookupNS uses context.Background internally; to specify the context, use
 // Resolver.LookupNS.
@@ -545,7 +545,7 @@
 // The returned name server names are validated to be properly
 // formatted presentation-format domain names. If the response contains
 // invalid names, those records are filtered out and an error
-// will be returned alongside the the remaining results, if any.
+// will be returned alongside the remaining results, if any.
 func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
 	records, err := r.lookupNS(ctx, name)
 	if err != nil {
@@ -585,7 +585,7 @@
 //
 // The returned names are validated to be properly formatted presentation-format
 // domain names. If the response contains invalid names, those records are filtered
-// out and an error will be returned alongside the the remaining results, if any.
+// out and an error will be returned alongside the remaining results, if any.
 //
 // When using the host C library resolver, at most one result will be
 // returned. To bypass the host resolver, use a custom Resolver.
@@ -601,7 +601,7 @@
 //
 // The returned names are validated to be properly formatted presentation-format
 // domain names. If the response contains invalid names, those records are filtered
-// out and an error will be returned alongside the the remaining results, if any.
+// out and an error will be returned alongside the remaining results, if any.
 func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
 	names, err := r.lookupAddr(ctx, addr)
 	if err != nil {
diff --git a/src/net/net.go b/src/net/net.go
index a7c65ff..ab6aeaa 100644
--- a/src/net/net.go
+++ b/src/net/net.go
@@ -396,8 +396,12 @@
 // An Error represents a network error.
 type Error interface {
 	error
-	Timeout() bool   // Is the error a timeout?
-	Temporary() bool // Is the error temporary?
+	Timeout() bool // Is the error a timeout?
+
+	// Deprecated: Temporary errors are not well-defined.
+	// Most "temporary" errors are timeouts, and the few exceptions are surprising.
+	// Do not use this method.
+	Temporary() bool
 }
 
 // Various errors contained in OpError.
diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go
index 074c5b9..bfc19ac 100644
--- a/src/net/rpc/server.go
+++ b/src/net/rpc/server.go
@@ -231,6 +231,10 @@
 	return server.register(rcvr, name, true)
 }
 
+// logRegisterError specifies whether to log problems during method registration.
+// To debug registration, recompile the package with this set to true.
+const logRegisterError = false
+
 func (server *Server) register(rcvr interface{}, name string, useName bool) error {
 	s := new(service)
 	s.typ = reflect.TypeOf(rcvr)
@@ -252,7 +256,7 @@
 	s.name = sname
 
 	// Install the methods
-	s.method = suitableMethods(s.typ, true)
+	s.method = suitableMethods(s.typ, logRegisterError)
 
 	if len(s.method) == 0 {
 		str := ""
@@ -274,9 +278,9 @@
 	return nil
 }
 
-// suitableMethods returns suitable Rpc methods of typ, it will report
-// error using log if reportErr is true.
-func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
+// suitableMethods returns suitable Rpc methods of typ. It will log
+// errors if logErr is true.
+func suitableMethods(typ reflect.Type, logErr bool) map[string]*methodType {
 	methods := make(map[string]*methodType)
 	for m := 0; m < typ.NumMethod(); m++ {
 		method := typ.Method(m)
@@ -288,7 +292,7 @@
 		}
 		// Method needs three ins: receiver, *args, *reply.
 		if mtype.NumIn() != 3 {
-			if reportErr {
+			if logErr {
 				log.Printf("rpc.Register: method %q has %d input parameters; needs exactly three\n", mname, mtype.NumIn())
 			}
 			continue
@@ -296,7 +300,7 @@
 		// First arg need not be a pointer.
 		argType := mtype.In(1)
 		if !isExportedOrBuiltinType(argType) {
-			if reportErr {
+			if logErr {
 				log.Printf("rpc.Register: argument type of method %q is not exported: %q\n", mname, argType)
 			}
 			continue
@@ -304,28 +308,28 @@
 		// Second arg must be a pointer.
 		replyType := mtype.In(2)
 		if replyType.Kind() != reflect.Ptr {
-			if reportErr {
+			if logErr {
 				log.Printf("rpc.Register: reply type of method %q is not a pointer: %q\n", mname, replyType)
 			}
 			continue
 		}
 		// Reply type must be exported.
 		if !isExportedOrBuiltinType(replyType) {
-			if reportErr {
+			if logErr {
 				log.Printf("rpc.Register: reply type of method %q is not exported: %q\n", mname, replyType)
 			}
 			continue
 		}
 		// Method needs one out.
 		if mtype.NumOut() != 1 {
-			if reportErr {
+			if logErr {
 				log.Printf("rpc.Register: method %q has %d output parameters; needs exactly one\n", mname, mtype.NumOut())
 			}
 			continue
 		}
 		// The return type of the method must be error.
 		if returnType := mtype.Out(0); returnType != typeOfError {
-			if reportErr {
+			if logErr {
 				log.Printf("rpc.Register: return type of method %q is %q, must be error\n", mname, returnType)
 			}
 			continue
diff --git a/src/os/exec/exec_windows_test.go b/src/os/exec/exec_windows_test.go
index fbccffe..bd4dfb3 100644
--- a/src/os/exec/exec_windows_test.go
+++ b/src/os/exec/exec_windows_test.go
@@ -10,6 +10,7 @@
 import (
 	"io"
 	"os"
+	"os/exec"
 	"strconv"
 	"syscall"
 	"testing"
@@ -41,3 +42,16 @@
 		t.Error(err)
 	}
 }
+
+func TestNoInheritHandles(t *testing.T) {
+	cmd := exec.Command("cmd", "/c exit 88")
+	cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true}
+	err := cmd.Run()
+	exitError, ok := err.(*exec.ExitError)
+	if !ok {
+		t.Fatalf("got error %v; want ExitError", err)
+	}
+	if exitError.ExitCode() != 88 {
+		t.Fatalf("got exit code %d; want 88", exitError.ExitCode())
+	}
+}
diff --git a/src/reflect/abi.go b/src/reflect/abi.go
index 9ddde3a..2ce7ca2 100644
--- a/src/reflect/abi.go
+++ b/src/reflect/abi.go
@@ -467,3 +467,45 @@
 	out.stackBytes -= retOffset
 	return abiDesc{in, out, stackCallArgsSize, retOffset, spill, stackPtrs, inRegPtrs, outRegPtrs}
 }
+
+// intFromReg loads an argSize sized integer from reg and places it at to.
+//
+// argSize must be non-zero, fit in a register, and a power-of-two.
+func intFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) {
+	memmove(to, r.IntRegArgAddr(reg, argSize), argSize)
+}
+
+// intToReg loads an argSize sized integer and stores it into reg.
+//
+// argSize must be non-zero, fit in a register, and a power-of-two.
+func intToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) {
+	memmove(r.IntRegArgAddr(reg, argSize), from, argSize)
+}
+
+// floatFromReg loads a float value from its register representation in r.
+//
+// argSize must be 4 or 8.
+func floatFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) {
+	switch argSize {
+	case 4:
+		*(*float32)(to) = archFloat32FromReg(r.Floats[reg])
+	case 8:
+		*(*float64)(to) = *(*float64)(unsafe.Pointer(&r.Floats[reg]))
+	default:
+		panic("bad argSize")
+	}
+}
+
+// floatToReg stores a float value in its register representation in r.
+//
+// argSize must be either 4 or 8.
+func floatToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) {
+	switch argSize {
+	case 4:
+		r.Floats[reg] = archFloat32ToReg(*(*float32)(from))
+	case 8:
+		r.Floats[reg] = *(*uint64)(from)
+	default:
+		panic("bad argSize")
+	}
+}
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 293d036..22885c5 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -905,6 +905,9 @@
 	{error(nil), error(nil), true},
 	{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
 	{fn1, fn2, true},
+	{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
+	{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
+	{MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true},
 
 	// Inequalities
 	{1, 2, false},
@@ -925,6 +928,9 @@
 	{fn1, fn3, false},
 	{fn3, fn3, false},
 	{[][]int{{1}}, [][]int{{2}}, false},
+	{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
+
+	// Fun with floating point.
 	{math.NaN(), math.NaN(), false},
 	{&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, false},
 	{&[1]float64{math.NaN()}, self{}, true},
@@ -932,7 +938,6 @@
 	{[]float64{math.NaN()}, self{}, true},
 	{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
 	{map[float64]float64{math.NaN(): 1}, self{}, true},
-	{&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false},
 
 	// Nil vs empty: not the same.
 	{[]int{}, []int(nil), false},
@@ -950,6 +955,9 @@
 	{&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false},
 	{Basic{1, 0.5}, NotBasic{1, 0.5}, false},
 	{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false},
+	{[]byte{1, 2, 3}, []MyByte{1, 2, 3}, false},
+	{[]MyByte{1, 2, 3}, MyBytes{1, 2, 3}, false},
+	{[]byte{1, 2, 3}, MyBytes{1, 2, 3}, false},
 
 	// Possible loops.
 	{&loop1, &loop1, true},
@@ -1049,6 +1057,82 @@
 	}
 }
 
+var deepEqualPerfTests = []struct {
+	x, y interface{}
+}{
+	{x: int8(99), y: int8(99)},
+	{x: []int8{99}, y: []int8{99}},
+	{x: int16(99), y: int16(99)},
+	{x: []int16{99}, y: []int16{99}},
+	{x: int32(99), y: int32(99)},
+	{x: []int32{99}, y: []int32{99}},
+	{x: int64(99), y: int64(99)},
+	{x: []int64{99}, y: []int64{99}},
+	{x: int(999999), y: int(999999)},
+	{x: []int{999999}, y: []int{999999}},
+
+	{x: uint8(99), y: uint8(99)},
+	{x: []uint8{99}, y: []uint8{99}},
+	{x: uint16(99), y: uint16(99)},
+	{x: []uint16{99}, y: []uint16{99}},
+	{x: uint32(99), y: uint32(99)},
+	{x: []uint32{99}, y: []uint32{99}},
+	{x: uint64(99), y: uint64(99)},
+	{x: []uint64{99}, y: []uint64{99}},
+	{x: uint(999999), y: uint(999999)},
+	{x: []uint{999999}, y: []uint{999999}},
+	{x: uintptr(999999), y: uintptr(999999)},
+	{x: []uintptr{999999}, y: []uintptr{999999}},
+
+	{x: float32(1.414), y: float32(1.414)},
+	{x: []float32{1.414}, y: []float32{1.414}},
+	{x: float64(1.414), y: float64(1.414)},
+	{x: []float64{1.414}, y: []float64{1.414}},
+
+	{x: complex64(1.414), y: complex64(1.414)},
+	{x: []complex64{1.414}, y: []complex64{1.414}},
+	{x: complex128(1.414), y: complex128(1.414)},
+	{x: []complex128{1.414}, y: []complex128{1.414}},
+
+	{x: true, y: true},
+	{x: []bool{true}, y: []bool{true}},
+
+	{x: "abcdef", y: "abcdef"},
+	{x: []string{"abcdef"}, y: []string{"abcdef"}},
+
+	{x: []byte("abcdef"), y: []byte("abcdef")},
+	{x: [][]byte{[]byte("abcdef")}, y: [][]byte{[]byte("abcdef")}},
+
+	{x: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}, y: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}},
+	{x: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}, y: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}},
+}
+
+func TestDeepEqualAllocs(t *testing.T) {
+	for _, tt := range deepEqualPerfTests {
+		t.Run(ValueOf(tt.x).Type().String(), func(t *testing.T) {
+			got := testing.AllocsPerRun(100, func() {
+				if !DeepEqual(tt.x, tt.y) {
+					t.Errorf("DeepEqual(%v, %v)=false", tt.x, tt.y)
+				}
+			})
+			if int(got) != 0 {
+				t.Errorf("DeepEqual(%v, %v) allocated %d times", tt.x, tt.y, int(got))
+			}
+		})
+	}
+}
+
+func BenchmarkDeepEqual(b *testing.B) {
+	for _, bb := range deepEqualPerfTests {
+		b.Run(ValueOf(bb.x).Type().String(), func(b *testing.B) {
+			b.ReportAllocs()
+			for i := 0; i < b.N; i++ {
+				sink = DeepEqual(bb.x, bb.y)
+			}
+		})
+	}
+}
+
 func check2ndField(x interface{}, offs uintptr, t *testing.T) {
 	s := ValueOf(x)
 	f := s.Type().Field(1)
@@ -7050,6 +7134,53 @@
 	})
 }
 
+func BenchmarkMap(b *testing.B) {
+	type V *int
+	value := ValueOf((V)(nil))
+	stringKeys := []string{}
+	mapOfStrings := map[string]V{}
+	uint64Keys := []uint64{}
+	mapOfUint64s := map[uint64]V{}
+	for i := 0; i < 100; i++ {
+		stringKey := fmt.Sprintf("key%d", i)
+		stringKeys = append(stringKeys, stringKey)
+		mapOfStrings[stringKey] = nil
+
+		uint64Key := uint64(i)
+		uint64Keys = append(uint64Keys, uint64Key)
+		mapOfUint64s[uint64Key] = nil
+	}
+
+	tests := []struct {
+		label          string
+		m, keys, value Value
+	}{
+		{"StringKeys", ValueOf(mapOfStrings), ValueOf(stringKeys), value},
+		{"Uint64Keys", ValueOf(mapOfUint64s), ValueOf(uint64Keys), value},
+	}
+
+	for _, tt := range tests {
+		b.Run(tt.label, func(b *testing.B) {
+			b.Run("MapIndex", func(b *testing.B) {
+				b.ReportAllocs()
+				for i := 0; i < b.N; i++ {
+					for j := tt.keys.Len() - 1; j >= 0; j-- {
+						tt.m.MapIndex(tt.keys.Index(j))
+					}
+				}
+			})
+			b.Run("SetMapIndex", func(b *testing.B) {
+				b.ReportAllocs()
+				for i := 0; i < b.N; i++ {
+					for j := tt.keys.Len() - 1; j >= 0; j-- {
+						tt.m.SetMapIndex(tt.keys.Index(j), tt.value)
+					}
+				}
+			})
+		})
+	}
+}
+
 func TestSwapper(t *testing.T) {
 	type I int
 	var a, b, c I
diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go
index d951d8d..94174de 100644
--- a/src/reflect/deepequal.go
+++ b/src/reflect/deepequal.go
@@ -6,7 +6,10 @@
 
 package reflect
 
-import "unsafe"
+import (
+	"internal/bytealg"
+	"unsafe"
+)
 
 // During deepValueEqual, must keep track of checks that are
 // in progress. The comparison algorithm assumes that all
@@ -102,6 +105,10 @@
 		if v1.Pointer() == v2.Pointer() {
 			return true
 		}
+		// Special case for []byte, which is common.
+		if v1.Type().Elem().Kind() == Uint8 {
+			return bytealg.Equal(v1.Bytes(), v2.Bytes())
+		}
 		for i := 0; i < v1.Len(); i++ {
 			if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
 				return false
@@ -149,6 +156,18 @@
 		}
 		// Can't do better than this:
 		return false
+	case Int, Int8, Int16, Int32, Int64:
+		return v1.Int() == v2.Int()
+	case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
+		return v1.Uint() == v2.Uint()
+	case String:
+		return v1.String() == v2.String()
+	case Bool:
+		return v1.Bool() == v2.Bool()
+	case Float32, Float64:
+		return v1.Float() == v2.Float()
+	case Complex64, Complex128:
+		return v1.Complex() == v2.Complex()
 	default:
 		// Normal equality suffices
 		return valueInterface(v1, false) == valueInterface(v2, false)
diff --git a/src/reflect/float32reg_generic.go b/src/reflect/float32reg_generic.go
new file mode 100644
index 0000000..381d458
--- /dev/null
+++ b/src/reflect/float32reg_generic.go
@@ -0,0 +1,21 @@
+// 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 "unsafe"
+
+// This file implements a straightforward conversion of a float32
+// value into its representation in a register. This conversion
+// applies for amd64 and arm64. It is also chosen for the case of
+// zero argument registers, but is not used.
+
+func archFloat32FromReg(reg uint64) float32 {
+	i := uint32(reg)
+	return *(*float32)(unsafe.Pointer(&i))
+}
+
+func archFloat32ToReg(val float32) uint64 {
+	return uint64(*(*uint32)(unsafe.Pointer(&val)))
+}
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 3c21721..33b81d7 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -508,7 +508,7 @@
 				// Copy values to "integer registers."
 				if v.flag&flagIndir != 0 {
 					offset := add(v.ptr, st.offset, "precomputed value offset")
-					memmove(regArgs.IntRegArgAddr(st.ireg, st.size), offset, st.size)
+					intToReg(&regArgs, st.ireg, st.size, offset)
 				} else {
 					if st.kind == abiStepPointer {
 						// Duplicate this pointer in the pointer area of the
@@ -524,7 +524,7 @@
 					panic("attempted to copy pointer to FP register")
 				}
 				offset := add(v.ptr, st.offset, "precomputed value offset")
-				memmove(regArgs.FloatRegArgAddr(st.freg, st.size), offset, st.size)
+				floatToReg(&regArgs, st.freg, st.size, offset)
 			default:
 				panic("unknown ABI part kind")
 			}
@@ -610,13 +610,13 @@
 				switch st.kind {
 				case abiStepIntReg:
 					offset := add(s, st.offset, "precomputed value offset")
-					memmove(offset, regArgs.IntRegArgAddr(st.ireg, st.size), st.size)
+					intFromReg(&regArgs, st.ireg, st.size, offset)
 				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, regArgs.FloatRegArgAddr(st.freg, st.size), st.size)
+					floatFromReg(&regArgs, st.freg, st.size, offset)
 				case abiStepStack:
 					panic("register-based return value has stack component")
 				default:
@@ -698,13 +698,13 @@
 					switch st.kind {
 					case abiStepIntReg:
 						offset := add(v.ptr, st.offset, "precomputed value offset")
-						memmove(offset, regs.IntRegArgAddr(st.ireg, st.size), st.size)
+						intFromReg(regs, st.ireg, st.size, offset)
 					case abiStepPointer:
 						s := add(v.ptr, st.offset, "precomputed value offset")
 						*((*unsafe.Pointer)(s)) = regs.Ptrs[st.ireg]
 					case abiStepFloatReg:
 						offset := add(v.ptr, st.offset, "precomputed value offset")
-						memmove(offset, regs.FloatRegArgAddr(st.freg, st.size), st.size)
+						floatFromReg(regs, st.freg, st.size, offset)
 					case abiStepStack:
 						panic("register-based return value has stack component")
 					default:
@@ -784,7 +784,7 @@
 					// Copy values to "integer registers."
 					if v.flag&flagIndir != 0 {
 						offset := add(v.ptr, st.offset, "precomputed value offset")
-						memmove(regs.IntRegArgAddr(st.ireg, st.size), offset, st.size)
+						intToReg(regs, st.ireg, st.size, offset)
 					} else {
 						// Only populate the Ints space on the return path.
 						// This is safe because out is kept alive until the
@@ -799,7 +799,7 @@
 						panic("attempted to copy pointer to FP register")
 					}
 					offset := add(v.ptr, st.offset, "precomputed value offset")
-					memmove(regs.FloatRegArgAddr(st.freg, st.size), offset, st.size)
+					floatToReg(regs, st.freg, st.size, offset)
 				default:
 					panic("unknown ABI part kind")
 				}
@@ -982,9 +982,9 @@
 					methodRegs.Ptrs[mStep.ireg] = *(*unsafe.Pointer)(from)
 					fallthrough // We need to make sure this ends up in Ints, too.
 				case abiStepIntReg:
-					memmove(methodRegs.IntRegArgAddr(mStep.ireg, mStep.size), from, mStep.size)
+					intToReg(&methodRegs, mStep.ireg, mStep.size, from)
 				case abiStepFloatReg:
-					memmove(methodRegs.FloatRegArgAddr(mStep.freg, mStep.size), from, mStep.size)
+					floatToReg(&methodRegs, mStep.freg, mStep.size, from)
 				default:
 					panic("unexpected method step")
 				}
@@ -1000,9 +1000,9 @@
 					// Do the pointer copy directly so we get a write barrier.
 					*(*unsafe.Pointer)(to) = valueRegs.Ptrs[vStep.ireg]
 				case abiStepIntReg:
-					memmove(to, valueRegs.IntRegArgAddr(vStep.ireg, vStep.size), vStep.size)
+					intFromReg(valueRegs, vStep.ireg, vStep.size, to)
 				case abiStepFloatReg:
-					memmove(to, valueRegs.FloatRegArgAddr(vStep.freg, vStep.size), vStep.size)
+					floatFromReg(valueRegs, vStep.freg, vStep.size, to)
 				default:
 					panic("unexpected value step")
 				}
@@ -1515,15 +1515,21 @@
 	// considered unexported. This is consistent with the
 	// behavior for structs, which allow read but not write
 	// of unexported fields.
-	key = key.assignTo("reflect.Value.MapIndex", tt.key, nil)
 
-	var k unsafe.Pointer
-	if key.flag&flagIndir != 0 {
-		k = key.ptr
+	var e unsafe.Pointer
+	if key.kind() == String && tt.key.Kind() == String && tt.elem.size <= maxValSize {
+		k := *(*string)(key.ptr)
+		e = mapaccess_faststr(v.typ, v.pointer(), k)
 	} else {
-		k = unsafe.Pointer(&key.ptr)
+		key = key.assignTo("reflect.Value.MapIndex", tt.key, nil)
+		var k unsafe.Pointer
+		if key.flag&flagIndir != 0 {
+			k = key.ptr
+		} else {
+			k = unsafe.Pointer(&key.ptr)
+		}
+		e = mapaccess(v.typ, v.pointer(), k)
 	}
-	e := mapaccess(v.typ, v.pointer(), k)
 	if e == nil {
 		return Value{}
 	}
@@ -2121,6 +2127,25 @@
 	v.mustBeExported()
 	key.mustBeExported()
 	tt := (*mapType)(unsafe.Pointer(v.typ))
+
+	if key.kind() == String && tt.key.Kind() == String && tt.elem.size <= maxValSize {
+		k := *(*string)(key.ptr)
+		if elem.typ == nil {
+			mapdelete_faststr(v.typ, v.pointer(), k)
+			return
+		}
+		elem.mustBeExported()
+		elem = elem.assignTo("reflect.Value.SetMapIndex", tt.elem, nil)
+		var e unsafe.Pointer
+		if elem.flag&flagIndir != 0 {
+			e = elem.ptr
+		} else {
+			e = unsafe.Pointer(&elem.ptr)
+		}
+		mapassign_faststr(v.typ, v.pointer(), k, e)
+		return
+	}
+
 	key = key.assignTo("reflect.Value.SetMapIndex", tt.key, nil)
 	var k unsafe.Pointer
 	if key.flag&flagIndir != 0 {
@@ -2915,8 +2940,7 @@
 	// from slice to pointer-to-array.
 	if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array {
 		n := t.Elem().Len()
-		h := (*unsafeheader.Slice)(v.ptr)
-		if n > h.Len {
+		if n > v.Len() {
 			return false
 		}
 	}
@@ -3183,10 +3207,10 @@
 // convertOp: []T -> *[N]T
 func cvtSliceArrayPtr(v Value, t Type) Value {
 	n := t.Elem().Len()
-	h := (*unsafeheader.Slice)(v.ptr)
-	if n > h.Len {
-		panic("reflect: cannot convert slice with length " + itoa.Itoa(h.Len) + " to pointer to array with length " + itoa.Itoa(n))
+	if n > v.Len() {
+		panic("reflect: cannot convert slice with length " + itoa.Itoa(v.Len()) + " to pointer to array with length " + itoa.Itoa(n))
 	}
+	h := (*unsafeheader.Slice)(v.ptr)
 	return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)}
 }
 
@@ -3253,12 +3277,21 @@
 func mapaccess(t *rtype, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer)
 
 //go:noescape
+func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer)
+
+//go:noescape
 func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer)
 
 //go:noescape
+func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer)
+
+//go:noescape
 func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer)
 
 //go:noescape
+func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string)
+
+//go:noescape
 func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter)
 
 //go:noescape
diff --git a/src/runtime/cgo/gcc_riscv64.S b/src/runtime/cgo/gcc_riscv64.S
index f429dc6..fdc7749 100644
--- a/src/runtime/cgo/gcc_riscv64.S
+++ b/src/runtime/cgo/gcc_riscv64.S
@@ -8,36 +8,38 @@
  * Calling into the gc tool chain, where all registers are caller save.
  * Called from standard RISCV ELF psABI, where x8-x9, x18-x27, f8-f9 and
  * f18-f27 are callee-save, so they must be saved explicitly, along with
- * x1 (LR).
+ * x1 (LR), x3 (GP) and x4 (TP).
  */
 .globl crosscall1
 crosscall1:
-	sd	x1, -200(sp)
-	addi	sp, sp, -200
-	sd	x8, 8(sp)
-	sd	x9, 16(sp)
-	sd	x18, 24(sp)
-	sd	x19, 32(sp)
-	sd	x20, 40(sp)
-	sd	x21, 48(sp)
-	sd	x22, 56(sp)
-	sd	x23, 64(sp)
-	sd	x24, 72(sp)
-	sd	x25, 80(sp)
-	sd	x26, 88(sp)
-	sd	x27, 96(sp)
-	fsd	f8, 104(sp)
-	fsd	f9, 112(sp)
-	fsd	f18, 120(sp)
-	fsd	f19, 128(sp)
-	fsd	f20, 136(sp)
-	fsd	f21, 144(sp)
-	fsd	f22, 152(sp)
-	fsd	f23, 160(sp)
-	fsd	f24, 168(sp)
-	fsd	f25, 176(sp)
-	fsd	f26, 184(sp)
-	fsd	f27, 192(sp)
+	sd	x1, -216(sp)
+	addi	sp, sp, -216
+	sd	x3, 8(sp)
+	sd	x4, 16(sp)
+	sd	x8, 24(sp)
+	sd	x9, 32(sp)
+	sd	x18, 40(sp)
+	sd	x19, 48(sp)
+	sd	x20, 56(sp)
+	sd	x21, 64(sp)
+	sd	x22, 72(sp)
+	sd	x23, 80(sp)
+	sd	x24, 88(sp)
+	sd	x25, 96(sp)
+	sd	x26, 104(sp)
+	sd	x27, 112(sp)
+	fsd	f8, 120(sp)
+	fsd	f9, 128(sp)
+	fsd	f18, 136(sp)
+	fsd	f19, 144(sp)
+	fsd	f20, 152(sp)
+	fsd	f21, 160(sp)
+	fsd	f22, 168(sp)
+	fsd	f23, 176(sp)
+	fsd	f24, 184(sp)
+	fsd	f25, 192(sp)
+	fsd	f26, 200(sp)
+	fsd	f27, 208(sp)
 
 	// a0 = *fn, a1 = *setg_gcc, a2 = *g
 	mv	s1, a0
@@ -47,31 +49,33 @@
 	jalr	ra, s1	// call fn
 
 	ld	x1, 0(sp)
-	ld	x8, 8(sp)
-	ld	x9, 16(sp)
-	ld	x18, 24(sp)
-	ld	x19, 32(sp)
-	ld	x20, 40(sp)
-	ld	x21, 48(sp)
-	ld	x22, 56(sp)
-	ld	x23, 64(sp)
-	ld	x24, 72(sp)
-	ld	x25, 80(sp)
-	ld	x26, 88(sp)
-	ld	x27, 96(sp)
-	fld	f8, 104(sp)
-	fld	f9, 112(sp)
-	fld	f18, 120(sp)
-	fld	f19, 128(sp)
-	fld	f20, 136(sp)
-	fld	f21, 144(sp)
-	fld	f22, 152(sp)
-	fld	f23, 160(sp)
-	fld	f24, 168(sp)
-	fld	f25, 176(sp)
-	fld	f26, 184(sp)
-	fld	f27, 192(sp)
-	addi	sp, sp, 200
+	ld	x3, 8(sp)
+	ld	x4, 16(sp)
+	ld	x8, 24(sp)
+	ld	x9, 32(sp)
+	ld	x18, 40(sp)
+	ld	x19, 48(sp)
+	ld	x20, 56(sp)
+	ld	x21, 64(sp)
+	ld	x22, 72(sp)
+	ld	x23, 80(sp)
+	ld	x24, 88(sp)
+	ld	x25, 96(sp)
+	ld	x26, 104(sp)
+	ld	x27, 112(sp)
+	fld	f8, 120(sp)
+	fld	f9, 128(sp)
+	fld	f18, 136(sp)
+	fld	f19, 144(sp)
+	fld	f20, 152(sp)
+	fld	f21, 160(sp)
+	fld	f22, 168(sp)
+	fld	f23, 176(sp)
+	fld	f24, 184(sp)
+	fld	f25, 192(sp)
+	fld	f26, 200(sp)
+	fld	f27, 208(sp)
+	addi	sp, sp, 216
 
 	jr	ra
 
diff --git a/src/runtime/cgo/gcc_sigaction.c b/src/runtime/cgo/gcc_sigaction.c
index dd28315..fcf1e50 100644
--- a/src/runtime/cgo/gcc_sigaction.c
+++ b/src/runtime/cgo/gcc_sigaction.c
@@ -49,13 +49,13 @@
 		sigemptyset(&act.sa_mask);
 		for (i = 0; i < 8 * sizeof(goact->mask); i++) {
 			if (goact->mask & ((uint64_t)(1)<<i)) {
-				sigaddset(&act.sa_mask, i+1);
+				sigaddset(&act.sa_mask, (int)(i+1));
 			}
 		}
-		act.sa_flags = goact->flags & ~SA_RESTORER;
+		act.sa_flags = (int)(goact->flags & ~(uint64_t)SA_RESTORER);
 	}
 
-	ret = sigaction(signum, goact ? &act : NULL, oldgoact ? &oldact : NULL);
+	ret = sigaction((int)signum, goact ? &act : NULL, oldgoact ? &oldact : NULL);
 	if (ret == -1) {
 		// runtime.rt_sigaction expects _cgo_sigaction to return errno on error.
 		_cgo_tsan_release();
@@ -70,11 +70,11 @@
 		}
 		oldgoact->mask = 0;
 		for (i = 0; i < 8 * sizeof(oldgoact->mask); i++) {
-			if (sigismember(&oldact.sa_mask, i+1) == 1) {
+			if (sigismember(&oldact.sa_mask, (int)(i+1)) == 1) {
 				oldgoact->mask |= (uint64_t)(1)<<i;
 			}
 		}
-		oldgoact->flags = oldact.sa_flags;
+		oldgoact->flags = (uint64_t)oldact.sa_flags;
 	}
 
 	_cgo_tsan_release();
diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go
index 5729942..ce7bed9 100644
--- a/src/runtime/crash_cgo_test.go
+++ b/src/runtime/crash_cgo_test.go
@@ -591,6 +591,7 @@
 	}
 
 	for _, test := range []string{"Segv", "SegvInCgo"} {
+		test := test
 		t.Run(test, func(t *testing.T) {
 			t.Parallel()
 			got := runTestProg(t, "testprogcgo", test)
diff --git a/src/runtime/map.go b/src/runtime/map.go
index 59b803d..985c297 100644
--- a/src/runtime/map.go
+++ b/src/runtime/map.go
@@ -1324,17 +1324,38 @@
 	return elem
 }
 
+//go:linkname reflect_mapaccess_faststr reflect.mapaccess_faststr
+func reflect_mapaccess_faststr(t *maptype, h *hmap, key string) unsafe.Pointer {
+	elem, ok := mapaccess2_faststr(t, h, key)
+	if !ok {
+		// reflect wants nil for a missing element
+		elem = nil
+	}
+	return elem
+}
+
 //go:linkname reflect_mapassign reflect.mapassign
 func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, elem unsafe.Pointer) {
 	p := mapassign(t, h, key)
 	typedmemmove(t.elem, p, elem)
 }
 
+//go:linkname reflect_mapassign_faststr reflect.mapassign_faststr
+func reflect_mapassign_faststr(t *maptype, h *hmap, key string, elem unsafe.Pointer) {
+	p := mapassign_faststr(t, h, key)
+	typedmemmove(t.elem, p, elem)
+}
+
 //go:linkname reflect_mapdelete reflect.mapdelete
 func reflect_mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
 	mapdelete(t, h, key)
 }
 
+//go:linkname reflect_mapdelete_faststr reflect.mapdelete_faststr
+func reflect_mapdelete_faststr(t *maptype, h *hmap, key string) {
+	mapdelete_faststr(t, h, key)
+}
+
 //go:linkname reflect_mapiterinit reflect.mapiterinit
 func reflect_mapiterinit(t *maptype, h *hmap, it *hiter) {
 	mapiterinit(t, h, it)
diff --git a/src/runtime/mpagealloc.go b/src/runtime/mpagealloc.go
index 071f1fc..862882c 100644
--- a/src/runtime/mpagealloc.go
+++ b/src/runtime/mpagealloc.go
@@ -155,7 +155,7 @@
 	// upper-bound. Note that the exclusive upper bound may be within a
 	// summary at this level, meaning if we just do the obvious computation
 	// hi will end up being an inclusive upper bound. Unfortunately, just
-	// adding 1 to that is too broad since we might be on the very edge of
+	// adding 1 to that is too broad since we might be on the very edge
 	// of a summary's max page count boundary for this level
 	// (1 << levelLogPages[level]). So, make limit an inclusive upper bound
 	// then shift, then add 1, so we get an exclusive upper bound at the end.
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 197441d..605e133 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -623,7 +623,7 @@
 	// Support cpu feature variables are used in code generated by the compiler
 	// to guard execution of instructions that can not be assumed to be always supported.
 	switch GOARCH {
-	case "386", "AMD64":
+	case "386", "amd64":
 		x86HasPOPCNT = cpu.X86.HasPOPCNT
 		x86HasSSE41 = cpu.X86.HasSSE41
 		x86HasFMA = cpu.X86.HasFMA
diff --git a/src/runtime/runtime-gdb.py b/src/runtime/runtime-gdb.py
index 8d96dfb..5bb605c 100644
--- a/src/runtime/runtime-gdb.py
+++ b/src/runtime/runtime-gdb.py
@@ -219,6 +219,9 @@
 			yield ('[{0}]'.format(i), (ptr + j).dereference())
 
 
+def paramtypematch(t, pattern):
+	return t.code == gdb.TYPE_CODE_TYPEDEF and str(t).startswith(".param") and pattern.match(str(t.target()))
+
 #
 #  Register all the *Printer classes above.
 #
@@ -228,6 +231,8 @@
 		try:
 			if klass.pattern.match(str(val.type)):
 				return klass(val)
+			elif paramtypematch(val.type, klass.pattern):
+				return klass(val.cast(val.type.target()))
 		except Exception:
 			pass
 	return matcher
@@ -387,7 +392,7 @@
 	def invoke(self, obj):
 		typename = str(obj.type)
 		for klass, fld in self.how:
-			if klass.pattern.match(typename):
+			if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern):
 				return obj[fld]
 
 
@@ -402,7 +407,7 @@
 	def invoke(self, obj):
 		typename = str(obj.type)
 		for klass, fld in self.how:
-			if klass.pattern.match(typename):
+			if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern):
 				return obj[fld]
 
 
diff --git a/src/runtime/time.go b/src/runtime/time.go
index ad267c3..46e9a8c 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -367,9 +367,9 @@
 
 // dodeltimer removes timer i from the current P's heap.
 // We are locked on the P when this is called.
-// It reports whether it saw no problems due to races.
+// It returns the smallest changed index in pp.timers.
 // The caller must have locked the timers for pp.
-func dodeltimer(pp *p, i int) {
+func dodeltimer(pp *p, i int) int {
 	if t := pp.timers[i]; t.pp.ptr() != pp {
 		throw("dodeltimer: wrong P")
 	} else {
@@ -381,16 +381,18 @@
 	}
 	pp.timers[last] = nil
 	pp.timers = pp.timers[:last]
+	smallestChanged := i
 	if i != last {
 		// Moving to i may have moved the last timer to a new parent,
 		// so sift up to preserve the heap guarantee.
-		siftupTimer(pp.timers, i)
+		smallestChanged = siftupTimer(pp.timers, i)
 		siftdownTimer(pp.timers, i)
 	}
 	if i == 0 {
 		updateTimer0When(pp)
 	}
 	atomic.Xadd(&pp.numTimers, -1)
+	return smallestChanged
 }
 
 // dodeltimer0 removes timer 0 from the current P's heap.
@@ -675,13 +677,14 @@
 		switch s := atomic.Load(&t.status); s {
 		case timerDeleted:
 			if atomic.Cas(&t.status, s, timerRemoving) {
-				dodeltimer(pp, i)
+				changed := dodeltimer(pp, i)
 				if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
 					badTimer()
 				}
 				atomic.Xadd(&pp.deletedTimers, -1)
-				// Look at this heap position again.
-				i--
+				// Go back to the earliest changed heap entry.
+				// "- 1" because the loop will add 1.
+				i = changed - 1
 			}
 		case timerModifiedEarlier, timerModifiedLater:
 			if atomic.Cas(&t.status, s, timerMoving) {
@@ -691,10 +694,11 @@
 				// We don't add it back yet because the
 				// heap manipulation could cause our
 				// loop to skip some other timer.
-				dodeltimer(pp, i)
+				changed := dodeltimer(pp, i)
 				moved = append(moved, t)
-				// Look at this heap position again.
-				i--
+				// Go back to the earliest changed heap entry.
+				// "- 1" because the loop will add 1.
+				i = changed - 1
 			}
 		case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving:
 			badTimer()
@@ -1044,7 +1048,10 @@
 // "panic holding locks" message. Instead, we panic while not
 // holding a lock.
 
-func siftupTimer(t []*timer, i int) {
+// siftupTimer puts the timer at position i in the right place
+// in the heap by moving it up toward the top of the heap.
+// It returns the smallest changed index.
+func siftupTimer(t []*timer, i int) int {
 	if i >= len(t) {
 		badTimer()
 	}
@@ -1064,8 +1071,11 @@
 	if tmp != t[i] {
 		t[i] = tmp
 	}
+	return i
 }
 
+// siftdownTimer puts the timer at position i in the right place
+// in the heap by moving it down toward the bottom of the heap.
 func siftdownTimer(t []*timer, i int) {
 	n := len(t)
 	if i >= n {
diff --git a/src/runtime/time_linux_amd64.s b/src/runtime/time_linux_amd64.s
index c88e92b..67cfdd8 100644
--- a/src/runtime/time_linux_amd64.s
+++ b/src/runtime/time_linux_amd64.s
@@ -12,14 +12,11 @@
 #define SYS_clock_gettime	228
 
 // func time.now() (sec int64, nsec int32, mono int64)
-TEXT time·now(SB),NOSPLIT,$16-24
+TEXT time·now<ABIInternal>(SB),NOSPLIT,$16-24
 	MOVQ	SP, R12 // Save old SP; R12 unchanged by C code.
 
 	MOVQ	g_m(R14), BX // BX unchanged by C code.
 
-	// Store CLOCK_REALTIME results directly to return space.
-	LEAQ	sec+0(FP), SI
-
 	// Set vdsoPC and vdsoSP for SIGPROF traceback.
 	// Save the old values on stack and restore them on exit,
 	// so this function is reentrant.
@@ -28,9 +25,10 @@
 	MOVQ	CX, 0(SP)
 	MOVQ	DX, 8(SP)
 
-	MOVQ	-8(SI), CX	// Sets CX to function return address.
+	LEAQ	sec+0(FP), DX
+	MOVQ	-8(DX), CX	// Sets CX to function return address.
 	MOVQ	CX, m_vdsoPC(BX)
-	MOVQ	SI, m_vdsoSP(BX)
+	MOVQ	DX, m_vdsoSP(BX)
 
 	CMPQ	R14, m_curg(BX)	// Only switch if on curg.
 	JNE	noswitch
@@ -39,10 +37,11 @@
 	MOVQ	(g_sched+gobuf_sp)(DX), SP	// Set SP to g0 stack
 
 noswitch:
-	SUBQ	$16, SP		// Space for monotonic time results
+	SUBQ	$32, SP		// Space for two time results
 	ANDQ	$~15, SP	// Align for C code
 
 	MOVL	$0, DI // CLOCK_REALTIME
+	LEAQ	16(SP), SI
 	MOVQ	runtime·vdsoClockgettimeSym(SB), AX
 	CMPQ	AX, $0
 	JEQ	fallback
@@ -54,25 +53,27 @@
 	CALL	AX
 
 ret:
-	MOVQ	0(SP), AX	// sec
-	MOVQ	8(SP), DX	// nsec
+	MOVQ	16(SP), AX	// realtime sec
+	MOVQ	24(SP), DI	// realtime nsec (moved to BX below)
+	MOVQ	0(SP), CX	// monotonic sec
+	IMULQ	$1000000000, CX
+	MOVQ	8(SP), DX	// monotonic nsec
 
 	MOVQ	R12, SP		// Restore real SP
+
 	// Restore vdsoPC, vdsoSP
 	// We don't worry about being signaled between the two stores.
 	// If we are not in a signal handler, we'll restore vdsoSP to 0,
 	// and no one will care about vdsoPC. If we are in a signal handler,
 	// we cannot receive another signal.
-	MOVQ	8(SP), CX
-	MOVQ	CX, m_vdsoSP(BX)
-	MOVQ	0(SP), CX
-	MOVQ	CX, m_vdsoPC(BX)
+	MOVQ	8(SP), SI
+	MOVQ	SI, m_vdsoSP(BX)
+	MOVQ	0(SP), SI
+	MOVQ	SI, m_vdsoPC(BX)
 
-	// sec is in AX, nsec in DX
-	// return nsec in AX
-	IMULQ	$1000000000, AX
-	ADDQ	DX, AX
-	MOVQ	AX, mono+16(FP)
+	// set result registers; AX is already correct
+	MOVQ	DI, BX
+	ADDQ	DX, CX
 	RET
 
 fallback:
diff --git a/src/strings/clone.go b/src/strings/clone.go
new file mode 100644
index 0000000..6097c6c
--- /dev/null
+++ b/src/strings/clone.go
@@ -0,0 +1,23 @@
+// 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 strings
+
+import (
+	"unsafe"
+)
+
+// Clone returns a fresh copy of s.
+// It guarantees to make a copy of s into a new allocation,
+// which can be important when retaining only a small substring
+// of a much larger string. Using Clone can help such programs
+// use less memory. Of course, since using Clone makes a copy,
+// overuse of Clone can make programs use more memory.
+// Clone should typically be used only rarely, and only when
+// profiling indicates that it is needed.
+func Clone(s string) string {
+	b := make([]byte, len(s))
+	copy(b, s)
+	return *(*string)(unsafe.Pointer(&b))
+}
diff --git a/src/strings/clone_test.go b/src/strings/clone_test.go
new file mode 100644
index 0000000..5396771
--- /dev/null
+++ b/src/strings/clone_test.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 strings_test
+
+import (
+	"reflect"
+	"strings"
+	"testing"
+	"unsafe"
+)
+
+func TestClone(t *testing.T) {
+	var cloneTests = []string{
+		"",
+		"short",
+		strings.Repeat("a", 42),
+	}
+	for _, input := range cloneTests {
+		clone := strings.Clone(input)
+		if clone != input {
+			t.Errorf("Clone(%q) = %q; want %q", input, clone, input)
+		}
+
+		inputHeader := (*reflect.StringHeader)(unsafe.Pointer(&input))
+		cloneHeader := (*reflect.StringHeader)(unsafe.Pointer(&clone))
+		if inputHeader.Data == cloneHeader.Data {
+			t.Errorf("Clone(%q) return value should not reference inputs backing memory.", input)
+		}
+	}
+}
+
+func BenchmarkClone(b *testing.B) {
+	var str = strings.Repeat("a", 42)
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		stringSink = strings.Clone(str)
+	}
+}
diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go
index 85b59ad..1555318 100644
--- a/src/syscall/exec_linux_test.go
+++ b/src/syscall/exec_linux_test.go
@@ -111,14 +111,6 @@
 			t.Skip("kernel doesn't support user namespaces")
 		}
 	}
-
-	// When running under the Go continuous build, skip tests for
-	// now when under Kubernetes. (where things are root but not quite)
-	// Both of these are our own environment variables.
-	// See Issue 12815.
-	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
-		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
-	}
 }
 
 func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
@@ -201,14 +193,6 @@
 		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
 	}
 
-	// When running under the Go continuous build, skip tests for
-	// now when under Kubernetes. (where things are root but not quite)
-	// Both of these are our own environment variables.
-	// See Issue 12815.
-	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
-		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
-	}
-
 	path := "/proc/net/dev"
 	if _, err := os.Stat(path); err != nil {
 		if os.IsNotExist(err) {
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index 18d1502..9d10d6a 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -390,8 +390,10 @@
 	}
 	fd = fd[:j]
 
+	willInheritHandles := len(fd) > 0 && !sys.NoInheritHandles
+
 	// Do not accidentally inherit more than these handles.
-	if len(fd) > 0 {
+	if willInheritHandles {
 		err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
 		if err != nil {
 			return 0, 0, err
@@ -401,9 +403,9 @@
 	pi := new(ProcessInformation)
 	flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT
 	if sys.Token != 0 {
-		err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
+		err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
 	} else {
-		err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
+		err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
 	}
 	if err != nil {
 		return 0, 0, err
diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go
index 6d428d5..f02fa45 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -161,6 +161,23 @@
 	return openat(dirfd, path, flags|O_LARGEFILE, mode)
 }
 
+func Pipe(p []int) error {
+	return Pipe2(p, 0)
+}
+
+//sysnb pipe2(p *[2]_C_int, flags int) (err error)
+
+func Pipe2(p []int, flags int) error {
+	if len(p) != 2 {
+		return EINVAL
+	}
+	var pp [2]_C_int
+	err := pipe2(&pp, flags)
+	p[0] = int(pp[0])
+	p[1] = int(pp[1])
+	return err
+}
+
 //sys	readlinkat(dirfd int, path string, buf []byte) (n int, err error)
 
 func Readlink(path string, buf []byte) (n int, err error) {
diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go
index 0db0374..9844205 100644
--- a/src/syscall/syscall_linux_386.go
+++ b/src/syscall/syscall_linux_386.go
@@ -22,32 +22,6 @@
 	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
-//sysnb	pipe(p *[2]_C_int) (err error)
-
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe(&pp)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 // 64-bit file system and 32-bit uid calls
 // (386 default is 32-bit file system and 16-bit uid).
 //sys	Dup2(oldfd int, newfd int) (err error)
diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go
index 5df3f79..04acd06 100644
--- a/src/syscall/syscall_linux_amd64.go
+++ b/src/syscall/syscall_linux_amd64.go
@@ -110,32 +110,6 @@
 	return Timeval{Sec: sec, Usec: usec}
 }
 
-//sysnb	pipe(p *[2]_C_int) (err error)
-
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe(&pp)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 func (r *PtraceRegs) PC() uint64 { return r.Rip }
 
 func (r *PtraceRegs) SetPC(pc uint64) { r.Rip = pc }
diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go
index e887cf7..f2f342e 100644
--- a/src/syscall/syscall_linux_arm.go
+++ b/src/syscall/syscall_linux_arm.go
@@ -22,36 +22,6 @@
 	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
-//sysnb pipe(p *[2]_C_int) (err error)
-
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	// Try pipe2 first for Android O, then try pipe for kernel 2.6.23.
-	err = pipe2(&pp, 0)
-	if err == ENOSYS {
-		err = pipe(&pp)
-	}
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 // Underlying system call writes to newoffset via pointer.
 // Implemented in assembly to avoid allocation.
 func seek(fd int, offset int64, whence int) (newoffset int64, err Errno)
diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go
index f575c84..990e732 100644
--- a/src/syscall/syscall_linux_arm64.go
+++ b/src/syscall/syscall_linux_arm64.go
@@ -42,7 +42,6 @@
 //sys	Setfsgid(gid int) (err error)
 //sys	Setfsuid(uid int) (err error)
 //sysnb	setrlimit(resource int, rlim *Rlimit) (err error)
-//sysnb	Setreuid(ruid int, euid int) (err error)
 //sys	Shutdown(fd int, how int) (err error)
 //sys	Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
 
@@ -146,30 +145,6 @@
 	return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
 }
 
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, 0)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 // Getrlimit prefers the prlimit64 system call. See issue 38604.
 func Getrlimit(resource int, rlim *Rlimit) error {
 	err := prlimit(0, resource, nil, rlim)
diff --git a/src/syscall/syscall_linux_mips64x.go b/src/syscall/syscall_linux_mips64x.go
index 5feb03e..fa0d279 100644
--- a/src/syscall/syscall_linux_mips64x.go
+++ b/src/syscall/syscall_linux_mips64x.go
@@ -103,30 +103,6 @@
 	return Timeval{Sec: sec, Usec: usec}
 }
 
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, 0)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 func Ioperm(from int, num int, on int) (err error) {
 	return ENOSYS
 }
diff --git a/src/syscall/syscall_linux_mipsx.go b/src/syscall/syscall_linux_mipsx.go
index 39104d7..568523e 100644
--- a/src/syscall/syscall_linux_mipsx.go
+++ b/src/syscall/syscall_linux_mipsx.go
@@ -112,29 +112,6 @@
 	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe() (p1 int, p2 int, err error)
-
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	p[0], p[1], err = pipe()
-	return
-}
-
 //sys	mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error)
 
 func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) {
diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go
index 495ae29..3e73f6f 100644
--- a/src/syscall/syscall_linux_ppc64x.go
+++ b/src/syscall/syscall_linux_ppc64x.go
@@ -82,30 +82,6 @@
 	return Timeval{Sec: sec, Usec: usec}
 }
 
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, 0)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 func (r *PtraceRegs) PC() uint64 { return r.Nip }
 
 func (r *PtraceRegs) SetPC(pc uint64) { r.Nip = pc }
diff --git a/src/syscall/syscall_linux_riscv64.go b/src/syscall/syscall_linux_riscv64.go
index 2a0fe64..bcb89c6 100644
--- a/src/syscall/syscall_linux_riscv64.go
+++ b/src/syscall/syscall_linux_riscv64.go
@@ -149,30 +149,6 @@
 	return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
 }
 
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, 0)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 func (r *PtraceRegs) PC() uint64 { return r.Pc }
 
 func (r *PtraceRegs) SetPC(pc uint64) { r.Pc = pc }
diff --git a/src/syscall/syscall_linux_s390x.go b/src/syscall/syscall_linux_s390x.go
index 0f6f627..123664f 100644
--- a/src/syscall/syscall_linux_s390x.go
+++ b/src/syscall/syscall_linux_s390x.go
@@ -74,30 +74,6 @@
 	return Timeval{Sec: sec, Usec: usec}
 }
 
-func Pipe(p []int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, 0)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
-//sysnb pipe2(p *[2]_C_int, flags int) (err error)
-
-func Pipe2(p []int, flags int) (err error) {
-	if len(p) != 2 {
-		return EINVAL
-	}
-	var pp [2]_C_int
-	err = pipe2(&pp, flags)
-	p[0] = int(pp[0])
-	p[1] = int(pp[1])
-	return
-}
-
 // Linux on s390x uses the old mmap interface, which requires arguments to be passed in a struct.
 // mmap2 also requires arguments to be passed in a struct; it is currently not exposed in <asm/unistd.h>.
 func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) {
diff --git a/src/syscall/zsyscall_linux_386.go b/src/syscall/zsyscall_linux_386.go
index ac822d6..a1394d3 100644
--- a/src/syscall/zsyscall_linux_386.go
+++ b/src/syscall/zsyscall_linux_386.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1048,26 +1058,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe(p *[2]_C_int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func Dup2(oldfd int, newfd int) (err error) {
 	_, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
 	if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_amd64.go b/src/syscall/zsyscall_linux_amd64.go
index ed37fa8..de6047d 100644
--- a/src/syscall/zsyscall_linux_amd64.go
+++ b/src/syscall/zsyscall_linux_amd64.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1644,23 +1654,3 @@
 	}
 	return
 }
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe(p *[2]_C_int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go
index 213aaf3..3663b2e 100644
--- a/src/syscall/zsyscall_linux_arm.go
+++ b/src/syscall/zsyscall_linux_arm.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1048,26 +1058,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe(p *[2]_C_int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
 	r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
 	fd = int(r0)
diff --git a/src/syscall/zsyscall_linux_arm64.go b/src/syscall/zsyscall_linux_arm64.go
index e2f9c0f..19a9c0c 100644
--- a/src/syscall/zsyscall_linux_arm64.go
+++ b/src/syscall/zsyscall_linux_arm64.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1555,16 +1565,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) {
 	r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/src/syscall/zsyscall_linux_mips.go b/src/syscall/zsyscall_linux_mips.go
index 617c2f5..966b2e1 100644
--- a/src/syscall/zsyscall_linux_mips.go
+++ b/src/syscall/zsyscall_linux_mips.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1646,28 +1656,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe() (p1 int, p2 int, err error) {
-	r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
-	p1 = int(r0)
-	p2 = int(r1)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) {
 	r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset))
 	xaddr = uintptr(r0)
diff --git a/src/syscall/zsyscall_linux_mips64.go b/src/syscall/zsyscall_linux_mips64.go
index 793d4b9..c6812a0 100644
--- a/src/syscall/zsyscall_linux_mips64.go
+++ b/src/syscall/zsyscall_linux_mips64.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1642,16 +1652,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func fstat(fd int, st *stat_t) (err error) {
 	_, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0)
 	if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_mips64le.go b/src/syscall/zsyscall_linux_mips64le.go
index 54e1760..eaaf7df 100644
--- a/src/syscall/zsyscall_linux_mips64le.go
+++ b/src/syscall/zsyscall_linux_mips64le.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1642,16 +1652,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func fstat(fd int, st *stat_t) (err error) {
 	_, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0)
 	if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_mipsle.go b/src/syscall/zsyscall_linux_mipsle.go
index ba7e211..bb159f1 100644
--- a/src/syscall/zsyscall_linux_mipsle.go
+++ b/src/syscall/zsyscall_linux_mipsle.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1646,28 +1656,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe() (p1 int, p2 int, err error) {
-	r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
-	p1 = int(r0)
-	p2 = int(r1)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) {
 	r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset))
 	xaddr = uintptr(r0)
diff --git a/src/syscall/zsyscall_linux_ppc64.go b/src/syscall/zsyscall_linux_ppc64.go
index c343772..8a43285 100644
--- a/src/syscall/zsyscall_linux_ppc64.go
+++ b/src/syscall/zsyscall_linux_ppc64.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1703,16 +1713,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func syncFileRange2(fd int, flags int, off int64, n int64) (err error) {
 	_, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE2, uintptr(fd), uintptr(flags), uintptr(off), uintptr(n), 0, 0)
 	if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_ppc64le.go b/src/syscall/zsyscall_linux_ppc64le.go
index acc34a7..274b55c 100644
--- a/src/syscall/zsyscall_linux_ppc64le.go
+++ b/src/syscall/zsyscall_linux_ppc64le.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1703,16 +1713,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func syncFileRange2(fd int, flags int, off int64, n int64) (err error) {
 	_, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE2, uintptr(fd), uintptr(flags), uintptr(off), uintptr(n), 0, 0)
 	if e1 != 0 {
diff --git a/src/syscall/zsyscall_linux_riscv64.go b/src/syscall/zsyscall_linux_riscv64.go
index d662d78..e21dc46 100644
--- a/src/syscall/zsyscall_linux_riscv64.go
+++ b/src/syscall/zsyscall_linux_riscv64.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1555,16 +1565,6 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) {
 	r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0)
 	n = int(r0)
diff --git a/src/syscall/zsyscall_linux_s390x.go b/src/syscall/zsyscall_linux_s390x.go
index 20f8c61..fc667e7 100644
--- a/src/syscall/zsyscall_linux_s390x.go
+++ b/src/syscall/zsyscall_linux_s390x.go
@@ -76,6 +76,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
@@ -1490,13 +1500,3 @@
 	}
 	return
 }
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func pipe2(p *[2]_C_int, flags int) (err error) {
-	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
diff --git a/src/testing/helper_test.go b/src/testing/helper_test.go
index b27fd62..6175410 100644
--- a/src/testing/helper_test.go
+++ b/src/testing/helper_test.go
@@ -33,6 +33,9 @@
 helperfuncs_test.go:21: 6
 helperfuncs_test.go:44: 7
 helperfuncs_test.go:56: 8
+--- FAIL: Test/sub2 (?s)
+helperfuncs_test.go:71: 11
+helperfuncs_test.go:75: recover 12
 helperfuncs_test.go:64: 9
 helperfuncs_test.go:60: 10
 `
@@ -71,38 +74,6 @@
 	}
 }
 
-func TestTBHelperLineNumer(t *T) {
-	var buf bytes.Buffer
-	ctx := newTestContext(1, newMatcher(regexp.MatchString, "", ""))
-	t1 := &T{
-		common: common{
-			signal: make(chan bool),
-			w:      &buf,
-		},
-		context: ctx,
-	}
-	t1.Run("Test", func(t *T) {
-		helperA := func(t *T) {
-			t.Helper()
-			t.Run("subtest", func(t *T) {
-				t.Helper()
-				t.Fatal("fatal error message")
-			})
-		}
-		helperA(t)
-	})
-
-	want := "helper_test.go:92: fatal error message"
-	got := ""
-	lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
-	if len(lines) > 0 {
-		got = strings.TrimSpace(lines[len(lines)-1])
-	}
-	if got != want {
-		t.Errorf("got output:\n\n%v\nwant:\n\n%v", got, want)
-	}
-}
-
 type noopWriter int
 
 func (nw *noopWriter) Write(b []byte) (int, error) { return len(b), nil }
diff --git a/src/testing/helperfuncs_test.go b/src/testing/helperfuncs_test.go
index df0476e..272b33c 100644
--- a/src/testing/helperfuncs_test.go
+++ b/src/testing/helperfuncs_test.go
@@ -65,6 +65,14 @@
 		t.Helper()
 		t.Error("9")
 	})
+
+	// Check that helper-ness propagates up through subtests
+	// to helpers above. See https://golang.org/issue/44887.
+	helperSubCallingHelper(t, "11")
+
+	// Check that helper-ness propagates up through panic/recover.
+	// See https://golang.org/issue/31154.
+	recoverHelper(t, "12")
 }
 
 func parallelTestHelper(t *T) {
@@ -78,3 +86,27 @@
 	}
 	wg.Wait()
 }
+
+func helperSubCallingHelper(t *T, msg string) {
+	t.Helper()
+	t.Run("sub2", func(t *T) {
+		t.Helper()
+		t.Fatal(msg)
+	})
+}
+
+func recoverHelper(t *T, msg string) {
+	t.Helper()
+	defer func() {
+		t.Helper()
+		if err := recover(); err != nil {
+			t.Errorf("recover %s", err)
+		}
+	}()
+	doPanic(t, msg)
+}
+
+func doPanic(t *T, msg string) {
+	t.Helper()
+	panic(msg)
+}
diff --git a/src/testing/match.go b/src/testing/match.go
index b18c6e7..d97e415 100644
--- a/src/testing/match.go
+++ b/src/testing/match.go
@@ -14,34 +14,45 @@
 
 // matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
 type matcher struct {
-	filter    []string
+	filter    filterMatch
 	matchFunc func(pat, str string) (bool, error)
 
 	mu       sync.Mutex
 	subNames map[string]int64
 }
 
+type filterMatch interface {
+	// matches checks the name against the receiver's pattern strings using the
+	// given match function.
+	matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)
+
+	// verify checks that the receiver's pattern strings are valid filters by
+	// calling the given match function.
+	verify(name string, matchString func(pat, str string) (bool, error)) error
+}
+
+// simpleMatch matches a test name if all of the pattern strings match in
+// sequence.
+type simpleMatch []string
+
+// alternationMatch matches a test name if one of the alternations match.
+type alternationMatch []filterMatch
+
 // TODO: fix test_main to avoid race and improve caching, also allowing to
 // eliminate this Mutex.
 var matchMutex sync.Mutex
 
 func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher {
-	var filter []string
+	var impl filterMatch
 	if patterns != "" {
-		filter = splitRegexp(patterns)
-		for i, s := range filter {
-			filter[i] = rewrite(s)
-		}
-		// Verify filters before doing any processing.
-		for i, s := range filter {
-			if _, err := matchString(s, "non-empty"); err != nil {
-				fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err)
-				os.Exit(1)
-			}
+		impl = splitRegexp(patterns)
+		if err := impl.verify(name, matchString); err != nil {
+			fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err)
+			os.Exit(1)
 		}
 	}
 	return &matcher{
-		filter:    filter,
+		filter:    impl,
 		matchFunc: matchString,
 		subNames:  map[string]int64{},
 	}
@@ -60,22 +71,63 @@
 	matchMutex.Lock()
 	defer matchMutex.Unlock()
 
+	if m.filter == nil {
+		return name, true, false
+	}
+
 	// We check the full array of paths each time to allow for the case that
 	// a pattern contains a '/'.
 	elem := strings.Split(name, "/")
-	for i, s := range elem {
-		if i >= len(m.filter) {
-			break
-		}
-		if ok, _ := m.matchFunc(m.filter[i], s); !ok {
-			return name, false, false
-		}
-	}
-	return name, true, len(elem) < len(m.filter)
+	ok, partial = m.filter.matches(elem, m.matchFunc)
+	return name, ok, partial
 }
 
-func splitRegexp(s string) []string {
-	a := make([]string, 0, strings.Count(s, "/"))
+func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
+	for i, s := range name {
+		if i >= len(m) {
+			break
+		}
+		if ok, _ := matchString(m[i], s); !ok {
+			return false, false
+		}
+	}
+	return true, len(name) < len(m)
+}
+
+func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
+	for i, s := range m {
+		m[i] = rewrite(s)
+	}
+	// Verify filters before doing any processing.
+	for i, s := range m {
+		if _, err := matchString(s, "non-empty"); err != nil {
+			return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err)
+		}
+	}
+	return nil
+}
+
+func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
+	for _, m := range m {
+		if ok, partial = m.matches(name, matchString); ok {
+			return ok, partial
+		}
+	}
+	return false, false
+}
+
+func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
+	for i, m := range m {
+		if err := m.verify(name, matchString); err != nil {
+			return fmt.Errorf("alternation %d of %s", i, err)
+		}
+	}
+	return nil
+}
+
+func splitRegexp(s string) filterMatch {
+	a := make(simpleMatch, 0, strings.Count(s, "/"))
+	b := make(alternationMatch, 0, strings.Count(s, "|"))
 	cs := 0
 	cp := 0
 	for i := 0; i < len(s); {
@@ -103,10 +155,24 @@
 				i = 0
 				continue
 			}
+		case '|':
+			if cs == 0 && cp == 0 {
+				a = append(a, s[:i])
+				s = s[i+1:]
+				i = 0
+				b = append(b, a)
+				a = make(simpleMatch, 0, len(a))
+				continue
+			}
 		}
 		i++
 	}
-	return append(a, s)
+
+	a = append(a, s)
+	if len(b) == 0 {
+		return a
+	}
+	return append(b, a)
 }
 
 // unique creates a unique name for the given parent and subname by affixing it
diff --git a/src/testing/match_test.go b/src/testing/match_test.go
index 8c09dc6..9ceadbb 100644
--- a/src/testing/match_test.go
+++ b/src/testing/match_test.go
@@ -5,8 +5,10 @@
 package testing
 
 import (
+	"fmt"
 	"reflect"
 	"regexp"
+	"strings"
 	"unicode"
 )
 
@@ -25,10 +27,11 @@
 }
 
 func TestSplitRegexp(t *T) {
-	res := func(s ...string) []string { return s }
+	res := func(s ...string) filterMatch { return simpleMatch(s) }
+	alt := func(m ...filterMatch) filterMatch { return alternationMatch(m) }
 	testCases := []struct {
 		pattern string
-		result  []string
+		result  filterMatch
 	}{
 		// Correct patterns
 		// If a regexp pattern is correct, all split regexps need to be correct
@@ -49,6 +52,8 @@
 		{`([)/][(])`, res(`([)/][(])`)},
 		{"[(]/[)]", res("[(]", "[)]")},
 
+		{"A/B|C/D", alt(res("A", "B"), res("C", "D"))},
+
 		// Faulty patterns
 		// Errors in original should produce at least one faulty regexp in results.
 		{")/", res(")/")},
@@ -71,10 +76,8 @@
 		// needs to have an error as well.
 		if _, err := regexp.Compile(tc.pattern); err != nil {
 			ok := true
-			for _, re := range a {
-				if _, err := regexp.Compile(re); err != nil {
-					ok = false
-				}
+			if err := a.verify("", regexp.MatchString); err != nil {
+				ok = false
 			}
 			if ok {
 				t.Errorf("%s: expected error in any of %q", tc.pattern, a)
@@ -113,6 +116,10 @@
 		{"TestFoo/", "TestBar", "x", false, false},
 		{"TestFoo/bar/baz", "TestBar", "x/bar/baz", false, false},
 
+		{"A/B|C/D", "TestA", "B", true, false},
+		{"A/B|C/D", "TestC", "D", true, false},
+		{"A/B|C/D", "TestA", "C", false, false},
+
 		// subtests only
 		{"", "TestFoo", "x", true, false},
 		{"/", "TestFoo", "x", true, false},
@@ -184,3 +191,13 @@
 		}
 	}
 }
+
+// GoString returns a string that is more readable than the default, which makes
+// it easier to read test errors.
+func (m alternationMatch) GoString() string {
+	s := make([]string, len(m))
+	for i, m := range m {
+		s[i] = fmt.Sprintf("%#v", m)
+	}
+	return fmt.Sprintf("(%s)", strings.Join(s, " | "))
+}
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 18a0657..567eb0d 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -533,6 +533,9 @@
 	var firstFrame, prevFrame, frame runtime.Frame
 	for more := true; more; prevFrame = frame {
 		frame, more = frames.Next()
+		if frame.Function == "runtime.gopanic" {
+			continue
+		}
 		if frame.Function == c.cleanupName {
 			frames = runtime.CallersFrames(c.cleanupPc)
 			continue
diff --git a/src/time/format.go b/src/time/format.go
index 7ae89c5..7373892 100644
--- a/src/time/format.go
+++ b/src/time/format.go
@@ -74,7 +74,7 @@
 // for compatibility with fixed-width Unix time formats. A leading zero represents
 // a zero-padded value.
 //
-// The formats  and 002 are space-padded and zero-padded
+// The formats __2 and 002 are space-padded and zero-padded
 // three-character day of year; there is no unpadded day of year format.
 //
 // A comma or decimal point followed by one or more zeros represents
@@ -146,10 +146,11 @@
 	stdFracSecond0                                 // ".0", ".00", ... , trailing zeros included
 	stdFracSecond9                                 // ".9", ".99", ..., trailing zeros omitted
 
-	stdNeedDate  = 1 << 8             // need month, day, year
-	stdNeedClock = 2 << 8             // need hour, minute, second
-	stdArgShift  = 16                 // extra argument in high bits, above low stdArgShift
-	stdMask      = 1<<stdArgShift - 1 // mask out argument
+	stdNeedDate       = 1 << 8             // need month, day, year
+	stdNeedClock      = 2 << 8             // need hour, minute, second
+	stdArgShift       = 16                 // extra argument in high bits, above low stdArgShift
+	stdSeparatorShift = 28                 // extra argument in high 4 bits for fractional second separators
+	stdMask           = 1<<stdArgShift - 1 // mask out argument
 )
 
 // std0x records the std values for "01", "02", ..., "06".
@@ -289,11 +290,11 @@
 				}
 				// String of digits must end here - only fractional second is all digits.
 				if !isDigit(layout, j) {
-					std := stdFracSecond0
+					code := stdFracSecond0
 					if layout[i+1] == '9' {
-						std = stdFracSecond9
+						code = stdFracSecond9
 					}
-					std |= (j - (i + 1)) << stdArgShift
+					std := stdFracSecond(code, j-(i+1), c)
 					return layout[0:i], std, layout[j:]
 				}
 			}
@@ -430,9 +431,36 @@
 	return x, nil
 }
 
+// The "std" value passed to formatNano contains two packed fields: the number of
+// digits after the decimal and the separator character (period or comma).
+// These functions pack and unpack that variable.
+func stdFracSecond(code, n, c int) int {
+	// Use 0xfff to make the failure case even more absurd.
+	if c == '.' {
+		return code | ((n & 0xfff) << stdArgShift)
+	}
+	return code | ((n & 0xfff) << stdArgShift) | 1<<stdSeparatorShift
+}
+
+func digitsLen(std int) int {
+	return (std >> stdArgShift) & 0xfff
+}
+
+func separator(std int) byte {
+	if (std >> stdSeparatorShift) == 0 {
+		return '.'
+	}
+	return ','
+}
+
 // formatNano appends a fractional second, as nanoseconds, to b
 // and returns the result.
-func formatNano(b []byte, nanosec uint, n int, trim bool) []byte {
+func formatNano(b []byte, nanosec uint, std int) []byte {
+	var (
+		n         = digitsLen(std)
+		separator = separator(std)
+		trim      = std&stdMask == stdFracSecond9
+	)
 	u := nanosec
 	var buf [9]byte
 	for start := len(buf); start > 0; {
@@ -452,7 +480,7 @@
 			return b
 		}
 	}
-	b = append(b, '.')
+	b = append(b, separator)
 	return append(b, buf[:n]...)
 }
 
@@ -733,7 +761,7 @@
 			b = appendInt(b, zone/60, 2)
 			b = appendInt(b, zone%60, 2)
 		case stdFracSecond0, stdFracSecond9:
-			b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9)
+			b = formatNano(b, uint(t.Nanosecond()), std)
 		}
 	}
 	return b
@@ -1165,7 +1193,7 @@
 		case stdFracSecond0:
 			// stdFracSecond0 requires the exact number of digits as specified in
 			// the layout.
-			ndigit := 1 + (std >> stdArgShift)
+			ndigit := 1 + digitsLen(std)
 			if len(value) < ndigit {
 				err = errBad
 				break
diff --git a/src/time/format_test.go b/src/time/format_test.go
index 1af41e2..93cbcf9 100644
--- a/src/time/format_test.go
+++ b/src/time/format_test.go
@@ -832,3 +832,23 @@
 	}
 
 }
+
+// Issue 48037
+func TestFormatFractionalSecondSeparators(t *testing.T) {
+	tests := []struct {
+		s, want string
+	}{
+		{`15:04:05.000`, `21:00:57.012`},
+		{`15:04:05.999`, `21:00:57.012`},
+		{`15:04:05,000`, `21:00:57,012`},
+		{`15:04:05,999`, `21:00:57,012`},
+	}
+
+	// The numeric time represents Thu Feb  4 21:00:57.012345600 PST 2009
+	time := Unix(0, 1233810057012345600)
+	for _, tt := range tests {
+		if q := time.Format(tt.s); q != tt.want {
+			t.Errorf("Format(%q) = got %q, want %q", tt.s, q, tt.want)
+		}
+	}
+}
diff --git a/src/time/internal_test.go b/src/time/internal_test.go
index 87a4208..2c75e44 100644
--- a/src/time/internal_test.go
+++ b/src/time/internal_test.go
@@ -12,7 +12,7 @@
 func initTestingZone() {
 	z, err := loadLocation("America/Los_Angeles", zoneSources[len(zoneSources)-1:])
 	if err != nil {
-		panic("cannot load America/Los_Angeles for testing: " + err.Error())
+		panic("cannot load America/Los_Angeles for testing: " + err.Error() + "; you may want to use -tags=timetzdata")
 	}
 	z.name = "Local"
 	localLoc = *z
diff --git a/src/time/sleep_test.go b/src/time/sleep_test.go
index e0172bf..c48e704 100644
--- a/src/time/sleep_test.go
+++ b/src/time/sleep_test.go
@@ -7,6 +7,7 @@
 import (
 	"errors"
 	"fmt"
+	"math/rand"
 	"runtime"
 	"strings"
 	"sync"
@@ -561,6 +562,72 @@
 	}
 }
 
+// Test that rapidly moving timers earlier and later doesn't cause
+// some of the sleep times to be lost.
+// Issue 47762
+func TestAdjustTimers(t *testing.T) {
+	var rnd = rand.New(rand.NewSource(Now().UnixNano()))
+
+	timers := make([]*Timer, 100)
+	states := make([]int, len(timers))
+	indices := rnd.Perm(len(timers))
+
+	for len(indices) != 0 {
+		var ii = rnd.Intn(len(indices))
+		var i = indices[ii]
+
+		var timer = timers[i]
+		var state = states[i]
+		states[i]++
+
+		switch state {
+		case 0:
+			timers[i] = NewTimer(0)
+		case 1:
+			<-timer.C // Timer is now idle.
+
+		// Reset to various long durations, which we'll cancel.
+		case 2:
+			if timer.Reset(1 * Minute) {
+				panic("shouldn't be active (1)")
+			}
+		case 4:
+			if timer.Reset(3 * Minute) {
+				panic("shouldn't be active (3)")
+			}
+		case 6:
+			if timer.Reset(2 * Minute) {
+				panic("shouldn't be active (2)")
+			}
+
+		// Stop and drain a long-duration timer.
+		case 3, 5, 7:
+			if !timer.Stop() {
+				t.Logf("timer %d state %d Stop returned false", i, state)
+				<-timer.C
+			}
+
+		// Start a short-duration timer we expect to select without blocking.
+		case 8:
+			if timer.Reset(0) {
+				t.Fatal("timer.Reset returned true")
+			}
+		case 9:
+			now := Now()
+			<-timer.C
+			dur := Since(now)
+			if dur > 750*Millisecond {
+				t.Errorf("timer %d took %v to complete", i, dur)
+			}
+
+		// Timer is done. Swap with tail and remove.
+		case 10:
+			indices[ii] = indices[len(indices)-1]
+			indices = indices[:len(indices)-1]
+		}
+	}
+}
+
 // Benchmark timer latency when the thread that creates the timer is busy with
 // other work and the timers must be serviced by other threads.
 // https://golang.org/issue/38860
diff --git a/src/time/time.go b/src/time/time.go
index 4ecc3d8..edf0c62 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -425,7 +425,6 @@
 	internalToUnix int64 = -unixToInternal
 
 	wallToInternal int64 = (1884*365 + 1884/4 - 1884/100 + 1884/400) * secondsPerDay
-	internalToWall int64 = -wallToInternal
 )
 
 // IsZero reports whether t represents the zero time instant,
@@ -1163,19 +1162,26 @@
 	return (t.unixSec())*1e9 + int64(t.nsec())
 }
 
-const timeBinaryVersion byte = 1
+const (
+	timeBinaryVersionV1 byte = iota + 1 // For general situation
+	timeBinaryVersionV2                 // For LMT only
+)
 
 // MarshalBinary implements the encoding.BinaryMarshaler interface.
 func (t Time) MarshalBinary() ([]byte, error) {
 	var offsetMin int16 // minutes east of UTC. -1 is UTC.
+	var offsetSec int8
+	version := timeBinaryVersionV1
 
 	if t.Location() == UTC {
 		offsetMin = -1
 	} else {
 		_, offset := t.Zone()
 		if offset%60 != 0 {
-			return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute")
+			version = timeBinaryVersionV2
+			offsetSec = int8(offset % 60)
 		}
+
 		offset /= 60
 		if offset < -32768 || offset == -1 || offset > 32767 {
 			return nil, errors.New("Time.MarshalBinary: unexpected zone offset")
@@ -1186,8 +1192,8 @@
 	sec := t.sec()
 	nsec := t.nsec()
 	enc := []byte{
-		timeBinaryVersion, // byte 0 : version
-		byte(sec >> 56),   // bytes 1-8: seconds
+		version,         // byte 0 : version
+		byte(sec >> 56), // bytes 1-8: seconds
 		byte(sec >> 48),
 		byte(sec >> 40),
 		byte(sec >> 32),
@@ -1202,6 +1208,9 @@
 		byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes
 		byte(offsetMin),
 	}
+	if version == timeBinaryVersionV2 {
+		enc = append(enc, byte(offsetSec))
+	}
 
 	return enc, nil
 }
@@ -1213,11 +1222,16 @@
 		return errors.New("Time.UnmarshalBinary: no data")
 	}
 
-	if buf[0] != timeBinaryVersion {
+	version := buf[0]
+	if version != timeBinaryVersionV1 && version != timeBinaryVersionV2 {
 		return errors.New("Time.UnmarshalBinary: unsupported version")
 	}
 
-	if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 {
+	wantLen := /*version*/ 1 + /*sec*/ 8 + /*nsec*/ 4 + /*zone offset*/ 2
+	if version == timeBinaryVersionV2 {
+		wantLen++
+	}
+	if len(buf) != wantLen {
 		return errors.New("Time.UnmarshalBinary: invalid length")
 	}
 
@@ -1230,6 +1244,9 @@
 
 	buf = buf[4:]
 	offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
+	if version == timeBinaryVersionV2 {
+		offset += int(buf[2])
+	}
 
 	*t = Time{}
 	t.wall = uint64(nsec)
diff --git a/src/time/time_test.go b/src/time/time_test.go
index cea5f2d..e2fb897 100644
--- a/src/time/time_test.go
+++ b/src/time/time_test.go
@@ -767,7 +767,6 @@
 	time Time
 	want string
 }{
-	{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.MarshalBinary: zone offset has fractional minute"},
 	{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.MarshalBinary: unexpected zone offset"},
 	{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.MarshalBinary: unexpected zone offset"},
 	{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.MarshalBinary: unexpected zone offset"},
@@ -1437,6 +1436,37 @@
 	}
 }
 
+func TestMarshalBinaryVersion2(t *testing.T) {
+	t0, err := Parse(RFC3339, "1880-01-01T00:00:00Z")
+	if err != nil {
+		t.Errorf("Failed to parse time, error = %v", err)
+	}
+	loc, err := LoadLocation("US/Eastern")
+	if err != nil {
+		t.Errorf("Failed to load location, error = %v", err)
+	}
+	t1 := t0.In(loc)
+	b, err := t1.MarshalBinary()
+	if err != nil {
+		t.Errorf("Failed to Marshal, error = %v", err)
+	}
+
+	t2 := Time{}
+	err = t2.UnmarshalBinary(b)
+	if err != nil {
+		t.Errorf("Failed to Unmarshal, error = %v", err)
+	}
+
+	if !(t0.Equal(t1) && t1.Equal(t2)) {
+		if !t0.Equal(t1) {
+			t.Errorf("The result t1: %+v after Marshal is not matched original t0: %+v", t1, t0)
+		}
+		if !t1.Equal(t2) {
+			t.Errorf("The result t2: %+v after Unmarshal is not matched original t1: %+v", t2, t1)
+		}
+	}
+}
+
 // Issue 17720: Zero value of time.Month fails to print
 func TestZeroMonthString(t *testing.T) {
 	if got, want := Month(0).String(), "%!Month(0)"; got != want {
diff --git a/test/abi/method_wrapper.go b/test/abi/method_wrapper.go
new file mode 100644
index 0000000..7aa262f
--- /dev/null
+++ b/test/abi/method_wrapper.go
@@ -0,0 +1,35 @@
+// run
+
+// 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
+
+type S int
+
+type T struct {
+	a int
+	S
+}
+
+//go:noinline
+func (s *S) M(a int, x [2]int, b float64, y [2]float64) (S, int, [2]int, float64, [2]float64) {
+	return *s, a, x, b, y
+}
+
+var s S = 42
+var t = &T{S: s}
+
+var fn = (*T).M // force a method wrapper
+
+func main() {
+	a := 123
+	x := [2]int{456, 789}
+	b := 1.2
+	y := [2]float64{3.4, 5.6}
+	s1, a1, x1, b1, y1 := fn(t, a, x, b, y)
+	if a1 != a || x1 != x || b1 != b || y1 != y || s1 != s {
+		panic("FAIL")
+	}
+}
diff --git a/test/codegen/bitfield.go b/test/codegen/bitfield.go
index 3ed9cfe..8327da6 100644
--- a/test/codegen/bitfield.go
+++ b/test/codegen/bitfield.go
@@ -77,11 +77,13 @@
 }
 
 // sbfiz
+// merge shifts into sbfiz: (x << lc) >> rc && lc > rc.
 func sbfiz1(x int64) int64 {
 	// arm64:"SBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR"
 	return (x << 4) >> 3
 }
 
+// merge shift and sign-extension into sbfiz.
 func sbfiz2(x int32) int64 {
 	return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]29",-"LSL"
 }
@@ -94,6 +96,8 @@
 	return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]5",-"LSL"
 }
 
+// sbfiz combinations.
+// merge shift with sbfiz into sbfiz.
 func sbfiz5(x int32) int32 {
 	// arm64:"SBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR"
 	return (x << 4) >> 3
@@ -112,6 +116,7 @@
 }
 
 // sbfx
+// merge shifts into sbfx: (x << lc) >> rc && lc <= rc.
 func sbfx1(x int64) int64 {
 	return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR"
 }
@@ -120,6 +125,7 @@
 	return (x << 60) >> 60 // arm64:"SBFX\tZR, R[0-9]+, [$]4",-"LSL",-"ASR"
 }
 
+//  merge shift and sign-extension into sbfx.
 func sbfx3(x int32) int64 {
 	return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]29",-"ASR"
 }
@@ -132,131 +138,181 @@
 	return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]5",-"ASR"
 }
 
-func sbfx6(x int32) int32 {
+func sbfx6(x int32) int64 {
+	return int64(x >> 30) // arm64:"SBFX\t[$]30, R[0-9]+, [$]2"
+}
+
+func sbfx7(x int16) int64 {
+	return int64(x >> 10) // arm64:"SBFX\t[$]10, R[0-9]+, [$]6"
+}
+
+func sbfx8(x int8) int64 {
+	return int64(x >> 5) // arm64:"SBFX\t[$]5, R[0-9]+, [$]3"
+}
+
+// sbfx combinations.
+// merge shifts with sbfiz into sbfx.
+func sbfx9(x int32) int32 {
 	return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR"
 }
 
 // merge sbfx and sign-extension into sbfx.
-func sbfx7(x int32) int64 {
+func sbfx10(x int32) int64 {
 	c := x + 5
 	return int64(c >> 20) // arm64"SBFX\t[$]20, R[0-9]+, [$]12",-"MOVW\tR[0-9]+, R[0-9]+"
 }
 
 // ubfiz
+// merge shifts into ubfiz: (x<<lc)>>rc && lc>rc
 func ubfiz1(x uint64) uint64 {
-	// arm64:"UBFIZ\t[$]3, R[0-9]+, [$]12",-"LSL",-"AND"
-	// s390x:"RISBGZ\t[$]49, [$]60, [$]3,",-"SLD",-"AND"
-	return (x & 0xfff) << 3
-}
-
-func ubfiz2(x uint64) uint64 {
-	// arm64:"UBFIZ\t[$]4, R[0-9]+, [$]12",-"LSL",-"AND"
-	// s390x:"RISBGZ\t[$]48, [$]59, [$]4,",-"SLD",-"AND"
-	return (x << 4) & 0xfff0
-}
-
-func ubfiz3(x uint32) uint64 {
-	return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]32",-"LSL"
-}
-
-func ubfiz4(x uint16) uint64 {
-	return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]16",-"LSL"
-}
-
-func ubfiz5(x uint8) uint64 {
-	return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]8",-"LSL"
-}
-
-func ubfiz6(x uint64) uint64 {
 	// arm64:"UBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"LSR"
 	// s390x:"RISBGZ\t[$]3, [$]62, [$]1, ",-"SLD",-"SRD"
 	return (x << 4) >> 3
 }
 
-func ubfiz7(x uint32) uint32 {
+// merge shift and zero-extension into ubfiz.
+func ubfiz2(x uint32) uint64 {
+	return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]32",-"LSL"
+}
+
+func ubfiz3(x uint16) uint64 {
+	return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]16",-"LSL"
+}
+
+func ubfiz4(x uint8) uint64 {
+	return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]8",-"LSL"
+}
+
+func ubfiz5(x uint8) uint64 {
+	return uint64(x) << 60 // arm64:"UBFIZ\t[$]60, R[0-9]+, [$]4",-"LSL"
+}
+
+func ubfiz6(x uint32) uint64 {
+	return uint64(x << 30) // arm64:"UBFIZ\t[$]30, R[0-9]+, [$]2",
+}
+
+func ubfiz7(x uint16) uint64 {
+	return uint64(x << 10) // arm64:"UBFIZ\t[$]10, R[0-9]+, [$]6",
+}
+
+func ubfiz8(x uint8) uint64 {
+	return uint64(x << 7) // arm64:"UBFIZ\t[$]7, R[0-9]+, [$]1",
+}
+
+// merge ANDconst into ubfiz.
+func ubfiz9(x uint64) uint64 {
+	// arm64:"UBFIZ\t[$]3, R[0-9]+, [$]12",-"LSL",-"AND"
+	// s390x:"RISBGZ\t[$]49, [$]60, [$]3,",-"SLD",-"AND"
+	return (x & 0xfff) << 3
+}
+
+func ubfiz10(x uint64) uint64 {
+	// arm64:"UBFIZ\t[$]4, R[0-9]+, [$]12",-"LSL",-"AND"
+	// s390x:"RISBGZ\t[$]48, [$]59, [$]4,",-"SLD",-"AND"
+	return (x << 4) & 0xfff0
+}
+
+// ubfiz combinations
+func ubfiz11(x uint32) uint32 {
 	// arm64:"UBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"LSR"
 	return (x << 4) >> 3
 }
 
-func ubfiz8(x uint64) uint64 {
+func ubfiz12(x uint64) uint64 {
 	// arm64:"UBFIZ\t[$]1, R[0-9]+, [$]20",-"LSL",-"LSR"
 	// s390x:"RISBGZ\t[$]43, [$]62, [$]1, ",-"SLD",-"SRD",-"AND"
 	return ((x & 0xfffff) << 4) >> 3
 }
 
-func ubfiz9(x uint64) uint64 {
+func ubfiz13(x uint64) uint64 {
 	// arm64:"UBFIZ\t[$]5, R[0-9]+, [$]13",-"LSL",-"LSR",-"AND"
 	return ((x << 3) & 0xffff) << 2
 }
 
-func ubfiz10(x uint64) uint64 {
+func ubfiz14(x uint64) uint64 {
 	// arm64:"UBFIZ\t[$]7, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND"
 	// s390x:"RISBGZ\t[$]45, [$]56, [$]7, ",-"SLD",-"SRD",-"AND"
 	return ((x << 5) & (0xfff << 5)) << 2
 }
 
 // ubfx
+// merge shifts into ubfx: (x<<lc)>>rc && lc<rc
 func ubfx1(x uint64) uint64 {
-	// arm64:"UBFX\t[$]25, R[0-9]+, [$]10",-"LSR",-"AND"
-	// s390x:"RISBGZ\t[$]54, [$]63, [$]39, ",-"SRD",-"AND"
-	return (x >> 25) & 1023
-}
-
-func ubfx2(x uint64) uint64 {
-	// arm64:"UBFX\t[$]4, R[0-9]+, [$]8",-"LSR",-"AND"
-	// s390x:"RISBGZ\t[$]56, [$]63, [$]60, ",-"SRD",-"AND"
-	return (x & 0x0ff0) >> 4
-}
-
-func ubfx3(x uint32) uint64 {
-	return uint64(x >> 15) // arm64:"UBFX\t[$]15, R[0-9]+, [$]17",-"LSR"
-}
-
-func ubfx4(x uint16) uint64 {
-	return uint64(x >> 9) // arm64:"UBFX\t[$]9, R[0-9]+, [$]7",-"LSR"
-}
-
-func ubfx5(x uint8) uint64 {
-	return uint64(x >> 3) // arm64:"UBFX\t[$]3, R[0-9]+, [$]5",-"LSR"
-}
-
-func ubfx6(x uint64) uint64 {
 	// arm64:"UBFX\t[$]1, R[0-9]+, [$]62",-"LSL",-"LSR"
 	// s390x:"RISBGZ\t[$]2, [$]63, [$]63,",-"SLD",-"SRD"
 	return (x << 1) >> 2
 }
 
-func ubfx7(x uint32) uint32 {
+// merge shift and zero-extension into ubfx.
+func ubfx2(x uint32) uint64 {
+	return uint64(x >> 15) // arm64:"UBFX\t[$]15, R[0-9]+, [$]17",-"LSR"
+}
+
+func ubfx3(x uint16) uint64 {
+	return uint64(x >> 9) // arm64:"UBFX\t[$]9, R[0-9]+, [$]7",-"LSR"
+}
+
+func ubfx4(x uint8) uint64 {
+	return uint64(x >> 3) // arm64:"UBFX\t[$]3, R[0-9]+, [$]5",-"LSR"
+}
+
+func ubfx5(x uint32) uint64 {
+	return uint64(x) >> 30 // arm64:"UBFX\t[$]30, R[0-9]+, [$]2"
+}
+
+func ubfx6(x uint16) uint64 {
+	return uint64(x) >> 10 // arm64:"UBFX\t[$]10, R[0-9]+, [$]6"
+}
+
+func ubfx7(x uint8) uint64 {
+	return uint64(x) >> 3 // arm64:"UBFX\t[$]3, R[0-9]+, [$]5"
+}
+
+// merge ANDconst into ubfx.
+func ubfx8(x uint64) uint64 {
+	// arm64:"UBFX\t[$]25, R[0-9]+, [$]10",-"LSR",-"AND"
+	// s390x:"RISBGZ\t[$]54, [$]63, [$]39, ",-"SRD",-"AND"
+	return (x >> 25) & 1023
+}
+
+func ubfx9(x uint64) uint64 {
+	// arm64:"UBFX\t[$]4, R[0-9]+, [$]8",-"LSR",-"AND"
+	// s390x:"RISBGZ\t[$]56, [$]63, [$]60, ",-"SRD",-"AND"
+	return (x & 0x0ff0) >> 4
+}
+
+// ubfx combinations.
+func ubfx10(x uint32) uint32 {
 	// arm64:"UBFX\t[$]1, R[0-9]+, [$]30",-"LSL",-"LSR"
 	return (x << 1) >> 2
 }
 
-func ubfx8(x uint64) uint64 {
+func ubfx11(x uint64) uint64 {
 	// arm64:"UBFX\t[$]1, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND"
 	// s390x:"RISBGZ\t[$]52, [$]63, [$]63,",-"SLD",-"SRD",-"AND"
 	return ((x << 1) >> 2) & 0xfff
 }
 
-func ubfx9(x uint64) uint64 {
+func ubfx12(x uint64) uint64 {
 	// arm64:"UBFX\t[$]4, R[0-9]+, [$]11",-"LSL",-"LSR",-"AND"
 	// s390x:"RISBGZ\t[$]53, [$]63, [$]60, ",-"SLD",-"SRD",-"AND"
 	return ((x >> 3) & 0xfff) >> 1
 }
 
-func ubfx10(x uint64) uint64 {
+func ubfx13(x uint64) uint64 {
 	// arm64:"UBFX\t[$]5, R[0-9]+, [$]56",-"LSL",-"LSR"
 	// s390x:"RISBGZ\t[$]8, [$]63, [$]59, ",-"SLD",-"SRD"
 	return ((x >> 2) << 5) >> 8
 }
 
-func ubfx11(x uint64) uint64 {
+func ubfx14(x uint64) uint64 {
 	// arm64:"UBFX\t[$]1, R[0-9]+, [$]19",-"LSL",-"LSR"
 	// s390x:"RISBGZ\t[$]45, [$]63, [$]63, ",-"SLD",-"SRD",-"AND"
 	return ((x & 0xfffff) << 3) >> 4
 }
 
 // merge ubfx and zero-extension into ubfx.
-func ubfx12(x uint64) bool {
+func ubfx15(x uint64) bool {
 	midr := x + 10
 	part_num := uint16((midr >> 4) & 0xfff)
 	if part_num == 0xd0c { // arm64:"UBFX\t[$]4, R[0-9]+, [$]12",-"MOVHU\tR[0-9]+, R[0-9]+"
diff --git a/test/codegen/bits.go b/test/codegen/bits.go
index 8117a62..8e973d5 100644
--- a/test/codegen/bits.go
+++ b/test/codegen/bits.go
@@ -6,6 +6,8 @@
 
 package codegen
 
+import "math/bits"
+
 /************************************
  * 64-bit instructions
  ************************************/
@@ -355,3 +357,9 @@
 	// amd64: "BTL", -"SHL"
 	return a[i>>5]&(1<<(i&31)) != 0
 }
+
+func issue48467(x, y uint64) uint64 {
+	// arm64: -"NEG"
+	d, borrow := bits.Sub64(x, y, 0)
+	return x - d&(-borrow)
+}
diff --git a/test/codegen/math.go b/test/codegen/math.go
index cd573db..df2ebd7 100644
--- a/test/codegen/math.go
+++ b/test/codegen/math.go
@@ -73,6 +73,7 @@
 	// s390x:"LPDFR\t",-"MOVD\t"     (no integer load/store)
 	// ppc64:"FABS\t"
 	// ppc64le:"FABS\t"
+	// riscv64:"FABSD\t"
 	// wasm:"F64Abs"
 	// arm/6:"ABSD\t"
 	sink64[0] = math.Abs(x)
@@ -96,6 +97,7 @@
 	// s390x:"CPSDR",-"MOVD"         (no integer load/store)
 	// ppc64:"FCPSGN"
 	// ppc64le:"FCPSGN"
+	// riscv64:"FSGNJD"
 	// wasm:"F64Copysign"
 	sink64[0] = math.Copysign(a, b)
 
@@ -103,6 +105,7 @@
 	// s390x:"LNDFR\t",-"MOVD\t"     (no integer load/store)
 	// ppc64:"FCPSGN"
 	// ppc64le:"FCPSGN"
+	// riscv64:"FSGNJD"
 	// arm64:"ORR", -"AND"
 	sink64[1] = math.Copysign(c, -1)
 
@@ -115,6 +118,7 @@
 	// s390x:"CPSDR\t",-"MOVD\t"     (no integer load/store)
 	// ppc64:"FCPSGN"
 	// ppc64le:"FCPSGN"
+	// riscv64:"FSGNJD"
 	sink64[3] = math.Copysign(-1, c)
 }
 
diff --git a/test/codegen/rotate.go b/test/codegen/rotate.go
index 519cc83..204efae 100644
--- a/test/codegen/rotate.go
+++ b/test/codegen/rotate.go
@@ -34,8 +34,15 @@
 	// ppc64le:"ROTL\t[$]9"
 	a += x<<9 ^ x>>55
 
-	// s390x:"RISBGZ\t[$]0, [$]63, [$]7, "
+	// amd64:"ROLQ\t[$]10"
+	// arm64:"ROR\t[$]54"
+	// s390x:"RISBGZ\t[$]0, [$]63, [$]10, "
+	// ppc64:"ROTL\t[$]10"
+	// ppc64le:"ROTL\t[$]10"
 	// arm64:"ROR\t[$]57" // TODO this is not great line numbering, but then again, the instruction did appear
+	// s390x:"RISBGZ\t[$]0, [$]63, [$]7, " // TODO ditto
+	a += bits.RotateLeft64(x, 10)
+
 	return a
 }
 
@@ -64,8 +71,16 @@
 	// ppc64le:"ROTLW\t[$]9"
 	a += x<<9 ^ x>>23
 
-	// s390x:"RLL\t[$]7"
+	// amd64:"ROLL\t[$]10"
+	// arm:"MOVW\tR\\d+@>22"
+	// arm64:"RORW\t[$]22"
+	// s390x:"RLL\t[$]10"
+	// ppc64:"ROTLW\t[$]10"
+	// ppc64le:"ROTLW\t[$]10"
 	// arm64:"RORW\t[$]25" // TODO this is not great line numbering, but then again, the instruction did appear
+	// s390x:"RLL\t[$]7" // TODO ditto
+	a += bits.RotateLeft32(x, 10)
+
 	return a
 }
 
@@ -211,3 +226,26 @@
 	a[i] = bits.RotateLeft32(a[3], 4) & 0xFFF00FFF
 	i++
 }
+
+// combined arithmetic and rotate on arm64
+func checkArithmeticWithRotate(a *[1000]uint64) {
+	// arm64: "AND\tR[0-9]+@>51, R[0-9]+, R[0-9]+"
+	a[2] = a[1] & bits.RotateLeft64(a[0], 13)
+	// arm64: "ORR\tR[0-9]+@>51, R[0-9]+, R[0-9]+"
+	a[5] = a[4] | bits.RotateLeft64(a[3], 13)
+	// arm64: "EOR\tR[0-9]+@>51, R[0-9]+, R[0-9]+"
+	a[8] = a[7] ^ bits.RotateLeft64(a[6], 13)
+	// arm64: "MVN\tR[0-9]+@>51, R[0-9]+"
+	a[10] = ^bits.RotateLeft64(a[9], 13)
+	// arm64: "BIC\tR[0-9]+@>51, R[0-9]+, R[0-9]+"
+	a[13] = a[12] &^ bits.RotateLeft64(a[11], 13)
+	// arm64: "EON\tR[0-9]+@>51, R[0-9]+, R[0-9]+"
+	a[16] = a[15] ^ ^bits.RotateLeft64(a[14], 13)
+	// arm64: "ORN\tR[0-9]+@>51, R[0-9]+, R[0-9]+"
+	a[19] = a[18] | ^bits.RotateLeft64(a[17], 13)
+	// arm64: "TST\tR[0-9]+@>51, R[0-9]+"
+	if a[18]&bits.RotateLeft64(a[19], 13) == 0 {
+		a[20] = 1
+	}
+
+}
diff --git a/test/fixedbugs/issue48289.go b/test/fixedbugs/issue48289.go
new file mode 100644
index 0000000..94dbeee
--- /dev/null
+++ b/test/fixedbugs/issue48289.go
@@ -0,0 +1,28 @@
+// run
+
+// 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"
+
+func main() {
+	ch := make(chan int, 1)
+
+	var ptrs [2]*int
+	for i := range ptrs {
+		ch <- i
+		select {
+		case x := <-ch:
+			ptrs[i] = &x
+		}
+	}
+
+	for i, ptr := range ptrs {
+		if *ptr != i {
+			panic(fmt.Sprintf("got *ptr %d, want %d", *ptr, i))
+		}
+	}
+}
diff --git a/test/fixedbugs/issue48301.go b/test/fixedbugs/issue48301.go
new file mode 100644
index 0000000..1ff9ffb
--- /dev/null
+++ b/test/fixedbugs/issue48301.go
@@ -0,0 +1,13 @@
+// errorcheck -G=0
+
+// 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.
+
+// Don't crash while reporting the error.
+
+package p
+
+func _() {
+	type T = T // ERROR "T uses T|invalid recursive type T"
+}
diff --git a/test/fixedbugs/issue48357.go b/test/fixedbugs/issue48357.go
new file mode 100644
index 0000000..5b39fc4
--- /dev/null
+++ b/test/fixedbugs/issue48357.go
@@ -0,0 +1,20 @@
+// run
+
+// 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 "reflect"
+
+type T [129]byte
+
+func main() {
+	m := map[string]T{}
+	v := reflect.ValueOf(m)
+	v.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(T{}))
+	g = m["a"]
+}
+
+var g T
diff --git a/test/fixedbugs/issue48459.go b/test/fixedbugs/issue48459.go
new file mode 100644
index 0000000..ceb7788
--- /dev/null
+++ b/test/fixedbugs/issue48459.go
@@ -0,0 +1,17 @@
+// 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 main
+
+func main() {
+	if true {
+		return
+	}
+
+	defer func() {
+		recover()
+	}()
+}
diff --git a/test/fixedbugs/issue48473.go b/test/fixedbugs/issue48473.go
new file mode 100644
index 0000000..8edef1f
--- /dev/null
+++ b/test/fixedbugs/issue48473.go
@@ -0,0 +1,30 @@
+// run
+
+// 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"
+
+func f(x uint64) uint64 {
+	s := "\x04"
+	c := s[0]
+	return x - x<<c<<4
+}
+
+func g(x uint32) uint32 {
+	s := "\x04"
+	c := s[0]
+	return x - x<<c<<4
+}
+
+func main() {
+	if want, got := uint64(0xffffffffffffff01), f(1); want != got {
+		panic(fmt.Sprintf("want %x got %x", want, got))
+	}
+	if want, got := uint32(0xffffff01), g(1); want != got {
+		panic(fmt.Sprintf("want %x got %x", want, got))
+	}
+}
diff --git a/test/fixedbugs/issue48476.go b/test/fixedbugs/issue48476.go
new file mode 100644
index 0000000..6b77f7c
--- /dev/null
+++ b/test/fixedbugs/issue48476.go
@@ -0,0 +1,21 @@
+// run
+
+// 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"
+
+//go:noinline
+func f(x uint64) uint64 {
+	s := "\x04"
+	c := s[0]
+	return x << c << 4
+}
+func main() {
+	if want, got := uint64(1<<8), f(1); want != got {
+		panic(fmt.Sprintf("want %x got %x", want, got))
+	}
+}
diff --git a/test/run.go b/test/run.go
index 76621d9..0c9c8c5 100644
--- a/test/run.go
+++ b/test/run.go
@@ -780,11 +780,13 @@
 			}
 
 		default:
-			// we don't know how to add -G for this test yet
-			if *verbose {
-				fmt.Printf("excl\t%s\n", t.goFileName())
+			if t.glevel != CompilerDefaultGLevel {
+				// we don't know how to add -G for this test yet
+				if *verbose {
+					fmt.Printf("excl\t%s\n", t.goFileName())
+				}
+				return false
 			}
-			return false
 		}
 
 		return true
@@ -1751,7 +1753,7 @@
 	// are the supported variants.
 	archVariants = map[string][]string{
 		"386":     {"GO386", "sse2", "softfloat"},
-		"amd64":   {},
+		"amd64":   {"GOAMD64", "v1", "v2", "v3", "v4"},
 		"arm":     {"GOARM", "5", "6", "7"},
 		"arm64":   {},
 		"mips":    {"GOMIPS", "hardfloat", "softfloat"},
@@ -2182,11 +2184,7 @@
 )
 
 var g3Failures = setOf(
-	"writebarrier.go", // correct diagnostics, but different lines (probably irgen's fault)
-
 	"typeparam/nested.go", // -G=3 doesn't support function-local types with generics
-
-	"typeparam/mdempsky/4.go", // -G=3 can't export functions with labeled breaks in loops
 )
 
 var unifiedFailures = setOf(
diff --git a/test/typeparam/issue46461.go b/test/typeparam/issue46461.go
new file mode 100644
index 0000000..2c54a6b
--- /dev/null
+++ b/test/typeparam/issue46461.go
@@ -0,0 +1,13 @@
+// compile -G=3
+
+// 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
+
+type T[U interface{ M() T[U] }] int
+
+type X int
+
+func (X) M() T[X] { return 0 }
diff --git a/test/typeparam/issue46461b.dir/a.go b/test/typeparam/issue46461b.dir/a.go
new file mode 100644
index 0000000..0d53b3e
--- /dev/null
+++ b/test/typeparam/issue46461b.dir/a.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 a
+
+type T[U interface{ M() T[U] }] int
diff --git a/test/typeparam/issue46461b.dir/b.go b/test/typeparam/issue46461b.dir/b.go
new file mode 100644
index 0000000..3393a37
--- /dev/null
+++ b/test/typeparam/issue46461b.dir/b.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.
+
+package b
+
+import "./a"
+
+type X int
+
+func (X) M() a.T[X] { return 0 }
diff --git a/test/typeparam/issue46461b.go b/test/typeparam/issue46461b.go
new file mode 100644
index 0000000..87b4ff4
--- /dev/null
+++ b/test/typeparam/issue46461b.go
@@ -0,0 +1,7 @@
+// compiledir -G=3
+
+// 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/typeparam/issue47797.go b/test/typeparam/issue47797.go
new file mode 100644
index 0000000..3e80d3c
--- /dev/null
+++ b/test/typeparam/issue47797.go
@@ -0,0 +1,22 @@
+// compile -G=3
+
+// 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
+
+type Foo[T any] struct {
+        Val T
+}
+
+func (f Foo[T]) Bat() {}
+
+type Bar struct {
+        Foo[int]
+}
+
+func foo() {
+        var b Bar
+        b.Bat()
+}
diff --git a/test/typeparam/issue48094b.dir/a.go b/test/typeparam/issue48094b.dir/a.go
new file mode 100644
index 0000000..a113a22
--- /dev/null
+++ b/test/typeparam/issue48094b.dir/a.go
@@ -0,0 +1,8 @@
+// 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() { G(0) }
+func G[T any](t T) {}
diff --git a/test/typeparam/issue48094b.dir/b.go b/test/typeparam/issue48094b.dir/b.go
new file mode 100644
index 0000000..242b34aa
--- /dev/null
+++ b/test/typeparam/issue48094b.dir/b.go
@@ -0,0 +1,9 @@
+// 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"
+
+func H() { a.F() }
diff --git a/test/typeparam/issue48094b.go b/test/typeparam/issue48094b.go
new file mode 100644
index 0000000..87b4ff4
--- /dev/null
+++ b/test/typeparam/issue48094b.go
@@ -0,0 +1,7 @@
+// compiledir -G=3
+
+// 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/typeparam/issue48137.go b/test/typeparam/issue48137.go
new file mode 100644
index 0000000..3dd7810
--- /dev/null
+++ b/test/typeparam/issue48137.go
@@ -0,0 +1,25 @@
+// run -gcflags=-G=3
+
+// 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
+
+type Constraint[T any] interface {
+	~func() T
+}
+
+func Foo[T Constraint[T]]() T {
+	var t T
+
+	t = func() T {
+		return t
+	}
+	return t
+}
+
+func main() {
+	type Bar func() Bar
+	Foo[Bar]()
+}
diff --git a/test/typeparam/issue48225.go b/test/typeparam/issue48225.go
new file mode 100644
index 0000000..887ffd8
--- /dev/null
+++ b/test/typeparam/issue48225.go
@@ -0,0 +1,37 @@
+// run -gcflags="-G=3"
+
+// 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 "reflect"
+
+type Foo[T any] struct {
+	val int
+}
+
+func (foo Foo[T]) Get() *T {
+	if foo.val != 1 {
+		panic("bad val field in Foo receiver")
+	}
+	return new(T)
+}
+
+var (
+	newInt    = Foo[int]{val: 1}.Get
+	newString = Foo[string]{val: 1}.Get
+)
+
+func main() {
+	i := newInt()
+	s := newString()
+
+	if t := reflect.TypeOf(i).String(); t != "*int" {
+		panic(t)
+	}
+	if t := reflect.TypeOf(s).String(); t != "*string" {
+		panic(t)
+	}
+}
diff --git a/test/typeparam/issue48253.go b/test/typeparam/issue48253.go
new file mode 100644
index 0000000..7bd0234
--- /dev/null
+++ b/test/typeparam/issue48253.go
@@ -0,0 +1,34 @@
+// run -gcflags="-G=3"
+
+// 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 (
+	"reflect"
+)
+
+type A[T any] struct {
+	B[int]
+}
+
+type B[T any] struct {
+}
+
+func (b B[T]) Bat() {
+	t := new(T)
+	if tt := reflect.TypeOf(t); tt.Kind() != reflect.Pointer || tt.Elem().Kind() != reflect.Int {
+		panic("unexpected type, want: *int, got: "+tt.String())
+	}
+}
+
+type Foo struct {
+	A[string]
+}
+func main() {
+	Foo{}.A.Bat()
+	Foo{}.A.B.Bat()
+	Foo{}.Bat()
+}
diff --git a/test/typeparam/issue48276a.go b/test/typeparam/issue48276a.go
new file mode 100644
index 0000000..060ac3e
--- /dev/null
+++ b/test/typeparam/issue48276a.go
@@ -0,0 +1,19 @@
+// run -gcflags=-G=3
+
+// 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"
+
+func main() {
+	IsZero[interface{}]("")
+}
+
+func IsZero[T comparable](val T) bool {
+	var zero T
+	fmt.Printf("%v:%v\n", zero, val)
+	return val != zero
+}
diff --git a/test/typeparam/issue48276a.out b/test/typeparam/issue48276a.out
new file mode 100644
index 0000000..7e8a8a9
--- /dev/null
+++ b/test/typeparam/issue48276a.out
@@ -0,0 +1 @@
+<nil>:
diff --git a/test/typeparam/issue48276b.go b/test/typeparam/issue48276b.go
new file mode 100644
index 0000000..67c3e3d
--- /dev/null
+++ b/test/typeparam/issue48276b.go
@@ -0,0 +1,15 @@
+// run -gcflags=-G=3
+
+// 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
+
+func main() {
+	f[interface{}](nil)
+}
+
+func f[T any](x T) {
+	var _ interface{} = x
+}
diff --git a/test/typeparam/issue48280.dir/a.go b/test/typeparam/issue48280.dir/a.go
new file mode 100644
index 0000000..17859e6
--- /dev/null
+++ b/test/typeparam/issue48280.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.
+
+package a
+
+type I[T I[T]] interface {
+	F() T
+}
+
+type S struct{}
diff --git a/test/typeparam/issue48280.dir/main.go b/test/typeparam/issue48280.dir/main.go
new file mode 100644
index 0000000..b9981c6
--- /dev/null
+++ b/test/typeparam/issue48280.dir/main.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.
+
+package main
+
+import "a"
+
+func main() {
+	_ = a.S{}
+}
diff --git a/test/typeparam/issue48280.go b/test/typeparam/issue48280.go
new file mode 100644
index 0000000..76930e5
--- /dev/null
+++ b/test/typeparam/issue48280.go
@@ -0,0 +1,7 @@
+// rundir -G=3
+
+// 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/typeparam/issue48306.dir/a.go b/test/typeparam/issue48306.dir/a.go
new file mode 100644
index 0000000..739750b
--- /dev/null
+++ b/test/typeparam/issue48306.dir/a.go
@@ -0,0 +1,9 @@
+// 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
+
+type I[T I[T]] interface {
+	F() T
+}
diff --git a/test/typeparam/issue48306.dir/main.go b/test/typeparam/issue48306.dir/main.go
new file mode 100644
index 0000000..5d602fe
--- /dev/null
+++ b/test/typeparam/issue48306.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.
+
+package main
+
+import "a"
+
+type S struct{}
+
+func (*S) F() *S { return nil }
+
+func main() {
+	var _ a.I[*S] = &S{}
+}
diff --git a/test/typeparam/issue48306.go b/test/typeparam/issue48306.go
new file mode 100644
index 0000000..76930e5
--- /dev/null
+++ b/test/typeparam/issue48306.go
@@ -0,0 +1,7 @@
+// rundir -G=3
+
+// 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/typeparam/issue48317.go b/test/typeparam/issue48317.go
new file mode 100644
index 0000000..c8f088d
--- /dev/null
+++ b/test/typeparam/issue48317.go
@@ -0,0 +1,38 @@
+// run -gcflags="-G=3"
+
+// 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 (
+	"encoding/json"
+)
+
+type A[T any] struct {
+	F1 string `json:"t1"`
+	F2 T      `json:"t2"`
+	B  B      `json:"t3"`
+}
+
+type B struct {
+	F4 int `json:"t4"`
+}
+
+func a[T any]() {
+	data := `{"t1":"1","t2":2,"t3":{"t4":4}}`
+	a1 := A[T]{}
+	if err := json.Unmarshal([]byte(data), &a1); err != nil {
+		panic(err)
+	}
+	if bytes, err := json.Marshal(&a1); err != nil {
+		panic(err)
+	} else if string(bytes) != data {
+		panic(string(bytes))
+	}
+}
+
+func main() {
+	a[int]()
+}
diff --git a/test/typeparam/issue48337a.dir/a.go b/test/typeparam/issue48337a.dir/a.go
new file mode 100644
index 0000000..6f1b128
--- /dev/null
+++ b/test/typeparam/issue48337a.dir/a.go
@@ -0,0 +1,32 @@
+// 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
+
+import (
+	"fmt"
+	"sync"
+)
+
+type WrapperWithLock[T any] interface {
+	PrintWithLock()
+}
+
+func NewWrapperWithLock[T any](value T) WrapperWithLock[T] {
+	return &wrapperWithLock[T]{
+		Object: value,
+	}
+}
+
+type wrapperWithLock[T any] struct {
+	Lock   sync.Mutex
+	Object T
+}
+
+func (w *wrapperWithLock[T]) PrintWithLock() {
+	w.Lock.Lock()
+	defer w.Lock.Unlock()
+
+	fmt.Println(w.Object)
+}
diff --git a/test/typeparam/issue48337a.dir/main.go b/test/typeparam/issue48337a.dir/main.go
new file mode 100644
index 0000000..16f7115
--- /dev/null
+++ b/test/typeparam/issue48337a.dir/main.go
@@ -0,0 +1,12 @@
+// 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"
+
+func main() {
+	obj := a.NewWrapperWithLock("this file does import sync")
+	obj.PrintWithLock()
+}
diff --git a/test/typeparam/issue48337a.go b/test/typeparam/issue48337a.go
new file mode 100644
index 0000000..76930e5
--- /dev/null
+++ b/test/typeparam/issue48337a.go
@@ -0,0 +1,7 @@
+// rundir -G=3
+
+// 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/typeparam/issue48337a.out b/test/typeparam/issue48337a.out
new file mode 100644
index 0000000..fa8d3ee
--- /dev/null
+++ b/test/typeparam/issue48337a.out
@@ -0,0 +1 @@
+this file does import sync
diff --git a/test/typeparam/issue48337b.dir/a.go b/test/typeparam/issue48337b.dir/a.go
new file mode 100644
index 0000000..a3c2e88
--- /dev/null
+++ b/test/typeparam/issue48337b.dir/a.go
@@ -0,0 +1,25 @@
+// 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
+
+type Container[T any] struct {
+	X T
+}
+
+func NewContainer[T any](x T) *Container[T] {
+	return &Container[T]{x}
+}
+
+type MetaContainer struct {
+	C *Container[Value]
+}
+
+type Value struct{}
+
+func NewMetaContainer() *MetaContainer {
+	c := NewContainer(Value{})
+	// c := &Container[Value]{Value{}} // <-- this works
+	return &MetaContainer{c}
+}
diff --git a/test/typeparam/issue48337b.dir/main.go b/test/typeparam/issue48337b.dir/main.go
new file mode 100644
index 0000000..0b2814c
--- /dev/null
+++ b/test/typeparam/issue48337b.dir/main.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.
+
+package main
+
+import "a"
+
+func main() {
+	a.NewMetaContainer()
+}
diff --git a/test/typeparam/issue48337b.go b/test/typeparam/issue48337b.go
new file mode 100644
index 0000000..76930e5
--- /dev/null
+++ b/test/typeparam/issue48337b.go
@@ -0,0 +1,7 @@
+// rundir -G=3
+
+// 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/typeparam/issue48344.go b/test/typeparam/issue48344.go
new file mode 100644
index 0000000..7ea539c
--- /dev/null
+++ b/test/typeparam/issue48344.go
@@ -0,0 +1,26 @@
+// run -gcflags=-G=3
+
+// 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
+
+type G[T any] interface {
+	g()
+}
+
+type Foo[T any] struct {
+}
+
+func (foo *Foo[T]) g() {
+
+}
+
+func f[T any]() {
+	v := []G[T]{}
+	v = append(v, &Foo[T]{})
+}
+func main() {
+	f[int]()
+}
diff --git a/test/typeparam/issue48453.go b/test/typeparam/issue48453.go
new file mode 100644
index 0000000..0f751d3
--- /dev/null
+++ b/test/typeparam/issue48453.go
@@ -0,0 +1,21 @@
+// run -gcflags=-G=3
+
+// 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
+
+//go:noinline
+func CopyMap[M interface{ ~map[K]V }, K comparable, V any](m M) M {
+	out := make(M, len(m))
+	for k, v := range m {
+		out[k] = v
+	}
+	return out
+}
+
+func main() {
+	var m map[*string]int
+	CopyMap(m)
+}
diff --git a/test/typeparam/issue48462.dir/a.go b/test/typeparam/issue48462.dir/a.go
new file mode 100644
index 0000000..26c704d
--- /dev/null
+++ b/test/typeparam/issue48462.dir/a.go
@@ -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.
+
+package a
+
+func Unique[T comparable](set []T) []T {
+	nset := make([]T, 0, 8)
+
+loop:
+	for _, s := range set {
+		for _, e := range nset {
+			if s == e {
+				continue loop
+			}
+		}
+
+		nset = append(nset, s)
+	}
+
+	return nset
+}
diff --git a/test/typeparam/issue48462.dir/main.go b/test/typeparam/issue48462.dir/main.go
new file mode 100644
index 0000000..8054ddd
--- /dev/null
+++ b/test/typeparam/issue48462.dir/main.go
@@ -0,0 +1,23 @@
+// 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"
+	"reflect"
+
+	"a"
+)
+
+func main() {
+	e := []int{1, 2, 2, 3, 1, 6}
+
+	got := a.Unique(e)
+	want := []int{1, 2, 3, 6}
+	if !reflect.DeepEqual(got, want) {
+		panic(fmt.Sprintf("got %d, want %d", got, want))
+	}
+
+}
diff --git a/test/typeparam/issue48462.go b/test/typeparam/issue48462.go
new file mode 100644
index 0000000..76930e5
--- /dev/null
+++ b/test/typeparam/issue48462.go
@@ -0,0 +1,7 @@
+// rundir -G=3
+
+// 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