cmd/compile: implement range over func

Add compiler support for range over functions.
See the large comment at the top of
cmd/compile/internal/rangefunc/rewrite.go for details.

DO NOT REVIEW
This is part of a sequence of CLs demonstrating a proposal
to expand the applicability of range loops.

For proposal #61405.

Change-Id: I05717f94e63089c503acc49b28b47edeb4e011b4
diff --git a/src/cmd/compile/internal/escape/call.go b/src/cmd/compile/internal/escape/call.go
index c69eca1..3e0993d 100644
--- a/src/cmd/compile/internal/escape/call.go
+++ b/src/cmd/compile/internal/escape/call.go
@@ -224,7 +224,7 @@
 //	defer func() { f(x1, y1) }()
 func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
 	k := e.heapHole()
-	if n.Op() == ir.ODEFER && e.loopDepth == 1 {
+	if n.Op() == ir.ODEFER && e.loopDepth == 1 && n.DeferAt == nil {
 		// Top-level defer arguments don't escape to the heap,
 		// but they do need to last until they're invoked.
 		k = e.later(e.discardHole())
diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go
index 5355edc..cc527ae 100644
--- a/src/cmd/compile/internal/ir/expr.go
+++ b/src/cmd/compile/internal/ir/expr.go
@@ -147,12 +147,13 @@
 type CallExpr struct {
 	miniExpr
 	origNode
-	X         Node
-	Args      Nodes
-	RType     Node    `mknode:"-"` // see reflectdata/helpers.go
-	KeepAlive []*Name // vars to be kept alive until call returns
-	IsDDD     bool
-	NoInline  bool
+	X          Node
+	Args       Nodes
+	DeferAt Node
+	RType      Node    `mknode:"-"` // see reflectdata/helpers.go
+	KeepAlive  []*Name // vars to be kept alive until call returns
+	IsDDD      bool
+	NoInline   bool
 }
 
 func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {
diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go
index b6653ab..95ad52e 100644
--- a/src/cmd/compile/internal/ir/stmt.go
+++ b/src/cmd/compile/internal/ir/stmt.go
@@ -242,7 +242,8 @@
 // in a different context (a separate goroutine or a later time).
 type GoDeferStmt struct {
 	miniStmt
-	Call Node
+	Call       Node
+	DeferAt Expr
 }
 
 func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go
index 6ee832e..f2eba2a 100644
--- a/src/cmd/compile/internal/ir/symtab.go
+++ b/src/cmd/compile/internal/ir/symtab.go
@@ -10,7 +10,9 @@
 )
 
 // Syms holds known symbols.
-var Syms struct {
+var Syms symsStruct
+
+type symsStruct struct {
 	AssertE2I         *obj.LSym
 	AssertE2I2        *obj.LSym
 	AssertI2I         *obj.LSym
@@ -21,6 +23,7 @@
 	CgoCheckPtrWrite  *obj.LSym
 	CheckPtrAlignment *obj.LSym
 	Deferproc         *obj.LSym
+	Deferprocat       *obj.LSym
 	DeferprocStack    *obj.LSym
 	Deferreturn       *obj.LSym
 	Duffcopy          *obj.LSym
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index df5de63..13e835d 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -11,6 +11,7 @@
 	"sort"
 
 	"cmd/compile/internal/base"
+	"cmd/compile/internal/rangefunc"
 	"cmd/compile/internal/syntax"
 	"cmd/compile/internal/types2"
 	"cmd/internal/src"
@@ -68,6 +69,10 @@
 	}
 
 	pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
+	base.ExitIfErrors()
+	if err != nil {
+		base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
+	}
 
 	// Check for anonymous interface cycles (#56103).
 	if base.Debug.InterfaceCycles == 0 {
@@ -88,6 +93,7 @@
 			})
 		}
 	}
+	base.ExitIfErrors()
 
 	// Implementation restriction: we don't allow not-in-heap types to
 	// be used as type arguments (#54765).
@@ -113,11 +119,16 @@
 			base.ErrorfAt(targ.pos, 0, "cannot use incomplete (or unallocatable) type as a type argument: %v", targ.typ)
 		}
 	}
-
 	base.ExitIfErrors()
-	if err != nil {
-		base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
-	}
+
+	// Rewrite range over function to explicit function calls
+	// with the loop bodies converted into new implicit closures.
+	// We do this now, before serialization to unified IR, so that if the
+	// implicit closures are inlined, we will have the unified IR form.
+	// If we do the rewrite in the back end, like between typecheck and walk,
+	// then the new implicit closure will not have a unified IR inline body,
+	// and bodyReaderFor will fail.
+	rangefunc.Rewrite(pkg, info, files)
 
 	return pkg, info
 }
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 610d02c..2beac37 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -694,6 +694,9 @@
 		if pri, ok := objReader[sym]; ok {
 			return pri.pr.objIdx(pri.idx, nil, explicits, shaped)
 		}
+		if sym.Pkg.Path == "runtime" {
+			return typecheck.LookupRuntime(sym.Name)
+		}
 		base.Fatalf("unresolved stub: %v", sym)
 	}
 
@@ -1771,7 +1774,14 @@
 		pos := r.pos()
 		op := r.op()
 		call := r.expr()
-		return ir.NewGoDeferStmt(pos, op, call)
+		stmt := ir.NewGoDeferStmt(pos, op, call)
+		if op == ir.ODEFER {
+			x := r.optExpr()
+			if x != nil {
+				stmt.DeferAt = x.(ir.Expr)
+			}
+		}
+		return stmt
 
 	case stmtExpr:
 		return r.expr()
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index f82e700..58e82cb 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -8,6 +8,7 @@
 	"fmt"
 	"internal/buildcfg"
 	"internal/pkgbits"
+	"os"
 
 	"cmd/compile/internal/base"
 	"cmd/compile/internal/ir"
@@ -1053,6 +1054,9 @@
 
 	sig, block := obj.Type().(*types2.Signature), decl.Body
 	body, closureVars := w.p.bodyIdx(sig, block, w.dict)
+	if len(closureVars) > 0 {
+		fmt.Fprintln(os.Stderr, "CLOSURE", closureVars)
+	}
 	assert(len(closureVars) == 0)
 
 	w.Sync(pkgbits.SyncFuncExt)
@@ -1261,6 +1265,9 @@
 		w.pos(stmt)
 		w.op(callOps[stmt.Tok])
 		w.expr(stmt.Call)
+		if stmt.Tok == syntax.Defer {
+			w.optExpr(stmt.DeferAt)
+		}
 
 	case *syntax.DeclStmt:
 		for _, decl := range stmt.DeclList {
@@ -2188,6 +2195,10 @@
 	var_ *types2.Var
 }
 
