// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gc import ( "cmd/compile/internal/types" "cmd/internal/src" "fmt" ) // Rewrite tree to use separate statements to enforce // order of evaluation. Makes walk easier, because it // can (after this runs) reorder at will within an expression. // // Rewrite m[k] op= r into m[k] = m[k] op r if op is / or %. // // Introduce temporaries as needed by runtime routines. // For example, the map runtime routines take the map key // by reference, so make sure all map keys are addressable // by copying them to temporaries as needed. // The same is true for channel operations. // // Arrange that map index expressions only appear in direct // assignments x = m[k] or m[k] = x, never in larger expressions. // // Arrange that receive expressions only appear in direct assignments // x = <-c or as standalone statements <-c, never in larger expressions. // TODO(rsc): The temporary introduction during multiple assignments // should be moved into this file, so that the temporaries can be cleaned // and so that conversions implicit in the OAS2FUNC and OAS2RECV // nodes can be made explicit and then have their temporaries cleaned. // TODO(rsc): Goto and multilevel break/continue can jump over // inserted VARKILL annotations. Work out a way to handle these. // The current implementation is safe, in that it will execute correctly. // But it won't reuse temporaries as aggressively as it might, and // it can result in unnecessary zeroing of those variables in the function // prologue. // Order holds state during the ordering process. type Order struct { out []*Node // list of generated statements temp []*Node // stack of temporary variables free map[string][]*Node // free list of unused temporaries, by type.LongString(). } // Order rewrites fn.Nbody to apply the ordering constraints // described in the comment at the top of the file. func order(fn *Node) { if Debug.W > 1 { s := fmt.Sprintf("\nbefore order %v", fn.Func.Nname.Sym) dumplist(s, fn.Nbody) } orderBlock(&fn.Nbody, map[string][]*Node{}) } // newTemp allocates a new temporary with the given type, // pushes it onto the temp stack, and returns it. // If clear is true, newTemp emits code to zero the temporary. func (o *Order) newTemp(t *types.Type, clear bool) *Node { var v *Node // Note: LongString is close to the type equality we want, // but not exactly. We still need to double-check with types.Identical. key := t.LongString() a := o.free[key] for i, n := range a { if types.Identical(t, n.Type) { v = a[i] a[i] = a[len(a)-1] a = a[:len(a)-1] o.free[key] = a break } } if v == nil { v = temp(t) } if clear { a := nod(OAS, v, nil) a = typecheck(a, ctxStmt) o.out = append(o.out, a) } o.temp = append(o.temp, v) return v } // copyExpr behaves like newTemp but also emits // code to initialize the temporary to the value n. // // The clear argument is provided for use when the evaluation // of tmp = n turns into a function call that is passed a pointer // to the temporary as the output space. If the call blocks before // tmp has been written, the garbage collector will still treat the // temporary as live, so we must zero it before entering that call. // Today, this only happens for channel receive operations. // (The other candidate would be map access, but map access // returns a pointer to the result data instead of taking a pointer // to be filled in.) func (o *Order) copyExpr(n *Node, t *types.Type, clear bool) *Node { v := o.newTemp(t, clear) a := nod(OAS, v, n) a = typecheck(a, ctxStmt) o.out = append(o.out, a) return v } // cheapExpr returns a cheap version of n. // The definition of cheap is that n is a variable or constant. // If not, cheapExpr allocates a new tmp, emits tmp = n, // and then returns tmp. func (o *Order) cheapExpr(n *Node) *Node { if n == nil { return nil } switch n.Op { case ONAME, OLITERAL: return n case OLEN, OCAP: l := o.cheapExpr(n.Left) if l == n.Left { return n } a := n.sepcopy() a.Left = l return typecheck(a, ctxExpr) } return o.copyExpr(n, n.Type, false) } // safeExpr returns a safe version of n. // The definition of safe is that n can appear multiple times // without violating the semantics of the original program, // and that assigning to the safe version has the same effect // as assigning to the original n. // // The intended use is to apply to x when rewriting x += y into x = x + y. func (o *Order) safeExpr(n *Node) *Node { switch n.Op { case ONAME, OLITERAL: return n case ODOT, OLEN, OCAP: l := o.safeExpr(n.Left) if l == n.Left { return n } a := n.sepcopy() a.Left = l return typecheck(a, ctxExpr) case ODOTPTR, ODEREF: l := o.cheapExpr(n.Left) if l == n.Left { return n } a := n.sepcopy() a.Left = l return typecheck(a, ctxExpr) case OINDEX, OINDEXMAP: var l *Node if n.Left.Type.IsArray() { l = o.safeExpr(n.Left) } else { l = o.cheapExpr(n.Left) } r := o.cheapExpr(n.Right) if l == n.Left && r == n.Right { return n } a := n.sepcopy() a.Left = l a.Right = r return typecheck(a, ctxExpr) default: Fatalf("order.safeExpr %v", n.Op) return nil // not reached } } // isaddrokay reports whether it is okay to pass n's address to runtime routines. // Taking the address of a variable makes the liveness and optimization analyses // lose track of where the variable's lifetime ends. To avoid hurting the analyses // of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay, // because we emit explicit VARKILL instructions marking the end of those // temporaries' lifetimes. func isaddrokay(n *Node) bool { return islvalue(n) && (n.Op != ONAME || n.Class() == PEXTERN || n.IsAutoTmp()) } // addrTemp ensures that n is okay to pass by address to runtime routines. // If the original argument n is not okay, addrTemp creates a tmp, emits // tmp = n, and then returns tmp. // The result of addrTemp MUST be assigned back to n, e.g. // n.Left = o.addrTemp(n.Left) func (o *Order) addrTemp(n *Node) *Node { if consttype(n) != CTxxx { // TODO: expand this to all static composite literal nodes? n = defaultlit(n, nil) dowidth(n.Type) vstat := readonlystaticname(n.Type) var s InitSchedule s.staticassign(vstat, n) if s.out != nil { Fatalf("staticassign of const generated code: %+v", n) } vstat = typecheck(vstat, ctxExpr) return vstat } if isaddrokay(n) { return n } return o.copyExpr(n, n.Type, false) } // mapKeyTemp prepares n to be a key in a map runtime call and returns n. // It should only be used for map runtime calls which have *_fast* versions. func (o *Order) mapKeyTemp(t *types.Type, n *Node) *Node { // Most map calls need to take the address of the key. // Exception: map*_fast* calls. See golang.org/issue/19015. if mapfast(t) == mapslow { return o.addrTemp(n) } return n } // mapKeyReplaceStrConv replaces OBYTES2STR by OBYTES2STRTMP // in n to avoid string allocations for keys in map lookups. // Returns a bool that signals if a modification was made. // // For: // x = m[string(k)] // x = m[T1{... Tn{..., string(k), ...}] // where k is []byte, T1 to Tn is a nesting of struct and array literals, // the allocation of backing bytes for the string can be avoided // by reusing the []byte backing array. These are special cases // for avoiding allocations when converting byte slices to strings. // It would be nice to handle these generally, but because // []byte keys are not allowed in maps, the use of string(k) // comes up in important cases in practice. See issue 3512. func mapKeyReplaceStrConv(n *Node) bool { var replaced bool switch n.Op { case OBYTES2STR: n.Op = OBYTES2STRTMP replaced = true case OSTRUCTLIT: for _, elem := range n.List.Slice() { if mapKeyReplaceStrConv(elem.Left) { replaced = true } } case OARRAYLIT: for _, elem := range n.List.Slice() { if elem.Op == OKEY { elem = elem.Right } if mapKeyReplaceStrConv(elem) { replaced = true } } } return replaced } type ordermarker int // markTemp returns the top of the temporary variable stack. func (o *Order) markTemp() ordermarker { return ordermarker(len(o.temp)) } // popTemp pops temporaries off the stack until reaching the mark, // which must have been returned by markTemp. func (o *Order) popTemp(mark ordermarker) { for _, n := range o.temp[mark:] { key := n.Type.LongString() o.free[key] = append(o.free[key], n) } o.temp = o.temp[:mark] } // cleanTempNoPop emits VARKILL instructions to *out // for each temporary above the mark on the temporary stack. // It does not pop the temporaries from the stack. func (o *Order) cleanTempNoPop(mark ordermarker) []*Node { var out []*Node for i := len(o.temp) - 1; i >= int(mark); i-- { n := o.temp[i] kill := nod(OVARKILL, n, nil) kill = typecheck(kill, ctxStmt) out = append(out, kill) } return out } // cleanTemp emits VARKILL instructions for each temporary above the // mark on the temporary stack and removes them from the stack. func (o *Order) cleanTemp(top ordermarker) { o.out = append(o.out, o.cleanTempNoPop(top)...) o.popTemp(top) } // stmtList orders each of the statements in the list. func (o *Order) stmtList(l Nodes) { s := l.Slice() for i := range s { orderMakeSliceCopy(s[i:]) o.stmt(s[i]) } } // orderMakeSliceCopy matches the pattern: // m = OMAKESLICE([]T, x); OCOPY(m, s) // and rewrites it to: // m = OMAKESLICECOPY([]T, x, s); nil func orderMakeSliceCopy(s []*Node) { if Debug.N != 0 || instrumenting { return } if len(s) < 2 { return } asn := s[0] copyn := s[1] if asn == nil || asn.Op != OAS { return } if asn.Left.Op != ONAME { return } if asn.Left.isBlank() { return } maken := asn.Right if maken == nil || maken.Op != OMAKESLICE { return } if maken.Esc == EscNone { return } if maken.Left == nil || maken.Right != nil { return } if copyn.Op != OCOPY { return } if copyn.Left.Op != ONAME { return } if asn.Left.Sym != copyn.Left.Sym { return } if copyn.Right.Op != ONAME { return } if copyn.Left.Sym == copyn.Right.Sym { return } maken.Op = OMAKESLICECOPY maken.Right = copyn.Right // Set bounded when m = OMAKESLICE([]T, len(s)); OCOPY(m, s) maken.SetBounded(maken.Left.Op == OLEN && samesafeexpr(maken.Left.Left, copyn.Right)) maken = typecheck(maken, ctxExpr) s[1] = nil // remove separate copy call return } // edge inserts coverage instrumentation for libfuzzer. func (o *Order) edge() { if Debug_libfuzzer == 0 { return } // Create a new uint8 counter to be allocated in section // __libfuzzer_extra_counters. counter := staticname(types.Types[TUINT8]) counter.Name.SetLibfuzzerExtraCounter(true) // counter += 1 incr := nod(OASOP, counter, nodintconst(1)) incr.SetSubOp(OADD) incr = typecheck(incr, ctxStmt) o.out = append(o.out, incr) } // orderBlock orders the block of statements in n into a new slice, // and then replaces the old slice in n with the new slice. // free is a map that can be used to obtain temporary variables by type. func orderBlock(n *Nodes, free map[string][]*Node) { var order Order order.free = free mark := order.markTemp() order.edge() order.stmtList(*n) order.cleanTemp(mark) n.Set(order.out) } // exprInPlace orders the side effects in *np and // leaves them as the init list of the final *np. // The result of exprInPlace MUST be assigned back to n, e.g. // n.Left = o.exprInPlace(n.Left) func (o *Order) exprInPlace(n *Node) *Node { var order Order order.free = o.free n = order.expr(n, nil) n = addinit(n, order.out) // insert new temporaries from order // at head of outer list. o.temp = append(o.temp, order.temp...) return n } // orderStmtInPlace orders the side effects of the single statement *np // and replaces it with the resulting statement list. // The result of orderStmtInPlace MUST be assigned back to n, e.g. // n.Left = orderStmtInPlace(n.Left) // free is a map that can be used to obtain temporary variables by type. func orderStmtInPlace(n *Node, free map[string][]*Node) *Node { var order Order order.free = free mark := order.markTemp() order.stmt(n) order.cleanTemp(mark) return liststmt(order.out) } // init moves n's init list to o.out. func (o *Order) init(n *Node) { if n.mayBeShared() { // For concurrency safety, don't mutate potentially shared nodes. // First, ensure that no work is required here. if n.Ninit.Len() > 0 { Fatalf("order.init shared node with ninit") } return } o.stmtList(n.Ninit) n.Ninit.Set(nil) } // call orders the call expression n. // n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY. func (o *Order) call(n *Node) { if n.Ninit.Len() > 0 { // Caller should have already called o.init(n). Fatalf("%v with unexpected ninit", n.Op) } // Builtin functions. if n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER { n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) o.exprList(n.List) return } fixVariadicCall(n) n.Left = o.expr(n.Left, nil) o.exprList(n.List) if n.Op == OCALLINTER { return } keepAlive := func(arg *Node) { // If the argument is really a pointer being converted to uintptr, // arrange for the pointer to be kept alive until the call returns, // by copying it into a temp and marking that temp // still alive when we pop the temp stack. if arg.Op == OCONVNOP && arg.Left.Type.IsUnsafePtr() { x := o.copyExpr(arg.Left, arg.Left.Type, false) arg.Left = x x.Name.SetAddrtaken(true) // ensure SSA keeps the x variable n.Nbody.Append(typecheck(nod(OVARLIVE, x, nil), ctxStmt)) } } // Check for "unsafe-uintptr" tag provided by escape analysis. for i, param := range n.Left.Type.Params().FieldSlice() { if param.Note == unsafeUintptrTag || param.Note == uintptrEscapesTag { if arg := n.List.Index(i); arg.Op == OSLICELIT { for _, elt := range arg.List.Slice() { keepAlive(elt) } } else { keepAlive(arg) } } } } // mapAssign appends n to o.out, introducing temporaries // to make sure that all map assignments have the form m[k] = x. // (Note: expr has already been called on n, so we know k is addressable.) // // If n is the multiple assignment form ..., m[k], ... = ..., x, ..., the rewrite is // t1 = m // t2 = k // ...., t3, ... = ..., x, ... // t1[t2] = t3 // // The temporaries t1, t2 are needed in case the ... being assigned // contain m or k. They are usually unnecessary, but in the unnecessary // cases they are also typically registerizable, so not much harm done. // And this only applies to the multiple-assignment form. // We could do a more precise analysis if needed, like in walk.go. func (o *Order) mapAssign(n *Node) { switch n.Op { default: Fatalf("order.mapAssign %v", n.Op) case OAS, OASOP: if n.Left.Op == OINDEXMAP { // Make sure we evaluate the RHS before starting the map insert. // We need to make sure the RHS won't panic. See issue 22881. if n.Right.Op == OAPPEND { s := n.Right.List.Slice()[1:] for i, n := range s { s[i] = o.cheapExpr(n) } } else { n.Right = o.cheapExpr(n.Right) } } o.out = append(o.out, n) case OAS2, OAS2DOTTYPE, OAS2MAPR, OAS2FUNC: var post []*Node for i, m := range n.List.Slice() { switch { case m.Op == OINDEXMAP: if !m.Left.IsAutoTmp() { m.Left = o.copyExpr(m.Left, m.Left.Type, false) } if !m.Right.IsAutoTmp() { m.Right = o.copyExpr(m.Right, m.Right.Type, false) } fallthrough case instrumenting && n.Op == OAS2FUNC && !m.isBlank(): t := o.newTemp(m.Type, false) n.List.SetIndex(i, t) a := nod(OAS, m, t) a = typecheck(a, ctxStmt) post = append(post, a) } } o.out = append(o.out, n) o.out = append(o.out, post...) } } // stmt orders the statement n, appending to o.out. // Temporaries created during the statement are cleaned // up using VARKILL instructions as possible. func (o *Order) stmt(n *Node) { if n == nil { return } lno := setlineno(n) o.init(n) switch n.Op { default: Fatalf("order.stmt %v", n.Op) case OVARKILL, OVARLIVE, OINLMARK: o.out = append(o.out, n) case OAS: t := o.markTemp() n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, n.Left) o.mapAssign(n) o.cleanTemp(t) case OASOP: t := o.markTemp() n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) if instrumenting || n.Left.Op == OINDEXMAP && (n.SubOp() == ODIV || n.SubOp() == OMOD) { // Rewrite m[k] op= r into m[k] = m[k] op r so // that we can ensure that if op panics // because r is zero, the panic happens before // the map assignment. n.Left = o.safeExpr(n.Left) l := treecopy(n.Left, src.NoXPos) if l.Op == OINDEXMAP { l.SetIndexMapLValue(false) } l = o.copyExpr(l, n.Left.Type, false) n.Right = nod(n.SubOp(), l, n.Right) n.Right = typecheck(n.Right, ctxExpr) n.Right = o.expr(n.Right, nil) n.Op = OAS n.ResetAux() } o.mapAssign(n) o.cleanTemp(t) case OAS2: t := o.markTemp() o.exprList(n.List) o.exprList(n.Rlist) o.mapAssign(n) o.cleanTemp(t) // Special: avoid copy of func call n.Right case OAS2FUNC: t := o.markTemp() o.exprList(n.List) o.init(n.Right) o.call(n.Right) o.as2(n) o.cleanTemp(t) // Special: use temporary variables to hold result, // so that runtime can take address of temporary. // No temporary for blank assignment. // // OAS2MAPR: make sure key is addressable if needed, // and make sure OINDEXMAP is not copied out. case OAS2DOTTYPE, OAS2RECV, OAS2MAPR: t := o.markTemp() o.exprList(n.List) switch r := n.Right; r.Op { case ODOTTYPE2, ORECV: r.Left = o.expr(r.Left, nil) case OINDEXMAP: r.Left = o.expr(r.Left, nil) r.Right = o.expr(r.Right, nil) // See similar conversion for OINDEXMAP below. _ = mapKeyReplaceStrConv(r.Right) r.Right = o.mapKeyTemp(r.Left.Type, r.Right) default: Fatalf("order.stmt: %v", r.Op) } o.okAs2(n) o.cleanTemp(t) // Special: does not save n onto out. case OBLOCK, OEMPTY: o.stmtList(n.List) // Special: n->left is not an expression; save as is. case OBREAK, OCONTINUE, ODCL, ODCLCONST, ODCLTYPE, OFALL, OGOTO, OLABEL, ORETJMP: o.out = append(o.out, n) // Special: handle call arguments. case OCALLFUNC, OCALLINTER, OCALLMETH: t := o.markTemp() o.call(n) o.out = append(o.out, n) o.cleanTemp(t) case OCLOSE, OCOPY, OPRINT, OPRINTN, ORECOVER, ORECV: t := o.markTemp() n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) o.exprList(n.List) o.exprList(n.Rlist) o.out = append(o.out, n) o.cleanTemp(t) // Special: order arguments to inner call but not call itself. case ODEFER, OGO: t := o.markTemp() o.init(n.Left) o.call(n.Left) o.out = append(o.out, n) o.cleanTemp(t) case ODELETE: t := o.markTemp() n.List.SetFirst(o.expr(n.List.First(), nil)) n.List.SetSecond(o.expr(n.List.Second(), nil)) n.List.SetSecond(o.mapKeyTemp(n.List.First().Type, n.List.Second())) o.out = append(o.out, n) o.cleanTemp(t) // Clean temporaries from condition evaluation at // beginning of loop body and after for statement. case OFOR: t := o.markTemp() n.Left = o.exprInPlace(n.Left) n.Nbody.Prepend(o.cleanTempNoPop(t)...) orderBlock(&n.Nbody, o.free) n.Right = orderStmtInPlace(n.Right, o.free) o.out = append(o.out, n) o.cleanTemp(t) // Clean temporaries from condition at // beginning of both branches. case OIF: t := o.markTemp() n.Left = o.exprInPlace(n.Left) n.Nbody.Prepend(o.cleanTempNoPop(t)...) n.Rlist.Prepend(o.cleanTempNoPop(t)...) o.popTemp(t) orderBlock(&n.Nbody, o.free) orderBlock(&n.Rlist, o.free) o.out = append(o.out, n) // Special: argument will be converted to interface using convT2E // so make sure it is an addressable temporary. case OPANIC: t := o.markTemp() n.Left = o.expr(n.Left, nil) if !n.Left.Type.IsInterface() { n.Left = o.addrTemp(n.Left) } o.out = append(o.out, n) o.cleanTemp(t) case ORANGE: // n.Right is the expression being ranged over. // order it, and then make a copy if we need one. // We almost always do, to ensure that we don't // see any value changes made during the loop. // Usually the copy is cheap (e.g., array pointer, // chan, slice, string are all tiny). // The exception is ranging over an array value // (not a slice, not a pointer to array), // which must make a copy to avoid seeing updates made during // the range body. Ranging over an array value is uncommon though. // Mark []byte(str) range expression to reuse string backing storage. // It is safe because the storage cannot be mutated. if n.Right.Op == OSTR2BYTES { n.Right.Op = OSTR2BYTESTMP } t := o.markTemp() n.Right = o.expr(n.Right, nil) orderBody := true switch n.Type.Etype { default: Fatalf("order.stmt range %v", n.Type) case TARRAY, TSLICE: if n.List.Len() < 2 || n.List.Second().isBlank() { // for i := range x will only use x once, to compute len(x). // No need to copy it. break } fallthrough case TCHAN, TSTRING: // chan, string, slice, array ranges use value multiple times. // make copy. r := n.Right if r.Type.IsString() && r.Type != types.Types[TSTRING] { r = nod(OCONV, r, nil) r.Type = types.Types[TSTRING] r = typecheck(r, ctxExpr) } n.Right = o.copyExpr(r, r.Type, false) case TMAP: if isMapClear(n) { // Preserve the body of the map clear pattern so it can // be detected during walk. The loop body will not be used // when optimizing away the range loop to a runtime call. orderBody = false break } // copy the map value in case it is a map literal. // TODO(rsc): Make tmp = literal expressions reuse tmp. // For maps tmp is just one word so it hardly matters. r := n.Right n.Right = o.copyExpr(r, r.Type, false) // prealloc[n] is the temp for the iterator. // hiter contains pointers and needs to be zeroed. prealloc[n] = o.newTemp(hiter(n.Type), true) } o.exprListInPlace(n.List) if orderBody { orderBlock(&n.Nbody, o.free) } o.out = append(o.out, n) o.cleanTemp(t) case ORETURN: o.exprList(n.List) o.out = append(o.out, n) // Special: clean case temporaries in each block entry. // Select must enter one of its blocks, so there is no // need for a cleaning at the end. // Doubly special: evaluation order for select is stricter // than ordinary expressions. Even something like p.c // has to be hoisted into a temporary, so that it cannot be // reordered after the channel evaluation for a different // case (if p were nil, then the timing of the fault would // give this away). case OSELECT: t := o.markTemp() for _, n2 := range n.List.Slice() { if n2.Op != OCASE { Fatalf("order select case %v", n2.Op) } r := n2.Left setlineno(n2) // Append any new body prologue to ninit. // The next loop will insert ninit into nbody. if n2.Ninit.Len() != 0 { Fatalf("order select ninit") } if r == nil { continue } switch r.Op { default: Dump("select case", r) Fatalf("unknown op in select %v", r.Op) // If this is case x := <-ch or case x, y := <-ch, the case has // the ODCL nodes to declare x and y. We want to delay that // declaration (and possible allocation) until inside the case body. // Delete the ODCL nodes here and recreate them inside the body below. case OSELRECV, OSELRECV2: if r.Colas() { i := 0 if r.Ninit.Len() != 0 && r.Ninit.First().Op == ODCL && r.Ninit.First().Left == r.Left { i++ } if i < r.Ninit.Len() && r.Ninit.Index(i).Op == ODCL && r.List.Len() != 0 && r.Ninit.Index(i).Left == r.List.First() { i++ } if i >= r.Ninit.Len() { r.Ninit.Set(nil) } } if r.Ninit.Len() != 0 { dumplist("ninit", r.Ninit) Fatalf("ninit on select recv") } // case x = <-c // case x, ok = <-c // r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c. // r->left == N means 'case <-c'. // c is always evaluated; x and ok are only evaluated when assigned. r.Right.Left = o.expr(r.Right.Left, nil) if r.Right.Left.Op != ONAME { r.Right.Left = o.copyExpr(r.Right.Left, r.Right.Left.Type, false) } // Introduce temporary for receive and move actual copy into case body. // avoids problems with target being addressed, as usual. // NOTE: If we wanted to be clever, we could arrange for just one // temporary per distinct type, sharing the temp among all receives // with that temp. Similarly one ok bool could be shared among all // the x,ok receives. Not worth doing until there's a clear need. if r.Left != nil && r.Left.isBlank() { r.Left = nil } if r.Left != nil { // use channel element type for temporary to avoid conversions, // such as in case interfacevalue = <-intchan. // the conversion happens in the OAS instead. tmp1 := r.Left if r.Colas() { tmp2 := nod(ODCL, tmp1, nil) tmp2 = typecheck(tmp2, ctxStmt) n2.Ninit.Append(tmp2) } r.Left = o.newTemp(r.Right.Left.Type.Elem(), r.Right.Left.Type.Elem().HasPointers()) tmp2 := nod(OAS, tmp1, r.Left) tmp2 = typecheck(tmp2, ctxStmt) n2.Ninit.Append(tmp2) } if r.List.Len() != 0 && r.List.First().isBlank() { r.List.Set(nil) } if r.List.Len() != 0 { tmp1 := r.List.First() if r.Colas() { tmp2 := nod(ODCL, tmp1, nil) tmp2 = typecheck(tmp2, ctxStmt) n2.Ninit.Append(tmp2) } r.List.Set1(o.newTemp(types.Types[TBOOL], false)) tmp2 := okas(tmp1, r.List.First()) tmp2 = typecheck(tmp2, ctxStmt) n2.Ninit.Append(tmp2) } orderBlock(&n2.Ninit, o.free) case OSEND: if r.Ninit.Len() != 0 { dumplist("ninit", r.Ninit) Fatalf("ninit on select send") } // case c <- x // r->left is c, r->right is x, both are always evaluated. r.Left = o.expr(r.Left, nil) if !r.Left.IsAutoTmp() { r.Left = o.copyExpr(r.Left, r.Left.Type, false) } r.Right = o.expr(r.Right, nil) if !r.Right.IsAutoTmp() { r.Right = o.copyExpr(r.Right, r.Right.Type, false) } } } // Now that we have accumulated all the temporaries, clean them. // Also insert any ninit queued during the previous loop. // (The temporary cleaning must follow that ninit work.) for _, n3 := range n.List.Slice() { orderBlock(&n3.Nbody, o.free) n3.Nbody.Prepend(o.cleanTempNoPop(t)...) // TODO(mdempsky): Is this actually necessary? // walkselect appears to walk Ninit. n3.Nbody.Prepend(n3.Ninit.Slice()...) n3.Ninit.Set(nil) } o.out = append(o.out, n) o.popTemp(t) // Special: value being sent is passed as a pointer; make it addressable. case OSEND: t := o.markTemp() n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) if instrumenting { // Force copying to the stack so that (chan T)(nil) <- x // is still instrumented as a read of x. n.Right = o.copyExpr(n.Right, n.Right.Type, false) } else { n.Right = o.addrTemp(n.Right) } o.out = append(o.out, n) o.cleanTemp(t) // TODO(rsc): Clean temporaries more aggressively. // Note that because walkswitch will rewrite some of the // switch into a binary search, this is not as easy as it looks. // (If we ran that code here we could invoke order.stmt on // the if-else chain instead.) // For now just clean all the temporaries at the end. // In practice that's fine. case OSWITCH: if Debug_libfuzzer != 0 && !hasDefaultCase(n) { // Add empty "default:" case for instrumentation. n.List.Append(nod(OCASE, nil, nil)) } t := o.markTemp() n.Left = o.expr(n.Left, nil) for _, ncas := range n.List.Slice() { if ncas.Op != OCASE { Fatalf("order switch case %v", ncas.Op) } o.exprListInPlace(ncas.List) orderBlock(&ncas.Nbody, o.free) } o.out = append(o.out, n) o.cleanTemp(t) } lineno = lno } func hasDefaultCase(n *Node) bool { for _, ncas := range n.List.Slice() { if ncas.Op != OCASE { Fatalf("expected case, found %v", ncas.Op) } if ncas.List.Len() == 0 { return true } } return false } // exprList orders the expression list l into o. func (o *Order) exprList(l Nodes) { s := l.Slice() for i := range s { s[i] = o.expr(s[i], nil) } } // exprListInPlace orders the expression list l but saves // the side effects on the individual expression ninit lists. func (o *Order) exprListInPlace(l Nodes) { s := l.Slice() for i := range s { s[i] = o.exprInPlace(s[i]) } } // prealloc[x] records the allocation to use for x. var prealloc = map[*Node]*Node{} // expr orders a single expression, appending side // effects to o.out as needed. // If this is part of an assignment lhs = *np, lhs is given. // Otherwise lhs == nil. (When lhs != nil it may be possible // to avoid copying the result of the expression to a temporary.) // The result of expr MUST be assigned back to n, e.g. // n.Left = o.expr(n.Left, lhs) func (o *Order) expr(n, lhs *Node) *Node { if n == nil { return n } lno := setlineno(n) o.init(n) switch n.Op { default: n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) o.exprList(n.List) o.exprList(n.Rlist) // Addition of strings turns into a function call. // Allocate a temporary to hold the strings. // Fewer than 5 strings use direct runtime helpers. case OADDSTR: o.exprList(n.List) if n.List.Len() > 5 { t := types.NewArray(types.Types[TSTRING], int64(n.List.Len())) prealloc[n] = o.newTemp(t, false) } // Mark string(byteSlice) arguments to reuse byteSlice backing // buffer during conversion. String concatenation does not // memorize the strings for later use, so it is safe. // However, we can do it only if there is at least one non-empty string literal. // Otherwise if all other arguments are empty strings, // concatstrings will return the reference to the temp string // to the caller. hasbyte := false haslit := false for _, n1 := range n.List.Slice() { hasbyte = hasbyte || n1.Op == OBYTES2STR haslit = haslit || n1.Op == OLITERAL && len(n1.StringVal()) != 0 } if haslit && hasbyte { for _, n2 := range n.List.Slice() { if n2.Op == OBYTES2STR { n2.Op = OBYTES2STRTMP } } } case OINDEXMAP: n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) needCopy := false if !n.IndexMapLValue() { // Enforce that any []byte slices we are not copying // can not be changed before the map index by forcing // the map index to happen immediately following the // conversions. See copyExpr a few lines below. needCopy = mapKeyReplaceStrConv(n.Right) if instrumenting { // Race detector needs the copy so it can // call treecopy on the result. needCopy = true } } // key must be addressable n.Right = o.mapKeyTemp(n.Left.Type, n.Right) if needCopy { n = o.copyExpr(n, n.Type, false) } // concrete type (not interface) argument might need an addressable // temporary to pass to the runtime conversion routine. case OCONVIFACE: n.Left = o.expr(n.Left, nil) if n.Left.Type.IsInterface() { break } if _, needsaddr := convFuncName(n.Left.Type, n.Type); needsaddr || isStaticCompositeLiteral(n.Left) { // Need a temp if we need to pass the address to the conversion function. // We also process static composite literal node here, making a named static global // whose address we can put directly in an interface (see OCONVIFACE case in walk). n.Left = o.addrTemp(n.Left) } case OCONVNOP: if n.Type.IsKind(TUNSAFEPTR) && n.Left.Type.IsKind(TUINTPTR) && (n.Left.Op == OCALLFUNC || n.Left.Op == OCALLINTER || n.Left.Op == OCALLMETH) { // When reordering unsafe.Pointer(f()) into a separate // statement, the conversion and function call must stay // together. See golang.org/issue/15329. o.init(n.Left) o.call(n.Left) if lhs == nil || lhs.Op != ONAME || instrumenting { n = o.copyExpr(n, n.Type, false) } } else { n.Left = o.expr(n.Left, nil) } case OANDAND, OOROR: // ... = LHS && RHS // // var r bool // r = LHS // if r { // or !r, for OROR // r = RHS // } // ... = r r := o.newTemp(n.Type, false) // Evaluate left-hand side. lhs := o.expr(n.Left, nil) o.out = append(o.out, typecheck(nod(OAS, r, lhs), ctxStmt)) // Evaluate right-hand side, save generated code. saveout := o.out o.out = nil t := o.markTemp() o.edge() rhs := o.expr(n.Right, nil) o.out = append(o.out, typecheck(nod(OAS, r, rhs), ctxStmt)) o.cleanTemp(t) gen := o.out o.out = saveout // If left-hand side doesn't cause a short-circuit, issue right-hand side. nif := nod(OIF, r, nil) if n.Op == OANDAND { nif.Nbody.Set(gen) } else { nif.Rlist.Set(gen) } o.out = append(o.out, nif) n = r case OCALLFUNC, OCALLINTER, OCALLMETH, OCAP, OCOMPLEX, OCOPY, OIMAG, OLEN, OMAKECHAN, OMAKEMAP, OMAKESLICE, OMAKESLICECOPY, ONEW, OREAL, ORECOVER, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES: if isRuneCount(n) { // len([]rune(s)) is rewritten to runtime.countrunes(s) later. n.Left.Left = o.expr(n.Left.Left, nil) } else { o.call(n) } if lhs == nil || lhs.Op != ONAME || instrumenting { n = o.copyExpr(n, n.Type, false) } case OAPPEND: // Check for append(x, make([]T, y)...) . if isAppendOfMake(n) { n.List.SetFirst(o.expr(n.List.First(), nil)) // order x n.List.Second().Left = o.expr(n.List.Second().Left, nil) // order y } else { o.exprList(n.List) } if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.First()) { n = o.copyExpr(n, n.Type, false) } case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: n.Left = o.expr(n.Left, nil) low, high, max := n.SliceBounds() low = o.expr(low, nil) low = o.cheapExpr(low) high = o.expr(high, nil) high = o.cheapExpr(high) max = o.expr(max, nil) max = o.cheapExpr(max) n.SetSliceBounds(low, high, max) if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) { n = o.copyExpr(n, n.Type, false) } case OCLOSURE: if n.Transient() && n.Func.Closure.Func.Cvars.Len() > 0 { prealloc[n] = o.newTemp(closureType(n), false) } case OSLICELIT, OCALLPART: n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) o.exprList(n.List) o.exprList(n.Rlist) if n.Transient() { var t *types.Type switch n.Op { case OSLICELIT: t = types.NewArray(n.Type.Elem(), n.Right.Int64Val()) case OCALLPART: t = partialCallType(n) } prealloc[n] = o.newTemp(t, false) } case ODOTTYPE, ODOTTYPE2: n.Left = o.expr(n.Left, nil) if !isdirectiface(n.Type) || instrumenting { n = o.copyExpr(n, n.Type, true) } case ORECV: n.Left = o.expr(n.Left, nil) n = o.copyExpr(n, n.Type, true) case OEQ, ONE, OLT, OLE, OGT, OGE: n.Left = o.expr(n.Left, nil) n.Right = o.expr(n.Right, nil) t := n.Left.Type switch { case t.IsString(): // Mark string(byteSlice) arguments to reuse byteSlice backing // buffer during conversion. String comparison does not // memorize the strings for later use, so it is safe. if n.Left.Op == OBYTES2STR { n.Left.Op = OBYTES2STRTMP } if n.Right.Op == OBYTES2STR { n.Right.Op = OBYTES2STRTMP } case t.IsStruct() || t.IsArray(): // for complex comparisons, we need both args to be // addressable so we can pass them to the runtime. n.Left = o.addrTemp(n.Left) n.Right = o.addrTemp(n.Right) } case OMAPLIT: // Order map by converting: // map[int]int{ // a(): b(), // c(): d(), // e(): f(), // } // to // m := map[int]int{} // m[a()] = b() // m[c()] = d() // m[e()] = f() // Then order the result. // Without this special case, order would otherwise compute all // the keys and values before storing any of them to the map. // See issue 26552. entries := n.List.Slice() statics := entries[:0] var dynamics []*Node for _, r := range entries { if r.Op != OKEY { Fatalf("OMAPLIT entry not OKEY: %v\n", r) } if !isStaticCompositeLiteral(r.Left) || !isStaticCompositeLiteral(r.Right) { dynamics = append(dynamics, r) continue } // Recursively ordering some static entries can change them to dynamic; // e.g., OCONVIFACE nodes. See #31777. r = o.expr(r, nil) if !isStaticCompositeLiteral(r.Left) || !isStaticCompositeLiteral(r.Right) { dynamics = append(dynamics, r) continue } statics = append(statics, r) } n.List.Set(statics) if len(dynamics) == 0 { break } // Emit the creation of the map (with all its static entries). m := o.newTemp(n.Type, false) as := nod(OAS, m, n) typecheck(as, ctxStmt) o.stmt(as) n = m // Emit eval+insert of dynamic entries, one at a time. for _, r := range dynamics { as := nod(OAS, nod(OINDEX, n, r.Left), r.Right) typecheck(as, ctxStmt) // Note: this converts the OINDEX to an OINDEXMAP o.stmt(as) } } lineno = lno return n } // okas creates and returns an assignment of val to ok, // including an explicit conversion if necessary. func okas(ok, val *Node) *Node { if !ok.isBlank() { val = conv(val, ok.Type) } return nod(OAS, ok, val) } // as2 orders OAS2XXXX nodes. It creates temporaries to ensure left-to-right assignment. // The caller should order the right-hand side of the assignment before calling order.as2. // It rewrites, // a, b, a = ... // as // tmp1, tmp2, tmp3 = ... // a, b, a = tmp1, tmp2, tmp3 // This is necessary to ensure left to right assignment order. func (o *Order) as2(n *Node) { tmplist := []*Node{} left := []*Node{} for ni, l := range n.List.Slice() { if !l.isBlank() { tmp := o.newTemp(l.Type, l.Type.HasPointers()) n.List.SetIndex(ni, tmp) tmplist = append(tmplist, tmp) left = append(left, l) } } o.out = append(o.out, n) as := nod(OAS2, nil, nil) as.List.Set(left) as.Rlist.Set(tmplist) as = typecheck(as, ctxStmt) o.stmt(as) } // okAs2 orders OAS2XXX with ok. // Just like as2, this also adds temporaries to ensure left-to-right assignment. func (o *Order) okAs2(n *Node) { var tmp1, tmp2 *Node if !n.List.First().isBlank() { typ := n.Right.Type tmp1 = o.newTemp(typ, typ.HasPointers()) } if !n.List.Second().isBlank() { tmp2 = o.newTemp(types.Types[TBOOL], false) } o.out = append(o.out, n) if tmp1 != nil { r := nod(OAS, n.List.First(), tmp1) r = typecheck(r, ctxStmt) o.mapAssign(r) n.List.SetFirst(tmp1) } if tmp2 != nil { r := okas(n.List.Second(), tmp2) r = typecheck(r, ctxStmt) o.mapAssign(r) n.List.SetSecond(tmp2) } }