summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/walk/order.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/walk/order.go')
-rw-r--r--src/cmd/compile/internal/walk/order.go1507
1 files changed, 1507 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go
new file mode 100644
index 0000000..861c122
--- /dev/null
+++ b/src/cmd/compile/internal/walk/order.go
@@ -0,0 +1,1507 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package walk
+
+import (
+ "fmt"
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "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"
+)
+
+// Rewrite tree to use separate statements to enforce
+// order of evaluation. Makes walk easier, because it
+// can (after this runs) reorder at will within an expression.
+//
+// Rewrite m[k] op= r into m[k] = m[k] op r if op is / or %.
+//
+// Introduce temporaries as needed by runtime routines.
+// For example, the map runtime routines take the map key
+// by reference, so make sure all map keys are addressable
+// by copying them to temporaries as needed.
+// The same is true for channel operations.
+//
+// Arrange that map index expressions only appear in direct
+// assignments x = m[k] or m[k] = x, never in larger expressions.
+//
+// Arrange that receive expressions only appear in direct assignments
+// x = <-c or as standalone statements <-c, never in larger expressions.
+
+// TODO(rsc): The temporary introduction during multiple assignments
+// should be moved into this file, so that the temporaries can be cleaned
+// and so that conversions implicit in the OAS2FUNC and OAS2RECV
+// nodes can be made explicit and then have their temporaries cleaned.
+
+// TODO(rsc): Goto and multilevel break/continue can jump over
+// inserted VARKILL annotations. Work out a way to handle these.
+// The current implementation is safe, in that it will execute correctly.
+// But it won't reuse temporaries as aggressively as it might, and
+// it can result in unnecessary zeroing of those variables in the function
+// prologue.
+
+// orderState holds state during the ordering process.
+type orderState struct {
+ out []ir.Node // list of generated statements
+ temp []*ir.Name // stack of temporary variables
+ free map[string][]*ir.Name // free list of unused temporaries, by type.LinkString().
+ edit func(ir.Node) ir.Node // cached closure of o.exprNoLHS
+}
+
+// Order rewrites fn.Nbody to apply the ordering constraints
+// described in the comment at the top of the file.
+func order(fn *ir.Func) {
+ if base.Flag.W > 1 {
+ s := fmt.Sprintf("\nbefore order %v", fn.Sym())
+ ir.DumpList(s, fn.Body)
+ }
+
+ orderBlock(&fn.Body, map[string][]*ir.Name{})
+}
+
+// append typechecks stmt and appends it to out.
+func (o *orderState) append(stmt ir.Node) {
+ o.out = append(o.out, typecheck.Stmt(stmt))
+}
+
+// newTemp allocates a new temporary with the given type,
+// pushes it onto the temp stack, and returns it.
+// If clear is true, newTemp emits code to zero the temporary.
+func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
+ var v *ir.Name
+ 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)
+ }
+ o.free[key] = a[:len(a)-1]
+ } else {
+ v = typecheck.Temp(t)
+ }
+ if clear {
+ o.append(ir.NewAssignStmt(base.Pos, v, nil))
+ }
+
+ o.temp = append(o.temp, v)
+ return v
+}
+
+// copyExpr behaves like newTemp but also emits
+// code to initialize the temporary to the value n.
+func (o *orderState) copyExpr(n ir.Node) *ir.Name {
+ return o.copyExpr1(n, false)
+}
+
+// copyExprClear is like copyExpr but clears the temp before assignment.
+// It is provided for use when the evaluation of tmp = n turns into
+// a function call that is passed a pointer to the temporary as the output space.
+// If the call blocks before tmp has been written,
+// the garbage collector will still treat the temporary as live,
+// so we must zero it before entering that call.
+// Today, this only happens for channel receive operations.
+// (The other candidate would be map access, but map access
+// returns a pointer to the result data instead of taking a pointer
+// to be filled in.)
+func (o *orderState) copyExprClear(n ir.Node) *ir.Name {
+ return o.copyExpr1(n, true)
+}
+
+func (o *orderState) copyExpr1(n ir.Node, clear bool) *ir.Name {
+ t := n.Type()
+ v := o.newTemp(t, clear)
+ o.append(ir.NewAssignStmt(base.Pos, v, n))
+ return v
+}
+
+// cheapExpr returns a cheap version of n.
+// The definition of cheap is that n is a variable or constant.
+// If not, cheapExpr allocates a new tmp, emits tmp = n,
+// and then returns tmp.
+func (o *orderState) cheapExpr(n ir.Node) ir.Node {
+ if n == nil {
+ return nil
+ }
+
+ switch n.Op() {
+ case ir.ONAME, ir.OLITERAL, ir.ONIL:
+ return n
+ case ir.OLEN, ir.OCAP:
+ n := n.(*ir.UnaryExpr)
+ l := o.cheapExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.UnaryExpr)
+ a.X = l
+ return typecheck.Expr(a)
+ }
+
+ return o.copyExpr(n)
+}
+
+// safeExpr returns a safe version of n.
+// The definition of safe is that n can appear multiple times
+// without violating the semantics of the original program,
+// and that assigning to the safe version has the same effect
+// as assigning to the original n.
+//
+// The intended use is to apply to x when rewriting x += y into x = x + y.
+func (o *orderState) safeExpr(n ir.Node) ir.Node {
+ switch n.Op() {
+ case ir.ONAME, ir.OLITERAL, ir.ONIL:
+ return n
+
+ case ir.OLEN, ir.OCAP:
+ n := n.(*ir.UnaryExpr)
+ l := o.safeExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.UnaryExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.ODOT:
+ n := n.(*ir.SelectorExpr)
+ l := o.safeExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.SelectorExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ l := o.cheapExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.SelectorExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ l := o.cheapExpr(n.X)
+ if l == n.X {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.StarExpr)
+ a.X = l
+ return typecheck.Expr(a)
+
+ case ir.OINDEX, ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ var l ir.Node
+ if n.X.Type().IsArray() {
+ l = o.safeExpr(n.X)
+ } else {
+ l = o.cheapExpr(n.X)
+ }
+ r := o.cheapExpr(n.Index)
+ if l == n.X && r == n.Index {
+ return n
+ }
+ a := ir.SepCopy(n).(*ir.IndexExpr)
+ a.X = l
+ a.Index = r
+ return typecheck.Expr(a)
+
+ default:
+ base.Fatalf("order.safeExpr %v", n.Op())
+ return nil // not reached
+ }
+}
+
+// isaddrokay reports whether it is okay to pass n's address to runtime routines.
+// Taking the address of a variable makes the liveness and optimization analyses
+// lose track of where the variable's lifetime ends. To avoid hurting the analyses
+// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
+// because we emit explicit VARKILL instructions marking the end of those
+// temporaries' lifetimes.
+func isaddrokay(n ir.Node) bool {
+ return ir.IsAddressable(n) && (n.Op() != ir.ONAME || n.(*ir.Name).Class == ir.PEXTERN || ir.IsAutoTmp(n))
+}
+
+// addrTemp ensures that n is okay to pass by address to runtime routines.
+// If the original argument n is not okay, addrTemp creates a tmp, emits
+// tmp = n, and then returns tmp.
+// The result of addrTemp MUST be assigned back to n, e.g.
+// n.Left = o.addrTemp(n.Left)
+func (o *orderState) addrTemp(n ir.Node) ir.Node {
+ if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL {
+ // TODO: expand this to all static composite literal nodes?
+ n = typecheck.DefaultLit(n, nil)
+ types.CalcSize(n.Type())
+ vstat := readonlystaticname(n.Type())
+ var s staticinit.Schedule
+ s.StaticAssign(vstat, 0, n, n.Type())
+ if s.Out != nil {
+ base.Fatalf("staticassign of const generated code: %+v", n)
+ }
+ vstat = typecheck.Expr(vstat).(*ir.Name)
+ return vstat
+ }
+ if isaddrokay(n) {
+ return n
+ }
+ return o.copyExpr(n)
+}
+
+// mapKeyTemp prepares n to be a key in a map runtime call and returns n.
+// It should only be used for map runtime calls which have *_fast* versions.
+func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
+ // Most map calls need to take the address of the key.
+ // Exception: map*_fast* calls. See golang.org/issue/19015.
+ alg := mapfast(t)
+ if alg == mapslow {
+ return o.addrTemp(n)
+ }
+ var kt *types.Type
+ switch alg {
+ case mapfast32:
+ kt = types.Types[types.TUINT32]
+ case mapfast64:
+ kt = types.Types[types.TUINT64]
+ case mapfast32ptr, mapfast64ptr:
+ kt = types.Types[types.TUNSAFEPTR]
+ case mapfaststr:
+ kt = types.Types[types.TSTRING]
+ }
+ nt := n.Type()
+ switch {
+ case nt == kt:
+ 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))
+ case nt.IsInteger() && kt.IsInteger():
+ // can directly convert (e.g. int32 to uint32)
+ if n.Op() == ir.OLITERAL && nt.IsSigned() {
+ // avoid constant overflow error
+ n = ir.NewConstExpr(constant.MakeUint64(uint64(ir.Int64Val(n))), n)
+ n.SetType(kt)
+ return n
+ }
+ return typecheck.Expr(ir.NewConvExpr(n.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 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))
+ return tmp
+ }
+}
+
+// mapKeyReplaceStrConv replaces OBYTES2STR by OBYTES2STRTMP
+// in n to avoid string allocations for keys in map lookups.
+// Returns a bool that signals if a modification was made.
+//
+// For:
+// x = m[string(k)]
+// x = m[T1{... Tn{..., string(k), ...}]
+// where k is []byte, T1 to Tn is a nesting of struct and array literals,
+// the allocation of backing bytes for the string can be avoided
+// by reusing the []byte backing array. These are special cases
+// for avoiding allocations when converting byte slices to strings.
+// It would be nice to handle these generally, but because
+// []byte keys are not allowed in maps, the use of string(k)
+// comes up in important cases in practice. See issue 3512.
+func mapKeyReplaceStrConv(n ir.Node) bool {
+ var replaced bool
+ switch n.Op() {
+ case ir.OBYTES2STR:
+ n := n.(*ir.ConvExpr)
+ n.SetOp(ir.OBYTES2STRTMP)
+ replaced = true
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elem := range n.List {
+ elem := elem.(*ir.StructKeyExpr)
+ if mapKeyReplaceStrConv(elem.Value) {
+ replaced = true
+ }
+ }
+ case ir.OARRAYLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elem := range n.List {
+ if elem.Op() == ir.OKEY {
+ elem = elem.(*ir.KeyExpr).Value
+ }
+ if mapKeyReplaceStrConv(elem) {
+ replaced = true
+ }
+ }
+ }
+ return replaced
+}
+
+type ordermarker int
+
+// markTemp returns the top of the temporary variable stack.
+func (o *orderState) markTemp() ordermarker {
+ return ordermarker(len(o.temp))
+}
+
+// popTemp pops temporaries off the stack until reaching the mark,
+// which must have been returned by markTemp.
+func (o *orderState) popTemp(mark ordermarker) {
+ for _, n := range o.temp[mark:] {
+ key := n.Type().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
+ for i := range s {
+ orderMakeSliceCopy(s[i:])
+ o.stmt(s[i])
+ }
+}
+
+// orderMakeSliceCopy matches the pattern:
+// m = OMAKESLICE([]T, x); OCOPY(m, s)
+// and rewrites it to:
+// m = OMAKESLICECOPY([]T, x, s); nil
+func orderMakeSliceCopy(s []ir.Node) {
+ if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
+ return
+ }
+ if len(s) < 2 || s[0] == nil || s[0].Op() != ir.OAS || s[1] == nil || s[1].Op() != ir.OCOPY {
+ return
+ }
+
+ as := s[0].(*ir.AssignStmt)
+ cp := s[1].(*ir.BinaryExpr)
+ if as.Y == nil || as.Y.Op() != ir.OMAKESLICE || ir.IsBlank(as.X) ||
+ as.X.Op() != ir.ONAME || cp.X.Op() != ir.ONAME || cp.Y.Op() != ir.ONAME ||
+ as.X.Name() != cp.X.Name() || cp.X.Name() == cp.Y.Name() {
+ // The line above this one is correct with the differing equality operators:
+ // we want as.X and cp.X to be the same name,
+ // but we want the initial data to be coming from a different name.
+ return
+ }
+
+ mk := as.Y.(*ir.MakeExpr)
+ if mk.Esc() == ir.EscNone || mk.Len == nil || mk.Cap != nil {
+ return
+ }
+ mk.SetOp(ir.OMAKESLICECOPY)
+ mk.Cap = cp.Y
+ // Set bounded when m = OMAKESLICE([]T, len(s)); OCOPY(m, s)
+ mk.SetBounded(mk.Len.Op() == ir.OLEN && ir.SameSafeExpr(mk.Len.(*ir.UnaryExpr).X, cp.Y))
+ as.Y = typecheck.Expr(mk)
+ s[1] = nil // remove separate copy call
+}
+
+// edge inserts coverage instrumentation for libfuzzer.
+func (o *orderState) edge() {
+ if base.Debug.Libfuzzer == 0 {
+ return
+ }
+
+ // Create a new uint8 counter to be allocated in section
+ // __libfuzzer_extra_counters.
+ counter := staticinit.StaticName(types.Types[types.TUINT8])
+ counter.SetLibfuzzerExtraCounter(true)
+ // As well as setting SetLibfuzzerExtraCounter, we preemptively set the
+ // symbol type to SLIBFUZZER_EXTRA_COUNTER so that the race detector
+ // instrumentation pass (which does not have access to the flags set by
+ // SetLibfuzzerExtraCounter) knows to ignore them. This information is
+ // lost by the time it reaches the compile step, so SetLibfuzzerExtraCounter
+ // is still necessary.
+ counter.Linksym().Type = objabi.SLIBFUZZER_EXTRA_COUNTER
+
+ // counter += 1
+ incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
+ o.append(incr)
+}
+
+// orderBlock orders the block of statements in n into a new slice,
+// and then replaces the old slice in n with the new slice.
+// free is a map that can be used to obtain temporary variables by type.
+func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
+ var order orderState
+ order.free = free
+ mark := order.markTemp()
+ order.edge()
+ order.stmtList(*n)
+ order.cleanTemp(mark)
+ *n = order.out
+}
+
+// exprInPlace orders the side effects in *np and
+// leaves them as the init list of the final *np.
+// The result of exprInPlace MUST be assigned back to n, e.g.
+// n.Left = o.exprInPlace(n.Left)
+func (o *orderState) exprInPlace(n ir.Node) ir.Node {
+ var order orderState
+ order.free = o.free
+ n = order.expr(n, nil)
+ n = ir.InitExpr(order.out, n)
+
+ // insert new temporaries from order
+ // at head of outer list.
+ o.temp = append(o.temp, order.temp...)
+ return n
+}
+
+// orderStmtInPlace orders the side effects of the single statement *np
+// and replaces it with the resulting statement list.
+// The result of orderStmtInPlace MUST be assigned back to n, e.g.
+// n.Left = orderStmtInPlace(n.Left)
+// free is a map that can be used to obtain temporary variables by type.
+func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
+ var order orderState
+ order.free = free
+ mark := order.markTemp()
+ order.stmt(n)
+ order.cleanTemp(mark)
+ return ir.NewBlockStmt(src.NoXPos, order.out)
+}
+
+// init moves n's init list to o.out.
+func (o *orderState) init(n ir.Node) {
+ if ir.MayBeShared(n) {
+ // For concurrency safety, don't mutate potentially shared nodes.
+ // First, ensure that no work is required here.
+ if len(n.Init()) > 0 {
+ base.Fatalf("order.init shared node with ninit")
+ }
+ return
+ }
+ o.stmtList(ir.TakeInit(n))
+}
+
+// call orders the call expression n.
+// n.Op is 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.OCALLINTER {
+ switch n := nn.(type) {
+ default:
+ base.Fatalf("unexpected call: %+v", n)
+ case *ir.UnaryExpr:
+ n.X = o.expr(n.X, nil)
+ case *ir.ConvExpr:
+ n.X = o.expr(n.X, nil)
+ case *ir.BinaryExpr:
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+ case *ir.MakeExpr:
+ n.Len = o.expr(n.Len, nil)
+ n.Cap = o.expr(n.Cap, nil)
+ case *ir.CallExpr:
+ o.exprList(n.Args)
+ }
+ return
+ }
+
+ n := nn.(*ir.CallExpr)
+ typecheck.FixVariadicCall(n)
+
+ if isFuncPCIntrinsic(n) && isIfaceOfFunc(n.Args[0]) {
+ // 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)
+ o.exprList(n.Args)
+}
+
+// mapAssign appends n to o.out.
+func (o *orderState) mapAssign(n ir.Node) {
+ switch n.Op() {
+ default:
+ base.Fatalf("order.mapAssign %v", n.Op())
+
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ if n.X.Op() == ir.OINDEXMAP {
+ n.Y = o.safeMapRHS(n.Y)
+ }
+ o.out = append(o.out, n)
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ if n.X.Op() == ir.OINDEXMAP {
+ n.Y = o.safeMapRHS(n.Y)
+ }
+ o.out = append(o.out, n)
+ }
+}
+
+func (o *orderState) safeMapRHS(r ir.Node) ir.Node {
+ // Make sure we evaluate the RHS before starting the map insert.
+ // We need to make sure the RHS won't panic. See issue 22881.
+ if r.Op() == ir.OAPPEND {
+ r := r.(*ir.CallExpr)
+ s := r.Args[1:]
+ for i, n := range s {
+ s[i] = o.cheapExpr(n)
+ }
+ return r
+ }
+ return o.cheapExpr(r)
+}
+
+// stmt orders the statement n, appending to o.out.
+// Temporaries created during the statement are cleaned
+// up using VARKILL instructions as possible.
+func (o *orderState) stmt(n ir.Node) {
+ if n == nil {
+ return
+ }
+
+ lno := ir.SetPos(n)
+ o.init(n)
+
+ switch n.Op() {
+ default:
+ base.Fatalf("order.stmt %v", n.Op())
+
+ case ir.OVARKILL, ir.OVARLIVE, ir.OINLMARK:
+ o.out = append(o.out, n)
+
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, n.X)
+ o.mapAssign(n)
+ o.cleanTemp(t)
+
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+
+ if base.Flag.Cfg.Instrumenting || n.X.Op() == ir.OINDEXMAP && (n.AsOp == ir.ODIV || n.AsOp == ir.OMOD) {
+ // Rewrite m[k] op= r into m[k] = m[k] op r so
+ // that we can ensure that if op panics
+ // because r is zero, the panic happens before
+ // the map assignment.
+ // DeepCopy is a big hammer here, but safeExpr
+ // makes sure there is nothing too deep being copied.
+ l1 := o.safeExpr(n.X)
+ l2 := ir.DeepCopy(src.NoXPos, l1)
+ if l2.Op() == ir.OINDEXMAP {
+ l2 := l2.(*ir.IndexExpr)
+ l2.Assigned = false
+ }
+ l2 = o.copyExpr(l2)
+ r := o.expr(typecheck.Expr(ir.NewBinaryExpr(n.Pos(), n.AsOp, l2, n.Y)), nil)
+ as := typecheck.Stmt(ir.NewAssignStmt(n.Pos(), l1, r))
+ o.mapAssign(as)
+ o.cleanTemp(t)
+ return
+ }
+
+ o.mapAssign(n)
+ o.cleanTemp(t)
+
+ case ir.OAS2:
+ n := n.(*ir.AssignListStmt)
+ t := o.markTemp()
+ o.exprList(n.Lhs)
+ o.exprList(n.Rhs)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Special: avoid copy of func call n.Right
+ case ir.OAS2FUNC:
+ n := n.(*ir.AssignListStmt)
+ t := o.markTemp()
+ o.exprList(n.Lhs)
+ 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.cleanTemp(t)
+
+ // Special: use temporary variables to hold result,
+ // so that runtime can take address of temporary.
+ // No temporary for blank assignment.
+ //
+ // OAS2MAPR: make sure key is addressable if needed,
+ // and make sure OINDEXMAP is not copied out.
+ case ir.OAS2DOTTYPE, ir.OAS2RECV, ir.OAS2MAPR:
+ n := n.(*ir.AssignListStmt)
+ t := o.markTemp()
+ o.exprList(n.Lhs)
+
+ switch r := n.Rhs[0]; r.Op() {
+ case ir.ODOTTYPE2:
+ r := r.(*ir.TypeAssertExpr)
+ r.X = o.expr(r.X, nil)
+ case ir.ODYNAMICDOTTYPE2:
+ r := r.(*ir.DynamicTypeAssertExpr)
+ r.X = o.expr(r.X, nil)
+ r.T = o.expr(r.T, nil)
+ case ir.ORECV:
+ r := r.(*ir.UnaryExpr)
+ r.X = o.expr(r.X, nil)
+ case ir.OINDEXMAP:
+ r := r.(*ir.IndexExpr)
+ r.X = o.expr(r.X, nil)
+ r.Index = o.expr(r.Index, nil)
+ // See similar conversion for OINDEXMAP below.
+ _ = mapKeyReplaceStrConv(r.Index)
+ r.Index = o.mapKeyTemp(r.X.Type(), r.Index)
+ default:
+ base.Fatalf("order.stmt: %v", r.Op())
+ }
+
+ o.as2ok(n)
+ o.cleanTemp(t)
+
+ // Special: does not save n onto out.
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
+ o.stmtList(n.List)
+
+ // Special: n->left is not an expression; save as is.
+ case ir.OBREAK,
+ ir.OCONTINUE,
+ ir.ODCL,
+ ir.ODCLCONST,
+ ir.ODCLTYPE,
+ ir.OFALL,
+ ir.OGOTO,
+ ir.OLABEL,
+ ir.OTAILCALL:
+ o.out = append(o.out, n)
+
+ // Special: handle call arguments.
+ case ir.OCALLFUNC, ir.OCALLINTER:
+ n := n.(*ir.CallExpr)
+ t := o.markTemp()
+ o.call(n)
+ o.out = append(o.out, n)
+ o.cleanTemp(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.OCHECKNIL, 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)
+
+ case ir.OCOPY:
+ n := n.(*ir.BinaryExpr)
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
+ n := n.(*ir.CallExpr)
+ t := o.markTemp()
+ o.call(n)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Special: order arguments to inner call but not call itself.
+ case ir.ODEFER, ir.OGO:
+ n := n.(*ir.GoDeferStmt)
+ t := o.markTemp()
+ o.init(n.Call)
+ o.call(n.Call)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.ODELETE:
+ n := n.(*ir.CallExpr)
+ t := o.markTemp()
+ n.Args[0] = o.expr(n.Args[0], nil)
+ n.Args[1] = o.expr(n.Args[1], nil)
+ n.Args[1] = o.mapKeyTemp(n.Args[0].Type(), n.Args[1])
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Clean temporaries from condition evaluation at
+ // beginning of loop body and after for statement.
+ case ir.OFOR:
+ n := n.(*ir.ForStmt)
+ t := o.markTemp()
+ n.Cond = o.exprInPlace(n.Cond)
+ n.Body.Prepend(o.cleanTempNoPop(t)...)
+ orderBlock(&n.Body, o.free)
+ n.Post = orderStmtInPlace(n.Post, o.free)
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // Clean temporaries from condition at
+ // beginning of both branches.
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ t := o.markTemp()
+ n.Cond = o.exprInPlace(n.Cond)
+ n.Body.Prepend(o.cleanTempNoPop(t)...)
+ n.Else.Prepend(o.cleanTempNoPop(t)...)
+ o.popTemp(t)
+ orderBlock(&n.Body, o.free)
+ orderBlock(&n.Else, o.free)
+ o.out = append(o.out, n)
+
+ case ir.ORANGE:
+ // n.Right is the expression being ranged over.
+ // order it, and then make a copy if we need one.
+ // We almost always do, to ensure that we don't
+ // see any value changes made during the loop.
+ // Usually the copy is cheap (e.g., array pointer,
+ // chan, slice, string are all tiny).
+ // The exception is ranging over an array value
+ // (not a slice, not a pointer to array),
+ // which must make a copy to avoid seeing updates made during
+ // the range body. Ranging over an array value is uncommon though.
+
+ // Mark []byte(str) range expression to reuse string backing storage.
+ // It is safe because the storage cannot be mutated.
+ n := n.(*ir.RangeStmt)
+ if n.X.Op() == ir.OSTR2BYTES {
+ n.X.(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP)
+ }
+
+ t := o.markTemp()
+ n.X = o.expr(n.X, nil)
+
+ orderBody := true
+ xt := typecheck.RangeExprType(n.X.Type())
+ switch xt.Kind() {
+ default:
+ base.Fatalf("order.stmt range %v", n.Type())
+
+ case types.TARRAY, types.TSLICE:
+ if n.Value == nil || ir.IsBlank(n.Value) {
+ // for i := range x will only use x once, to compute len(x).
+ // No need to copy it.
+ break
+ }
+ fallthrough
+
+ case types.TCHAN, types.TSTRING:
+ // chan, string, slice, array ranges use value multiple times.
+ // make copy.
+ r := n.X
+
+ if r.Type().IsString() && r.Type() != types.Types[types.TSTRING] {
+ r = ir.NewConvExpr(base.Pos, ir.OCONV, nil, r)
+ r.SetType(types.Types[types.TSTRING])
+ r = typecheck.Expr(r)
+ }
+
+ n.X = o.copyExpr(r)
+
+ case types.TMAP:
+ if isMapClear(n) {
+ // Preserve the body of the map clear pattern so it can
+ // be detected during walk. The loop body will not be used
+ // when optimizing away the range loop to a runtime call.
+ orderBody = false
+ break
+ }
+
+ // copy the map value in case it is a map literal.
+ // TODO(rsc): Make tmp = literal expressions reuse tmp.
+ // For maps tmp is just one word so it hardly matters.
+ r := n.X
+ n.X = o.copyExpr(r)
+
+ // n.Prealloc is the temp for the iterator.
+ // MapIterType contains pointers and needs to be zeroed.
+ n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true)
+ }
+ n.Key = o.exprInPlace(n.Key)
+ n.Value = o.exprInPlace(n.Value)
+ if orderBody {
+ orderBlock(&n.Body, o.free)
+ }
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ o.exprList(n.Results)
+ o.out = append(o.out, n)
+
+ // Special: clean case temporaries in each block entry.
+ // Select must enter one of its blocks, so there is no
+ // need for a cleaning at the end.
+ // Doubly special: evaluation order for select is stricter
+ // than ordinary expressions. Even something like p.c
+ // has to be hoisted into a temporary, so that it cannot be
+ // reordered after the channel evaluation for a different
+ // case (if p were nil, then the timing of the fault would
+ // give this away).
+ case ir.OSELECT:
+ n := n.(*ir.SelectStmt)
+ t := o.markTemp()
+ for _, ncas := range n.Cases {
+ r := ncas.Comm
+ ir.SetPos(ncas)
+
+ // Append any new body prologue to ninit.
+ // The next loop will insert ninit into nbody.
+ if len(ncas.Init()) != 0 {
+ base.Fatalf("order select ninit")
+ }
+ if r == nil {
+ continue
+ }
+ switch r.Op() {
+ default:
+ ir.Dump("select case", r)
+ base.Fatalf("unknown op in select %v", r.Op())
+
+ case ir.OSELRECV2:
+ // case x, ok = <-c
+ r := r.(*ir.AssignListStmt)
+ recv := r.Rhs[0].(*ir.UnaryExpr)
+ recv.X = o.expr(recv.X, nil)
+ if !ir.IsAutoTmp(recv.X) {
+ recv.X = o.copyExpr(recv.X)
+ }
+ init := ir.TakeInit(r)
+
+ colas := r.Def
+ do := func(i int, t *types.Type) {
+ n := r.Lhs[i]
+ if ir.IsBlank(n) {
+ return
+ }
+ // If this is case x := <-ch or case x, y := <-ch, the case has
+ // the ODCL nodes to declare x and y. We want to delay that
+ // declaration (and possible allocation) until inside the case body.
+ // Delete the ODCL nodes here and recreate them inside the body below.
+ if colas {
+ if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n {
+ init = init[1:]
+
+ // 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)
+ }
+ tmp := o.newTemp(t, t.HasPointers())
+ as := typecheck.Stmt(ir.NewAssignStmt(base.Pos, n, typecheck.Conv(tmp, n.Type())))
+ ncas.PtrInit().Append(as)
+ r.Lhs[i] = tmp
+ }
+ do(0, recv.X.Type().Elem())
+ do(1, types.Types[types.TBOOL])
+ if len(init) != 0 {
+ ir.DumpList("ninit", r.Init())
+ base.Fatalf("ninit on select recv")
+ }
+ orderBlock(ncas.PtrInit(), o.free)
+
+ case ir.OSEND:
+ r := r.(*ir.SendStmt)
+ if len(r.Init()) != 0 {
+ ir.DumpList("ninit", r.Init())
+ base.Fatalf("ninit on select send")
+ }
+
+ // case c <- x
+ // r->left is c, r->right is x, both are always evaluated.
+ r.Chan = o.expr(r.Chan, nil)
+
+ if !ir.IsAutoTmp(r.Chan) {
+ r.Chan = o.copyExpr(r.Chan)
+ }
+ r.Value = o.expr(r.Value, nil)
+ if !ir.IsAutoTmp(r.Value) {
+ r.Value = o.copyExpr(r.Value)
+ }
+ }
+ }
+ // Now that we have accumulated all the temporaries, clean them.
+ // Also insert any ninit queued during the previous loop.
+ // (The temporary cleaning must follow that ninit work.)
+ for _, cas := range n.Cases {
+ orderBlock(&cas.Body, o.free)
+ cas.Body.Prepend(o.cleanTempNoPop(t)...)
+
+ // TODO(mdempsky): Is this actually necessary?
+ // walkSelect appears to walk Ninit.
+ cas.Body.Prepend(ir.TakeInit(cas)...)
+ }
+
+ o.out = append(o.out, n)
+ o.popTemp(t)
+
+ // Special: value being sent is passed as a pointer; make it addressable.
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ t := o.markTemp()
+ n.Chan = o.expr(n.Chan, nil)
+ n.Value = o.expr(n.Value, nil)
+ if base.Flag.Cfg.Instrumenting {
+ // Force copying to the stack so that (chan T)(nil) <- x
+ // is still instrumented as a read of x.
+ n.Value = o.copyExpr(n.Value)
+ } else {
+ n.Value = o.addrTemp(n.Value)
+ }
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+
+ // TODO(rsc): Clean temporaries more aggressively.
+ // Note that because walkSwitch will rewrite some of the
+ // switch into a binary search, this is not as easy as it looks.
+ // (If we ran that code here we could invoke order.stmt on
+ // the if-else chain instead.)
+ // For now just clean all the temporaries at the end.
+ // In practice that's fine.
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+ if base.Debug.Libfuzzer != 0 && !hasDefaultCase(n) {
+ // Add empty "default:" case for instrumentation.
+ n.Cases = append(n.Cases, ir.NewCaseStmt(base.Pos, nil, nil))
+ }
+
+ t := o.markTemp()
+ n.Tag = o.expr(n.Tag, nil)
+ for _, ncas := range n.Cases {
+ o.exprListInPlace(ncas.List)
+ orderBlock(&ncas.Body, o.free)
+ }
+
+ o.out = append(o.out, n)
+ o.cleanTemp(t)
+ }
+
+ base.Pos = lno
+}
+
+func hasDefaultCase(n *ir.SwitchStmt) bool {
+ for _, ncas := range n.Cases {
+ if len(ncas.List) == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// exprList orders the expression list l into o.
+func (o *orderState) exprList(l ir.Nodes) {
+ s := l
+ for i := range s {
+ s[i] = o.expr(s[i], nil)
+ }
+}
+
+// exprListInPlace orders the expression list l but saves
+// the side effects on the individual expression ninit lists.
+func (o *orderState) exprListInPlace(l ir.Nodes) {
+ s := l
+ for i := range s {
+ s[i] = o.exprInPlace(s[i])
+ }
+}
+
+func (o *orderState) exprNoLHS(n ir.Node) ir.Node {
+ return o.expr(n, nil)
+}
+
+// expr orders a single expression, appending side
+// effects to o.out as needed.
+// If this is part of an assignment lhs = *np, lhs is given.
+// Otherwise lhs == nil. (When lhs != nil it may be possible
+// to avoid copying the result of the expression to a temporary.)
+// The result of expr MUST be assigned back to n, e.g.
+// n.Left = o.expr(n.Left, lhs)
+func (o *orderState) expr(n, lhs ir.Node) ir.Node {
+ if n == nil {
+ return n
+ }
+ lno := ir.SetPos(n)
+ n = o.expr1(n, lhs)
+ base.Pos = lno
+ return n
+}
+
+func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
+ o.init(n)
+
+ switch n.Op() {
+ default:
+ if o.edit == nil {
+ o.edit = o.exprNoLHS // create closure once
+ }
+ ir.EditChildren(n, o.edit)
+ return n
+
+ // Addition of strings turns into a function call.
+ // Allocate a temporary to hold the strings.
+ // Fewer than 5 strings use direct runtime helpers.
+ case ir.OADDSTR:
+ n := n.(*ir.AddStringExpr)
+ o.exprList(n.List)
+
+ if len(n.List) > 5 {
+ t := types.NewArray(types.Types[types.TSTRING], int64(len(n.List)))
+ n.Prealloc = o.newTemp(t, false)
+ }
+
+ // Mark string(byteSlice) arguments to reuse byteSlice backing
+ // buffer during conversion. String concatenation does not
+ // memorize the strings for later use, so it is safe.
+ // However, we can do it only if there is at least one non-empty string literal.
+ // Otherwise if all other arguments are empty strings,
+ // concatstrings will return the reference to the temp string
+ // to the caller.
+ hasbyte := false
+
+ haslit := false
+ for _, n1 := range n.List {
+ hasbyte = hasbyte || n1.Op() == ir.OBYTES2STR
+ haslit = haslit || n1.Op() == ir.OLITERAL && len(ir.StringVal(n1)) != 0
+ }
+
+ if haslit && hasbyte {
+ for _, n2 := range n.List {
+ if n2.Op() == ir.OBYTES2STR {
+ n2 := n2.(*ir.ConvExpr)
+ n2.SetOp(ir.OBYTES2STRTMP)
+ }
+ }
+ }
+ return n
+
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ n.X = o.expr(n.X, nil)
+ n.Index = o.expr(n.Index, nil)
+ needCopy := false
+
+ if !n.Assigned {
+ // Enforce that any []byte slices we are not copying
+ // can not be changed before the map index by forcing
+ // the map index to happen immediately following the
+ // conversions. See copyExpr a few lines below.
+ needCopy = mapKeyReplaceStrConv(n.Index)
+
+ if base.Flag.Cfg.Instrumenting {
+ // Race detector needs the copy.
+ needCopy = true
+ }
+ }
+
+ // key must be addressable
+ n.Index = o.mapKeyTemp(n.X.Type(), n.Index)
+ if needCopy {
+ return o.copyExpr(n)
+ }
+ return n
+
+ // concrete type (not interface) argument might need an addressable
+ // temporary to pass to the runtime conversion routine.
+ case ir.OCONVIFACE, ir.OCONVIDATA:
+ n := n.(*ir.ConvExpr)
+ n.X = o.expr(n.X, nil)
+ if n.X.Type().IsInterface() {
+ return n
+ }
+ 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/OCONVIDATA case in walk).
+ n.X = o.addrTemp(n.X)
+ }
+ return n
+
+ case ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ 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
+ // together. See golang.org/issue/15329.
+ o.init(call)
+ o.call(call)
+ if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
+ return o.copyExpr(n)
+ }
+ } else {
+ n.X = o.expr(n.X, nil)
+ }
+ return n
+
+ case ir.OANDAND, ir.OOROR:
+ // ... = LHS && RHS
+ //
+ // var r bool
+ // r = LHS
+ // if r { // or !r, for OROR
+ // r = RHS
+ // }
+ // ... = r
+
+ n := n.(*ir.LogicalExpr)
+ r := o.newTemp(n.Type(), false)
+
+ // Evaluate left-hand side.
+ lhs := o.expr(n.X, nil)
+ o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, lhs)))
+
+ // Evaluate right-hand side, save generated code.
+ saveout := o.out
+ o.out = nil
+ t := o.markTemp()
+ o.edge()
+ rhs := o.expr(n.Y, nil)
+ o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, rhs)))
+ o.cleanTemp(t)
+ gen := o.out
+ o.out = saveout
+
+ // If left-hand side doesn't cause a short-circuit, issue right-hand side.
+ nif := ir.NewIfStmt(base.Pos, r, nil, nil)
+ if n.Op() == ir.OANDAND {
+ nif.Body = gen
+ } else {
+ nif.Else = gen
+ }
+ o.out = append(o.out, nif)
+ return r
+
+ case ir.OCALLMETH:
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+ panic("unreachable")
+
+ case ir.OCALLFUNC,
+ ir.OCALLINTER,
+ ir.OCAP,
+ ir.OCOMPLEX,
+ ir.OCOPY,
+ ir.OIMAG,
+ ir.OLEN,
+ ir.OMAKECHAN,
+ ir.OMAKEMAP,
+ ir.OMAKESLICE,
+ ir.OMAKESLICECOPY,
+ ir.ONEW,
+ ir.OREAL,
+ ir.ORECOVERFP,
+ ir.OSTR2BYTES,
+ ir.OSTR2BYTESTMP,
+ ir.OSTR2RUNES:
+
+ if isRuneCount(n) {
+ // len([]rune(s)) is rewritten to runtime.countrunes(s) later.
+ conv := n.(*ir.UnaryExpr).X.(*ir.ConvExpr)
+ conv.X = o.expr(conv.X, nil)
+ } else {
+ o.call(n)
+ }
+
+ if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
+ return o.copyExpr(n)
+ }
+ return n
+
+ case ir.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)
+ if isAppendOfMake(n) {
+ n.Args[0] = o.expr(n.Args[0], nil) // order x
+ mk := n.Args[1].(*ir.MakeExpr)
+ mk.Len = o.expr(mk.Len, nil) // order y
+ } else {
+ o.exprList(n.Args)
+ }
+
+ if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.Args[0]) {
+ return o.copyExpr(n)
+ }
+ return n
+
+ case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR:
+ n := n.(*ir.SliceExpr)
+ n.X = o.expr(n.X, nil)
+ n.Low = o.cheapExpr(o.expr(n.Low, nil))
+ n.High = o.cheapExpr(o.expr(n.High, nil))
+ n.Max = o.cheapExpr(o.expr(n.Max, nil))
+ if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.X) {
+ return o.copyExpr(n)
+ }
+ return n
+
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ if n.Transient() && len(n.Func.ClosureVars) > 0 {
+ n.Prealloc = o.newTemp(typecheck.ClosureType(n), false)
+ }
+ return n
+
+ case ir.OMETHVALUE:
+ n := n.(*ir.SelectorExpr)
+ n.X = o.expr(n.X, nil)
+ if n.Transient() {
+ t := typecheck.MethodValueType(n)
+ n.Prealloc = o.newTemp(t, false)
+ }
+ return n
+
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ o.exprList(n.List)
+ if n.Transient() {
+ t := types.NewArray(n.Type().Elem(), n.Len)
+ n.Prealloc = o.newTemp(t, false)
+ }
+ return n
+
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ n := n.(*ir.TypeAssertExpr)
+ n.X = o.expr(n.X, nil)
+ if !types.IsDirectIface(n.Type()) || base.Flag.Cfg.Instrumenting {
+ return o.copyExprClear(n)
+ }
+ return n
+
+ case ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ n.X = o.expr(n.X, nil)
+ return o.copyExprClear(n)
+
+ case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ n := n.(*ir.BinaryExpr)
+ n.X = o.expr(n.X, nil)
+ n.Y = o.expr(n.Y, nil)
+
+ t := n.X.Type()
+ switch {
+ case t.IsString():
+ // Mark string(byteSlice) arguments to reuse byteSlice backing
+ // buffer during conversion. String comparison does not
+ // memorize the strings for later use, so it is safe.
+ if n.X.Op() == ir.OBYTES2STR {
+ n.X.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
+ }
+ if n.Y.Op() == ir.OBYTES2STR {
+ n.Y.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
+ }
+
+ case t.IsStruct() || t.IsArray():
+ // for complex comparisons, we need both args to be
+ // addressable so we can pass them to the runtime.
+ n.X = o.addrTemp(n.X)
+ n.Y = o.addrTemp(n.Y)
+ }
+ return n
+
+ case ir.OMAPLIT:
+ // Order map by converting:
+ // map[int]int{
+ // a(): b(),
+ // c(): d(),
+ // e(): f(),
+ // }
+ // to
+ // m := map[int]int{}
+ // m[a()] = b()
+ // m[c()] = d()
+ // m[e()] = f()
+ // Then order the result.
+ // Without this special case, order would otherwise compute all
+ // the keys and values before storing any of them to the map.
+ // See issue 26552.
+ n := n.(*ir.CompLitExpr)
+ entries := n.List
+ statics := entries[:0]
+ var dynamics []*ir.KeyExpr
+ for _, r := range entries {
+ r := r.(*ir.KeyExpr)
+
+ if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
+ dynamics = append(dynamics, r)
+ continue
+ }
+
+ // Recursively ordering some static entries can change them to dynamic;
+ // e.g., OCONVIFACE nodes. See #31777.
+ r = o.expr(r, nil).(*ir.KeyExpr)
+ if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
+ dynamics = append(dynamics, r)
+ continue
+ }
+
+ statics = append(statics, r)
+ }
+ n.List = statics
+
+ if len(dynamics) == 0 {
+ return n
+ }
+
+ // Emit the creation of the map (with all its static entries).
+ m := o.newTemp(n.Type(), false)
+ as := ir.NewAssignStmt(base.Pos, m, n)
+ typecheck.Stmt(as)
+ o.stmt(as)
+
+ // Emit eval+insert of dynamic entries, one at a time.
+ for _, r := range dynamics {
+ as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value)
+ typecheck.Stmt(as) // Note: this converts the OINDEX to an OINDEXMAP
+ o.stmt(as)
+ }
+ return m
+ }
+
+ // No return - type-assertions above. Each case must return for itself.
+}
+
+// as2func orders OAS2FUNC nodes. It creates temporaries to ensure left-to-right assignment.
+// The caller should order the right-hand side of the assignment before calling order.as2func.
+// It rewrites,
+// a, b, a = ...
+// as
+// tmp1, tmp2, tmp3 = ...
+// a, b, a = tmp1, tmp2, tmp3
+// This is necessary to ensure left to right assignment order.
+func (o *orderState) as2func(n *ir.AssignListStmt) {
+ results := n.Rhs[0].Type()
+ as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)
+ for i, nl := range n.Lhs {
+ if !ir.IsBlank(nl) {
+ typ := results.Field(i).Type
+ tmp := o.newTemp(typ, typ.HasPointers())
+ n.Lhs[i] = tmp
+ as.Lhs = append(as.Lhs, nl)
+ as.Rhs = append(as.Rhs, tmp)
+ }
+ }
+
+ o.out = append(o.out, n)
+ o.stmt(typecheck.Stmt(as))
+}
+
+// as2ok orders OAS2XXX with ok.
+// Just like as2func, this also adds temporaries to ensure left-to-right assignment.
+func (o *orderState) as2ok(n *ir.AssignListStmt) {
+ as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)
+
+ do := func(i int, typ *types.Type) {
+ if nl := n.Lhs[i]; !ir.IsBlank(nl) {
+ var tmp ir.Node = o.newTemp(typ, typ.HasPointers())
+ n.Lhs[i] = tmp
+ as.Lhs = append(as.Lhs, nl)
+ if i == 1 {
+ // The "ok" result is an untyped boolean according to the Go
+ // spec. We need to explicitly convert it to the LHS type in
+ // case the latter is a defined boolean type (#8475).
+ tmp = typecheck.Conv(tmp, nl.Type())
+ }
+ as.Rhs = append(as.Rhs, tmp)
+ }
+ }
+
+ do(0, n.Rhs[0].Type())
+ do(1, types.Types[types.TBOOL])
+
+ o.out = append(o.out, n)
+ o.stmt(typecheck.Stmt(as))
+}
+
+// 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
+}