+func (p posVar) String() string {
+	return p.pos.String() + ":" + p.var_.String()
+}
+
 func (w *writer) exprList(expr syntax.Expr) {
 	w.Sync(pkgbits.SyncExprList)
 	w.exprs(unpackListExpr(expr))
diff --git a/src/cmd/compile/internal/rangefunc/rewrite.go b/src/cmd/compile/internal/rangefunc/rewrite.go
new file mode 100644
index 0000000..09f8fe8
--- /dev/null
+++ b/src/cmd/compile/internal/rangefunc/rewrite.go
@@ -0,0 +1,1228 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package rangefunc rewrites range-over-func to code that doesn't use range-over-funcs.
+Rewriting the construct in the front end, before noder, means the functions generated during
+the rewrite are available in a noder-generated representation for inlining by the back end.
+
+# Theory of Operation
+
+The basic idea is to rewrite
+
+	for x := range f {
+		...
+	}
+
+into
+
+	f(func(x T) bool {
+		...
+	})
+
+But it's not usually that easy.
+
+# Range variables
+
+For a range not using :=, the assigned variables cannot be function arguments
+in the generated body function. Instead, we allocate fake parameters and
+start the body with an assignment. For example:
+
+	for bigExpr1, bigExpr2 = range f {
+		...
+	}
+
+becomes
+
+	f(func(#p1 #t1, #p2 #t2) bool {
+		bigExpr1, bigExpr2 = #p1, #p2
+		...
+	})
+
+(All the generated variables have a # at the start to signal that they
+are internal variables when looking at the generated code in a
+debugger. Because variables have all been resolved to the specific
+objects they represent, there is no danger of using plain "p1" and
+colliding with a Go variable named "p1"; the # is just nice to have,
+not for correctness.)
+
+It can also happen that there are fewer range variables than function
+arguments, in which case we end up with something like
+
+	f(func(x T, _ T2) bool {
+		...
+	})
+
+or
+
+	f(func(#p1 #t1, #p2 #t2, _ #t3) bool {
+		bigExpr1, bigExpr2 = #p1, #p2
+		...
+	})
+
+# Return
+
+If the body contains a "break", that break turns into "return false",
+to tell f to stop. And if the body contains a "continue", that turns
+into "return true", to tell f to proceed with the next value.
+Those are the easy cases.
+
+If the body contains a return or a break/continue/goto L, then we need
+to rewrite that into code that breaks out of the loop and then
+triggers that control flow. In general we rewrite
+
+	for x := range f {
+		...
+	}
+
+into
+
+	{
+		var #next int
+		f(func(x T) bool {
+			...
+			return true
+		})
+		... check #next ...
+	}
+
+The variable #next is an integer code that says what to do when f
+returns. Each difficult statement sets #next and then returns false to
+stop f.
+
+A plain "return" rewrites to {#next = -1; return false}.
+The return false breaks the loop. Then when f returns, the "check
+#next" section includes
+
+	if #next == -1 { return }
+
+which causes the return we want.
+
+Return with arguments is more involved. We need somewhere to store the
+arguments while we break out of f, so we add them to the var
+declaration, like:
+
+	{
+		var (
+			#next int
+			#r1 type1
+			#r2 type2
+		)
+		f(func(x T) bool {
+			...
+			{
+				// return a, b
+				#r1, #r2 = a, b
+				#next = -2
+				return false
+			}
+			...
+			return true
+		})
+		if #next == -2 { return #r1, #r2 }
+	}
+
+# Nested Loops
+
+So far we've only considered a single loop. If a function contains a
+sequence of loops, each can be translated individually. But loops can
+be nested. It would work to translate the innermost loop and then
+translate the loop around it, and so on, except that there'd be a lot
+of rewriting of rewritten code and the overall traversals could end up
+taking time quadratic in the depth of the nesting. To avoid all that,
+we use a single rewriting pass that handles a top-most range-over-func
+loop and all the range-over-func loops it contains at the same time.
+
+If we need to return from inside a doubly-nested loop, the rewrites
+above stay the same, but the check after the inner loop only says
+
+	if #next < 0 { return false }
+
+to stop the outer loop so it can do the actual return. That is,
+
+	for range f {
+		for range g {
+			...
+			return a, b
+			...
+		}
+	}
+
+becomes
+
+	{
+		var (
+			#next int
+			#r1 type1
+			#r2 type2
+		)
+		f(func() {
+			g(func() {
+				...
+				{
+					// return a, b
+					#r1, #r2 = a, b
+					#next = -2
+					return false
+				}
+				...
+				return true
+			})
+			if #next < 0 {
+				return false
+			}
+			return true
+		})
+		if #next == -2 {
+			return #r1, #r2
+		}
+	}
+
+Note that the #next < 0 after the inner loop handles both kinds of
+return with a single check.
+
+# Labeled break/continue of range-over-func loops
+
+For a labeled break or continue of an outer range-over-func, we
+use positive #next values. Any such labeled break or continue
+really means "do N breaks" or "do N breaks and 1 continue".
+We encode that as 2*N or 2*N+1 respectively.
+Loops that might need to propagate a labeled break or continue
+add one or both of these to the #next checks:
+
+	if #next >= 2 {
+		#next -= 2
+		return false
+	}
+
+	if #next >= 1 {
+		#next -= 1
+		return true
+	}
+
+For example
+
+	F: for range f {
+		for range g {
+			for range h {
+				...
+				break F
+				...
+				...
+				continue F
+				...
+			}
+		}
+		...
+	}
+
+becomes
+
+	{
+		var #next int
+		f(func() {
+			g(func() {
+				h(func() {
+					...
+					{
+						// break F
+						#next = 4
+						return false
+					}
+					...
+					{
+						// continue F
+						#next = 3
+						return false
+					}
+					...
+					return true
+				})
+				if #next >= 2 {
+					#next -= 2
+					return false
+				}
+				return true
+			})
+			if #next >= 2 {
+				#next -= 2
+				return false
+			}
+			if #next >= 1 {
+				#next -= 1
+				return true
+			}
+			...
+			return true
+		})
+	}
+
+Note that the post-h checks only consider a break,
+since no generated code tries to continue g.
+
+# Gotos and other labeled break/continue
+
+The final control flow translations are goto and break/continue of a
+non-range-over-func statement. In both cases, we may need to break out
+of one or more range-over-func loops before we can do the actual
+control flow statement. Each such break/continue/goto L statement is
+assigned a unique negative #next value (below -2, since -1 and -2 are
+for the two kinds of return). Then the post-checks for a given loop
+test for the specific codes that refer to labels directly targetable
+from that block. Otherwise, the generic
+
+	if #next < 0 { return false }
+
+check handles stopping the next loop to get one step closer to the label.
+
+For example
+
+	Top: print("start\n")
+	for range f {
+		for range g {
+			for range h {
+				...
+				goto Top
+				...
+			}
+		}
+	}
+
+
+becomes
+
+	Top: print("start\n")
+	{
+		var #next int
+		f(func() {
+			g(func() {
+				h(func() {
+					...
+					{
+						// goto Top
+						#next = -3
+						return false
+					}
+					...
+					return true
+				})
+				if #next < 0 {
+					return false
+				}
+				return true
+			})
+			if #next < 0 {
+				return false
+			}
+			return true
+		})
+		if #next == -3 {
+			#next = 0
+			goto Top
+		}
+	}
+
+Labeled break/continue to non-range-over-funcs are handled the same
+way as goto.
+
+# Defers
+
+The last wrinkle is handling defer statements. If we have
+
+	for range f {
+		defer print("A")
+	}
+
+we cannot rewrite that into
+
+	f(func() {
+		defer print("A")
+	})
+
+because the deferred code will run at the end of the iteration, not
+the end of the containing function. To fix that, the runtime provides
+a special hook that lets us obtain a defer "token" representing the
+outer function and then use it in a later defer to attach the deferred
+code to that outer function.
+
+Normally,
+
+	defer print("A")
+
+compiles to
+
+	runtime.deferproc(func() { print("A") })
+
+This changes in a range-over-func. For example:
+
+	for range f {
+		defer print("A")
+	}
+
+compiles to
+
+	var #defers = runtime.deferrangefunc()
+	f(func() {
+		runtime.deferprocat(func() { print("A") }, #defers)
+	})
+
+For this rewriting phase, we insert the explicit initialization of
+#defers and then attach the #defers variable to the CallStmt
+representing the defer. That variable will be propagated to the
+backend and will cause the backend to compile the defer using
+deferprocat instead of an ordinary deferproc.
+*/
+package rangefunc
+
+import (
+	"cmd/compile/internal/base"
+	"cmd/compile/internal/syntax"
+	"cmd/compile/internal/types2"
+	"fmt"
+	"go/constant"
+	"os"
+)
+
+// nopos is the zero syntax.Pos.
+var nopos syntax.Pos
+
+// A rewriter implements rewriting the range-over-funcs in a given function.
+type rewriter struct {
+	pkg   *types2.Package
+	info  *types2.Info
+	outer *syntax.FuncType
+	body  *syntax.BlockStmt
+
+	// References to important types and values.
+	anyName   *syntax.Name
+	anyObj    types2.Object
+	boolName  *syntax.Name
+	boolObj   types2.Object
+	intName   *syntax.Name
+	intObj    types2.Object
+	trueName  *syntax.Name
+	falseName *syntax.Name
+
+	// Branch numbering, computed as needed.
+	branchNext map[branch]int             // branch -> #next value
+	labelLoop  map[string]*syntax.ForStmt // label -> rangefunc loop it is declared inside (nil for no loop)
+
+	// Stack of nodes being visited.
+	stack    []syntax.Node // all nodes
+	forStack []*forLoop    // range-over-func loops
+
+	rewritten map[*syntax.ForStmt]syntax.Stmt
+
+	// Declared variables in generated code for outermost loop.
+	declStmt *syntax.DeclStmt
+	nextName *syntax.Name
+	retNames []*syntax.Name
+	defers   *syntax.Name
+}
+
+// A branch is a single labeled branch.
+type branch struct {
+	tok   syntax.Token
+	label string
+}
+
+// A forLoop describes a single range-over-func loop being processed.
+type forLoop struct {
+	nfor *syntax.ForStmt // actual syntax
+
+	checkRet      bool     // add check for "return" after loop
+	checkRetArgs  bool     // add check for "return args" after loop
+	checkBreak    bool     // add check for "break" after loop
+	checkContinue bool     // add check for "continue" after loop
+	checkBranch   []branch // add check for labeled branch after loop
+}
+
+// Rewrite rewrites all the range-over-funcs in the files.
+func Rewrite(pkg *types2.Package, info *types2.Info, files []*syntax.File) {
+	for _, file := range files {
+		syntax.Inspect(file, func(n syntax.Node) bool {
+			switch n := n.(type) {
+			case *syntax.FuncDecl:
+				rewriteFunc(pkg, info, n.Type, n.Body)
+				return false
+			case *syntax.FuncLit:
+				rewriteFunc(pkg, info, n.Type, n.Body)
+				return false
+			}
+			return true
+		})
+	}
+}
+
+// rewriteFunc rewrites all the range-over-funcs in a single function (a top-level func or a func literal).
+// The typ and body are the function's type and body.
+func rewriteFunc(pkg *types2.Package, info *types2.Info, typ *syntax.FuncType, body *syntax.BlockStmt) {
+	if body == nil {
+		return
+	}
+	r := &rewriter{
+		pkg:   pkg,
+		info:  info,
+		outer: typ,
+		body:  body,
+	}
+	syntax.Inspect(body, r.inspect)
+	if (base.Flag.W != 0) && r.forStack != nil {
+		syntax.Fdump(os.Stderr, body)
+	}
+}
+
+// inspect is a callback for syntax.Inspect that drives the actual rewriting.
+// If it sees a func literal, it kicks off a separate rewrite for that literal.
+// Otherwise, it maintains a stack of range-over-func loops and
+// converts each in turn.
+func (r *rewriter) inspect(n syntax.Node) bool {
+	switch n := n.(type) {
+	case *syntax.FuncLit:
+		rewriteFunc(r.pkg, r.info, n.Type, n.Body)
+		return false
+
+	default:
+		// Push n onto stack.
+		r.stack = append(r.stack, n)
+		if nfor, ok := forRangeFunc(n); ok {
+			loop := &forLoop{nfor: nfor}
+			r.forStack = append(r.forStack, loop)
+			r.startLoop(loop)
+		}
+
+	case nil:
+		// n == nil signals that we are done visiting
+		// the top-of-stack node's children. Find it.
+		n = r.stack[len(r.stack)-1]
+
+		// If we are inside a range-over-func,
+		// take this moment to replace any break/continue/goto/return
+		// statements directly contained in this node.
+		// Also replace any converted for statements
+		// with the rewritten block.
+		switch n := n.(type) {
+		case *syntax.BlockStmt:
+			for i, s := range n.List {
+				n.List[i] = r.editStmt(s)
+			}
+		case *syntax.CaseClause:
+			for i, s := range n.Body {
+				n.Body[i] = r.editStmt(s)
+			}
+		case *syntax.CommClause:
+			for i, s := range n.Body {
+				n.Body[i] = r.editStmt(s)
+			}
+		case *syntax.LabeledStmt:
+			n.Stmt = r.editStmt(n.Stmt)
+		}
+
+		// Pop n.
+		if len(r.forStack) > 0 && r.stack[len(r.stack)-1] == r.forStack[len(r.forStack)-1].nfor {
+			r.endLoop(r.forStack[len(r.forStack)-1])
+			r.forStack = r.forStack[:len(r.forStack)-1]
+		}
+		r.stack = r.stack[:len(r.stack)-1]
+	}
+	return true
+}
+
+// startLoop sets up for converting a range-over-func loop.
+func (r *rewriter) startLoop(loop *forLoop) {
+	// For first loop in function, allocate syntax for any, bool, int, true, and false.
+	if r.anyName == nil {
+		pos := loop.nfor.Pos()
+		r.anyName, r.anyObj = r.builtinType(pos, "any")
+		r.boolName, r.boolObj = r.builtinType(pos, "bool")
+		r.intName, r.intObj = r.builtinType(pos, "int")
+		r.trueName, _ = r.builtinConst(pos, "true")
+		r.falseName, _ = r.builtinConst(pos, "false")
+		r.rewritten = make(map[*syntax.ForStmt]syntax.Stmt)
+	}
+}
+
+// editStmt returns the replacement for the statement x,
+// or x itself if it should be left alone.
+// This includes the for loops we are converting,
+// as left in x.rewritten by r.endLoop.
+func (r *rewriter) editStmt(x syntax.Stmt) syntax.Stmt {
+	if x, ok := x.(*syntax.ForStmt); ok {
+		if s := r.rewritten[x]; s != nil {
+			return s
+		}
+	}
+
+	if len(r.forStack) > 0 {
+		switch x := x.(type) {
+		case *syntax.BranchStmt:
+			return r.editBranch(x)
+		case *syntax.CallStmt:
+			if x.Tok == syntax.Defer {
+				return r.editDefer(x)
+			}
+		case *syntax.ReturnStmt:
+			return r.editReturn(x)
+		}
+	}
+
+	return x
+}
+
+// editDefer returns the replacement for the defer statement x.
+// See the "Defers" section in the package doc comment above for more context.
+func (r *rewriter) editDefer(x *syntax.CallStmt) syntax.Stmt {
+	if r.defers == nil {
+		// Declare and initialize the #defers token.
+		init := &syntax.CallExpr{
+			Fun: runtimeSym(r.info, "deferrangefunc"),
+		}
+		stv := syntax.TypeAndValue{Type: r.anyObj.Type()}
+		stv.SetIsValue()
+		init.SetTypeInfo(stv)
+		r.defers, _ = r.declVar("#defers", r.anyName, init)
+	}
+
+	// Attach the token as an "extra" argument to the defer.
+	x.DeferAt = r.useVar(r.defers)
+	setPos(x.DeferAt, x.Pos())
+	return x
+}
+
+// editReturn returns the replacement for the return statement x.
+// See the "Return" section in the package doc comment above for more context.
+func (r *rewriter) editReturn(x *syntax.ReturnStmt) syntax.Stmt {
+	// #next = -1 is return with no arguments; -2 is return with arguments.
+	var next int
+	if x.Results == nil {
+		next = -1
+		r.forStack[0].checkRet = true
+	} else {
+		next = -2
+		r.forStack[0].checkRetArgs = true
+	}
+
+	// Tell the loops along the way to check for a return.
+	for _, loop := range r.forStack[1:] {
+		loop.checkRet = true
+	}
+
+	// Assign results, set #next, and return false.
+	bl := &syntax.BlockStmt{}
+	if x.Results != nil {
+		if r.retNames == nil {
+			for i, a := range r.outer.ResultList {
+				n, _ := r.declVar(fmt.Sprintf("#r%d", i+1), a.Type, nil)
+				r.retNames = append(r.retNames, n)
+			}
+		}
+		bl.List = append(bl.List, &syntax.AssignStmt{Lhs: r.useList(r.retNames), Rhs: x.Results})
+	}
+	bl.List = append(bl.List, &syntax.AssignStmt{Lhs: r.useVar(r.next()), Rhs: r.intConst(next)})
+	bl.List = append(bl.List, &syntax.ReturnStmt{Results: r.useVar(r.falseName)})
+	setPos(bl, x.Pos())
+	return bl
+}
+
+// editBranch returns the replacement for the branch statement x,
+// or x itself if it should be left alone.
+// See the package doc comment above for more context.
+func (r *rewriter) editBranch(x *syntax.BranchStmt) syntax.Stmt {
+	if x.Tok == syntax.Fallthrough {
+		// Fallthrough is unaffected by the rewrite.
+		return x
+	}
+
+	// Find target of break/continue/goto in r.forStack.
+	// (The target may not be in r.forStack at all.)
+	targ := x.Target
+	i := len(r.forStack) - 1
+	for i >= 0 && r.forStack[i].nfor != targ {
+		i--
+	}
+	if i < 0 && x.Label == nil {
+		// Unlabeled break or continue that's not nfor must be inside nfor. Leave alone.
+		return x
+	}
+
+	// Compute the value to assign to #next and the specific return to use.
+	var next int
+	var ret *syntax.ReturnStmt
+	if x.Tok == syntax.Goto || i < 0 {
+		// goto Label
+		// or break/continue of labeled non-range-over-func loop.
+		// We may be able to leave it alone, or we may have to break
+		// out of one or more nested loops and then use #next to signal
+		// to complete the break/continue/goto.
+		// Figure out which range-over-func loop contains the label.
+		r.computeBranchNext()
+		nfor := r.forStack[len(r.forStack)-1].nfor
+		label := x.Label.Value
+		targ := r.labelLoop[label]
+		if nfor == targ {
+			// Label is in the current func literal; use it directly.
+			return x
+		}
+
+		// Set #next to the code meaning break/continue/goto label.
+		next = r.branchNext[branch{x.Tok, label}]
+
+		// Break out of nested loops up to targ.
+		i := len(r.forStack) - 1
+		for i >= 0 && r.forStack[i].nfor != targ {
+			i--
+		}
+		i++ // skip loop containing targ or, when i=-1, outermost func body
+
+		// Mark loop we exit to get to targ to check for that branch.
+		top := r.forStack[i]
+		top.checkBranch = append(top.checkBranch, branch{x.Tok, label})
+
+		// Mark loops along the way to check for a plain return, so they break.
+		for i++; i < len(r.forStack); i++ {
+			r.forStack[i].checkRet = true
+		}
+
+		// In the innermost loop, use a plain "return false".
+		ret = &syntax.ReturnStmt{Results: r.useVar(r.falseName)}
+	} else {
+		// break/continue of labeled non-range-over-func loop.
+		depth := len(r.forStack) - 1 - i
+
+		// For continue of innermost loop, use "return true".
+		// Otherwise we are breaking the innermost loop, so "return false".
+		retVal := r.falseName
+		if depth == 0 && x.Tok == syntax.Continue {
+			retVal = r.trueName
+		}
+		ret = &syntax.ReturnStmt{Results: r.useVar(retVal)}
+
+		// If we're only operating on the innermost loop, the return is all we need.
+		if depth == 0 {
+			setPos(ret, x.Pos())
+			return ret
+		}
+
+		// The loop inside the one we are break/continue-ing
+		// needs to make that happen when we break out of it.
+		if x.Tok == syntax.Continue {
+			r.forStack[i+1].checkContinue = true
+		} else {
+			r.forStack[i+1].checkBreak = true
+		}
+
+		// The loops along the way just need to break.
+		for j := i + 2; j < len(r.forStack); j++ {
+			r.forStack[j].checkBreak = true
+		}
+
+		// Set next to break the appropriate number of times;
+		// the final time may be a continue, not a break.
+		next = 2 * depth
+		if x.Tok == syntax.Continue {
+			next--
+		}
+	}
+
+	// Assign #next = next and do the return.
+	as := &syntax.AssignStmt{Lhs: r.useVar(r.next()), Rhs: r.intConst(next)}
+	bl := &syntax.BlockStmt{
+		List: []syntax.Stmt{as, ret},
+	}
+	setPos(bl, x.Pos())
+	return bl
+}
+
+// computeBranchNext computes the branchNext numbering
+// and determines which labels end up inside which range-over-func loop bodies.
+func (r *rewriter) computeBranchNext() {
+	if r.labelLoop != nil {
+		return
+	}
+
+	r.labelLoop = make(map[string]*syntax.ForStmt)
+	r.branchNext = make(map[branch]int)
+
+	var labels []string
+	var stack []syntax.Node
+	var forStack []*syntax.ForStmt
+	forStack = append(forStack, nil)
+	syntax.Inspect(r.body, func(n syntax.Node) bool {
+		if n != nil {
+			stack = append(stack, n)
+			if nfor, ok := forRangeFunc(n); ok {
+				forStack = append(forStack, nfor)
+			}
+			if n, ok := n.(*syntax.LabeledStmt); ok {
+				l := n.Label.Value
+				labels = append(labels, l)
+				f := forStack[len(forStack)-1]
+				r.labelLoop[l] = f
+			}
+		} else {
+			n := stack[len(stack)-1]
+			stack = stack[:len(stack)-1]
+			if n == forStack[len(forStack)-1] {
+				forStack = forStack[:len(forStack)-1]
+			}
+		}
+		return true
+	})
+
+	// Assign numbers to all the labels we observed.
+	used := -2
+	for _, l := range labels {
+		used -= 3
+		r.branchNext[branch{syntax.Break, l}] = used
+		r.branchNext[branch{syntax.Continue, l}] = used + 1
+		r.branchNext[branch{syntax.Goto, l}] = used + 2
+	}
+}
+
+// endLoop finishes the conversion of a range-over-func loop.
+// We have inspected and rewritten the body of the loop and can now
+// construct the body function and rewrite the for loop into a call
+// bracketed by any declarations and checks it requires.
+func (r *rewriter) endLoop(loop *forLoop) {
+	// Pick apart for range X { ... }
+	nfor := loop.nfor
+	start, end := nfor.Pos(), nfor.Body.Rbrace // start, end position of for loop
+	rclause := nfor.Init.(*syntax.RangeClause)
+	rfunc := types2.CoreType(rclause.X.GetTypeInfo().Type).(*types2.Signature) // type of X - func(func(...)bool)
+	ftyp := rfunc.Params().At(0).Type().(*types2.Signature)                    // func(...) bool
+
+	// Build X(bodyFunc)
+	call := &syntax.ExprStmt{
+		X: &syntax.CallExpr{
+			Fun: rclause.X,
+			ArgList: []syntax.Expr{
+				r.bodyFunc(nfor.Body.List, rclause.Lhs, rclause.Def, ftyp, start, end),
+			},
+		},
+	}
+	setPos(call, start)
+
+	// Build checks based on #next after X(bodyFunc)
+	checks := r.checks(loop, end)
+
+	// Rewrite for vars := range X { ... } to
+	//
+	//	{
+	//		r.declStmt
+	//		call
+	//		checks
+	//	}
+	//
+	// The r.declStmt can be added to by this loop or any inner loop
+	// during the creation of r.bodyFunc; it is only emitted in the outermost
+	// converted range loop.
+	block := &syntax.BlockStmt{Rbrace: end}
+	setPos(block, start)
+	if len(r.forStack) == 1 && r.declStmt != nil {
+		setPos(r.declStmt, start)
+		block.List = append(block.List, r.declStmt)
+	}
+	block.List = append(block.List, call)
+	block.List = append(block.List, checks...)
+
+	if len(r.forStack) == 1 { // ending an outermost loop
+		r.declStmt = nil
+		r.nextName = nil
+		r.retNames = nil
+		r.defers = nil
+	}
+
+	r.rewritten[nfor] = block
+}
+
+// bodyFunc converts the loop body (control flow has already been updated)
+// to a func literal that can be passed to the range function.
+//
+// vars is the range variables from the range statement.
+// def indicates whether this is a := range statement.
+// ftyp is the type of the function we are creating
+// start and end are the syntax positions to use for new nodes
+// that should be at the start or end of the loop.
+func (r *rewriter) bodyFunc(body []syntax.Stmt, vars syntax.Expr, def bool, ftyp *types2.Signature, start, end syntax.Pos) *syntax.FuncLit {
+	// Starting X(bodyFunc); build up bodyFunc first.
+	var params, results []*types2.Var
+	results = append(results, types2.NewVar(start, nil, "", r.boolObj.Type()))
+	bodyFunc := &syntax.FuncLit{
+		Type: &syntax.FuncType{
+			ParamList:  []*syntax.Field{},
+			ResultList: []*syntax.Field{{Type: r.boolName}},
+		},
+		Body: &syntax.BlockStmt{
+			List:   []syntax.Stmt{},
+			Rbrace: end,
+		},
+	}
+	setPos(bodyFunc, start)
+
+	// Arguments to function are exactly the range variables
+	// with := and initialize the range variables otherwise.
+	addParam := func(x syntax.Expr, i int) {
+		typ := ftyp.Params().At(i).Type()
+		typName := declType(start, fmt.Sprintf("#t%d", i+1), typ)
+
+		var paramName *syntax.Name
+		var paramVar *types2.Var
+		if def && x != nil {
+			// Reuse range variable as parameter.
+			paramName = x.(*syntax.Name)
+			paramVar = r.info.Defs[paramName].(*types2.Var)
+		} else {
+			// Declare new parameter and assign it to range expression.
+			paramName, paramVar = r.newVar(start, fmt.Sprintf("#p%d", 1+len(bodyFunc.Type.ParamList)), typ)
+			if x != nil {
+				as := &syntax.AssignStmt{Lhs: x, Rhs: r.useVar(paramName)}
+				as.SetPos(x.Pos())
+				setPos(as.Rhs, x.Pos())
+				bodyFunc.Body.List = append(bodyFunc.Body.List, as)
+			}
+		}
+		f := &syntax.Field{
+			Name: paramName,
+			Type: typName,
+		}
+		f.SetPos(start)
+		bodyFunc.Type.ParamList = append(bodyFunc.Type.ParamList, f)
+		params = append(params, paramVar)
+	}
+	if vars != nil {
+		if list, ok := vars.(*syntax.ListExpr); ok {
+			for i, x := range list.ElemList {
+				addParam(x, i)
+			}
+		} else {
+			addParam(vars, 0)
+		}
+	}
+	for len(params) < ftyp.Params().Len() {
+		addParam(nil, len(params))
+	}
+
+	stv := syntax.TypeAndValue{
+		Type: types2.NewSignatureType(nil, nil, nil,
+			types2.NewTuple(params...),
+			types2.NewTuple(results...),
+			false),
+	}
+	stv.SetIsValue()
+	bodyFunc.SetTypeInfo(stv)
+
+	// Original loop body (already rewritten by editStmt during inspect).
+	bodyFunc.Body.List = append(bodyFunc.Body.List, body...)
+
+	// return true to continue at end of loop body
+	ret := &syntax.ReturnStmt{Results: r.trueName}
+	ret.SetPos(end)
+	bodyFunc.Body.List = append(bodyFunc.Body.List, ret)
+
+	return bodyFunc
+}
+
+// checks returns the post-call checks that need to be done for the given loop.
+func (r *rewriter) checks(loop *forLoop, pos syntax.Pos) []syntax.Stmt {
+	var list []syntax.Stmt
+	if len(loop.checkBranch) > 0 {
+		did := make(map[branch]bool)
+		for _, br := range loop.checkBranch {
+			if did[br] {
+				continue
+			}
+			did[br] = true
+			doBranch := &syntax.BranchStmt{Tok: br.tok, Label: &syntax.Name{Value: br.label}}
+			list = append(list, r.ifNext(syntax.Eql, r.branchNext[br], doBranch))
+		}
+	}
+	if len(r.forStack) == 1 {
+		if loop.checkRetArgs {
+			list = append(list, r.ifNext(syntax.Eql, -2, retStmt(r.useList(r.retNames))))
+		}
+		if loop.checkRet {
+			list = append(list, r.ifNext(syntax.Eql, -1, retStmt(nil)))
+		}
+	} else {
+		if loop.checkRetArgs || loop.checkRet {
+			// Note: next < 0 also handles gotos handled by outer loops.
+			// We set checkRet in that case to trigger this check.
+			list = append(list, r.ifNext(syntax.Lss, 0, retStmt(r.useVar(r.falseName))))
+		}
+		if loop.checkBreak {
+			list = append(list, r.ifNext(syntax.Geq, 2, retStmt(r.useVar(r.falseName))))
+		}
+		if loop.checkContinue {
+			list = append(list, r.ifNext(syntax.Eql, 1, retStmt(r.useVar(r.trueName))))
+		}
+	}
+
+	for _, j := range list {
+		setPos(j, pos)
+	}
+	return list
+}
+
+// retStmt returns a return statement returning the given return values.
+func retStmt(results syntax.Expr) *syntax.ReturnStmt {
+	return &syntax.ReturnStmt{Results: results}
+}
+
+// ifNext returns the statement:
+//
+//	if #next op c { adjust; then }
+//
+// When op is >=, adjust is #next -= c.
+// When op is == and c is not -1 or -2, adjust is #next = 0.
+// Otherwise adjust is omitted.
+func (r *rewriter) ifNext(op syntax.Operator, c int, then syntax.Stmt) syntax.Stmt {
+	nif := &syntax.IfStmt{
+		Cond: &syntax.Operation{Op: op, X: r.useVar(r.next()), Y: r.intConst(c)},
+		Then: &syntax.BlockStmt{
+			List: []syntax.Stmt{then},
+		},
+	}
+
+	if op == syntax.Geq {
+		sub := &syntax.AssignStmt{
+			Op:  syntax.Sub,
+			Lhs: r.useVar(r.next()),
+			Rhs: r.intConst(c),
+		}
+		nif.Then.List = []syntax.Stmt{sub, then}
+	}
+	if op == syntax.Eql && c != -1 && c != -2 {
+		clr := &syntax.AssignStmt{
+			Lhs: r.useVar(r.next()),
+			Rhs: r.intConst(0),
+		}
+		nif.Then.List = []syntax.Stmt{clr, then}
+	}
+
+	return nif
+}
+
+// next returns a reference to the #next variable.
+func (r *rewriter) next() *syntax.Name {
+	if r.nextName == nil {
+		r.nextName, _ = r.declVar("#next", r.intName, nil)
+	}
+	return r.useVar(r.nextName)
+}
+
+// forRangeFunc checks whether n is a range-over-func.
+// If so, it returns n.(*syntax.ForStmt), true.
+// Otherwise it returns nil, false.
+func forRangeFunc(n syntax.Node) (*syntax.ForStmt, bool) {
+	nfor, ok := n.(*syntax.ForStmt)
+	if !ok {
+		return nil, false
+	}
+	nrange, ok := nfor.Init.(*syntax.RangeClause)
+	if !ok {
+		return nil, false
+	}
+	_, ok = types2.CoreType(nrange.X.GetTypeInfo().Type).(*types2.Signature)
+	if !ok {
+		return nil, false
+	}
+	return nfor, true
+}
+
+// builtinType returns references to the builtin type with the given name.
+func (r *rewriter) builtinType(pos syntax.Pos, name string) (*syntax.Name, types2.Object) {
+	obj := types2.Universe.Lookup(name)
+	n := syntax.NewName(pos, name)
+	stv := syntax.TypeAndValue{Type: obj.Type()}
+	n.SetTypeInfo(stv)
+	r.info.Uses[n] = obj
+	return n, obj
+}
+
+// builtinConst returns references to the builtin constant with the given name.
+func (r *rewriter) builtinConst(pos syntax.Pos, name string) (*syntax.Name, types2.Object) {
+	obj := types2.Universe.Lookup(name)
+	n := syntax.NewName(pos, name)
+	stv := syntax.TypeAndValue{Type: obj.Type(), Value: obj.(*types2.Const).Val()}
+	stv.SetIsValue()
+	n.SetTypeInfo(stv)
+	r.info.Uses[n] = obj
+	return n, obj
+}
+
+// intConst returns syntax for an integer literal with the given value.
+func (r *rewriter) intConst(c int) *syntax.BasicLit {
+	lit := &syntax.BasicLit{
+		Value: fmt.Sprint(c),
+		Kind:  syntax.IntLit,
+	}
+	stv := syntax.TypeAndValue{Type: r.intObj.Type(), Value: constant.MakeInt64(int64(c))}
+	stv.SetIsValue()
+	lit.SetTypeInfo(stv)
+	return lit
+}
+
+// useVar returns syntax for a reference to decl, which should be its declaration.
+func (r *rewriter) useVar(decl *syntax.Name) *syntax.Name {
+	obj := r.info.Uses[decl]
+	n := syntax.NewName(nopos, decl.Value)
+	stv := syntax.TypeAndValue{Type: obj.Type()}
+	stv.SetIsValue()
+	n.SetTypeInfo(stv)
+	r.info.Uses[n] = obj
+	return n
+}
+
+// useList is useVar for a list of decls.
+func (r *rewriter) useList(decls []*syntax.Name) syntax.Expr {
+	var new []syntax.Expr
+	for _, decl := range decls {
+		new = append(new, r.useVar(decl))
+	}
+	if len(new) == 1 {
+		return new[0]
+	}
+	return &syntax.ListExpr{ElemList: new}
+}
+
+// newVar declares a new variable with the given name and type.
+// The returned name must be added to a declaration list.
+func (r *rewriter) newVar(pos syntax.Pos, name string, typ types2.Type) (*syntax.Name, *types2.Var) {
+	obj := types2.NewVar(pos, r.pkg, name, typ)
+	n := syntax.NewName(pos, name)
+	stv := syntax.TypeAndValue{Type: typ}
+	stv.SetIsValue()
+	n.SetTypeInfo(stv)
+	r.info.Uses[n] = obj
+	return n, obj
+}
+
+// declVar declares a variable with a given name type and initializer value.
+func (r *rewriter) declVar(name string, typ, init syntax.Expr) (*syntax.Name, *types2.Var) {
+	if r.declStmt == nil {
+		r.declStmt = &syntax.DeclStmt{}
+	}
+	stmt := r.declStmt
+	n, obj := r.newVar(stmt.Pos(), name, typ.GetTypeInfo().Type)
+	r.info.Defs[n] = obj
+	stmt.DeclList = append(stmt.DeclList, &syntax.VarDecl{
+		NameList: []*syntax.Name{n},
+		Type:     typ,
+		Values:   init,
+	})
+	return n, obj
+}
+
+// declType declares a type with the given name and type.
+// This is more like "type name = typ" than "type name typ".
+func declType(pos syntax.Pos, name string, typ types2.Type) *syntax.Name {
+	n := syntax.NewName(pos, name)
+	n.SetTypeInfo(syntax.TypeAndValue{Type: typ})
+	return n
+}
+
+// runtimePkg is a fake runtime package that contains what we need to refer to in package runtime.
+var runtimePkg = func() *types2.Package {
+	var nopos syntax.Pos
+	pkg := types2.NewPackage("runtime", "runtime")
+	anyType := types2.Universe.Lookup("any").Type()
+
+	// func deferrangefunc() unsafe.Pointer
+	obj := types2.NewVar(nopos, pkg, "deferrangefunc", types2.NewSignatureType(nil, nil, nil, nil, types2.NewTuple(types2.NewParam(nopos, pkg, "extra", anyType)), false))
+	pkg.Scope().Insert(obj)
+
+	return pkg
+}()
+
+// runtimeSym returns a reference to a symbol in the fake runtime package.
+func runtimeSym(info *types2.Info, name string) *syntax.Name {
+	obj := runtimePkg.Scope().Lookup(name)
+	n := syntax.NewName(nopos, "runtime."+name)
+	stv := syntax.TypeAndValue{Type: obj.Type()}
+	stv.SetIsValue()
+	n.SetTypeInfo(stv)
+	info.Uses[n] = obj
+	return n
+}
+
+// setPos walks the top structure of x that has no position assigned
+// and assigns it all to have positon pos.
+// When setPos encounters a syntax node with a position assigned,
+// setPos does not look inside that node.
+// setPos only needs to handle syntax we create in this package;
+// all other syntax should have positions assigned already.
+func setPos(x syntax.Node, pos syntax.Pos) {
+	if x == nil || x.Pos() != nopos {
+		return
+	}
+	x.SetPos(pos)
+	switch x := x.(type) {
+	default:
+		panic(fmt.Sprintf("setPos(%T)", x))
+	case *syntax.AssignStmt:
+		setPos(x.Lhs, pos)
+		setPos(x.Rhs, pos)
+	case *syntax.BasicLit:
+		// nothing
+	case *syntax.BlockStmt:
+		for _, s := range x.List {
+			setPos(s, pos)
+		}
+		if x.Rbrace != nopos {
+			x.Rbrace = pos
+		}
+	case *syntax.BranchStmt:
+		if x.Label != nil {
+			setPos(x.Label, pos)
+		}
+	case *syntax.CallExpr:
+		setPos(x.Fun, pos)
+		for _, a := range x.ArgList {
+			setPos(a, pos)
+		}
+	case *syntax.DeclStmt:
+		for _, d := range x.DeclList {
+			setPos(d, pos)
+		}
+	case *syntax.ExprStmt:
+		setPos(x.X, pos)
+	case *syntax.Field:
+		if x.Name != nil {
+			setPos(x.Name, pos)
+		}
+		setPos(x.Type, pos)
+	case *syntax.FuncLit:
+		setPos(x.Type, pos)
+		setPos(x.Body, pos)
+	case *syntax.FuncType:
+		for _, f := range x.ParamList {
+			setPos(f, pos)
+		}
+		for _, f := range x.ResultList {
+			setPos(f, pos)
+		}
+	case *syntax.IfStmt:
+		setPos(x.Init, pos)
+		setPos(x.Cond, pos)
+		setPos(x.Then, pos)
+		setPos(x.Else, pos)
+	case *syntax.ListExpr:
+		for _, x := range x.ElemList {
+			setPos(x, pos)
+		}
+	case *syntax.Name:
+		// nothing
+	case *syntax.Operation:
+		setPos(x.X, pos)
+		setPos(x.Y, pos)
+	case *syntax.ReturnStmt:
+		setPos(x.Results, pos)
+	case *syntax.VarDecl:
+		for _, n := range x.NameList {
+			setPos(n, pos)
+		}
+		setPos(x.Type, pos)
+		setPos(x.Values, pos)
+	}
+}
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index ed32fa7..10d54d8 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -100,6 +100,7 @@
 	ir.Syms.CgoCheckPtrWrite = typecheck.LookupRuntimeFunc("cgoCheckPtrWrite")
 	ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignment")
 	ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc")
+	ir.Syms.Deferprocat = typecheck.LookupRuntimeFunc("deferprocat")
 	ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack")
 	ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn")
 	ir.Syms.Duffcopy = typecheck.LookupRuntimeFunc("duffcopy")
@@ -1480,10 +1481,10 @@
 			s.openDeferRecord(n.Call.(*ir.CallExpr))
 		} else {
 			d := callDefer
-			if n.Esc() == ir.EscNever {
+			if n.Esc() == ir.EscNever && n.DeferAt == nil {
 				d = callDeferStack
 			}
-			s.callResult(n.Call.(*ir.CallExpr), d)
+			s.call(n.Call.(*ir.CallExpr), d, false, n.DeferAt)
 		}
 	case ir.OGO:
 		n := n.(*ir.GoDeferStmt)
@@ -5141,20 +5142,21 @@
 }
 
 func (s *state) callResult(n *ir.CallExpr, k callKind) *ssa.Value {
-	return s.call(n, k, false)
+	return s.call(n, k, false, nil)
 }
 
 func (s *state) callAddr(n *ir.CallExpr, k callKind) *ssa.Value {
-	return s.call(n, k, true)
+	return s.call(n, k, true, nil)
 }
 
 // Calls the function n using the specified call type.
 // Returns the address of the return value (or nil if none).
