]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/cmd/compile/internal/walk/order.go
cmd/compile: move FuncPC intrinsic handling to common helper
[gostls13.git] / src / cmd / compile / internal / walk / order.go
index 19d9551566e92438aed97cc4fc44985ebbd85bf6..4d9b2fbee566ef79128cd1c61b4e9ed884b5e6e1 100644 (file)
@@ -7,15 +7,14 @@ package walk
 import (
        "fmt"
        "go/constant"
-       "internal/buildcfg"
 
        "cmd/compile/internal/base"
-       "cmd/compile/internal/escape"
        "cmd/compile/internal/ir"
        "cmd/compile/internal/reflectdata"
        "cmd/compile/internal/staticinit"
        "cmd/compile/internal/typecheck"
        "cmd/compile/internal/types"
+       "cmd/internal/objabi"
        "cmd/internal/src"
 )
 
@@ -37,34 +36,22 @@ import (
 // Arrange that receive expressions only appear in direct assignments
 // x = <-c or as standalone statements <-c, never in larger expressions.
 
-// TODO(rsc): The temporary introduction during multiple assignments
-// should be moved into this file, so that the temporaries can be cleaned
-// and so that conversions implicit in the OAS2FUNC and OAS2RECV
-// nodes can be made explicit and then have their temporaries cleaned.
-
-// TODO(rsc): Goto and multilevel break/continue can jump over
-// inserted VARKILL annotations. Work out a way to handle these.
-// The current implementation is safe, in that it will execute correctly.
-// But it won't reuse temporaries as aggressively as it might, and
-// it can result in unnecessary zeroing of those variables in the function
-// prologue.
-
 // orderState holds state during the ordering process.
 type orderState struct {
        out  []ir.Node             // list of generated statements
        temp []*ir.Name            // stack of temporary variables
-       free map[string][]*ir.Name // free list of unused temporaries, by type.LongString().
+       free map[string][]*ir.Name // free list of unused temporaries, by type.LinkString().
        edit func(ir.Node) ir.Node // cached closure of o.exprNoLHS
 }
 
-// Order rewrites fn.Nbody to apply the ordering constraints
+// order rewrites fn.Nbody to apply the ordering constraints
 // described in the comment at the top of the file.
 func order(fn *ir.Func) {
        if base.Flag.W > 1 {
                s := fmt.Sprintf("\nbefore order %v", fn.Sym())
                ir.DumpList(s, fn.Body)
        }
-
+       ir.SetPos(fn) // Set reasonable position for instrumenting code. See issue 53688.
        orderBlock(&fn.Body, map[string][]*ir.Name{})
 }
 
@@ -78,21 +65,15 @@ func (o *orderState) append(stmt ir.Node) {
 // If clear is true, newTemp emits code to zero the temporary.
 func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
        var v *ir.Name
-       // Note: LongString is close to the type equality we want,
-       // but not exactly. We still need to double-check with types.Identical.
-       key := t.LongString()
-       a := o.free[key]
-       for i, n := range a {
-               if types.Identical(t, n.Type()) {
-                       v = a[i]
-                       a[i] = a[len(a)-1]
-                       a = a[:len(a)-1]
-                       o.free[key] = a
-                       break
+       key := t.LinkString()
+       if a := o.free[key]; len(a) > 0 {
+               v = a[len(a)-1]
+               if !types.Identical(t, v.Type()) {
+                       base.Fatalf("expected %L to have type %v", v, t)
                }
-       }
-       if v == nil {
-               v = typecheck.Temp(t)
+               o.free[key] = a[:len(a)-1]
+       } else {
+               v = typecheck.TempAt(base.Pos, ir.CurFunc, t)
        }
        if clear {
                o.append(ir.NewAssignStmt(base.Pos, v, nil))
@@ -147,7 +128,7 @@ func (o *orderState) cheapExpr(n ir.Node) ir.Node {
                if l == n.X {
                        return n
                }
-               a := ir.SepCopy(n).(*ir.UnaryExpr)
+               a := ir.Copy(n).(*ir.UnaryExpr)
                a.X = l
                return typecheck.Expr(a)
        }
@@ -173,7 +154,7 @@ func (o *orderState) safeExpr(n ir.Node) ir.Node {
                if l == n.X {
                        return n
                }
-               a := ir.SepCopy(n).(*ir.UnaryExpr)
+               a := ir.Copy(n).(*ir.UnaryExpr)
                a.X = l
                return typecheck.Expr(a)
 
@@ -183,7 +164,7 @@ func (o *orderState) safeExpr(n ir.Node) ir.Node {
                if l == n.X {
                        return n
                }
-               a := ir.SepCopy(n).(*ir.SelectorExpr)
+               a := ir.Copy(n).(*ir.SelectorExpr)
                a.X = l
                return typecheck.Expr(a)
 
@@ -193,7 +174,7 @@ func (o *orderState) safeExpr(n ir.Node) ir.Node {
                if l == n.X {
                        return n
                }
-               a := ir.SepCopy(n).(*ir.SelectorExpr)
+               a := ir.Copy(n).(*ir.SelectorExpr)
                a.X = l
                return typecheck.Expr(a)
 
@@ -203,7 +184,7 @@ func (o *orderState) safeExpr(n ir.Node) ir.Node {
                if l == n.X {
                        return n
                }
-               a := ir.SepCopy(n).(*ir.StarExpr)
+               a := ir.Copy(n).(*ir.StarExpr)
                a.X = l
                return typecheck.Expr(a)
 
@@ -219,7 +200,7 @@ func (o *orderState) safeExpr(n ir.Node) ir.Node {
                if l == n.X && r == n.Index {
                        return n
                }
-               a := ir.SepCopy(n).(*ir.IndexExpr)
+               a := ir.Copy(n).(*ir.IndexExpr)
                a.X = l
                a.Index = r
                return typecheck.Expr(a)
@@ -230,21 +211,12 @@ func (o *orderState) safeExpr(n ir.Node) ir.Node {
        }
 }
 
-// isaddrokay reports whether it is okay to pass n's address to runtime routines.
-// Taking the address of a variable makes the liveness and optimization analyses
-// lose track of where the variable's lifetime ends. To avoid hurting the analyses
-// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
-// because we emit explicit VARKILL instructions marking the end of those
-// temporaries' lifetimes.
-func isaddrokay(n ir.Node) bool {
-       return ir.IsAddressable(n) && (n.Op() != ir.ONAME || n.(*ir.Name).Class == ir.PEXTERN || ir.IsAutoTmp(n))
-}
-
 // addrTemp ensures that n is okay to pass by address to runtime routines.
 // If the original argument n is not okay, addrTemp creates a tmp, emits
 // tmp = n, and then returns tmp.
 // The result of addrTemp MUST be assigned back to n, e.g.
-//     n.Left = o.addrTemp(n.Left)
+//
+//     n.Left = o.addrTemp(n.Left)
 func (o *orderState) addrTemp(n ir.Node) ir.Node {
        if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL {
                // TODO: expand this to all static composite literal nodes?
@@ -259,7 +231,7 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node {
                vstat = typecheck.Expr(vstat).(*ir.Name)
                return vstat
        }
-       if isaddrokay(n) {
+       if ir.IsAddressable(n) {
                return n
        }
        return o.copyExpr(n)
@@ -267,7 +239,13 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node {
 
 // mapKeyTemp prepares n to be a key in a map runtime call and returns n.
 // It should only be used for map runtime calls which have *_fast* versions.
-func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
+// The first parameter is the position of n's containing node, for use in case
+// that n's position is not unique (e.g., if n is an ONAME).
+func (o *orderState) mapKeyTemp(outerPos src.XPos, t *types.Type, n ir.Node) ir.Node {
+       pos := outerPos
+       if ir.HasUniquePos(n) {
+               pos = n.Pos()
+       }
        // Most map calls need to take the address of the key.
        // Exception: map*_fast* calls. See golang.org/issue/19015.
        alg := mapfast(t)
@@ -291,7 +269,7 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
                return n
        case nt.Kind() == kt.Kind(), nt.IsPtrShaped() && kt.IsPtrShaped():
                // can directly convert (e.g. named type to underlying type, or one pointer to another)
-               return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONVNOP, kt, n))
+               return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, kt, n))
        case nt.IsInteger() && kt.IsInteger():
                // can directly convert (e.g. int32 to uint32)
                if n.Op() == ir.OLITERAL && nt.IsSigned() {
@@ -300,20 +278,20 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
                        n.SetType(kt)
                        return n
                }
-               return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONV, kt, n))
+               return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, kt, n))
        default:
                // Unsafe cast through memory.
                // We'll need to do a load with type kt. Create a temporary of type kt to
                // ensure sufficient alignment. nt may be under-aligned.
-               if kt.Align < nt.Align {
+               if uint8(kt.Alignment()) < uint8(nt.Alignment()) {
                        base.Fatalf("mapKeyTemp: key type is not sufficiently aligned, kt=%v nt=%v", kt, nt)
                }
                tmp := o.newTemp(kt, true)
                // *(*nt)(&tmp) = n
                var e ir.Node = typecheck.NodAddr(tmp)
-               e = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, nt.PtrTo(), e)
-               e = ir.NewStarExpr(n.Pos(), e)
-               o.append(ir.NewAssignStmt(base.Pos, e, n))
+               e = ir.NewConvExpr(pos, ir.OCONVNOP, nt.PtrTo(), e)
+               e = ir.NewStarExpr(pos, e)
+               o.append(ir.NewAssignStmt(pos, e, n))
                return tmp
        }
 }
@@ -323,8 +301,10 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
 // Returns a bool that signals if a modification was made.
 //
 // For:
-//  x = m[string(k)]
-//  x = m[T1{... Tn{..., string(k), ...}]
+//
+//     x = m[string(k)]
+//     x = m[T1{... Tn{..., string(k), ...}}]
+//
 // where k is []byte, T1 to Tn is a nesting of struct and array literals,
 // the allocation of backing bytes for the string can be avoided
 // by reusing the []byte backing array. These are special cases
@@ -372,31 +352,12 @@ func (o *orderState) markTemp() ordermarker {
 // which must have been returned by markTemp.
 func (o *orderState) popTemp(mark ordermarker) {
        for _, n := range o.temp[mark:] {
-               key := n.Type().LongString()
+               key := n.Type().LinkString()
                o.free[key] = append(o.free[key], n)
        }
        o.temp = o.temp[:mark]
 }
 
-// cleanTempNoPop emits VARKILL instructions to *out
-// for each temporary above the mark on the temporary stack.
-// It does not pop the temporaries from the stack.
-func (o *orderState) cleanTempNoPop(mark ordermarker) []ir.Node {
-       var out []ir.Node
-       for i := len(o.temp) - 1; i >= int(mark); i-- {
-               n := o.temp[i]
-               out = append(out, typecheck.Stmt(ir.NewUnaryExpr(base.Pos, ir.OVARKILL, n)))
-       }
-       return out
-}
-
-// cleanTemp emits VARKILL instructions for each temporary above the
-// mark on the temporary stack and removes them from the stack.
-func (o *orderState) cleanTemp(top ordermarker) {
-       o.out = append(o.out, o.cleanTempNoPop(top)...)
-       o.popTemp(top)
-}
-
 // stmtList orders each of the statements in the list.
 func (o *orderState) stmtList(l ir.Nodes) {
        s := l
@@ -407,9 +368,12 @@ func (o *orderState) stmtList(l ir.Nodes) {
 }
 
 // orderMakeSliceCopy matches the pattern:
-//  m = OMAKESLICE([]T, x); OCOPY(m, s)
+//
+//     m = OMAKESLICE([]T, x); OCOPY(m, s)
+//
 // and rewrites it to:
-//  m = OMAKESLICECOPY([]T, x, s); nil
+//
+//     m = OMAKESLICECOPY([]T, x, s); nil
 func orderMakeSliceCopy(s []ir.Node) {
        if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
                return
@@ -447,33 +411,57 @@ func (o *orderState) edge() {
                return
        }
 
-       // Create a new uint8 counter to be allocated in section
-       // __libfuzzer_extra_counters.
+       // Create a new uint8 counter to be allocated in section __sancov_cntrs
        counter := staticinit.StaticName(types.Types[types.TUINT8])
-       counter.SetLibfuzzerExtraCounter(true)
-
-       // counter += 1
-       incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
-       o.append(incr)
+       counter.SetLibfuzzer8BitCounter(true)
+       // As well as setting SetLibfuzzer8BitCounter, we preemptively set the
+       // symbol type to SLIBFUZZER_8BIT_COUNTER so that the race detector
+       // instrumentation pass (which does not have access to the flags set by
+       // SetLibfuzzer8BitCounter) knows to ignore them. This information is
+       // lost by the time it reaches the compile step, so SetLibfuzzer8BitCounter
+       // is still necessary.
+       counter.Linksym().Type = objabi.SLIBFUZZER_8BIT_COUNTER
+
+       // We guarantee that the counter never becomes zero again once it has been
+       // incremented once. This implementation follows the NeverZero optimization
+       // presented by the paper:
+       // "AFL++: Combining Incremental Steps of Fuzzing Research"
+       // The NeverZero policy avoids the overflow to 0 by setting the counter to one
+       // after it reaches 255 and so, if an edge is executed at least one time, the entry is
+       // never 0.
+       // Another policy presented in the paper is the Saturated Counters policy which
+       // freezes the counter when it reaches the value of 255. However, a range
+       // of experiments showed that that decreases overall performance.
+       o.append(ir.NewIfStmt(base.Pos,
+               ir.NewBinaryExpr(base.Pos, ir.OEQ, counter, ir.NewInt(base.Pos, 0xff)),
+               []ir.Node{ir.NewAssignStmt(base.Pos, counter, ir.NewInt(base.Pos, 1))},
+               []ir.Node{ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(base.Pos, 1))}))
 }
 
 // orderBlock orders the block of statements in n into a new slice,
 // and then replaces the old slice in n with the new slice.
 // free is a map that can be used to obtain temporary variables by type.
 func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
+       if len(*n) != 0 {
+               // Set reasonable position for instrumenting code. See issue 53688.
+               // It would be nice if ir.Nodes had a position (the opening {, probably),
+               // but it doesn't. So we use the first statement's position instead.
+               ir.SetPos((*n)[0])
+       }
        var order orderState
        order.free = free
        mark := order.markTemp()
        order.edge()
        order.stmtList(*n)
-       order.cleanTemp(mark)
+       order.popTemp(mark)
        *n = order.out
 }
 
 // exprInPlace orders the side effects in *np and
 // leaves them as the init list of the final *np.
 // The result of exprInPlace MUST be assigned back to n, e.g.
-//     n.Left = o.exprInPlace(n.Left)
+//
+//     n.Left = o.exprInPlace(n.Left)
 func (o *orderState) exprInPlace(n ir.Node) ir.Node {
        var order orderState
        order.free = o.free
@@ -489,14 +477,16 @@ func (o *orderState) exprInPlace(n ir.Node) ir.Node {
 // orderStmtInPlace orders the side effects of the single statement *np
 // and replaces it with the resulting statement list.
 // The result of orderStmtInPlace MUST be assigned back to n, e.g.
-//     n.Left = orderStmtInPlace(n.Left)
+//
+//     n.Left = orderStmtInPlace(n.Left)
+//
 // free is a map that can be used to obtain temporary variables by type.
 func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
        var order orderState
        order.free = free
        mark := order.markTemp()
        order.stmt(n)
-       order.cleanTemp(mark)
+       order.popTemp(mark)
        return ir.NewBlockStmt(src.NoXPos, order.out)
 }
 
@@ -514,15 +504,18 @@ func (o *orderState) init(n ir.Node) {
 }
 
 // call orders the call expression n.
-// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
+// n.Op is OCALLFUNC/OCALLINTER or a builtin like OCOPY.
 func (o *orderState) call(nn ir.Node) {
        if len(nn.Init()) > 0 {
                // Caller should have already called o.init(nn).
                base.Fatalf("%v with unexpected ninit", nn.Op())
        }
+       if nn.Op() == ir.OCALLMETH {
+               base.FatalfAt(nn.Pos(), "OCALLMETH missed by typecheck")
+       }
 
        // Builtin functions.
-       if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER {
+       if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLINTER {
                switch n := nn.(type) {
                default:
                        base.Fatalf("unexpected call: %+v", n)
@@ -543,50 +536,17 @@ func (o *orderState) call(nn ir.Node) {
        }
 
        n := nn.(*ir.CallExpr)
-       typecheck.FixVariadicCall(n)
+       typecheck.AssertFixedCall(n)
 
-       if isFuncPCIntrinsic(n) && isIfaceOfFunc(n.Args[0]) {
+       if ir.IsFuncPCIntrinsic(n) && ir.IsIfaceOfFunc(n.Args[0]) != nil {
                // For internal/abi.FuncPCABIxxx(fn), if fn is a defined function,
                // do not introduce temporaries here, so it is easier to rewrite it
                // to symbol address reference later in walk.
                return
        }
 
-       n.X = o.expr(n.X, nil)
+       n.Fun = o.expr(n.Fun, nil)
        o.exprList(n.Args)
-
-       if n.Op() == ir.OCALLINTER {
-               return
-       }
-       keepAlive := func(arg ir.Node) {
-               // If the argument is really a pointer being converted to uintptr,
-               // arrange for the pointer to be kept alive until the call returns,
-               // by copying it into a temp and marking that temp
-               // still alive when we pop the temp stack.
-               if arg.Op() == ir.OCONVNOP {
-                       arg := arg.(*ir.ConvExpr)
-                       if arg.X.Type().IsUnsafePtr() {
-                               x := o.copyExpr(arg.X)
-                               arg.X = x
-                               x.SetAddrtaken(true) // ensure SSA keeps the x variable
-                               n.KeepAlive = append(n.KeepAlive, x)
-                       }
-               }
-       }
-
-       // Check for "unsafe-uintptr" tag provided by escape analysis.
-       for i, param := range n.X.Type().Params().FieldSlice() {
-               if param.Note == escape.UnsafeUintptrNote || param.Note == escape.UintptrEscapesNote {
-                       if arg := n.Args[i]; arg.Op() == ir.OSLICELIT {
-                               arg := arg.(*ir.CompLitExpr)
-                               for _, elt := range arg.List {
-                                       keepAlive(elt)
-                               }
-                       } else {
-                               keepAlive(arg)
-                       }
-               }
-       }
 }
 
 // mapAssign appends n to o.out.
@@ -625,8 +585,6 @@ func (o *orderState) safeMapRHS(r ir.Node) ir.Node {
 }
 
 // stmt orders the statement n, appending to o.out.
-// Temporaries created during the statement are cleaned
-// up using VARKILL instructions as possible.
 func (o *orderState) stmt(n ir.Node) {
        if n == nil {
                return
@@ -639,7 +597,7 @@ func (o *orderState) stmt(n ir.Node) {
        default:
                base.Fatalf("order.stmt %v", n.Op())
 
-       case ir.OVARKILL, ir.OVARLIVE, ir.OINLMARK:
+       case ir.OINLMARK:
                o.out = append(o.out, n)
 
        case ir.OAS:
@@ -648,7 +606,7 @@ func (o *orderState) stmt(n ir.Node) {
                n.X = o.expr(n.X, nil)
                n.Y = o.expr(n.Y, n.X)
                o.mapAssign(n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        case ir.OASOP:
                n := n.(*ir.AssignOpStmt)
@@ -673,12 +631,12 @@ func (o *orderState) stmt(n ir.Node) {
                        r := o.expr(typecheck.Expr(ir.NewBinaryExpr(n.Pos(), n.AsOp, l2, n.Y)), nil)
                        as := typecheck.Stmt(ir.NewAssignStmt(n.Pos(), l1, r))
                        o.mapAssign(as)
-                       o.cleanTemp(t)
+                       o.popTemp(t)
                        return
                }
 
                o.mapAssign(n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        case ir.OAS2:
                n := n.(*ir.AssignListStmt)
@@ -686,17 +644,28 @@ func (o *orderState) stmt(n ir.Node) {
                o.exprList(n.Lhs)
                o.exprList(n.Rhs)
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        // Special: avoid copy of func call n.Right
        case ir.OAS2FUNC:
                n := n.(*ir.AssignListStmt)
                t := o.markTemp()
                o.exprList(n.Lhs)
-               o.init(n.Rhs[0])
-               o.call(n.Rhs[0])
-               o.as2func(n)
-               o.cleanTemp(t)
+               call := n.Rhs[0]
+               o.init(call)
+               if ic, ok := call.(*ir.InlinedCallExpr); ok {
+                       o.stmtList(ic.Body)
+
+                       n.SetOp(ir.OAS2)
+                       n.Rhs = ic.ReturnVars
+
+                       o.exprList(n.Rhs)
+                       o.out = append(o.out, n)
+               } else {
+                       o.call(call)
+                       o.as2func(n)
+               }
+               o.popTemp(t)
 
        // Special: use temporary variables to hold result,
        // so that runtime can take address of temporary.
@@ -713,6 +682,11 @@ func (o *orderState) stmt(n ir.Node) {
                case ir.ODOTTYPE2:
                        r := r.(*ir.TypeAssertExpr)
                        r.X = o.expr(r.X, nil)
+               case ir.ODYNAMICDOTTYPE2:
+                       r := r.(*ir.DynamicTypeAssertExpr)
+                       r.X = o.expr(r.X, nil)
+                       r.RType = o.expr(r.RType, nil)
+                       r.ITab = o.expr(r.ITab, nil)
                case ir.ORECV:
                        r := r.(*ir.UnaryExpr)
                        r.X = o.expr(r.X, nil)
@@ -722,13 +696,13 @@ func (o *orderState) stmt(n ir.Node) {
                        r.Index = o.expr(r.Index, nil)
                        // See similar conversion for OINDEXMAP below.
                        _ = mapKeyReplaceStrConv(r.Index)
-                       r.Index = o.mapKeyTemp(r.X.Type(), r.Index)
+                       r.Index = o.mapKeyTemp(r.Pos(), r.X.Type(), r.Index)
                default:
                        base.Fatalf("order.stmt: %v", r.Op())
                }
 
                o.as2ok(n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        // Special: does not save n onto out.
        case ir.OBLOCK:
@@ -739,8 +713,6 @@ func (o *orderState) stmt(n ir.Node) {
        case ir.OBREAK,
                ir.OCONTINUE,
                ir.ODCL,
-               ir.ODCLCONST,
-               ir.ODCLTYPE,
                ir.OFALL,
                ir.OGOTO,
                ir.OLABEL,
@@ -748,19 +720,30 @@ func (o *orderState) stmt(n ir.Node) {
                o.out = append(o.out, n)
 
        // Special: handle call arguments.
-       case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
+       case ir.OCALLFUNC, ir.OCALLINTER:
                n := n.(*ir.CallExpr)
                t := o.markTemp()
                o.call(n)
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
+
+       case ir.OINLCALL:
+               n := n.(*ir.InlinedCallExpr)
+               o.stmtList(n.Body)
+
+               // discard results; double-check for no side effects
+               for _, result := range n.ReturnVars {
+                       if staticinit.AnySideEffects(result) {
+                               base.FatalfAt(result.Pos(), "inlined call result has side effects: %v", result)
+                       }
+               }
 
-       case ir.OCLOSE, ir.ORECV:
+       case ir.OCHECKNIL, ir.OCLEAR, ir.OCLOSE, ir.OPANIC, ir.ORECV:
                n := n.(*ir.UnaryExpr)
                t := o.markTemp()
                n.X = o.expr(n.X, nil)
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        case ir.OCOPY:
                n := n.(*ir.BinaryExpr)
@@ -768,14 +751,14 @@ func (o *orderState) stmt(n ir.Node) {
                n.X = o.expr(n.X, nil)
                n.Y = o.expr(n.Y, nil)
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
-       case ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+       case ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP:
                n := n.(*ir.CallExpr)
                t := o.markTemp()
-               o.exprList(n.Args)
+               o.call(n)
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        // Special: order arguments to inner call but not call itself.
        case ir.ODEFER, ir.OGO:
@@ -783,27 +766,17 @@ func (o *orderState) stmt(n ir.Node) {
                t := o.markTemp()
                o.init(n.Call)
                o.call(n.Call)
-               if n.Call.Op() == ir.ORECOVER {
-                       // Special handling of "defer recover()". We need to evaluate the FP
-                       // argument before wrapping.
-                       var init ir.Nodes
-                       n.Call = walkRecover(n.Call.(*ir.CallExpr), &init)
-                       o.stmtList(init)
-               }
-               if buildcfg.Experiment.RegabiDefer {
-                       o.wrapGoDefer(n)
-               }
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        case ir.ODELETE:
                n := n.(*ir.CallExpr)
                t := o.markTemp()
                n.Args[0] = o.expr(n.Args[0], nil)
                n.Args[1] = o.expr(n.Args[1], nil)
-               n.Args[1] = o.mapKeyTemp(n.Args[0].Type(), n.Args[1])
+               n.Args[1] = o.mapKeyTemp(n.Pos(), n.Args[0].Type(), n.Args[1])
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        // Clean temporaries from condition evaluation at
        // beginning of loop body and after for statement.
@@ -811,11 +784,10 @@ func (o *orderState) stmt(n ir.Node) {
                n := n.(*ir.ForStmt)
                t := o.markTemp()
                n.Cond = o.exprInPlace(n.Cond)
-               n.Body.Prepend(o.cleanTempNoPop(t)...)
                orderBlock(&n.Body, o.free)
                n.Post = orderStmtInPlace(n.Post, o.free)
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        // Clean temporaries from condition at
        // beginning of both branches.
@@ -823,23 +795,11 @@ func (o *orderState) stmt(n ir.Node) {
                n := n.(*ir.IfStmt)
                t := o.markTemp()
                n.Cond = o.exprInPlace(n.Cond)
-               n.Body.Prepend(o.cleanTempNoPop(t)...)
-               n.Else.Prepend(o.cleanTempNoPop(t)...)
                o.popTemp(t)
                orderBlock(&n.Body, o.free)
                orderBlock(&n.Else, o.free)
                o.out = append(o.out, n)
 
-       case ir.OPANIC:
-               n := n.(*ir.UnaryExpr)
-               t := o.markTemp()
-               n.X = o.expr(n.X, nil)
-               if !n.X.Type().IsEmptyInterface() {
-                       base.FatalfAt(n.Pos(), "bad argument to panic: %L", n.X)
-               }
-               o.out = append(o.out, n)
-               o.cleanTemp(t)
-
        case ir.ORANGE:
                // n.Right is the expression being ranged over.
                // order it, and then make a copy if we need one.
@@ -855,8 +815,14 @@ func (o *orderState) stmt(n ir.Node) {
                // Mark []byte(str) range expression to reuse string backing storage.
                // It is safe because the storage cannot be mutated.
                n := n.(*ir.RangeStmt)
-               if n.X.Op() == ir.OSTR2BYTES {
-                       n.X.(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP)
+               if x, ok := n.X.(*ir.ConvExpr); ok {
+                       switch x.Op() {
+                       case ir.OSTR2BYTES:
+                               x.SetOp(ir.OSTR2BYTESTMP)
+                               fallthrough
+                       case ir.OSTR2BYTESTMP:
+                               x.MarkNonNil() // "range []byte(nil)" is fine
+                       }
                }
 
                t := o.markTemp()
@@ -864,11 +830,14 @@ func (o *orderState) stmt(n ir.Node) {
 
                orderBody := true
                xt := typecheck.RangeExprType(n.X.Type())
-               switch xt.Kind() {
+               switch k := xt.Kind(); {
                default:
                        base.Fatalf("order.stmt range %v", n.Type())
 
-               case types.TARRAY, types.TSLICE:
+               case types.IsInt[k]:
+                       // Used only once, no need to copy.
+
+               case k == types.TARRAY, k == types.TSLICE:
                        if n.Value == nil || ir.IsBlank(n.Value) {
                                // for i := range x will only use x once, to compute len(x).
                                // No need to copy it.
@@ -876,7 +845,7 @@ func (o *orderState) stmt(n ir.Node) {
                        }
                        fallthrough
 
-               case types.TCHAN, types.TSTRING:
+               case k == types.TCHAN, k == types.TSTRING:
                        // chan, string, slice, array ranges use value multiple times.
                        // make copy.
                        r := n.X
@@ -889,7 +858,7 @@ func (o *orderState) stmt(n ir.Node) {
 
                        n.X = o.copyExpr(r)
 
-               case types.TMAP:
+               case k == types.TMAP:
                        if isMapClear(n) {
                                // Preserve the body of the map clear pattern so it can
                                // be detected during walk. The loop body will not be used
@@ -906,7 +875,7 @@ func (o *orderState) stmt(n ir.Node) {
 
                        // n.Prealloc is the temp for the iterator.
                        // MapIterType contains pointers and needs to be zeroed.
-                       n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true)
+                       n.Prealloc = o.newTemp(reflectdata.MapIterType(), true)
                }
                n.Key = o.exprInPlace(n.Key)
                n.Value = o.exprInPlace(n.Value)
@@ -914,7 +883,7 @@ func (o *orderState) stmt(n ir.Node) {
                        orderBlock(&n.Body, o.free)
                }
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        case ir.ORETURN:
                n := n.(*ir.ReturnStmt)
@@ -973,6 +942,12 @@ func (o *orderState) stmt(n ir.Node) {
                                        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)
@@ -985,7 +960,7 @@ func (o *orderState) stmt(n ir.Node) {
                                do(0, recv.X.Type().Elem())
                                do(1, types.Types[types.TBOOL])
                                if len(init) != 0 {
-                                       ir.DumpList("ninit", r.Init())
+                                       ir.DumpList("ninit", init)
                                        base.Fatalf("ninit on select recv")
                                }
                                orderBlock(ncas.PtrInit(), o.free)
@@ -1015,7 +990,6 @@ func (o *orderState) stmt(n ir.Node) {
                // (The temporary cleaning must follow that ninit work.)
                for _, cas := range n.Cases {
                        orderBlock(&cas.Body, o.free)
-                       cas.Body.Prepend(o.cleanTempNoPop(t)...)
 
                        // TODO(mdempsky): Is this actually necessary?
                        // walkSelect appears to walk Ninit.
@@ -1039,7 +1013,7 @@ func (o *orderState) stmt(n ir.Node) {
                        n.Value = o.addrTemp(n.Value)
                }
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
 
        // TODO(rsc): Clean temporaries more aggressively.
        // Note that because walkSwitch will rewrite some of the
@@ -1063,7 +1037,7 @@ func (o *orderState) stmt(n ir.Node) {
                }
 
                o.out = append(o.out, n)
-               o.cleanTemp(t)
+               o.popTemp(t)
        }
 
        base.Pos = lno
@@ -1105,7 +1079,8 @@ func (o *orderState) exprNoLHS(n ir.Node) ir.Node {
 // Otherwise lhs == nil. (When lhs != nil it may be possible
 // to avoid copying the result of the expression to a temporary.)
 // The result of expr MUST be assigned back to n, e.g.
-//     n.Left = o.expr(n.Left, lhs)
+//
+//     n.Left = o.expr(n.Left, lhs)
 func (o *orderState) expr(n, lhs ir.Node) ir.Node {
        if n == nil {
                return n
@@ -1184,7 +1159,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                }
 
                // key must be addressable
-               n.Index = o.mapKeyTemp(n.X.Type(), n.Index)
+               n.Index = o.mapKeyTemp(n.Pos(), n.X.Type(), n.Index)
                if needCopy {
                        return o.copyExpr(n)
                }
@@ -1198,7 +1173,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                if n.X.Type().IsInterface() {
                        return n
                }
-               if _, _, needsaddr := convFuncName(n.X.Type(), n.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
+               if _, _, needsaddr := dataWordFuncName(n.X.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
                        // Need a temp if we need to pass the address to the conversion function.
                        // We also process static composite literal node here, making a named static global
                        // whose address we can put directly in an interface (see OCONVIFACE case in walk).
@@ -1208,7 +1183,10 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
 
        case ir.OCONVNOP:
                n := n.(*ir.ConvExpr)
-               if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER || n.X.Op() == ir.OCALLMETH) {
+               if n.X.Op() == ir.OCALLMETH {
+                       base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
+               }
+               if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER) {
                        call := n.X.(*ir.CallExpr)
                        // When reordering unsafe.Pointer(f()) into a separate
                        // statement, the conversion and function call must stay
@@ -1247,7 +1225,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                o.edge()
                rhs := o.expr(n.Y, nil)
                o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, rhs)))
-               o.cleanTemp(t)
+               o.popTemp(t)
                gen := o.out
                o.out = saveout
 
@@ -1261,9 +1239,12 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                o.out = append(o.out, nif)
                return r
 
+       case ir.OCALLMETH:
+               base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+               panic("unreachable")
+
        case ir.OCALLFUNC,
                ir.OCALLINTER,
-               ir.OCALLMETH,
                ir.OCAP,
                ir.OCOMPLEX,
                ir.OCOPY,
@@ -1273,9 +1254,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                ir.OMAKEMAP,
                ir.OMAKESLICE,
                ir.OMAKESLICECOPY,
+               ir.OMAX,
+               ir.OMIN,
                ir.ONEW,
                ir.OREAL,
-               ir.ORECOVER,
+               ir.ORECOVERFP,
                ir.OSTR2BYTES,
                ir.OSTR2BYTESTMP,
                ir.OSTR2RUNES:
@@ -1293,6 +1276,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                }
                return n
 
+       case ir.OINLCALL:
+               n := n.(*ir.InlinedCallExpr)
+               o.stmtList(n.Body)
+               return n.SingleResult()
+
        case ir.OAPPEND:
                // Check for append(x, make([]T, y)...) .
                n := n.(*ir.CallExpr)
@@ -1327,11 +1315,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
                }
                return n
 
-       case ir.OCALLPART:
+       case ir.OMETHVALUE:
                n := n.(*ir.SelectorExpr)
                n.X = o.expr(n.X, nil)
                if n.Transient() {
-                       t := typecheck.PartialCallType(n)
+                       t := typecheck.MethodValueType(n)
                        n.Prealloc = o.newTemp(t, false)
                }
                return n
@@ -1436,10 +1424,23 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
 
                // Emit eval+insert of dynamic entries, one at a time.
                for _, r := range dynamics {
-                       as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value)
-                       typecheck.Stmt(as) // Note: this converts the OINDEX to an OINDEXMAP
+                       lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, r.Key)).(*ir.IndexExpr)
+                       base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs)
+                       lhs.RType = n.RType
+
+                       as := ir.NewAssignStmt(base.Pos, lhs, r.Value)
+                       typecheck.Stmt(as)
                        o.stmt(as)
                }
+
+               // Remember that we issued these assignments so we can include that count
+               // in the map alloc hint.
+               // We're assuming here that all the keys in the map literal are distinct.
+               // If any are equal, this will be an overcount. Probably not worth accounting
+               // for that, as equal keys in map literals are rare, and at worst we waste
+               // a bit of space.
+               n.Len += int64(len(dynamics))
+
                return m
        }
 
@@ -1449,10 +1450,14 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
 // as2func orders OAS2FUNC nodes. It creates temporaries to ensure left-to-right assignment.
 // The caller should order the right-hand side of the assignment before calling order.as2func.
 // It rewrites,
+//
 //     a, b, a = ...
+//
 // as
+//
 //     tmp1, tmp2, tmp3 = ...
 //     a, b, a = tmp1, tmp2, tmp3
+//
 // This is necessary to ensure left to right assignment order.
 func (o *orderState) as2func(n *ir.AssignListStmt) {
        results := n.Rhs[0].Type()
@@ -1497,323 +1502,3 @@ func (o *orderState) as2ok(n *ir.AssignListStmt) {
        o.out = append(o.out, n)
        o.stmt(typecheck.Stmt(as))
 }
-
-var wrapGoDefer_prgen int
-
-// wrapGoDefer wraps the target of a "go" or "defer" statement with a
-// new "function with no arguments" closure. Specifically, it converts
-//
-//   defer f(x, y)
-//
-// to
-//
-//   x1, y1 := x, y
-//   defer func() { f(x1, y1) }()
-//
-// This is primarily to enable a quicker bringup of defers under the
-// new register ABI; by doing this conversion, we can simplify the
-// code in the runtime that invokes defers on the panic path.
-func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
-       call := n.Call
-
-       var callX ir.Node        // thing being called
-       var callArgs []ir.Node   // call arguments
-       var keepAlive []*ir.Name // KeepAlive list from call, if present
-
-       // A helper to recreate the call within the closure.
-       var mkNewCall func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node
-
-       // Defer calls come in many shapes and sizes; not all of them
-       // are ir.CallExpr's. Examine the type to see what we're dealing with.
-       switch x := call.(type) {
-       case *ir.CallExpr:
-               callX = x.X
-               callArgs = x.Args
-               keepAlive = x.KeepAlive
-               mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
-                       newcall := ir.NewCallExpr(pos, op, fun, args)
-                       newcall.IsDDD = x.IsDDD
-                       return ir.Node(newcall)
-               }
-       case *ir.UnaryExpr: // ex: OCLOSE
-               callArgs = []ir.Node{x.X}
-               mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
-                       if len(args) != 1 {
-                               panic("internal error, expecting single arg")
-                       }
-                       return ir.Node(ir.NewUnaryExpr(pos, op, args[0]))
-               }
-       case *ir.BinaryExpr: // ex: OCOPY
-               callArgs = []ir.Node{x.X, x.Y}
-               mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
-                       if len(args) != 2 {
-                               panic("internal error, expecting two args")
-                       }
-                       return ir.Node(ir.NewBinaryExpr(pos, op, args[0], args[1]))
-               }
-       default:
-               panic("unhandled op")
-       }
-
-       // No need to wrap if called func has no args, no receiver, and no results.
-       // However in the case of "defer func() { ... }()" we need to
-       // protect against the possibility of directClosureCall rewriting
-       // things so that the call does have arguments.
-       //
-       // Do wrap method calls (OCALLMETH, OCALLINTER), because it has
-       // a receiver.
-       //
-       // Also do wrap builtin functions, because they may be expanded to
-       // calls with arguments (e.g. ORECOVER).
-       //
-       // TODO: maybe not wrap if the called function has no arguments and
-       // only in-register results?
-       if len(callArgs) == 0 && call.Op() == ir.OCALLFUNC && callX.Type().NumResults() == 0 {
-               if c, ok := call.(*ir.CallExpr); ok && callX != nil && callX.Op() == ir.OCLOSURE {
-                       clo := callX.(*ir.ClosureExpr)
-                       clo.Func.SetClosureCalled(false)
-                       clo.IsGoWrap = true
-                       c.PreserveClosure = true
-               }
-               return
-       }
-
-       if c, ok := call.(*ir.CallExpr); ok {
-               // To simplify things, turn f(a, b, []T{c, d, e}...) back
-               // into f(a, b, c, d, e) -- when the final call is run through the
-               // type checker below, it will rebuild the proper slice literal.
-               undoVariadic(c)
-               callX = c.X
-               callArgs = c.Args
-       }
-
-       // This is set to true if the closure we're generating escapes
-       // (needs heap allocation).
-       cloEscapes := func() bool {
-               if n.Op() == ir.OGO {
-                       // For "go", assume that all closures escape.
-                       return true
-               }
-               // For defer, just use whatever result escape analysis
-               // has determined for the defer.
-               return n.Esc() != ir.EscNever
-       }()
-
-       // A helper for making a copy of an argument. Note that it is
-       // not safe to use o.copyExpr(arg) if we're putting a
-       // reference to the temp into the closure (as opposed to
-       // copying it in by value), since in the by-reference case we
-       // need a temporary whose lifetime extends to the end of the
-       // function (as opposed to being local to the current block or
-       // statement being ordered).
-       mkArgCopy := func(arg ir.Node) *ir.Name {
-               t := arg.Type()
-               byval := t.Size() <= 128 || cloEscapes
-               var argCopy *ir.Name
-               if byval {
-                       argCopy = o.copyExpr(arg)
-               } else {
-                       argCopy = typecheck.Temp(t)
-                       o.append(ir.NewAssignStmt(base.Pos, argCopy, arg))
-               }
-               // The value of 128 below is meant to be consistent with code
-               // in escape analysis that picks byval/byaddr based on size.
-               argCopy.SetByval(byval)
-               return argCopy
-       }
-
-       // getUnsafeArg looks for an unsafe.Pointer arg that has been
-       // previously captured into the call's keepalive list, returning
-       // the name node for it if found.
-       getUnsafeArg := func(arg ir.Node) *ir.Name {
-               // Look for uintptr(unsafe.Pointer(name))
-               if arg.Op() != ir.OCONVNOP {
-                       return nil
-               }
-               if !arg.Type().IsUintptr() {
-                       return nil
-               }
-               if !arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
-                       return nil
-               }
-               arg = arg.(*ir.ConvExpr).X
-               argname, ok := arg.(*ir.Name)
-               if !ok {
-                       return nil
-               }
-               for i := range keepAlive {
-                       if argname == keepAlive[i] {
-                               return argname
-                       }
-               }
-               return nil
-       }
-
-       // Copy the arguments to the function into temps.
-       //
-       // For calls with uintptr(unsafe.Pointer(...)) args that are being
-       // kept alive (see code in (*orderState).call that does this), use
-       // the existing arg copy instead of creating a new copy.
-       unsafeArgs := make([]*ir.Name, len(callArgs))
-       origArgs := callArgs
-       var newNames []*ir.Name
-       for i := range callArgs {
-               arg := callArgs[i]
-               var argname *ir.Name
-               unsafeArgName := getUnsafeArg(arg)
-               if unsafeArgName != nil {
-                       // arg has been copied already, use keepalive copy
-                       argname = unsafeArgName
-                       unsafeArgs[i] = unsafeArgName
-               } else {
-                       argname = mkArgCopy(arg)
-               }
-               newNames = append(newNames, argname)
-       }
-
-       // Deal with cases where the function expression (what we're
-       // calling) is not a simple function symbol.
-       var fnExpr *ir.Name
-       var methSelectorExpr *ir.SelectorExpr
-       if callX != nil {
-               switch {
-               case callX.Op() == ir.ODOTMETH || callX.Op() == ir.ODOTINTER:
-                       // Handle defer of a method call, e.g. "defer v.MyMethod(x, y)"
-                       n := callX.(*ir.SelectorExpr)
-                       n.X = mkArgCopy(n.X)
-                       methSelectorExpr = n
-                       if callX.Op() == ir.ODOTINTER {
-                               // Currently for "defer i.M()" if i is nil it panics at the
-                               // point of defer statement, not when deferred function is called.
-                               // (I think there is an issue discussing what is the intended
-                               // behavior but I cannot find it.)
-                               // We need to do the nil check outside of the wrapper.
-                               tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X))
-                               c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, tab)
-                               c.SetTypecheck(1)
-                               o.append(c)
-                       }
-               case !(callX.Op() == ir.ONAME && callX.(*ir.Name).Class == ir.PFUNC):
-                       // Deal with "defer returnsafunc()(x, y)" (for
-                       // example) by copying the callee expression.
-                       fnExpr = mkArgCopy(callX)
-                       if callX.Op() == ir.OCLOSURE {
-                               // For "defer func(...)", in addition to copying the
-                               // closure into a temp, mark it as no longer directly
-                               // called.
-                               callX.(*ir.ClosureExpr).Func.SetClosureCalled(false)
-                       }
-               }
-       }
-
-       // Create a new no-argument function that we'll hand off to defer.
-       var noFuncArgs []*ir.Field
-       noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil)
-       wrapGoDefer_prgen++
-       outerfn := ir.CurFunc
-       wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen)
-       sym := types.LocalPkg.Lookup(wrapname)
-       fn := typecheck.DeclFunc(sym, noargst)
-       fn.SetIsHiddenClosure(true)
-       fn.SetWrapper(true)
-
-       // helper for capturing reference to a var declared in an outer scope.
-       capName := func(pos src.XPos, fn *ir.Func, n *ir.Name) *ir.Name {
-               t := n.Type()
-               cv := ir.CaptureName(pos, fn, n)
-               cv.SetType(t)
-               return typecheck.Expr(cv).(*ir.Name)
-       }
-
-       // Call args (x1, y1) need to be captured as part of the newly
-       // created closure.
-       newCallArgs := []ir.Node{}
-       for i := range newNames {
-               var arg ir.Node
-               arg = capName(callArgs[i].Pos(), fn, newNames[i])
-               if unsafeArgs[i] != nil {
-                       arg = ir.NewConvExpr(arg.Pos(), origArgs[i].Op(), origArgs[i].Type(), arg)
-               }
-               newCallArgs = append(newCallArgs, arg)
-       }
-       // Also capture the function or method expression (if needed) into
-       // the closure.
-       if fnExpr != nil {
-               callX = capName(callX.Pos(), fn, fnExpr)
-       }
-       if methSelectorExpr != nil {
-               methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name))
-       }
-       ir.FinishCaptureNames(n.Pos(), outerfn, fn)
-
-       // This flags a builtin as opposed to a regular call.
-       irregular := (call.Op() != ir.OCALLFUNC &&
-               call.Op() != ir.OCALLMETH &&
-               call.Op() != ir.OCALLINTER)
-
-       // Construct new function body:  f(x1, y1)
-       op := ir.OCALL
-       if irregular {
-               op = call.Op()
-       }
-       newcall := mkNewCall(call.Pos(), op, callX, newCallArgs)
-
-       // Type-check the result.
-       if !irregular {
-               typecheck.Call(newcall.(*ir.CallExpr))
-       } else {
-               typecheck.Stmt(newcall)
-       }
-
-       // Finalize body, register function on the main decls list.
-       fn.Body = []ir.Node{newcall}
-       typecheck.FinishFuncBody()
-       typecheck.Func(fn)
-       typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
-
-       // Create closure expr
-       clo := ir.NewClosureExpr(n.Pos(), fn)
-       fn.OClosure = clo
-       clo.SetType(fn.Type())
-
-       // Set escape properties for closure.
-       if n.Op() == ir.OGO {
-               // For "go", assume that the closure is going to escape.
-               clo.SetEsc(ir.EscHeap)
-               clo.IsGoWrap = true
-       } else {
-               // For defer, just use whatever result escape analysis
-               // has determined for the defer.
-               if n.Esc() == ir.EscNever {
-                       clo.SetTransient(true)
-                       clo.SetEsc(ir.EscNone)
-               }
-       }
-
-       // Create new top level call to closure over argless function.
-       topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, []ir.Node{})
-       typecheck.Call(topcall)
-
-       // Tag the call to insure that directClosureCall doesn't undo our work.
-       topcall.PreserveClosure = true
-
-       fn.SetClosureCalled(false)
-
-       // Finally, point the defer statement at the newly generated call.
-       n.Call = topcall
-}
-
-// isFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions.
-func isFuncPCIntrinsic(n *ir.CallExpr) bool {
-       if n.Op() != ir.OCALLFUNC || n.X.Op() != ir.ONAME {
-               return false
-       }
-       fn := n.X.(*ir.Name).Sym()
-       return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") &&
-               (fn.Pkg.Path == "internal/abi" || fn.Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "internal/abi")
-}
-
-// isIfaceOfFunc returns whether n is an interface conversion from a direct reference of a func.
-func isIfaceOfFunc(n ir.Node) bool {
-       return n.Op() == ir.OCONVIFACE && n.(*ir.ConvExpr).X.Op() == ir.ONAME && n.(*ir.ConvExpr).X.(*ir.Name).Class == ir.PFUNC
-}