| // 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 x op= y into x = x op y. |
| // |
| // 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 |
| } |
| |
| // 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) |
| } |
| |
| orderblockNodes(&fn.Nbody) |
| } |
| |
| // Ordertemp allocates a new temporary with the given type, |
| // pushes it onto the temp stack, and returns it. |
| // If clear is true, ordertemp emits code to zero the temporary. |
| func ordertemp(t *types.Type, order *Order, clear bool) *Node { |
| var_ := temp(t) |
| if clear { |
| a := nod(OAS, var_, nil) |
| a = typecheck(a, Etop) |
| order.out = append(order.out, a) |
| } |
| |
| order.temp = append(order.temp, var_) |
| return var_ |
| } |
| |
| // Ordercopyexpr behaves like ordertemp 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 ordercopyexpr(n *Node, t *types.Type, order *Order, clear int) *Node { |
| var_ := ordertemp(t, order, clear != 0) |
| a := nod(OAS, var_, n) |
| a = typecheck(a, Etop) |
| order.out = append(order.out, a) |
| return var_ |
| } |
| |
| // Ordercheapexpr returns a cheap version of n. |
| // The definition of cheap is that n is a variable or constant. |
| // If not, ordercheapexpr allocates a new tmp, emits tmp = n, |
| // and then returns tmp. |
| func ordercheapexpr(n *Node, order *Order) *Node { |
| if n == nil { |
| return nil |
| } |
| switch n.Op { |
| case ONAME, OLITERAL: |
| return n |
| case OLEN, OCAP: |
| l := ordercheapexpr(n.Left, order) |
| if l == n.Left { |
| return n |
| } |
| a := *n |
| a.Orig = &a |
| a.Left = l |
| return typecheck(&a, Erv) |
| } |
| |
| return ordercopyexpr(n, n.Type, order, 0) |
| } |
| |
| // Ordersafeexpr 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 ordersafeexpr(n *Node, order *Order) *Node { |
| switch n.Op { |
| case ONAME, OLITERAL: |
| return n |
| |
| case ODOT, OLEN, OCAP: |
| l := ordersafeexpr(n.Left, order) |
| if l == n.Left { |
| return n |
| } |
| a := *n |
| a.Orig = &a |
| a.Left = l |
| return typecheck(&a, Erv) |
| |
| case ODOTPTR, OIND: |
| l := ordercheapexpr(n.Left, order) |
| if l == n.Left { |
| return n |
| } |
| a := *n |
| a.Orig = &a |
| a.Left = l |
| return typecheck(&a, Erv) |
| |
| case OINDEX, OINDEXMAP: |
| var l *Node |
| if n.Left.Type.IsArray() { |
| l = ordersafeexpr(n.Left, order) |
| } else { |
| l = ordercheapexpr(n.Left, order) |
| } |
| r := ordercheapexpr(n.Right, order) |
| if l == n.Left && r == n.Right { |
| return n |
| } |
| a := *n |
| a.Orig = &a |
| a.Left = l |
| a.Right = r |
| return typecheck(&a, Erv) |
| default: |
| Fatalf("ordersafeexpr %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()) |
| } |
| |
| // Orderaddrtemp ensures that n is okay to pass by address to runtime routines. |
| // If the original argument n is not okay, orderaddrtemp creates a tmp, emits |
| // tmp = n, and then returns tmp. |
| // The result of orderaddrtemp MUST be assigned back to n, e.g. |
| // n.Left = orderaddrtemp(n.Left, order) |
| func orderaddrtemp(n *Node, order *Order) *Node { |
| if consttype(n) >= 0 { |
| // TODO: expand this to all static composite literal nodes? |
| n = defaultlit(n, nil) |
| dowidth(n.Type) |
| vstat := staticname(n.Type) |
| vstat.Name.SetReadonly(true) |
| var out []*Node |
| staticassign(vstat, n, &out) |
| if out != nil { |
| Fatalf("staticassign of const generated code: %+v", n) |
| } |
| vstat = typecheck(vstat, Erv) |
| return vstat |
| } |
| if isaddrokay(n) { |
| return n |
| } |
| return ordercopyexpr(n, n.Type, order, 0) |
| } |
| |
| // ordermapkeytemp 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 ordermapkeytemp(t *types.Type, n *Node, order *Order) *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 orderaddrtemp(n, order) |
| } |
| return n |
| } |
| |
| type ordermarker int |
| |
| // Marktemp returns the top of the temporary variable stack. |
| func marktemp(order *Order) ordermarker { |
| return ordermarker(len(order.temp)) |
| } |
| |
| // Poptemp pops temporaries off the stack until reaching the mark, |
| // which must have been returned by marktemp. |
| func poptemp(mark ordermarker, order *Order) { |
| order.temp = order.temp[:mark] |
| } |
| |
| // Cleantempnopop emits to *out VARKILL instructions for each temporary |
| // above the mark on the temporary stack, but it does not pop them |
| // from the stack. |
| func cleantempnopop(mark ordermarker, order *Order, out *[]*Node) { |
| var kill *Node |
| |
| for i := len(order.temp) - 1; i >= int(mark); i-- { |
| n := order.temp[i] |
| if n.Name.Keepalive() { |
| n.Name.SetKeepalive(false) |
| n.SetAddrtaken(true) // ensure SSA keeps the n variable |
| kill = nod(OVARLIVE, n, nil) |
| kill = typecheck(kill, Etop) |
| *out = append(*out, kill) |
| } |
| kill = nod(OVARKILL, n, nil) |
| kill = typecheck(kill, Etop) |
| *out = append(*out, kill) |
| } |
| } |
| |
| // Cleantemp emits VARKILL instructions for each temporary above the |
| // mark on the temporary stack and removes them from the stack. |
| func cleantemp(top ordermarker, order *Order) { |
| cleantempnopop(top, order, &order.out) |
| poptemp(top, order) |
| } |
| |
| // Orderstmtlist orders each of the statements in the list. |
| func orderstmtlist(l Nodes, order *Order) { |
| for _, n := range l.Slice() { |
| orderstmt(n, order) |
| } |
| } |
| |
| // Orderblock orders the block of statements l onto a new list, |
| // and returns the ordered list. |
| func orderblock(l Nodes) []*Node { |
| var order Order |
| mark := marktemp(&order) |
| orderstmtlist(l, &order) |
| cleantemp(mark, &order) |
| return order.out |
| } |
| |
| // OrderblockNodes orders the block of statements in n into a new slice, |
| // and then replaces the old slice in n with the new slice. |
| func orderblockNodes(n *Nodes) { |
| var order Order |
| mark := marktemp(&order) |
| orderstmtlist(*n, &order) |
| cleantemp(mark, &order) |
| n.Set(order.out) |
| } |
| |
| // Orderexprinplace orders the side effects in *np and |
| // leaves them as the init list of the final *np. |
| // The result of orderexprinplace MUST be assigned back to n, e.g. |
| // n.Left = orderexprinplace(n.Left, outer) |
| func orderexprinplace(n *Node, outer *Order) *Node { |
| var order Order |
| n = orderexpr(n, &order, nil) |
| n = addinit(n, order.out) |
| |
| // insert new temporaries from order |
| // at head of outer list. |
| outer.temp = append(outer.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) |
| func orderstmtinplace(n *Node) *Node { |
| var order Order |
| mark := marktemp(&order) |
| orderstmt(n, &order) |
| cleantemp(mark, &order) |
| return liststmt(order.out) |
| } |
| |
| // Orderinit moves n's init list to order->out. |
| func orderinit(n *Node, order *Order) { |
| 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("orderinit shared node with ninit") |
| } |
| return |
| } |
| orderstmtlist(n.Ninit, order) |
| n.Ninit.Set(nil) |
| } |
| |
| // Ismulticall reports whether the list l is f() for a multi-value function. |
| // Such an f() could appear as the lone argument to a multi-arg function. |
| func ismulticall(l Nodes) bool { |
| // one arg only |
| if l.Len() != 1 { |
| return false |
| } |
| n := l.First() |
| |
| // must be call |
| switch n.Op { |
| default: |
| return false |
| |
| case OCALLFUNC, OCALLMETH, OCALLINTER: |
| break |
| } |
| |
| // call must return multiple values |
| return n.Left.Type.Results().NumFields() > 1 |
| } |
| |
| // Copyret emits t1, t2, ... = n, where n is a function call, |
| // and then returns the list t1, t2, .... |
| func copyret(n *Node, order *Order) []*Node { |
| if !n.Type.IsFuncArgStruct() { |
| Fatalf("copyret %v %d", n.Type, n.Left.Type.Results().NumFields()) |
| } |
| |
| var l1 []*Node |
| var l2 []*Node |
| for _, t := range n.Type.Fields().Slice() { |
| tmp := temp(t.Type) |
| l1 = append(l1, tmp) |
| l2 = append(l2, tmp) |
| } |
| |
| as := nod(OAS2, nil, nil) |
| as.List.Set(l1) |
| as.Rlist.Set1(n) |
| as = typecheck(as, Etop) |
| orderstmt(as, order) |
| |
| return l2 |
| } |
| |
| // Ordercallargs orders the list of call arguments *l. |
| func ordercallargs(l *Nodes, order *Order) { |
| if ismulticall(*l) { |
| // return f() where f() is multiple values. |
| l.Set(copyret(l.First(), order)) |
| } else { |
| orderexprlist(*l, order) |
| } |
| } |
| |
| // Ordercall orders the call expression n. |
| // n->op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY. |
| func ordercall(n *Node, order *Order) { |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) // ODDDARG temp |
| ordercallargs(&n.List, order) |
| |
| if n.Op == OCALLFUNC { |
| keepAlive := func(i int) { |
| // 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. |
| xp := n.List.Addr(i) |
| for (*xp).Op == OCONVNOP && !(*xp).Type.IsPtr() { |
| xp = &(*xp).Left |
| } |
| x := *xp |
| if x.Type.IsPtr() { |
| x = ordercopyexpr(x, x.Type, order, 0) |
| x.Name.SetKeepalive(true) |
| *xp = x |
| } |
| } |
| |
| for i, t := range n.Left.Type.Params().FieldSlice() { |
| // Check for "unsafe-uintptr" tag provided by escape analysis. |
| if t.Isddd() && !n.Isddd() { |
| if t.Note == uintptrEscapesTag { |
| for ; i < n.List.Len(); i++ { |
| keepAlive(i) |
| } |
| } |
| } else { |
| if t.Note == unsafeUintptrTag || t.Note == uintptrEscapesTag { |
| keepAlive(i) |
| } |
| } |
| } |
| } |
| } |
| |
| // Ordermapassign appends n to order->out, introducing temporaries |
| // to make sure that all map assignments have the form m[k] = x. |
| // (Note: orderexpr has already been called on n, so we know k is addressable.) |
| // |
| // If n is the multiple assignment form ..., m[k], ... = ..., 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 ordermapassign(n *Node, order *Order) { |
| switch n.Op { |
| default: |
| Fatalf("ordermapassign %v", n.Op) |
| |
| case OAS: |
| order.out = append(order.out, n) |
| |
| case OAS2, OAS2DOTTYPE, OAS2MAPR, OAS2FUNC: |
| var post []*Node |
| var m *Node |
| var a *Node |
| for i1, n1 := range n.List.Slice() { |
| if n1.Op == OINDEXMAP { |
| m = n1 |
| if !m.Left.IsAutoTmp() { |
| m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0) |
| } |
| if !m.Right.IsAutoTmp() { |
| m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0) |
| } |
| n.List.SetIndex(i1, ordertemp(m.Type, order, false)) |
| a = nod(OAS, m, n.List.Index(i1)) |
| a = typecheck(a, Etop) |
| post = append(post, a) |
| } else if instrumenting && n.Op == OAS2FUNC && !isblank(n.List.Index(i1)) { |
| m = n.List.Index(i1) |
| t := ordertemp(m.Type, order, false) |
| n.List.SetIndex(i1, t) |
| a = nod(OAS, m, t) |
| a = typecheck(a, Etop) |
| post = append(post, a) |
| } |
| } |
| |
| order.out = append(order.out, n) |
| order.out = append(order.out, post...) |
| } |
| } |
| |
| // Orderstmt orders the statement n, appending to order->out. |
| // Temporaries created during the statement are cleaned |
| // up using VARKILL instructions as possible. |
| func orderstmt(n *Node, order *Order) { |
| if n == nil { |
| return |
| } |
| |
| lno := setlineno(n) |
| |
| orderinit(n, order) |
| |
| switch n.Op { |
| default: |
| Fatalf("orderstmt %v", n.Op) |
| |
| case OVARKILL, OVARLIVE: |
| order.out = append(order.out, n) |
| |
| case OAS: |
| t := marktemp(order) |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, n.Left) |
| ordermapassign(n, order) |
| cleantemp(t, order) |
| |
| case OAS2, |
| OCLOSE, |
| OCOPY, |
| OPRINT, |
| OPRINTN, |
| ORECOVER, |
| ORECV: |
| t := marktemp(order) |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) |
| orderexprlist(n.List, order) |
| orderexprlist(n.Rlist, order) |
| switch n.Op { |
| case OAS2: |
| ordermapassign(n, order) |
| default: |
| order.out = append(order.out, n) |
| } |
| cleantemp(t, order) |
| |
| case OASOP: |
| // Special: rewrite l op= r into l = l op r. |
| // This simplifies quite a few operations; |
| // most important is that it lets us separate |
| // out map read from map write when l is |
| // a map index expression. |
| t := marktemp(order) |
| |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Left = ordersafeexpr(n.Left, order) |
| tmp1 := treecopy(n.Left, src.NoXPos) |
| if tmp1.Op == OINDEXMAP { |
| tmp1.Etype = 0 // now an rvalue not an lvalue |
| } |
| tmp1 = ordercopyexpr(tmp1, n.Left.Type, order, 0) |
| // TODO(marvin): Fix Node.EType type union. |
| n.Right = nod(Op(n.Etype), tmp1, n.Right) |
| n.Right = typecheck(n.Right, Erv) |
| n.Right = orderexpr(n.Right, order, nil) |
| n.Etype = 0 |
| n.Op = OAS |
| ordermapassign(n, order) |
| cleantemp(t, order) |
| |
| // Special: make sure key is addressable if needed, |
| // and make sure OINDEXMAP is not copied out. |
| case OAS2MAPR: |
| t := marktemp(order) |
| |
| orderexprlist(n.List, order) |
| r := n.Rlist.First() |
| r.Left = orderexpr(r.Left, order, nil) |
| r.Right = orderexpr(r.Right, order, nil) |
| |
| // See case OINDEXMAP below. |
| if r.Right.Op == OARRAYBYTESTR { |
| r.Right.Op = OARRAYBYTESTRTMP |
| } |
| r.Right = ordermapkeytemp(r.Left.Type, r.Right, order) |
| orderokas2(n, order) |
| cleantemp(t, order) |
| |
| // Special: avoid copy of func call n->rlist->n. |
| case OAS2FUNC: |
| t := marktemp(order) |
| |
| orderexprlist(n.List, order) |
| ordercall(n.Rlist.First(), order) |
| orderas2(n, order) |
| cleantemp(t, order) |
| |
| // Special: use temporary variables to hold result, |
| // so that assertI2Tetc can take address of temporary. |
| // No temporary for blank assignment. |
| case OAS2DOTTYPE: |
| t := marktemp(order) |
| |
| orderexprlist(n.List, order) |
| n.Rlist.First().Left = orderexpr(n.Rlist.First().Left, order, nil) // i in i.(T) |
| orderokas2(n, order) |
| cleantemp(t, order) |
| |
| // Special: use temporary variables to hold result, |
| // so that chanrecv can take address of temporary. |
| case OAS2RECV: |
| t := marktemp(order) |
| |
| orderexprlist(n.List, order) |
| n.Rlist.First().Left = orderexpr(n.Rlist.First().Left, order, nil) // arg to recv |
| ch := n.Rlist.First().Left.Type |
| tmp1 := ordertemp(ch.Elem(), order, types.Haspointers(ch.Elem())) |
| tmp2 := ordertemp(types.Types[TBOOL], order, false) |
| order.out = append(order.out, n) |
| r := nod(OAS, n.List.First(), tmp1) |
| r = typecheck(r, Etop) |
| ordermapassign(r, order) |
| r = okas(n.List.Second(), tmp2) |
| r = typecheck(r, Etop) |
| ordermapassign(r, order) |
| n.List.Set2(tmp1, tmp2) |
| cleantemp(t, order) |
| |
| // Special: does not save n onto out. |
| case OBLOCK, OEMPTY: |
| orderstmtlist(n.List, order) |
| |
| // Special: n->left is not an expression; save as is. |
| case OBREAK, |
| OCONTINUE, |
| ODCL, |
| ODCLCONST, |
| ODCLTYPE, |
| OFALL, |
| OXFALL, |
| OGOTO, |
| OLABEL, |
| ORETJMP: |
| order.out = append(order.out, n) |
| |
| // Special: handle call arguments. |
| case OCALLFUNC, OCALLINTER, OCALLMETH: |
| t := marktemp(order) |
| |
| ordercall(n, order) |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| |
| // Special: order arguments to inner call but not call itself. |
| case ODEFER, OPROC: |
| t := marktemp(order) |
| |
| switch n.Left.Op { |
| // Delete will take the address of the key. |
| // Copy key into new temp and do not clean it |
| // (it persists beyond the statement). |
| case ODELETE: |
| orderexprlist(n.Left.List, order) |
| |
| if mapfast(n.Left.List.First().Type) == mapslow { |
| t1 := marktemp(order) |
| np := n.Left.List.Addr(1) // map key |
| *np = ordercopyexpr(*np, (*np).Type, order, 0) |
| poptemp(t1, order) |
| } |
| |
| default: |
| ordercall(n.Left, order) |
| } |
| |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| |
| case ODELETE: |
| t := marktemp(order) |
| n.List.SetFirst(orderexpr(n.List.First(), order, nil)) |
| n.List.SetSecond(orderexpr(n.List.Second(), order, nil)) |
| n.List.SetSecond(ordermapkeytemp(n.List.First().Type, n.List.Second(), order)) |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| |
| // Clean temporaries from condition evaluation at |
| // beginning of loop body and after for statement. |
| case OFOR: |
| t := marktemp(order) |
| |
| n.Left = orderexprinplace(n.Left, order) |
| var l []*Node |
| cleantempnopop(t, order, &l) |
| n.Nbody.Prepend(l...) |
| orderblockNodes(&n.Nbody) |
| n.Right = orderstmtinplace(n.Right) |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| |
| // Clean temporaries from condition at |
| // beginning of both branches. |
| case OIF: |
| t := marktemp(order) |
| |
| n.Left = orderexprinplace(n.Left, order) |
| var l []*Node |
| cleantempnopop(t, order, &l) |
| n.Nbody.Prepend(l...) |
| l = nil |
| cleantempnopop(t, order, &l) |
| n.Rlist.Prepend(l...) |
| poptemp(t, order) |
| orderblockNodes(&n.Nbody) |
| n.Rlist.Set(orderblock(n.Rlist)) |
| order.out = append(order.out, n) |
| |
| // Special: argument will be converted to interface using convT2E |
| // so make sure it is an addressable temporary. |
| case OPANIC: |
| t := marktemp(order) |
| |
| n.Left = orderexpr(n.Left, order, nil) |
| if !n.Left.Type.IsInterface() { |
| n.Left = orderaddrtemp(n.Left, order) |
| } |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| |
| 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 == OSTRARRAYBYTE { |
| n.Right.Op = OSTRARRAYBYTETMP |
| } |
| |
| t := marktemp(order) |
| n.Right = orderexpr(n.Right, order, nil) |
| switch n.Type.Etype { |
| default: |
| Fatalf("orderstmt range %v", n.Type) |
| |
| case TARRAY, TSLICE: |
| if n.List.Len() < 2 || isblank(n.List.Second()) { |
| // 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, Erv) |
| } |
| |
| n.Right = ordercopyexpr(r, r.Type, order, 0) |
| |
| case TMAP: |
| // 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 = ordercopyexpr(r, r.Type, order, 0) |
| |
| // n->alloc is the temp for the iterator. |
| prealloc[n] = ordertemp(types.Types[TUINT8], order, true) |
| } |
| for i := range n.List.Slice() { |
| n.List.SetIndex(i, orderexprinplace(n.List.Index(i), order)) |
| } |
| orderblockNodes(&n.Nbody) |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| |
| case ORETURN: |
| ordercallargs(&n.List, order) |
| order.out = append(order.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 := marktemp(order) |
| |
| var tmp1 *Node |
| var tmp2 *Node |
| var r *Node |
| for _, n2 := range n.List.Slice() { |
| if n2.Op != OXCASE { |
| 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 { |
| 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 = orderexpr(r.Right.Left, order, nil) |
| |
| if r.Right.Left.Op != ONAME { |
| r.Right.Left = ordercopyexpr(r.Right.Left, r.Right.Left.Type, order, 0) |
| } |
| |
| // 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 && isblank(r.Left) { |
| 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, Etop) |
| n2.Ninit.Append(tmp2) |
| } |
| |
| r.Left = ordertemp(r.Right.Left.Type.Elem(), order, types.Haspointers(r.Right.Left.Type.Elem())) |
| tmp2 = nod(OAS, tmp1, r.Left) |
| tmp2 = typecheck(tmp2, Etop) |
| n2.Ninit.Append(tmp2) |
| } |
| |
| if r.List.Len() != 0 && isblank(r.List.First()) { |
| r.List.Set(nil) |
| } |
| if r.List.Len() != 0 { |
| tmp1 = r.List.First() |
| if r.Colas() { |
| tmp2 = nod(ODCL, tmp1, nil) |
| tmp2 = typecheck(tmp2, Etop) |
| n2.Ninit.Append(tmp2) |
| } |
| |
| r.List.Set1(ordertemp(types.Types[TBOOL], order, false)) |
| tmp2 = okas(tmp1, r.List.First()) |
| tmp2 = typecheck(tmp2, Etop) |
| n2.Ninit.Append(tmp2) |
| } |
| n2.Ninit.Set(orderblock(n2.Ninit)) |
| |
| 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 = orderexpr(r.Left, order, nil) |
| |
| if !r.Left.IsAutoTmp() { |
| r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0) |
| } |
| r.Right = orderexpr(r.Right, order, nil) |
| if !r.Right.IsAutoTmp() { |
| r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0) |
| } |
| } |
| } |
| |
| orderblockNodes(&n2.Nbody) |
| } |
| // 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() { |
| s := n3.Ninit.Slice() |
| cleantempnopop(t, order, &s) |
| n3.Nbody.Prepend(s...) |
| n3.Ninit.Set(nil) |
| } |
| |
| order.out = append(order.out, n) |
| poptemp(t, order) |
| |
| // Special: value being sent is passed as a pointer; make it addressable. |
| case OSEND: |
| t := marktemp(order) |
| |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) |
| if instrumenting { |
| // Force copying to the stack so that (chan T)(nil) <- x |
| // is still instrumented as a read of x. |
| n.Right = ordercopyexpr(n.Right, n.Right.Type, order, 0) |
| } else { |
| n.Right = orderaddrtemp(n.Right, order) |
| } |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| |
| // 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 orderstmt on |
| // the if-else chain instead.) |
| // For now just clean all the temporaries at the end. |
| // In practice that's fine. |
| case OSWITCH: |
| t := marktemp(order) |
| |
| n.Left = orderexpr(n.Left, order, nil) |
| for _, n4 := range n.List.Slice() { |
| if n4.Op != OXCASE { |
| Fatalf("order switch case %v", n4.Op) |
| } |
| orderexprlistinplace(n4.List, order) |
| orderblockNodes(&n4.Nbody) |
| } |
| |
| order.out = append(order.out, n) |
| cleantemp(t, order) |
| } |
| |
| lineno = lno |
| } |
| |
| // Orderexprlist orders the expression list l into order. |
| func orderexprlist(l Nodes, order *Order) { |
| s := l.Slice() |
| for i := range s { |
| s[i] = orderexpr(s[i], order, nil) |
| } |
| } |
| |
| // Orderexprlist orders the expression list l but saves |
| // the side effects on the individual expression ninit lists. |
| func orderexprlistinplace(l Nodes, order *Order) { |
| s := l.Slice() |
| for i := range s { |
| s[i] = orderexprinplace(s[i], order) |
| } |
| } |
| |
| // prealloc[x] records the allocation to use for x. |
| var prealloc = map[*Node]*Node{} |
| |
| // Orderexpr orders a single expression, appending side |
| // effects to order->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 orderexpr MUST be assigned back to n, e.g. |
| // n.Left = orderexpr(n.Left, order, lhs) |
| func orderexpr(n *Node, order *Order, lhs *Node) *Node { |
| if n == nil { |
| return n |
| } |
| |
| lno := setlineno(n) |
| orderinit(n, order) |
| |
| switch n.Op { |
| default: |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) |
| orderexprlist(n.List, order) |
| orderexprlist(n.Rlist, order) |
| |
| // 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: |
| orderexprlist(n.List, order) |
| |
| if n.List.Len() > 5 { |
| t := types.NewArray(types.Types[TSTRING], int64(n.List.Len())) |
| prealloc[n] = ordertemp(t, order, 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 == OARRAYBYTESTR |
| haslit = haslit || n1.Op == OLITERAL && len(n1.Val().U.(string)) != 0 |
| } |
| |
| if haslit && hasbyte { |
| for _, n2 := range n.List.Slice() { |
| if n2.Op == OARRAYBYTESTR { |
| n2.Op = OARRAYBYTESTRTMP |
| } |
| } |
| } |
| |
| case OCMPSTR: |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) |
| |
| // 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 == OARRAYBYTESTR { |
| n.Left.Op = OARRAYBYTESTRTMP |
| } |
| if n.Right.Op == OARRAYBYTESTR { |
| n.Right.Op = OARRAYBYTESTRTMP |
| } |
| |
| // key must be addressable |
| case OINDEXMAP: |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) |
| needCopy := false |
| |
| if n.Etype == 0 && instrumenting { |
| // Race detector needs the copy so it can |
| // call treecopy on the result. |
| needCopy = true |
| } |
| |
| // For x = m[string(k)] where k is []byte, the allocation of |
| // backing bytes for the string can be avoided by reusing |
| // the []byte backing array. This is a special case that it |
| // would be nice to handle more generally, but because |
| // there are no []byte-keyed maps, this specific case comes |
| // up in important cases in practice. See issue 3512. |
| // Nothing can change the []byte we are not copying before |
| // the map index, because the map access is going to |
| // be forced to happen immediately following this |
| // conversion (by the ordercopyexpr a few lines below). |
| if n.Etype == 0 && n.Right.Op == OARRAYBYTESTR { |
| n.Right.Op = OARRAYBYTESTRTMP |
| needCopy = true |
| } |
| |
| n.Right = ordermapkeytemp(n.Left.Type, n.Right, order) |
| if needCopy { |
| n = ordercopyexpr(n, n.Type, order, 0) |
| } |
| |
| // concrete type (not interface) argument must be addressable |
| // temporary to pass to runtime. |
| case OCONVIFACE: |
| n.Left = orderexpr(n.Left, order, nil) |
| |
| if !n.Left.Type.IsInterface() { |
| n.Left = orderaddrtemp(n.Left, order) |
| } |
| |
| 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. |
| orderinit(n.Left, order) |
| ordercall(n.Left, order) |
| if lhs == nil || lhs.Op != ONAME || instrumenting { |
| n = ordercopyexpr(n, n.Type, order, 0) |
| } |
| } else { |
| n.Left = orderexpr(n.Left, order, nil) |
| } |
| |
| case OANDAND, OOROR: |
| mark := marktemp(order) |
| n.Left = orderexpr(n.Left, order, nil) |
| |
| // Clean temporaries from first branch at beginning of second. |
| // Leave them on the stack so that they can be killed in the outer |
| // context in case the short circuit is taken. |
| var s []*Node |
| |
| cleantempnopop(mark, order, &s) |
| n.Right = addinit(n.Right, s) |
| n.Right = orderexprinplace(n.Right, order) |
| |
| case OCALLFUNC, |
| OCALLINTER, |
| OCALLMETH, |
| OCAP, |
| OCOMPLEX, |
| OCOPY, |
| OIMAG, |
| OLEN, |
| OMAKECHAN, |
| OMAKEMAP, |
| OMAKESLICE, |
| ONEW, |
| OREAL, |
| ORECOVER, |
| OSTRARRAYBYTE, |
| OSTRARRAYBYTETMP, |
| OSTRARRAYRUNE: |
| ordercall(n, order) |
| if lhs == nil || lhs.Op != ONAME || instrumenting { |
| n = ordercopyexpr(n, n.Type, order, 0) |
| } |
| |
| case OAPPEND: |
| ordercallargs(&n.List, order) |
| if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.First()) { |
| n = ordercopyexpr(n, n.Type, order, 0) |
| } |
| |
| case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: |
| n.Left = orderexpr(n.Left, order, nil) |
| low, high, max := n.SliceBounds() |
| low = orderexpr(low, order, nil) |
| low = ordercheapexpr(low, order) |
| high = orderexpr(high, order, nil) |
| high = ordercheapexpr(high, order) |
| max = orderexpr(max, order, nil) |
| max = ordercheapexpr(max, order) |
| n.SetSliceBounds(low, high, max) |
| if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) { |
| n = ordercopyexpr(n, n.Type, order, 0) |
| } |
| |
| case OCLOSURE: |
| if n.Noescape() && n.Func.Cvars.Len() > 0 { |
| prealloc[n] = ordertemp(types.Types[TUINT8], order, false) // walk will fill in correct type |
| } |
| |
| case OARRAYLIT, OSLICELIT, OCALLPART: |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) |
| orderexprlist(n.List, order) |
| orderexprlist(n.Rlist, order) |
| if n.Noescape() { |
| prealloc[n] = ordertemp(types.Types[TUINT8], order, false) // walk will fill in correct type |
| } |
| |
| case ODDDARG: |
| if n.Noescape() { |
| // The ddd argument does not live beyond the call it is created for. |
| // Allocate a temporary that will be cleaned up when this statement |
| // completes. We could be more aggressive and try to arrange for it |
| // to be cleaned up when the call completes. |
| prealloc[n] = ordertemp(n.Type.Elem(), order, false) |
| } |
| |
| case ODOTTYPE, ODOTTYPE2: |
| n.Left = orderexpr(n.Left, order, nil) |
| // TODO(rsc): The isfat is for consistency with componentgen and walkexpr. |
| // It needs to be removed in all three places. |
| // That would allow inlining x.(struct{*int}) the same as x.(*int). |
| if !isdirectiface(n.Type) || isfat(n.Type) || instrumenting { |
| n = ordercopyexpr(n, n.Type, order, 1) |
| } |
| |
| case ORECV: |
| n.Left = orderexpr(n.Left, order, nil) |
| n = ordercopyexpr(n, n.Type, order, 1) |
| |
| case OEQ, ONE: |
| n.Left = orderexpr(n.Left, order, nil) |
| n.Right = orderexpr(n.Right, order, nil) |
| t := n.Left.Type |
| if t.IsStruct() || t.IsArray() { |
| // for complex comparisons, we need both args to be |
| // addressable so we can pass them to the runtime. |
| n.Left = orderaddrtemp(n.Left, order) |
| n.Right = orderaddrtemp(n.Right, order) |
| } |
| } |
| |
| 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 !isblank(ok) { |
| val = conv(val, ok.Type) |
| } |
| return nod(OAS, ok, val) |
| } |
| |
| // orderas2 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 orderas2. |
| // 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 orderas2(n *Node, order *Order) { |
| tmplist := []*Node{} |
| left := []*Node{} |
| for _, l := range n.List.Slice() { |
| if !isblank(l) { |
| tmp := ordertemp(l.Type, order, types.Haspointers(l.Type)) |
| tmplist = append(tmplist, tmp) |
| left = append(left, l) |
| } |
| } |
| |
| order.out = append(order.out, n) |
| |
| as := nod(OAS2, nil, nil) |
| as.List.Set(left) |
| as.Rlist.Set(tmplist) |
| as = typecheck(as, Etop) |
| orderstmt(as, order) |
| |
| ti := 0 |
| for ni, l := range n.List.Slice() { |
| if !isblank(l) { |
| n.List.SetIndex(ni, tmplist[ti]) |
| ti++ |
| } |
| } |
| } |
| |
| // orderokas2 orders OAS2 with ok. |
| // Just like orderas2(), this also adds temporaries to ensure left-to-right assignment. |
| func orderokas2(n *Node, order *Order) { |
| var tmp1, tmp2 *Node |
| if !isblank(n.List.First()) { |
| typ := n.Rlist.First().Type |
| tmp1 = ordertemp(typ, order, types.Haspointers(typ)) |
| } |
| |
| if !isblank(n.List.Second()) { |
| tmp2 = ordertemp(types.Types[TBOOL], order, false) |
| } |
| |
| order.out = append(order.out, n) |
| |
| if tmp1 != nil { |
| r := nod(OAS, n.List.First(), tmp1) |
| r = typecheck(r, Etop) |
| ordermapassign(r, order) |
| n.List.SetFirst(tmp1) |
| } |
| if tmp2 != nil { |
| r := okas(n.List.Second(), tmp2) |
| r = typecheck(r, Etop) |
| ordermapassign(r, order) |
| n.List.SetSecond(tmp2) |
| } |
| } |