-func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Value {
+func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool, deferExtra ir.Expr) *ssa.Value {
 	s.prevCall = nil
 	var callee *ir.Name    // target function (if static)
 	var closure *ssa.Value // ptr to closure to run (if dynamic)
 	var codeptr *ssa.Value // ptr to target code (if dynamic)
+	var dextra *ssa.Value  // defer extra arg
 	var rcvr *ssa.Value    // receiver to set
 	fn := n.X
 	var ACArgs []*types.Type    // AuxCall args
@@ -5210,6 +5212,9 @@
 			closure = iclosure
 		}
 	}
+	if deferExtra != nil {
+		dextra = s.expr(deferExtra)
+	}
 
 	params := callABI.ABIAnalyze(n.X.Type(), false /* Do not set (register) nNames from caller side -- can cause races. */)
 	types.CalcSize(fn.Type())
@@ -5259,6 +5264,13 @@
 			callArgs = append(callArgs, closure)
 			stksize += int64(types.PtrSize)
 			argStart += int64(types.PtrSize)
+			if dextra != nil {
+				// Extra token of type any for deferproc
+				ACArgs = append(ACArgs, types.Types[types.TINTER])
+				callArgs = append(callArgs, dextra)
+				stksize += 2 * int64(types.PtrSize)
+				argStart += 2 * int64(types.PtrSize)
+			}
 		}
 
 		// Set receiver (for interface calls).
@@ -5294,11 +5306,15 @@
 		// call target
 		switch {
 		case k == callDefer:
-			aux := ssa.StaticAuxCall(ir.Syms.Deferproc, s.f.ABIDefault.ABIAnalyzeTypes(nil, ACArgs, ACResults)) // TODO paramResultInfo for DeferProc
+			sym := ir.Syms.Deferproc
+			if dextra != nil {
+				sym = ir.Syms.Deferprocat
+			}
+			aux := ssa.StaticAuxCall(sym, s.f.ABIDefault.ABIAnalyzeTypes(nil, ACArgs, ACResults)) // TODO paramResultInfo for Deferproc
 			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
 		case k == callGo:
 			aux := ssa.StaticAuxCall(ir.Syms.Newproc, s.f.ABIDefault.ABIAnalyzeTypes(nil, ACArgs, ACResults))
-			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) // TODO paramResultInfo for NewProc
+			call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) // TODO paramResultInfo for Newproc
 		case closure != nil:
 			// rawLoad because loading the code pointer from a
 			// closure is always safe, but IsSanitizerSafeAddr
diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go
index 6580f05..de277fc 100644
--- a/src/cmd/compile/internal/syntax/nodes.go
+++ b/src/cmd/compile/internal/syntax/nodes.go
@@ -17,6 +17,7 @@
 	//    associated with that production; usually the left-most one
 	//    ('[' for IndexExpr, 'if' for IfStmt, etc.)
 	Pos() Pos
+	SetPos(Pos)
 	aNode()
 }
 
@@ -26,8 +27,9 @@
 	pos Pos
 }
 
-func (n *node) Pos() Pos { return n.pos }
-func (*node) aNode()     {}
+func (n *node) Pos() Pos       { return n.pos }
+func (n *node) SetPos(pos Pos) { n.pos = pos }
+func (*node) aNode()           {}
 
 // ----------------------------------------------------------------------------
 // Files
@@ -389,8 +391,9 @@
 	}
 
 	CallStmt struct {
-		Tok  token // Go or Defer
-		Call Expr
+		Tok     token // Go or Defer
+		Call    Expr
+		DeferAt Expr // argument to runtime.deferprocat
 		stmt
 	}
 
diff --git a/src/cmd/compile/internal/syntax/tokens.go b/src/cmd/compile/internal/syntax/tokens.go
index 6dece1a..b08f699 100644
--- a/src/cmd/compile/internal/syntax/tokens.go
+++ b/src/cmd/compile/internal/syntax/tokens.go
@@ -4,7 +4,9 @@
 
 package syntax
 
-type token uint
+type Token uint
+
+type token = Token
 
 //go:generate stringer -type token -linecomment tokens.go
 
diff --git a/src/cmd/compile/internal/typecheck/_builtin/runtime.go b/src/cmd/compile/internal/typecheck/_builtin/runtime.go
index 2e1e94b..54a2b71 100644
--- a/src/cmd/compile/internal/typecheck/_builtin/runtime.go
+++ b/src/cmd/compile/internal/typecheck/_builtin/runtime.go
@@ -117,6 +117,9 @@
 func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
 func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
 
+// defer in range over func
+func deferrangefunc() interface{}
+
 func fastrand() uint32
 
 // *byte is really *runtime.Type
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
index bf87b4d..01e97a4 100644
--- a/src/cmd/compile/internal/typecheck/builtin.go
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -103,129 +103,130 @@
 	{"panicnildottype", funcTag, 72},
 	{"ifaceeq", funcTag, 73},
 	{"efaceeq", funcTag, 73},
-	{"fastrand", funcTag, 74},
-	{"makemap64", funcTag, 76},
-	{"makemap", funcTag, 77},
-	{"makemap_small", funcTag, 78},
-	{"mapaccess1", funcTag, 79},
-	{"mapaccess1_fast32", funcTag, 80},
-	{"mapaccess1_fast64", funcTag, 81},
-	{"mapaccess1_faststr", funcTag, 82},
-	{"mapaccess1_fat", funcTag, 83},
-	{"mapaccess2", funcTag, 84},
-	{"mapaccess2_fast32", funcTag, 85},
-	{"mapaccess2_fast64", funcTag, 86},
-	{"mapaccess2_faststr", funcTag, 87},
-	{"mapaccess2_fat", funcTag, 88},
-	{"mapassign", funcTag, 79},
-	{"mapassign_fast32", funcTag, 80},
-	{"mapassign_fast32ptr", funcTag, 89},
-	{"mapassign_fast64", funcTag, 81},
-	{"mapassign_fast64ptr", funcTag, 89},
-	{"mapassign_faststr", funcTag, 82},
-	{"mapiterinit", funcTag, 90},
-	{"mapdelete", funcTag, 90},
-	{"mapdelete_fast32", funcTag, 91},
-	{"mapdelete_fast64", funcTag, 92},
-	{"mapdelete_faststr", funcTag, 93},
-	{"mapiternext", funcTag, 94},
-	{"mapclear", funcTag, 95},
-	{"makechan64", funcTag, 97},
-	{"makechan", funcTag, 98},
-	{"chanrecv1", funcTag, 100},
-	{"chanrecv2", funcTag, 101},
-	{"chansend1", funcTag, 103},
+	{"deferrangefunc", funcTag, 74},
+	{"fastrand", funcTag, 75},
+	{"makemap64", funcTag, 77},
+	{"makemap", funcTag, 78},
+	{"makemap_small", funcTag, 79},
+	{"mapaccess1", funcTag, 80},
+	{"mapaccess1_fast32", funcTag, 81},
+	{"mapaccess1_fast64", funcTag, 82},
+	{"mapaccess1_faststr", funcTag, 83},
+	{"mapaccess1_fat", funcTag, 84},
+	{"mapaccess2", funcTag, 85},
+	{"mapaccess2_fast32", funcTag, 86},
+	{"mapaccess2_fast64", funcTag, 87},
+	{"mapaccess2_faststr", funcTag, 88},
+	{"mapaccess2_fat", funcTag, 89},
+	{"mapassign", funcTag, 80},
+	{"mapassign_fast32", funcTag, 81},
+	{"mapassign_fast32ptr", funcTag, 90},
+	{"mapassign_fast64", funcTag, 82},
+	{"mapassign_fast64ptr", funcTag, 90},
+	{"mapassign_faststr", funcTag, 83},
+	{"mapiterinit", funcTag, 91},
+	{"mapdelete", funcTag, 91},
+	{"mapdelete_fast32", funcTag, 92},
+	{"mapdelete_fast64", funcTag, 93},
+	{"mapdelete_faststr", funcTag, 94},
+	{"mapiternext", funcTag, 95},
+	{"mapclear", funcTag, 96},
+	{"makechan64", funcTag, 98},
+	{"makechan", funcTag, 99},
+	{"chanrecv1", funcTag, 101},
+	{"chanrecv2", funcTag, 102},
+	{"chansend1", funcTag, 104},
 	{"closechan", funcTag, 30},
-	{"writeBarrier", varTag, 105},
-	{"typedmemmove", funcTag, 106},
-	{"typedmemclr", funcTag, 107},
-	{"typedslicecopy", funcTag, 108},
-	{"selectnbsend", funcTag, 109},
-	{"selectnbrecv", funcTag, 110},
-	{"selectsetpc", funcTag, 111},
-	{"selectgo", funcTag, 112},
+	{"writeBarrier", varTag, 106},
+	{"typedmemmove", funcTag, 107},
+	{"typedmemclr", funcTag, 108},
+	{"typedslicecopy", funcTag, 109},
+	{"selectnbsend", funcTag, 110},
+	{"selectnbrecv", funcTag, 111},
+	{"selectsetpc", funcTag, 112},
+	{"selectgo", funcTag, 113},
 	{"block", funcTag, 9},
-	{"makeslice", funcTag, 113},
-	{"makeslice64", funcTag, 114},
-	{"makeslicecopy", funcTag, 115},
-	{"growslice", funcTag, 117},
-	{"unsafeslicecheckptr", funcTag, 118},
+	{"makeslice", funcTag, 114},
+	{"makeslice64", funcTag, 115},
+	{"makeslicecopy", funcTag, 116},
+	{"growslice", funcTag, 118},
+	{"unsafeslicecheckptr", funcTag, 119},
 	{"panicunsafeslicelen", funcTag, 9},
 	{"panicunsafeslicenilptr", funcTag, 9},
-	{"unsafestringcheckptr", funcTag, 119},
+	{"unsafestringcheckptr", funcTag, 120},
 	{"panicunsafestringlen", funcTag, 9},
 	{"panicunsafestringnilptr", funcTag, 9},
-	{"mulUintptr", funcTag, 120},
-	{"memmove", funcTag, 121},
-	{"memclrNoHeapPointers", funcTag, 122},
-	{"memclrHasPointers", funcTag, 122},
-	{"memequal", funcTag, 123},
-	{"memequal0", funcTag, 124},
-	{"memequal8", funcTag, 124},
-	{"memequal16", funcTag, 124},
-	{"memequal32", funcTag, 124},
-	{"memequal64", funcTag, 124},
-	{"memequal128", funcTag, 124},
-	{"f32equal", funcTag, 125},
-	{"f64equal", funcTag, 125},
-	{"c64equal", funcTag, 125},
-	{"c128equal", funcTag, 125},
-	{"strequal", funcTag, 125},
-	{"interequal", funcTag, 125},
-	{"nilinterequal", funcTag, 125},
-	{"memhash", funcTag, 126},
-	{"memhash0", funcTag, 127},
-	{"memhash8", funcTag, 127},
-	{"memhash16", funcTag, 127},
-	{"memhash32", funcTag, 127},
-	{"memhash64", funcTag, 127},
-	{"memhash128", funcTag, 127},
-	{"f32hash", funcTag, 128},
-	{"f64hash", funcTag, 128},
-	{"c64hash", funcTag, 128},
-	{"c128hash", funcTag, 128},
-	{"strhash", funcTag, 128},
-	{"interhash", funcTag, 128},
-	{"nilinterhash", funcTag, 128},
-	{"int64div", funcTag, 129},
-	{"uint64div", funcTag, 130},
-	{"int64mod", funcTag, 129},
-	{"uint64mod", funcTag, 130},
-	{"float64toint64", funcTag, 131},
-	{"float64touint64", funcTag, 132},
-	{"float64touint32", funcTag, 133},
-	{"int64tofloat64", funcTag, 134},
-	{"int64tofloat32", funcTag, 136},
-	{"uint64tofloat64", funcTag, 137},
-	{"uint64tofloat32", funcTag, 138},
-	{"uint32tofloat64", funcTag, 139},
-	{"complex128div", funcTag, 140},
-	{"getcallerpc", funcTag, 141},
-	{"getcallersp", funcTag, 141},
+	{"mulUintptr", funcTag, 121},
+	{"memmove", funcTag, 122},
+	{"memclrNoHeapPointers", funcTag, 123},
+	{"memclrHasPointers", funcTag, 123},
+	{"memequal", funcTag, 124},
+	{"memequal0", funcTag, 125},
+	{"memequal8", funcTag, 125},
+	{"memequal16", funcTag, 125},
+	{"memequal32", funcTag, 125},
+	{"memequal64", funcTag, 125},
+	{"memequal128", funcTag, 125},
+	{"f32equal", funcTag, 126},
+	{"f64equal", funcTag, 126},
+	{"c64equal", funcTag, 126},
+	{"c128equal", funcTag, 126},
+	{"strequal", funcTag, 126},
+	{"interequal", funcTag, 126},
+	{"nilinterequal", funcTag, 126},
+	{"memhash", funcTag, 127},
+	{"memhash0", funcTag, 128},
+	{"memhash8", funcTag, 128},
+	{"memhash16", funcTag, 128},
+	{"memhash32", funcTag, 128},
+	{"memhash64", funcTag, 128},
+	{"memhash128", funcTag, 128},
+	{"f32hash", funcTag, 129},
+	{"f64hash", funcTag, 129},
+	{"c64hash", funcTag, 129},
+	{"c128hash", funcTag, 129},
+	{"strhash", funcTag, 129},
+	{"interhash", funcTag, 129},
+	{"nilinterhash", funcTag, 129},
+	{"int64div", funcTag, 130},
+	{"uint64div", funcTag, 131},
+	{"int64mod", funcTag, 130},
+	{"uint64mod", funcTag, 131},
+	{"float64toint64", funcTag, 132},
+	{"float64touint64", funcTag, 133},
+	{"float64touint32", funcTag, 134},
+	{"int64tofloat64", funcTag, 135},
+	{"int64tofloat32", funcTag, 137},
+	{"uint64tofloat64", funcTag, 138},
+	{"uint64tofloat32", funcTag, 139},
+	{"uint32tofloat64", funcTag, 140},
+	{"complex128div", funcTag, 141},
+	{"getcallerpc", funcTag, 142},
+	{"getcallersp", funcTag, 142},
 	{"racefuncenter", funcTag, 31},
 	{"racefuncexit", funcTag, 9},
 	{"raceread", funcTag, 31},
 	{"racewrite", funcTag, 31},
-	{"racereadrange", funcTag, 142},
-	{"racewriterange", funcTag, 142},
-	{"msanread", funcTag, 142},
-	{"msanwrite", funcTag, 142},
-	{"msanmove", funcTag, 143},
-	{"asanread", funcTag, 142},
-	{"asanwrite", funcTag, 142},
-	{"checkptrAlignment", funcTag, 144},
-	{"checkptrArithmetic", funcTag, 146},
-	{"libfuzzerTraceCmp1", funcTag, 147},
-	{"libfuzzerTraceCmp2", funcTag, 148},
-	{"libfuzzerTraceCmp4", funcTag, 149},
-	{"libfuzzerTraceCmp8", funcTag, 150},
-	{"libfuzzerTraceConstCmp1", funcTag, 147},
-	{"libfuzzerTraceConstCmp2", funcTag, 148},
-	{"libfuzzerTraceConstCmp4", funcTag, 149},
-	{"libfuzzerTraceConstCmp8", funcTag, 150},
-	{"libfuzzerHookStrCmp", funcTag, 151},
-	{"libfuzzerHookEqualFold", funcTag, 151},
-	{"addCovMeta", funcTag, 153},
+	{"racereadrange", funcTag, 143},
+	{"racewriterange", funcTag, 143},
+	{"msanread", funcTag, 143},
+	{"msanwrite", funcTag, 143},
+	{"msanmove", funcTag, 144},
+	{"asanread", funcTag, 143},
+	{"asanwrite", funcTag, 143},
+	{"checkptrAlignment", funcTag, 145},
+	{"checkptrArithmetic", funcTag, 147},
+	{"libfuzzerTraceCmp1", funcTag, 148},
+	{"libfuzzerTraceCmp2", funcTag, 149},
+	{"libfuzzerTraceCmp4", funcTag, 150},
+	{"libfuzzerTraceCmp8", funcTag, 151},
+	{"libfuzzerTraceConstCmp1", funcTag, 148},
+	{"libfuzzerTraceConstCmp2", funcTag, 149},
+	{"libfuzzerTraceConstCmp4", funcTag, 150},
+	{"libfuzzerTraceConstCmp8", funcTag, 151},
+	{"libfuzzerHookStrCmp", funcTag, 152},
+	{"libfuzzerHookEqualFold", funcTag, 152},
+	{"addCovMeta", funcTag, 154},
 	{"x86HasPOPCNT", varTag, 6},
 	{"x86HasSSE41", varTag, 6},
 	{"x86HasFMA", varTag, 6},
@@ -234,7 +235,7 @@
 }
 
 func runtimeTypes() []*types.Type {
-	var typs [154]*types.Type
+	var typs [155]*types.Type
 	typs[0] = types.ByteType
 	typs[1] = types.NewPtr(typs[0])
 	typs[2] = types.Types[types.TANY]
@@ -309,86 +310,87 @@
 	typs[71] = newSig(params(typs[1], typs[1], typs[1]), nil)
 	typs[72] = newSig(params(typs[1]), nil)
 	typs[73] = newSig(params(typs[57], typs[7], typs[7]), params(typs[6]))
-	typs[74] = newSig(nil, params(typs[62]))
-	typs[75] = types.NewMap(typs[2], typs[2])
-	typs[76] = newSig(params(typs[1], typs[22], typs[3]), params(typs[75]))
-	typs[77] = newSig(params(typs[1], typs[15], typs[3]), params(typs[75]))
-	typs[78] = newSig(nil, params(typs[75]))
-	typs[79] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3]))
-	typs[80] = newSig(params(typs[1], typs[75], typs[62]), params(typs[3]))
-	typs[81] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3]))
-	typs[82] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3]))
-	typs[83] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3]))
-	typs[84] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3], typs[6]))
-	typs[85] = newSig(params(typs[1], typs[75], typs[62]), params(typs[3], typs[6]))
-	typs[86] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3], typs[6]))
-	typs[87] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3], typs[6]))
-	typs[88] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3], typs[6]))
-	typs[89] = newSig(params(typs[1], typs[75], typs[7]), params(typs[3]))
-	typs[90] = newSig(params(typs[1], typs[75], typs[3]), nil)
-	typs[91] = newSig(params(typs[1], typs[75], typs[62]), nil)
-	typs[92] = newSig(params(typs[1], typs[75], typs[24]), nil)
-	typs[93] = newSig(params(typs[1], typs[75], typs[28]), nil)
-	typs[94] = newSig(params(typs[3]), nil)
-	typs[95] = newSig(params(typs[1], typs[75]), nil)
-	typs[96] = types.NewChan(typs[2], types.Cboth)
-	typs[97] = newSig(params(typs[1], typs[22]), params(typs[96]))
-	typs[98] = newSig(params(typs[1], typs[15]), params(typs[96]))
-	typs[99] = types.NewChan(typs[2], types.Crecv)
-	typs[100] = newSig(params(typs[99], typs[3]), nil)
-	typs[101] = newSig(params(typs[99], typs[3]), params(typs[6]))
-	typs[102] = types.NewChan(typs[2], types.Csend)
-	typs[103] = newSig(params(typs[102], typs[3]), nil)
-	typs[104] = types.NewArray(typs[0], 3)
-	typs[105] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[104]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
-	typs[106] = newSig(params(typs[1], typs[3], typs[3]), nil)
-	typs[107] = newSig(params(typs[1], typs[3]), nil)
-	typs[108] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
-	typs[109] = newSig(params(typs[102], typs[3]), params(typs[6]))
-	typs[110] = newSig(params(typs[3], typs[99]), params(typs[6], typs[6]))
-	typs[111] = newSig(params(typs[57]), nil)
-	typs[112] = newSig(params(typs[1], typs[1], typs[57], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
-	typs[113] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
-	typs[114] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
-	typs[115] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
-	typs[116] = types.NewSlice(typs[2])
-	typs[117] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[116]))
-	typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil)
-	typs[119] = newSig(params(typs[7], typs[22]), nil)
-	typs[120] = newSig(params(typs[5], typs[5]), params(typs[5], typs[6]))
-	typs[121] = newSig(params(typs[3], typs[3], typs[5]), nil)
-	typs[122] = newSig(params(typs[7], typs[5]), nil)
-	typs[123] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
-	typs[124] = newSig(params(typs[3], typs[3]), params(typs[6]))
-	typs[125] = newSig(params(typs[7], typs[7]), params(typs[6]))
-	typs[126] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
-	typs[127] = newSig(params(typs[7], typs[5]), params(typs[5]))
-	typs[128] = newSig(params(typs[3], typs[5]), params(typs[5]))
-	typs[129] = newSig(params(typs[22], typs[22]), params(typs[22]))
-	typs[130] = newSig(params(typs[24], typs[24]), params(typs[24]))
-	typs[131] = newSig(params(typs[20]), params(typs[22]))
-	typs[132] = newSig(params(typs[20]), params(typs[24]))
-	typs[133] = newSig(params(typs[20]), params(typs[62]))
-	typs[134] = newSig(params(typs[22]), params(typs[20]))
-	typs[135] = types.Types[types.TFLOAT32]
-	typs[136] = newSig(params(typs[22]), params(typs[135]))
-	typs[137] = newSig(params(typs[24]), params(typs[20]))
-	typs[138] = newSig(params(typs[24]), params(typs[135]))
-	typs[139] = newSig(params(typs[62]), params(typs[20]))
-	typs[140] = newSig(params(typs[26], typs[26]), params(typs[26]))
-	typs[141] = newSig(nil, params(typs[5]))
-	typs[142] = newSig(params(typs[5], typs[5]), nil)
-	typs[143] = newSig(params(typs[5], typs[5], typs[5]), nil)
-	typs[144] = newSig(params(typs[7], typs[1], typs[5]), nil)
-	typs[145] = types.NewSlice(typs[7])
-	typs[146] = newSig(params(typs[7], typs[145]), nil)
-	typs[147] = newSig(params(typs[66], typs[66], typs[17]), nil)
-	typs[148] = newSig(params(typs[60], typs[60], typs[17]), nil)
-	typs[149] = newSig(params(typs[62], typs[62], typs[17]), nil)
-	typs[150] = newSig(params(typs[24], typs[24], typs[17]), nil)
-	typs[151] = newSig(params(typs[28], typs[28], typs[17]), nil)
-	typs[152] = types.NewArray(typs[0], 16)
-	typs[153] = newSig(params(typs[7], typs[62], typs[152], typs[28], typs[15], typs[66], typs[66]), params(typs[62]))
+	typs[74] = newSig(nil, params(typs[10]))
+	typs[75] = newSig(nil, params(typs[62]))
+	typs[76] = types.NewMap(typs[2], typs[2])
+	typs[77] = newSig(params(typs[1], typs[22], typs[3]), params(typs[76]))
+	typs[78] = newSig(params(typs[1], typs[15], typs[3]), params(typs[76]))
+	typs[79] = newSig(nil, params(typs[76]))
+	typs[80] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3]))
+	typs[81] = newSig(params(typs[1], typs[76], typs[62]), params(typs[3]))
+	typs[82] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3]))
+	typs[83] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3]))
+	typs[84] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3]))
+	typs[85] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3], typs[6]))
+	typs[86] = newSig(params(typs[1], typs[76], typs[62]), params(typs[3], typs[6]))
+	typs[87] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3], typs[6]))
+	typs[88] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3], typs[6]))
+	typs[89] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3], typs[6]))
+	typs[90] = newSig(params(typs[1], typs[76], typs[7]), params(typs[3]))
+	typs[91] = newSig(params(typs[1], typs[76], typs[3]), nil)
+	typs[92] = newSig(params(typs[1], typs[76], typs[62]), nil)
+	typs[93] = newSig(params(typs[1], typs[76], typs[24]), nil)
+	typs[94] = newSig(params(typs[1], typs[76], typs[28]), nil)
+	typs[95] = newSig(params(typs[3]), nil)
+	typs[96] = newSig(params(typs[1], typs[76]), nil)
+	typs[97] = types.NewChan(typs[2], types.Cboth)
+	typs[98] = newSig(params(typs[1], typs[22]), params(typs[97]))
+	typs[99] = newSig(params(typs[1], typs[15]), params(typs[97]))
+	typs[100] = types.NewChan(typs[2], types.Crecv)
+	typs[101] = newSig(params(typs[100], typs[3]), nil)
+	typs[102] = newSig(params(typs[100], typs[3]), params(typs[6]))
+	typs[103] = types.NewChan(typs[2], types.Csend)
+	typs[104] = newSig(params(typs[103], typs[3]), nil)
+	typs[105] = types.NewArray(typs[0], 3)
+	typs[106] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[105]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
+	typs[107] = newSig(params(typs[1], typs[3], typs[3]), nil)
+	typs[108] = newSig(params(typs[1], typs[3]), nil)
+	typs[109] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
+	typs[110] = newSig(params(typs[103], typs[3]), params(typs[6]))
+	typs[111] = newSig(params(typs[3], typs[100]), params(typs[6], typs[6]))
+	typs[112] = newSig(params(typs[57]), nil)
+	typs[113] = newSig(params(typs[1], typs[1], typs[57], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
+	typs[114] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
+	typs[115] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
+	typs[116] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
+	typs[117] = types.NewSlice(typs[2])
+	typs[118] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[117]))
+	typs[119] = newSig(params(typs[1], typs[7], typs[22]), nil)
+	typs[120] = newSig(params(typs[7], typs[22]), nil)
+	typs[121] = newSig(params(typs[5], typs[5]), params(typs[5], typs[6]))
+	typs[122] = newSig(params(typs[3], typs[3], typs[5]), nil)
+	typs[123] = newSig(params(typs[7], typs[5]), nil)
+	typs[124] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
+	typs[125] = newSig(params(typs[3], typs[3]), params(typs[6]))
+	typs[126] = newSig(params(typs[7], typs[7]), params(typs[6]))
+	typs[127] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
+	typs[128] = newSig(params(typs[7], typs[5]), params(typs[5]))
+	typs[129] = newSig(params(typs[3], typs[5]), params(typs[5]))
+	typs[130] = newSig(params(typs[22], typs[22]), params(typs[22]))
+	typs[131] = newSig(params(typs[24], typs[24]), params(typs[24]))
+	typs[132] = newSig(params(typs[20]), params(typs[22]))
+	typs[133] = newSig(params(typs[20]), params(typs[24]))
+	typs[134] = newSig(params(typs[20]), params(typs[62]))
+	typs[135] = newSig(params(typs[22]), params(typs[20]))
+	typs[136] = types.Types[types.TFLOAT32]
+	typs[137] = newSig(params(typs[22]), params(typs[136]))
+	typs[138] = newSig(params(typs[24]), params(typs[20]))
+	typs[139] = newSig(params(typs[24]), params(typs[136]))
+	typs[140] = newSig(params(typs[62]), params(typs[20]))
+	typs[141] = newSig(params(typs[26], typs[26]), params(typs[26]))
+	typs[142] = newSig(nil, params(typs[5]))
+	typs[143] = newSig(params(typs[5], typs[5]), nil)
+	typs[144] = newSig(params(typs[5], typs[5], typs[5]), nil)
+	typs[145] = newSig(params(typs[7], typs[1], typs[5]), nil)
+	typs[146] = types.NewSlice(typs[7])
+	typs[147] = newSig(params(typs[7], typs[146]), nil)
+	typs[148] = newSig(params(typs[66], typs[66], typs[17]), nil)
+	typs[149] = newSig(params(typs[60], typs[60], typs[17]), nil)
+	typs[150] = newSig(params(typs[62], typs[62], typs[17]), nil)
+	typs[151] = newSig(params(typs[24], typs[24], typs[17]), nil)
+	typs[152] = newSig(params(typs[28], typs[28], typs[17]), nil)
+	typs[153] = types.NewArray(typs[0], 16)
+	typs[154] = newSig(params(typs[7], typs[62], typs[153], typs[28], typs[15], typs[66], typs[66]), params(typs[62]))
 	return typs[:]
 }
 
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
index 909e7d6..3f5f122 100644
--- a/src/cmd/compile/internal/walk/expr.go
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -586,6 +586,17 @@
 		return e
 	}
 
+	if name, ok := n.X.(*ir.Name); ok {
+		sym := name.Sym()
+		if sym.Pkg.Path == "go.runtime" && sym.Name == "deferrangefunc" {
+			// Call to runtime.deferrangefunc is being shared with a range-over-func
+			// body that might add defers to this frame, so we cannot use open-coded defers
+			// and we need to call deferreturn even if we don't see any other explicit defers.
+			ir.CurFunc.SetHasDefer(true)
+			ir.CurFunc.SetOpenCodedDeferDisallowed(true)
+		}
+	}
+
 	walkCall1(n, init)
 	return n
 }
diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go
index c6a03d2..c2fe88c 100644
--- a/src/cmd/compile/internal/walk/stmt.go
+++ b/src/cmd/compile/internal/walk/stmt.go
@@ -55,6 +55,7 @@
 		if n.Typecheck() == 0 {
 			base.Fatalf("missing typecheck: %+v", n)
 		}
+
 		init := ir.TakeInit(n)
 		n = walkExpr(n, &init)
 		if n.Op() == ir.ONAME {
@@ -106,10 +107,11 @@
 		n := n.(*ir.GoDeferStmt)
 		ir.CurFunc.SetHasDefer(true)
 		ir.CurFunc.NumDefers++
-		if ir.CurFunc.NumDefers > maxOpenDefers {
+		if ir.CurFunc.NumDefers > maxOpenDefers || n.DeferAt != nil {
 			// Don't allow open-coded defers if there are more than
 			// 8 defers in the function, since we use a single
 			// byte to record active defers.
+			// Also don't allow if we need to use deferprocat.
 			ir.CurFunc.SetOpenCodedDeferDisallowed(true)
 		}
 		if n.Esc() != ir.EscNever {
diff --git a/test/range.go b/test/range.go
index 9ed0654..1fd19af 100644
--- a/test/range.go
+++ b/test/range.go
@@ -434,44 +434,326 @@
 }
 
 func testint1() {
+	bad := false
 	j := 0
 	for i := range int(4) {
 		if i != j {
 			println("range var", i, "want", j)
+			bad = true
 		}
 		j++
 	}
 	if j != 4 {
 		println("wrong count ranging over 4:", j)
+		bad = true
+	}
+	if bad {
+		panic("testint1")
 	}
 }
 
 func testint2() {
+	bad := false
 	j := 0
 	for i := range 4 {
 		if i != j {
 			println("range var", i, "want", j)
+			bad = true
 		}
 		j++
 	}
 	if j != 4 {
 		println("wrong count ranging over 4:", j)
+		bad = true
+	}
+	if bad {
+		panic("testint2")
 	}
 }
 
 func testint3() {
+	bad := false
 	type MyInt int
-
 	j := MyInt(0)
 	for i := range MyInt(4) {
 		if i != j {
 			println("range var", i, "want", j)
+			bad = true
 		}
 		j++
 	}
 	if j != 4 {
 		println("wrong count ranging over 4:", j)
+		bad = true
 	}
+	if bad {
+		panic("testint3")
+	}
+}
+
+var gj int
+
+func yield4x(yield func() bool) bool {
+	return yield() && yield() && yield() && yield()
+}
+
+func yield4(yield func(int) bool) bool {
+	return yield(1) && yield(2) && yield(3) && yield(4)
+}
+
+func yield3(yield func(int) bool) bool {
+	return yield(1) && yield(2) && yield(3)
+}
+
+func yield2(yield func(int) bool) bool {
+	return yield(1) && yield(2)
+}
+
+func testfunc0() {
+	j := 0
+	for range yield4x {
+		j++
+	}
+	if j != 4 {
+		println("wrong count ranging over yield4x:", j)
+		panic("testfunc0")
+	}
+
+	j = 0
+	for range yield4 {
+		j++
+	}
+	if j != 4 {
+		println("wrong count ranging over yield4:", j)
+		panic("testfunc0")
+	}
+}
+
+func testfunc1() {
+	bad := false
+	j := 1
+	for i := range yield4 {
+		if i != j {
+			println("range var", i, "want", j)
+			bad = true
+		}
+		j++
+	}
+	if j != 5 {
+		println("wrong count ranging over f:", j)
+		bad = true
+	}
+	if bad {
+		panic("testfunc1")
+	}
+}
+
+func testfunc2() {
+	bad := false
+	j := 1
+	var i int
+	for i = range yield4 {
+		if i != j {
+			println("range var", i, "want", j)
+			bad = true
+		}
+		j++
+	}
+	if j != 5 {
+		println("wrong count ranging over f:", j)
+		bad = true
+	}
+	if i != 4 {
+		println("wrong final i ranging over f:", i)
+		bad = true
+	}
+	if bad {
+		panic("testfunc2")
+	}
+}
+
+func testfunc3() {
+	bad := false
+	j := 1
+	var i int
+	for i = range yield4 {
+		if i != j {
+			println("range var", i, "want", j)
+			bad = true
+		}
+		j++
+		if i == 2 {
+			break
+		}
+		continue
+	}
+	if j != 3 {
+		println("wrong count ranging over f:", j)
+		bad = true
+	}
+	if i != 2 {
+		println("wrong final i ranging over f:", i)
+		bad = true
+	}
+	if bad {
+		panic("testfunc3")
+	}
+}
+
+func testfunc4() {
+	bad := false
+	j := 1
+	var i int
+	func() {
+		for i = range yield4 {
+			if i != j {
+				println("range var", i, "want", j)
+				bad = true
+			}
+			j++
+			if i == 2 {
+				return
+			}
+		}
+	}()
+	if j != 3 {
+		println("wrong count ranging over f:", j)
+		bad = true
+	}
+	if i != 2 {
+		println("wrong final i ranging over f:", i)
+		bad = true
+	}
+	if bad {
+		panic("testfunc3")
+	}
+}
+
+func func5() (int, int) {
+	for i := range yield4 {
+		return 10, i
+	}
+	panic("still here")
+}
+
+func testfunc5() {
+	x, y := func5()
+	if x != 10 || y != 1 {
+		println("wrong results", x, y, "want", 10, 1)
+		panic("testfunc5")
+	}
+}
+
+func func6() (z, w int) {
+	for i := range yield4 {
+		z = 10
+		w = i
+		return
+	}
+	panic("still here")
+}
+
+func testfunc6() {
+	x, y := func6()
+	if x != 10 || y != 1 {
+		println("wrong results", x, y, "want", 10, 1)
+		panic("testfunc6")
+	}
+}
+
+var saved []int
+
+func save(x int) {
+	saved = append(saved, x)
+}
+
+func printslice(s []int) {
+	print("[")
+	for i, x := range s {
+		if i > 0 {
+			print(", ")
+		}
+		print(x)
+	}
+	print("]")
+}
+
+func eqslice(s, t []int) bool {
+	if len(s) != len(t) {
+		return false
+	}
+	for i, x := range s {
+		if x != t[i] {
+			return false
+		}
+	}
+	return true
+}
+
+func func7() {
+	defer save(-1)
+	for i := range yield4 {
+		defer save(i)
+	}
+	defer save(5)
+}
+
+func checkslice(name string, saved, want []int) {
+	if !eqslice(saved, want) {
+		print("wrong results ")
+		printslice(saved)
+		print(" want ")
+		printslice(want)
+		print("\n")
+		panic(name)
+	}
+}
+
+func testfunc7() {
+	saved = nil
+	func7()
+	want := []int{5, 4, 3, 2, 1, -1}
+	checkslice("testfunc7", saved, want)
+}
+
+func func8() {
+	defer save(-1)
+	for i := range yield2 {
+		for j := range yield3 {
+			defer save(i*10 + j)
+		}
+		defer save(i)
+	}
+	defer save(-2)
+	for i := range yield4 {
+		defer save(i)
+	}
+	defer save(-3)
+}
+
+func testfunc8() {
+	saved = nil
+	func8()
+	want := []int{-3, 4, 3, 2, 1, -2, 2, 23, 22, 21, 1, 13, 12, 11, -1}
+	checkslice("testfunc8", saved, want)
+}
+
+func func9() {
+	n := 0
+	for range yield2 {
+		for range yield3 {
+			n++
+			defer save(n)
+			return
+		}
+	}
+}
+
+func testfunc9() {
+	saved = nil
+	func9()
+	want := []int{6, 5, 4, 3, 2, 1}
+	checkslice("testfunc9", saved, want)
 }
 
 // test that range evaluates the index and value expressions
@@ -510,6 +792,33 @@
 		println("wrong number of calls:", ncalls, "!= 0")
 		panic("fail")
 	}
+
+	ncalls = 0
+	si = 0
+	sv = 0
+	for *getvar(&i), *getvar(&v) = range iter2(1, 2) {
+		si += i
+		sv += v
+	}
+	if ncalls != 4 {
+		println("wrong number of calls:", ncalls, "!= 4")
+		panic("fail")
+	}
+	if si != 1 || sv != 3 {
+		println("wrong sum in testcalls", si, sv)
+		panic("fail")
+	}
+}
+
+func iter2(list ...int) func(func(int, int) bool) bool {
+	return func(yield func(int, int) bool) bool {
+		for i, x := range list {
+			if !yield(i, x) {
+				return false
+			}
+		}
+		return true
+	}
 }
 
 func main() {
@@ -531,8 +840,17 @@
 	testmap()
 	testmap1()
 	testmap2()
-	testcalls()
 	testint1()
 	testint2()
 	testint3()
+	testfunc0()
+	testfunc1()
+	testfunc2()
+	testfunc3()
+	testfunc4()
+	testfunc5()
+	testfunc6()
+	testfunc7()
+	testfunc8()
+	testcalls()
 }
diff --git a/test/rangegen.go b/test/rangegen.go
new file mode 100644
index 0000000..8241c12
--- /dev/null
+++ b/test/rangegen.go
@@ -0,0 +1,333 @@
+// runoutput
+
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Torture test for range-over-func.
+//
+// cmd/internal/testdir runs this like
+//
+//	go run rangegen.go >x.go
+//	go run x.go
+//
+// but a longer version can be run using
+//
+//	go run rangegen.go long
+//
+// In that second form, rangegen takes care of compiling
+// and running the code it generates, in batches.
+// That form takes 10-20 minutes to run.
+
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"strings"
+)
+
+const verbose = false
+
+func main() {
+	long := len(os.Args) > 1 && os.Args[1] == "long"
+	log.SetFlags(0)
+	log.SetPrefix("rangegen: ")
+
+	b := new(bytes.Buffer)
+	tests := ""
+	flush := func(force bool) {
+		if !long || (strings.Count(tests, "\n") < 1000 && !force) {
+			return
+		}
+		p(b, mainCode, tests)
+		err := os.WriteFile("tmp.go", b.Bytes(), 0666)
+		if err != nil {
+			log.Fatal(err)
+		}
+		out, err := exec.Command("go", "run", "tmp.go").CombinedOutput()
+		if err != nil {
+			log.Fatalf("go run tmp.go: %v\n%s", err, out)
+		}
+		print(".")
+		if force {
+			print("\nPASS\n")
+		}
+		b.Reset()
+		tests = ""
+		p(b, "package main\n\n")
+		p(b, "const verbose = %v\n\n", verbose)
+	}
+
+	p(b, "package main\n\n")
+	p(b, "const verbose = %v\n\n", verbose)
+	max := 2
+	if !long {
+		max = 5
+	}
+	for i := 1; i <= max; i++ {
+		maxDouble := -1
+		if long {
+			maxDouble = i
+		}
+		for double := -1; double <= maxDouble; double++ {
+			code := gen(new(bytes.Buffer), "", "", "", i, double, func(c int) bool { return true })
+			for j := 0; j < code; j++ {
+				hi := j + 1
+				if long {
+					hi = code
+				}
+				for k := j; k < hi && k < code; k++ {
+					s := fmt.Sprintf("%d_%d_%d_%d", i, double+1, j, k)
+					code0 := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return c == j || c == k })
+					code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return c == j || c == k })
+					if code0 != code1 {
+						panic("bad generator")
+					}
+					tests += "test" + s + "()\n"
+					p(b, testCode, "test"+s, []int{j, k}, "testFunc"+s, "testSlice"+s)
+					flush(false)
+				}
+			}
+		}
+	}
+	for i := 1; i <= max; i++ {
+		maxDouble := -1
+		if long {
+			maxDouble = i
+		}
+		for double := -1; double <= maxDouble; double++ {
+			s := fmt.Sprintf("%d_%d", i, double+1)
+			code := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return true })
+			code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return true })
+			if code != code1 {
+				panic("bad generator")
+			}
+			tests += "test" + s + "()\n"
+			var all []int
+			for j := 0; j < code; j++ {
+				all = append(all, j)
+			}
+			p(b, testCode, "test"+s, all, "testFunc"+s, "testSlice"+s)
+			flush(false)
+		}
+	}
+	if long {
+		flush(true)
+		os.Remove("tmp.go")
+		return
+	}
+
+	p(b, mainCode, tests)
+
+	os.Stdout.Write(b.Bytes())
+}
+
+func p(b *bytes.Buffer, format string, args ...any) {
+	fmt.Fprintf(b, format, args...)
+}
+
+func gen(b *bytes.Buffer, name, prefix, rangeExpr string, depth, double int, allowed func(int) bool) int {
+	p(b, "func %s(o *output, code int) int {\n", name)
+	p(b, "	dfr := 0; _ = dfr\n")
+	code := genLoop(b, 0, prefix, rangeExpr, depth, double, 0, "", allowed)
+	p(b, "	return 0\n")
+	p(b, "}\n\n")
+	return code
+}
+
+func genLoop(b *bytes.Buffer, d int, prefix, rangeExpr string, depth, double, code int, labelSuffix string, allowed func(int) bool) int {
+	limit := 1
+	if d == double {
+		limit = 2
+	}
+	for rep := 0; rep < limit; rep++ {
+		if rep == 1 {
+			labelSuffix = "R"
+		}
+		s := fmt.Sprintf("%d%s", d, labelSuffix)
+		p(b, "	o.log(`top%s`)\n", s)
+		p(b, "	l%sa := 0\n", s)
+		p(b, "goto L%sa; L%sa:	o.log(`L%sa`)\n", s, s, s)
+		p(b, "	if l%sa++; l%sa >= 2 { o.log(`loop L%sa`); return -1 }\n", s, s, s)
+		p(b, "	l%sfor := 0\n", s)
+		p(b, "goto L%sfor; L%sfor: for f := 0; f < 1; f++ { o.log(`L%sfor`)\n", s, s, s)
+		p(b, "	if l%sfor++; l%sfor >= 2 { o.log(`loop L%sfor`); return -1 }\n", s, s, s)
+		p(b, "	l%ssw := 0\n", s)
+		p(b, "goto L%ssw; L%ssw: switch { default: o.log(`L%ssw`)\n", s, s, s)
+		p(b, "	if l%ssw++; l%ssw >= 2 { o.log(`loop L%ssw`); return -1 }\n", s, s, s)
+		p(b, "	l%ssel := 0\n", s)
+		p(b, "goto L%ssel; L%ssel: select { default: o.log(`L%ssel`)\n", s, s, s)
+		p(b, "	if l%ssel++; l%ssel >= 2 { o.log(`loop L%ssel`); return -1 }\n", s, s, s)
+		p(b, "	l%s := 0\n", s)
+		p(b, "goto L%s; L%s:	for %s i%s := range %s {\n", s, s, prefix, s, rangeExpr)
+		p(b, "	o.log1(`L%s top`, i%s)\n", s, s)
+		p(b, "	if l%s++; l%s >= 4 { o.log(`loop L%s`); return -1 }\n", s, s, s)
+		printTests := func() {
+			if code++; allowed(code) {
+				p(b, "	if code == %v { break }\n", code)
+			}
+			if code++; allowed(code) {
+				p(b, "	if code == %v { continue }\n", code)
+			}
+			if code++; allowed(code) {
+				p(b, "	switch { case code == %v: continue }\n", code)
+			}
+			if code++; allowed(code) {
+				p(b, "	if code == %v { return %[1]v }\n", code)
+			}
+			if code++; allowed(code) {
+				p(b, "	if code == %v { select { default: break } }\n", code)
+			}
+			if code++; allowed(code) {
+				p(b, "	if code == %v { switch { default: break } }\n", code)
+			}
+			if code++; allowed(code) {
+				p(b, "	if code == %v { dfr++; defer o.log1(`defer %d`, dfr) }\n", code, code)
+			}
+			for i := d; i > 0; i-- {
+				suffix := labelSuffix
+				if i < double {
+					suffix = ""
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { break L%d%s }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { select { default: break L%d%s } }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { break L%d%s }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { break L%d%ssw }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { break L%d%ssel }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { break L%d%sfor }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { continue L%d%sfor }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { goto L%d%sa }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { goto L%d%s }\n", code, i, suffix)
+				}
+				if code++; allowed(code) {
+					p(b, "	if code == %v { goto L%d%sb }\n", code, i, suffix)
+				}
+			}
+		}
+		printTests()
+		if d < depth {
+			if rep == 1 {
+				double = d // signal to children to use the rep=1 labels
+			}
+			code = genLoop(b, d+1, prefix, rangeExpr, depth, double, code, labelSuffix, allowed)
+			printTests()
+		}
+		p(b, "	o.log(`L%s bot`)\n", s)
+		p(b, "	}\n")
+		p(b, "	o.log(`L%ssel bot`)\n", s)
+		p(b, "	}\n")
+		p(b, "	o.log(`L%ssw bot`)\n", s)
+		p(b, "	}\n")
+		p(b, "	o.log(`L%sfor bot`)\n", s)
+		p(b, "	}\n")
+		p(b, "	o.log(`done%s`)\n", s)
+		p(b, "goto L%sb; L%sb: o.log(`L%sb`)\n", s, s, s)
+	}
+	return code
+}
+
+var testCode = `
+func %s() {
+	all := %#v
+	for i := 0; i < len(all); i++ {
+		c := all[i]
+		outFunc := run(%s, c)
+		outSlice := run(%s, c)
+		if !outFunc.eq(outSlice) {
+			println("mismatch", "%[3]s", "%[4]s", c)
+			println()
+			println("func:")
+			outFunc.print()
+			println()
+			println("slice:")
+			outSlice.print()
+			panic("mismatch")
+		}
+	}
+	if verbose {
+		println("did", "%[3]s", "%[4]s", len(all))
+	}
+}
+`
+
+var mainCode = `
+
+func main() {
+	if verbose {
+		println("main")
+	}
+	%s
+}
+
+func yield2(yield func(int)bool) bool { return yield(1) && yield(2) }
+var slice2 = []int{1,2}
+
+type output struct {
+	ret int
+	trace []any
+}
+
+func (o *output) log(x any) {
+	o.trace = append(o.trace, x)
+}
+
+func (o *output) log1(x, y any) {
+	o.trace = append(o.trace, x, y)
+}
+
+func (o *output) eq(p *output) bool{
+	if o.ret != p.ret  || len(o.trace) != len(p.trace) {
+		return false
+	}
+	for i ,x := range o.trace {
+		if x != p.trace[i] {
+			return false
+		}
+	}
+	return true
+}
+
+func (o *output) print() {
+	println("ret", o.ret, "trace-len", len(o.trace))
+	for i := 0; i < len(o.trace); i++ {
+		print("#", i, " ")
+		switch x := o.trace[i].(type) {
+		case int:
+			print(x)
+		case string:
+			print(x)
+		default:
+			print(x)
+		}
+		print("\n")
+	}
+}
+
+func run(f func(*output, int)int, i int) *output {
+	o := &output{}
+	o.ret = f(o, i)
+	return o
+}
+
+`