| // Copyright 2009 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/internal/obj" |
| "fmt" |
| "strings" |
| ) |
| |
| var mpzero Mpint |
| |
| // The constant is known to runtime. |
| const ( |
| tmpstringbufsize = 32 |
| ) |
| |
| func walk(fn *Node) { |
| Curfn = fn |
| |
| if Debug['W'] != 0 { |
| s := fmt.Sprintf("\nbefore %v", Sconv(Curfn.Nname.Sym, 0)) |
| dumplist(s, Curfn.Nbody) |
| } |
| |
| lno := int(lineno) |
| |
| // Final typecheck for any unused variables. |
| // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. |
| for l := fn.Dcl; l != nil; l = l.Next { |
| if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO { |
| typecheck(&l.N, Erv|Easgn) |
| } |
| } |
| |
| // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. |
| for l := fn.Dcl; l != nil; l = l.Next { |
| if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Defn != nil && l.N.Defn.Op == OTYPESW && l.N.Used != 0 { |
| l.N.Defn.Left.Used++ |
| } |
| } |
| |
| for l := fn.Dcl; l != nil; l = l.Next { |
| if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used != 0 { |
| continue |
| } |
| if l.N.Defn != nil && l.N.Defn.Op == OTYPESW { |
| if l.N.Defn.Left.Used != 0 { |
| continue |
| } |
| lineno = l.N.Defn.Left.Lineno |
| Yyerror("%v declared and not used", Sconv(l.N.Sym, 0)) |
| l.N.Defn.Left.Used = 1 // suppress repeats |
| } else { |
| lineno = l.N.Lineno |
| Yyerror("%v declared and not used", Sconv(l.N.Sym, 0)) |
| } |
| } |
| |
| lineno = int32(lno) |
| if nerrors != 0 { |
| return |
| } |
| walkstmtlist(Curfn.Nbody) |
| if Debug['W'] != 0 { |
| s := fmt.Sprintf("after walk %v", Sconv(Curfn.Nname.Sym, 0)) |
| dumplist(s, Curfn.Nbody) |
| } |
| |
| heapmoves() |
| if Debug['W'] != 0 && Curfn.Enter != nil { |
| s := fmt.Sprintf("enter %v", Sconv(Curfn.Nname.Sym, 0)) |
| dumplist(s, Curfn.Enter) |
| } |
| } |
| |
| func walkstmtlist(l *NodeList) { |
| for ; l != nil; l = l.Next { |
| walkstmt(&l.N) |
| } |
| } |
| |
| func samelist(a *NodeList, b *NodeList) bool { |
| for ; a != nil && b != nil; (func() { a = a.Next; b = b.Next })() { |
| if a.N != b.N { |
| return false |
| } |
| } |
| return a == b |
| } |
| |
| func paramoutheap(fn *Node) int { |
| for l := fn.Dcl; l != nil; l = l.Next { |
| switch l.N.Class { |
| case PPARAMOUT, |
| PPARAMOUT | PHEAP: |
| return int(l.N.Addrtaken) |
| |
| // stop early - parameters are over |
| case PAUTO, |
| PAUTO | PHEAP: |
| return 0 |
| } |
| } |
| |
| return 0 |
| } |
| |
| // adds "adjust" to all the argument locations for the call n. |
| // n must be a defer or go node that has already been walked. |
| func adjustargs(n *Node, adjust int) { |
| var arg *Node |
| var lhs *Node |
| |
| callfunc := n.Left |
| for args := callfunc.List; args != nil; args = args.Next { |
| arg = args.N |
| if arg.Op != OAS { |
| Yyerror("call arg not assignment") |
| } |
| lhs = arg.Left |
| if lhs.Op == ONAME { |
| // This is a temporary introduced by reorder1. |
| // The real store to the stack appears later in the arg list. |
| continue |
| } |
| |
| if lhs.Op != OINDREG { |
| Yyerror("call argument store does not use OINDREG") |
| } |
| |
| // can't really check this in machine-indep code. |
| //if(lhs->val.u.reg != D_SP) |
| // yyerror("call arg assign not indreg(SP)"); |
| lhs.Xoffset += int64(adjust) |
| } |
| } |
| |
| func walkstmt(np **Node) { |
| n := *np |
| if n == nil { |
| return |
| } |
| if n.Dodata == 2 { // don't walk, generated by anylit. |
| return |
| } |
| |
| setlineno(n) |
| |
| walkstmtlist(n.Ninit) |
| |
| switch n.Op { |
| default: |
| if n.Op == ONAME { |
| Yyerror("%v is not a top level statement", Sconv(n.Sym, 0)) |
| } else { |
| Yyerror("%v is not a top level statement", Oconv(int(n.Op), 0)) |
| } |
| Dump("nottop", n) |
| |
| case OAS, |
| OASOP, |
| OAS2, |
| OAS2DOTTYPE, |
| OAS2RECV, |
| OAS2FUNC, |
| OAS2MAPR, |
| OCLOSE, |
| OCOPY, |
| OCALLMETH, |
| OCALLINTER, |
| OCALL, |
| OCALLFUNC, |
| ODELETE, |
| OSEND, |
| OPRINT, |
| OPRINTN, |
| OPANIC, |
| OEMPTY, |
| ORECOVER: |
| if n.Typecheck == 0 { |
| Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign)) |
| } |
| init := n.Ninit |
| n.Ninit = nil |
| walkexpr(&n, &init) |
| addinit(&n, init) |
| if (*np).Op == OCOPY && n.Op == OCONVNOP { |
| n.Op = OEMPTY // don't leave plain values as statements. |
| } |
| |
| // special case for a receive where we throw away |
| // the value received. |
| case ORECV: |
| if n.Typecheck == 0 { |
| Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign)) |
| } |
| init := n.Ninit |
| n.Ninit = nil |
| |
| walkexpr(&n.Left, &init) |
| n = mkcall1(chanfn("chanrecv1", 2, n.Left.Type), nil, &init, typename(n.Left.Type), n.Left, nodnil()) |
| walkexpr(&n, &init) |
| |
| addinit(&n, init) |
| |
| case OBREAK, |
| ODCL, |
| OCONTINUE, |
| OFALL, |
| OGOTO, |
| OLABEL, |
| ODCLCONST, |
| ODCLTYPE, |
| OCHECKNIL, |
| OVARKILL: |
| break |
| |
| case OBLOCK: |
| walkstmtlist(n.List) |
| |
| case OXCASE: |
| Yyerror("case statement out of place") |
| n.Op = OCASE |
| fallthrough |
| |
| case OCASE: |
| walkstmt(&n.Right) |
| |
| case ODEFER: |
| Hasdefer = 1 |
| switch n.Left.Op { |
| case OPRINT, |
| OPRINTN: |
| walkprintfunc(&n.Left, &n.Ninit) |
| |
| case OCOPY: |
| n.Left = copyany(n.Left, &n.Ninit, 1) |
| |
| default: |
| walkexpr(&n.Left, &n.Ninit) |
| } |
| |
| // make room for size & fn arguments. |
| adjustargs(n, 2*Widthptr) |
| |
| case OFOR: |
| if n.Ntest != nil { |
| walkstmtlist(n.Ntest.Ninit) |
| init := n.Ntest.Ninit |
| n.Ntest.Ninit = nil |
| walkexpr(&n.Ntest, &init) |
| addinit(&n.Ntest, init) |
| } |
| |
| walkstmt(&n.Nincr) |
| walkstmtlist(n.Nbody) |
| |
| case OIF: |
| walkexpr(&n.Ntest, &n.Ninit) |
| walkstmtlist(n.Nbody) |
| walkstmtlist(n.Nelse) |
| |
| case OPROC: |
| switch n.Left.Op { |
| case OPRINT, |
| OPRINTN: |
| walkprintfunc(&n.Left, &n.Ninit) |
| |
| case OCOPY: |
| n.Left = copyany(n.Left, &n.Ninit, 1) |
| |
| default: |
| walkexpr(&n.Left, &n.Ninit) |
| } |
| |
| // make room for size & fn arguments. |
| adjustargs(n, 2*Widthptr) |
| |
| case ORETURN: |
| walkexprlist(n.List, &n.Ninit) |
| if n.List == nil { |
| break |
| } |
| if (Curfn.Type.Outnamed != 0 && count(n.List) > 1) || paramoutheap(Curfn) != 0 { |
| // assign to the function out parameters, |
| // so that reorder3 can fix up conflicts |
| rl := (*NodeList)(nil) |
| |
| var cl int |
| for ll := Curfn.Dcl; ll != nil; ll = ll.Next { |
| cl = int(ll.N.Class) &^ PHEAP |
| if cl == PAUTO { |
| break |
| } |
| if cl == PPARAMOUT { |
| rl = list(rl, ll.N) |
| } |
| } |
| |
| if samelist(rl, n.List) { |
| // special return in disguise |
| n.List = nil |
| |
| break |
| } |
| |
| if count(n.List) == 1 && count(rl) > 1 { |
| // OAS2FUNC in disguise |
| f := n.List.N |
| |
| if f.Op != OCALLFUNC && f.Op != OCALLMETH && f.Op != OCALLINTER { |
| Fatal("expected return of call, have %v", Nconv(f, 0)) |
| } |
| n.List = concat(list1(f), ascompatet(int(n.Op), rl, &f.Type, 0, &n.Ninit)) |
| break |
| } |
| |
| // move function calls out, to make reorder3's job easier. |
| walkexprlistsafe(n.List, &n.Ninit) |
| |
| ll := ascompatee(int(n.Op), rl, n.List, &n.Ninit) |
| n.List = reorder3(ll) |
| break |
| } |
| |
| ll := ascompatte(int(n.Op), nil, 0, Getoutarg(Curfn.Type), n.List, 1, &n.Ninit) |
| n.List = ll |
| |
| case ORETJMP: |
| break |
| |
| case OSELECT: |
| walkselect(n) |
| |
| case OSWITCH: |
| walkswitch(n) |
| |
| case ORANGE: |
| walkrange(n) |
| |
| case OXFALL: |
| Yyerror("fallthrough statement out of place") |
| n.Op = OFALL |
| } |
| |
| if n.Op == ONAME { |
| Fatal("walkstmt ended up with name: %v", Nconv(n, obj.FmtSign)) |
| } |
| |
| *np = n |
| } |
| |
| /* |
| * walk the whole tree of the body of an |
| * expression or simple statement. |
| * the types expressions are calculated. |
| * compile-time constants are evaluated. |
| * complex side effects like statements are appended to init |
| */ |
| func walkexprlist(l *NodeList, init **NodeList) { |
| for ; l != nil; l = l.Next { |
| walkexpr(&l.N, init) |
| } |
| } |
| |
| func walkexprlistsafe(l *NodeList, init **NodeList) { |
| for ; l != nil; l = l.Next { |
| l.N = safeexpr(l.N, init) |
| walkexpr(&l.N, init) |
| } |
| } |
| |
| func walkexprlistcheap(l *NodeList, init **NodeList) { |
| for ; l != nil; l = l.Next { |
| l.N = cheapexpr(l.N, init) |
| walkexpr(&l.N, init) |
| } |
| } |
| |
| func walkexpr(np **Node, init **NodeList) { |
| n := *np |
| |
| if n == nil { |
| return |
| } |
| |
| if init == &n.Ninit { |
| // not okay to use n->ninit when walking n, |
| // because we might replace n with some other node |
| // and would lose the init list. |
| Fatal("walkexpr init == &n->ninit") |
| } |
| |
| if n.Ninit != nil { |
| walkstmtlist(n.Ninit) |
| *init = concat(*init, n.Ninit) |
| n.Ninit = nil |
| } |
| |
| // annoying case - not typechecked |
| if n.Op == OKEY { |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| return |
| } |
| |
| lno := setlineno(n) |
| |
| if Debug['w'] > 1 { |
| Dump("walk-before", n) |
| } |
| |
| if n.Typecheck != 1 { |
| Fatal("missed typecheck: %v\n", Nconv(n, obj.FmtSign)) |
| } |
| |
| switch n.Op { |
| default: |
| Dump("walk", n) |
| Fatal("walkexpr: switch 1 unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) |
| |
| case OTYPE, |
| ONONAME, |
| OINDREG, |
| OEMPTY, |
| OPARAM: |
| goto ret |
| |
| case ONOT, |
| OMINUS, |
| OPLUS, |
| OCOM, |
| OREAL, |
| OIMAG, |
| ODOTMETH, |
| ODOTINTER: |
| walkexpr(&n.Left, init) |
| goto ret |
| |
| case OIND: |
| walkexpr(&n.Left, init) |
| goto ret |
| |
| case ODOT: |
| usefield(n) |
| walkexpr(&n.Left, init) |
| goto ret |
| |
| case ODOTPTR: |
| usefield(n) |
| if n.Op == ODOTPTR && n.Left.Type.Type.Width == 0 { |
| // No actual copy will be generated, so emit an explicit nil check. |
| n.Left = cheapexpr(n.Left, init) |
| |
| checknil(n.Left, init) |
| } |
| |
| walkexpr(&n.Left, init) |
| goto ret |
| |
| case OEFACE: |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| goto ret |
| |
| case OSPTR, |
| OITAB: |
| walkexpr(&n.Left, init) |
| goto ret |
| |
| case OLEN, |
| OCAP: |
| walkexpr(&n.Left, init) |
| |
| // replace len(*[10]int) with 10. |
| // delayed until now to preserve side effects. |
| t := n.Left.Type |
| |
| if Isptr[t.Etype] != 0 { |
| t = t.Type |
| } |
| if Isfixedarray(t) { |
| safeexpr(n.Left, init) |
| Nodconst(n, n.Type, t.Bound) |
| n.Typecheck = 1 |
| } |
| |
| goto ret |
| |
| case OLSH, |
| ORSH: |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| t := n.Left.Type |
| n.Bounded = bounded(n.Right, 8*t.Width) |
| if Debug['m'] != 0 && n.Etype != 0 && !Isconst(n.Right, CTINT) { |
| Warn("shift bounds check elided") |
| } |
| goto ret |
| |
| // Use results from call expression as arguments for complex. |
| case OAND, |
| OSUB, |
| OHMUL, |
| OLT, |
| OLE, |
| OGE, |
| OGT, |
| OADD, |
| OCOMPLEX, |
| OLROT: |
| if n.Op == OCOMPLEX && n.Left == nil && n.Right == nil { |
| n.Left = n.List.N |
| n.Right = n.List.Next.N |
| } |
| |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| goto ret |
| |
| case OOR, |
| OXOR: |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| walkrotate(&n) |
| goto ret |
| |
| case OEQ, |
| ONE: |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| |
| // Disable safemode while compiling this code: the code we |
| // generate internally can refer to unsafe.Pointer. |
| // In this case it can happen if we need to generate an == |
| // for a struct containing a reflect.Value, which itself has |
| // an unexported field of type unsafe.Pointer. |
| old_safemode := safemode |
| |
| safemode = 0 |
| walkcompare(&n, init) |
| safemode = old_safemode |
| goto ret |
| |
| case OANDAND, |
| OOROR: |
| walkexpr(&n.Left, init) |
| |
| // cannot put side effects from n->right on init, |
| // because they cannot run before n->left is checked. |
| // save elsewhere and store on the eventual n->right. |
| ll := (*NodeList)(nil) |
| |
| walkexpr(&n.Right, &ll) |
| addinit(&n.Right, ll) |
| goto ret |
| |
| case OPRINT, |
| OPRINTN: |
| walkexprlist(n.List, init) |
| n = walkprint(n, init) |
| goto ret |
| |
| case OPANIC: |
| n = mkcall("gopanic", nil, init, n.Left) |
| goto ret |
| |
| case ORECOVER: |
| n = mkcall("gorecover", n.Type, init, Nod(OADDR, nodfp, nil)) |
| goto ret |
| |
| case OLITERAL: |
| n.Addable = 1 |
| goto ret |
| |
| case OCLOSUREVAR, |
| OCFUNC: |
| n.Addable = 1 |
| goto ret |
| |
| case ONAME: |
| if n.Class&PHEAP == 0 && n.Class != PPARAMREF { |
| n.Addable = 1 |
| } |
| goto ret |
| |
| case OCALLINTER: |
| t := n.Left.Type |
| if n.List != nil && n.List.N.Op == OAS { |
| goto ret |
| } |
| walkexpr(&n.Left, init) |
| walkexprlist(n.List, init) |
| ll := ascompatte(int(n.Op), n, int(n.Isddd), getinarg(t), n.List, 0, init) |
| n.List = reorder1(ll) |
| goto ret |
| |
| case OCALLFUNC: |
| if n.Left.Op == OCLOSURE { |
| // Transform direct call of a closure to call of a normal function. |
| // transformclosure already did all preparation work. |
| |
| // Append captured variables to argument list. |
| n.List = concat(n.List, n.Left.Enter) |
| |
| n.Left.Enter = nil |
| |
| // Replace OCLOSURE with ONAME/PFUNC. |
| n.Left = n.Left.Closure.Nname |
| |
| // Update type of OCALLFUNC node. |
| // Output arguments had not changed, but their offsets could. |
| if n.Left.Type.Outtuple == 1 { |
| t := getoutargx(n.Left.Type).Type |
| if t.Etype == TFIELD { |
| t = t.Type |
| } |
| n.Type = t |
| } else { |
| n.Type = getoutargx(n.Left.Type) |
| } |
| } |
| |
| t := n.Left.Type |
| if n.List != nil && n.List.N.Op == OAS { |
| goto ret |
| } |
| |
| walkexpr(&n.Left, init) |
| walkexprlist(n.List, init) |
| |
| ll := ascompatte(int(n.Op), n, int(n.Isddd), getinarg(t), n.List, 0, init) |
| n.List = reorder1(ll) |
| goto ret |
| |
| case OCALLMETH: |
| t := n.Left.Type |
| if n.List != nil && n.List.N.Op == OAS { |
| goto ret |
| } |
| walkexpr(&n.Left, init) |
| walkexprlist(n.List, init) |
| ll := ascompatte(int(n.Op), n, 0, getthis(t), list1(n.Left.Left), 0, init) |
| lr := ascompatte(int(n.Op), n, int(n.Isddd), getinarg(t), n.List, 0, init) |
| ll = concat(ll, lr) |
| n.Left.Left = nil |
| ullmancalc(n.Left) |
| n.List = reorder1(ll) |
| goto ret |
| |
| case OAS: |
| *init = concat(*init, n.Ninit) |
| n.Ninit = nil |
| |
| walkexpr(&n.Left, init) |
| n.Left = safeexpr(n.Left, init) |
| |
| if oaslit(n, init) { |
| goto ret |
| } |
| |
| if n.Right == nil || iszero(n.Right) && flag_race == 0 { |
| goto ret |
| } |
| |
| switch n.Right.Op { |
| default: |
| walkexpr(&n.Right, init) |
| |
| // x = i.(T); n->left is x, n->right->left is i. |
| // orderstmt made sure x is addressable. |
| case ODOTTYPE: |
| walkexpr(&n.Right.Left, init) |
| |
| n1 := Nod(OADDR, n.Left, nil) |
| r := n.Right // i.(T) |
| |
| from := "I" |
| |
| to := "T" |
| if isnilinter(r.Left.Type) { |
| from = "E" |
| } |
| if isnilinter(r.Type) { |
| to = "E" |
| } else if Isinter(r.Type) { |
| to = "I" |
| } |
| |
| buf := fmt.Sprintf("assert%s2%s", from, to) |
| |
| fn := syslook(buf, 1) |
| argtype(fn, r.Left.Type) |
| argtype(fn, r.Type) |
| |
| n = mkcall1(fn, nil, init, typename(r.Type), r.Left, n1) |
| walkexpr(&n, init) |
| goto ret |
| |
| // x = <-c; n->left is x, n->right->left is c. |
| // orderstmt made sure x is addressable. |
| case ORECV: |
| walkexpr(&n.Right.Left, init) |
| |
| n1 := Nod(OADDR, n.Left, nil) |
| r := n.Right.Left // the channel |
| n = mkcall1(chanfn("chanrecv1", 2, r.Type), nil, init, typename(r.Type), r, n1) |
| walkexpr(&n, init) |
| goto ret |
| } |
| |
| if n.Left != nil && n.Right != nil { |
| r := convas(Nod(OAS, n.Left, n.Right), init) |
| r.Dodata = n.Dodata |
| n = r |
| n = applywritebarrier(n, init) |
| } |
| |
| goto ret |
| |
| case OAS2: |
| *init = concat(*init, n.Ninit) |
| n.Ninit = nil |
| walkexprlistsafe(n.List, init) |
| walkexprlistsafe(n.Rlist, init) |
| ll := ascompatee(OAS, n.List, n.Rlist, init) |
| ll = reorder3(ll) |
| for lr := ll; lr != nil; lr = lr.Next { |
| lr.N = applywritebarrier(lr.N, init) |
| } |
| n = liststmt(ll) |
| goto ret |
| |
| // a,b,... = fn() |
| case OAS2FUNC: |
| *init = concat(*init, n.Ninit) |
| |
| n.Ninit = nil |
| r := n.Rlist.N |
| walkexprlistsafe(n.List, init) |
| walkexpr(&r, init) |
| |
| ll := ascompatet(int(n.Op), n.List, &r.Type, 0, init) |
| for lr := ll; lr != nil; lr = lr.Next { |
| lr.N = applywritebarrier(lr.N, init) |
| } |
| n = liststmt(concat(list1(r), ll)) |
| goto ret |
| |
| // x, y = <-c |
| // orderstmt made sure x is addressable. |
| case OAS2RECV: |
| *init = concat(*init, n.Ninit) |
| |
| n.Ninit = nil |
| r := n.Rlist.N |
| walkexprlistsafe(n.List, init) |
| walkexpr(&r.Left, init) |
| var n1 *Node |
| if isblank(n.List.N) { |
| n1 = nodnil() |
| } else { |
| n1 = Nod(OADDR, n.List.N, nil) |
| } |
| n1.Etype = 1 // addr does not escape |
| fn := chanfn("chanrecv2", 2, r.Left.Type) |
| r = mkcall1(fn, n.List.Next.N.Type, init, typename(r.Left.Type), r.Left, n1) |
| n = Nod(OAS, n.List.Next.N, r) |
| typecheck(&n, Etop) |
| goto ret |
| |
| // a,b = m[i]; |
| case OAS2MAPR: |
| *init = concat(*init, n.Ninit) |
| |
| n.Ninit = nil |
| r := n.Rlist.N |
| walkexprlistsafe(n.List, init) |
| walkexpr(&r.Left, init) |
| walkexpr(&r.Right, init) |
| t := r.Left.Type |
| p := "" |
| if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing. |
| switch Simsimtype(t.Down) { |
| case TINT32, |
| TUINT32: |
| p = "mapaccess2_fast32" |
| |
| case TINT64, |
| TUINT64: |
| p = "mapaccess2_fast64" |
| |
| case TSTRING: |
| p = "mapaccess2_faststr" |
| } |
| } |
| |
| var key *Node |
| if p != "" { |
| // fast versions take key by value |
| key = r.Right |
| } else { |
| // standard version takes key by reference |
| // orderexpr made sure key is addressable. |
| key = Nod(OADDR, r.Right, nil) |
| |
| p = "mapaccess2" |
| } |
| |
| // from: |
| // a,b = m[i] |
| // to: |
| // var,b = mapaccess2*(t, m, i) |
| // a = *var |
| a := n.List.N |
| |
| fn := mapfn(p, t) |
| r = mkcall1(fn, getoutargx(fn.Type), init, typename(t), r.Left, key) |
| |
| // mapaccess2* returns a typed bool, but due to spec changes, |
| // the boolean result of i.(T) is now untyped so we make it the |
| // same type as the variable on the lhs. |
| if !isblank(n.List.Next.N) { |
| r.Type.Type.Down.Type = n.List.Next.N.Type |
| } |
| n.Rlist = list1(r) |
| n.Op = OAS2FUNC |
| |
| // don't generate a = *var if a is _ |
| if !isblank(a) { |
| var_ := temp(Ptrto(t.Type)) |
| var_.Typecheck = 1 |
| n.List.N = var_ |
| walkexpr(&n, init) |
| *init = list(*init, n) |
| n = Nod(OAS, a, Nod(OIND, var_, nil)) |
| } |
| |
| typecheck(&n, Etop) |
| walkexpr(&n, init) |
| |
| // mapaccess needs a zero value to be at least this big. |
| if zerosize < t.Type.Width { |
| zerosize = t.Type.Width |
| } |
| |
| // TODO: ptr is always non-nil, so disable nil check for this OIND op. |
| goto ret |
| |
| case ODELETE: |
| *init = concat(*init, n.Ninit) |
| n.Ninit = nil |
| map_ := n.List.N |
| key := n.List.Next.N |
| walkexpr(&map_, init) |
| walkexpr(&key, init) |
| |
| // orderstmt made sure key is addressable. |
| key = Nod(OADDR, key, nil) |
| |
| t := map_.Type |
| n = mkcall1(mapfndel("mapdelete", t), nil, init, typename(t), map_, key) |
| goto ret |
| |
| // a,b = i.(T) |
| // orderstmt made sure a is addressable. |
| case OAS2DOTTYPE: |
| *init = concat(*init, n.Ninit) |
| |
| n.Ninit = nil |
| r := n.Rlist.N |
| walkexprlistsafe(n.List, init) |
| walkexpr(&r.Left, init) |
| var n1 *Node |
| if isblank(n.List.N) { |
| n1 = nodnil() |
| } else { |
| n1 = Nod(OADDR, n.List.N, nil) |
| } |
| n1.Etype = 1 // addr does not escape |
| |
| from := "I" |
| |
| to := "T" |
| if isnilinter(r.Left.Type) { |
| from = "E" |
| } |
| if isnilinter(r.Type) { |
| to = "E" |
| } else if Isinter(r.Type) { |
| to = "I" |
| } |
| buf := fmt.Sprintf("assert%s2%s2", from, to) |
| |
| fn := syslook(buf, 1) |
| argtype(fn, r.Left.Type) |
| argtype(fn, r.Type) |
| |
| t := Types[TBOOL] |
| ok := n.List.Next.N |
| if !isblank(ok) { |
| t = ok.Type |
| } |
| r = mkcall1(fn, t, init, typename(r.Type), r.Left, n1) |
| n = Nod(OAS, ok, r) |
| typecheck(&n, Etop) |
| goto ret |
| |
| case ODOTTYPE, |
| ODOTTYPE2: |
| Fatal("walkexpr ODOTTYPE") // should see inside OAS or OAS2 only |
| |
| case OCONVIFACE: |
| walkexpr(&n.Left, init) |
| |
| // Optimize convT2E as a two-word copy when T is pointer-shaped. |
| if isnilinter(n.Type) && isdirectiface(n.Left.Type) { |
| l := Nod(OEFACE, typename(n.Left.Type), n.Left) |
| l.Type = n.Type |
| l.Typecheck = n.Typecheck |
| n = l |
| goto ret |
| } |
| |
| // Build name of function: convI2E etc. |
| // Not all names are possible |
| // (e.g., we'll never generate convE2E or convE2I). |
| from := "T" |
| |
| to := "I" |
| if isnilinter(n.Left.Type) { |
| from = "E" |
| } else if Isinter(n.Left.Type) { |
| from = "I" |
| } |
| if isnilinter(n.Type) { |
| to = "E" |
| } |
| buf := fmt.Sprintf("conv%s2%s", from, to) |
| |
| fn := syslook(buf, 1) |
| ll := (*NodeList)(nil) |
| if !Isinter(n.Left.Type) { |
| ll = list(ll, typename(n.Left.Type)) |
| } |
| if !isnilinter(n.Type) { |
| ll = list(ll, typename(n.Type)) |
| } |
| if !Isinter(n.Left.Type) && !isnilinter(n.Type) { |
| sym := Pkglookup(fmt.Sprintf("%v.%v", Tconv(n.Left.Type, obj.FmtLeft), Tconv(n.Type, obj.FmtLeft)), itabpkg) |
| if sym.Def == nil { |
| l := Nod(ONAME, nil, nil) |
| l.Sym = sym |
| l.Type = Ptrto(Types[TUINT8]) |
| l.Addable = 1 |
| l.Class = PEXTERN |
| l.Xoffset = 0 |
| sym.Def = l |
| ggloblsym(sym, int32(Widthptr), obj.DUPOK|obj.NOPTR) |
| } |
| |
| l := Nod(OADDR, sym.Def, nil) |
| l.Addable = 1 |
| ll = list(ll, l) |
| |
| if isdirectiface(n.Left.Type) { |
| /* For pointer types, we can make a special form of optimization |
| * |
| * These statements are put onto the expression init list: |
| * Itab *tab = atomicloadtype(&cache); |
| * if(tab == nil) |
| * tab = typ2Itab(type, itype, &cache); |
| * |
| * The CONVIFACE expression is replaced with this: |
| * OEFACE{tab, ptr}; |
| */ |
| l := temp(Ptrto(Types[TUINT8])) |
| |
| n1 := Nod(OAS, l, sym.Def) |
| typecheck(&n1, Etop) |
| *init = list(*init, n1) |
| |
| fn := syslook("typ2Itab", 1) |
| n1 = Nod(OCALL, fn, nil) |
| n1.List = ll |
| typecheck(&n1, Erv) |
| walkexpr(&n1, init) |
| |
| n2 := Nod(OIF, nil, nil) |
| n2.Ntest = Nod(OEQ, l, nodnil()) |
| n2.Nbody = list1(Nod(OAS, l, n1)) |
| n2.Likely = -1 |
| typecheck(&n2, Etop) |
| *init = list(*init, n2) |
| |
| l = Nod(OEFACE, l, n.Left) |
| l.Typecheck = n.Typecheck |
| l.Type = n.Type |
| n = l |
| goto ret |
| } |
| } |
| |
| if Isinter(n.Left.Type) { |
| ll = list(ll, n.Left) |
| } else { |
| // regular types are passed by reference to avoid C vararg calls |
| // orderexpr arranged for n->left to be a temporary for all |
| // the conversions it could see. comparison of an interface |
| // with a non-interface, especially in a switch on interface value |
| // with non-interface cases, is not visible to orderstmt, so we |
| // have to fall back on allocating a temp here. |
| if islvalue(n.Left) { |
| ll = list(ll, Nod(OADDR, n.Left, nil)) |
| } else { |
| ll = list(ll, Nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil)) |
| } |
| } |
| |
| argtype(fn, n.Left.Type) |
| argtype(fn, n.Type) |
| dowidth(fn.Type) |
| n = Nod(OCALL, fn, nil) |
| n.List = ll |
| typecheck(&n, Erv) |
| walkexpr(&n, init) |
| goto ret |
| |
| case OCONV, |
| OCONVNOP: |
| if Thearch.Thechar == '5' { |
| if Isfloat[n.Left.Type.Etype] != 0 { |
| if n.Type.Etype == TINT64 { |
| n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64])) |
| goto ret |
| } |
| |
| if n.Type.Etype == TUINT64 { |
| n = mkcall("float64touint64", n.Type, init, conv(n.Left, Types[TFLOAT64])) |
| goto ret |
| } |
| } |
| |
| if Isfloat[n.Type.Etype] != 0 { |
| if n.Left.Type.Etype == TINT64 { |
| n = mkcall("int64tofloat64", n.Type, init, conv(n.Left, Types[TINT64])) |
| goto ret |
| } |
| |
| if n.Left.Type.Etype == TUINT64 { |
| n = mkcall("uint64tofloat64", n.Type, init, conv(n.Left, Types[TUINT64])) |
| goto ret |
| } |
| } |
| } |
| |
| walkexpr(&n.Left, init) |
| goto ret |
| |
| case OANDNOT: |
| walkexpr(&n.Left, init) |
| n.Op = OAND |
| n.Right = Nod(OCOM, n.Right, nil) |
| typecheck(&n.Right, Erv) |
| walkexpr(&n.Right, init) |
| goto ret |
| |
| case OMUL: |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| walkmul(&n, init) |
| goto ret |
| |
| case ODIV, |
| OMOD: |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| |
| /* |
| * rewrite complex div into function call. |
| */ |
| et := int(n.Left.Type.Etype) |
| |
| if Iscomplex[et] != 0 && n.Op == ODIV { |
| t := n.Type |
| n = mkcall("complex128div", Types[TCOMPLEX128], init, conv(n.Left, Types[TCOMPLEX128]), conv(n.Right, Types[TCOMPLEX128])) |
| n = conv(n, t) |
| goto ret |
| } |
| |
| // Nothing to do for float divisions. |
| if Isfloat[et] != 0 { |
| goto ret |
| } |
| |
| // Try rewriting as shifts or magic multiplies. |
| walkdiv(&n, init) |
| |
| /* |
| * rewrite 64-bit div and mod into function calls |
| * on 32-bit architectures. |
| */ |
| switch n.Op { |
| case OMOD, |
| ODIV: |
| if Widthreg >= 8 || (et != TUINT64 && et != TINT64) { |
| goto ret |
| } |
| if et == TINT64 { |
| namebuf = "int64" |
| } else { |
| namebuf = "uint64" |
| } |
| if n.Op == ODIV { |
| namebuf += "div" |
| } else { |
| namebuf += "mod" |
| } |
| n = mkcall(namebuf, n.Type, init, conv(n.Left, Types[et]), conv(n.Right, Types[et])) |
| |
| default: |
| break |
| } |
| |
| goto ret |
| |
| case OINDEX: |
| walkexpr(&n.Left, init) |
| |
| // save the original node for bounds checking elision. |
| // If it was a ODIV/OMOD walk might rewrite it. |
| r := n.Right |
| |
| walkexpr(&n.Right, init) |
| |
| // if range of type cannot exceed static array bound, |
| // disable bounds check. |
| if n.Bounded { |
| goto ret |
| } |
| t := n.Left.Type |
| if t != nil && Isptr[t.Etype] != 0 { |
| t = t.Type |
| } |
| if Isfixedarray(t) { |
| n.Bounded = bounded(r, t.Bound) |
| if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) { |
| Warn("index bounds check elided") |
| } |
| if Smallintconst(n.Right) && !n.Bounded { |
| Yyerror("index out of bounds") |
| } |
| } else if Isconst(n.Left, CTSTR) { |
| n.Bounded = bounded(r, int64(len(n.Left.Val.U.Sval.S))) |
| if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) { |
| Warn("index bounds check elided") |
| } |
| if Smallintconst(n.Right) { |
| if !n.Bounded { |
| Yyerror("index out of bounds") |
| } else { |
| // replace "abc"[1] with 'b'. |
| // delayed until now because "abc"[1] is not |
| // an ideal constant. |
| v := Mpgetfix(n.Right.Val.U.Xval) |
| |
| Nodconst(n, n.Type, int64(n.Left.Val.U.Sval.S[v])) |
| n.Typecheck = 1 |
| } |
| } |
| } |
| |
| if Isconst(n.Right, CTINT) { |
| if Mpcmpfixfix(n.Right.Val.U.Xval, &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.Xval, Maxintval[TINT]) > 0 { |
| Yyerror("index out of bounds") |
| } |
| } |
| goto ret |
| |
| case OINDEXMAP: |
| if n.Etype == 1 { |
| goto ret |
| } |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| |
| t := n.Left.Type |
| p := "" |
| if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing. |
| switch Simsimtype(t.Down) { |
| case TINT32, |
| TUINT32: |
| p = "mapaccess1_fast32" |
| |
| case TINT64, |
| TUINT64: |
| p = "mapaccess1_fast64" |
| |
| case TSTRING: |
| p = "mapaccess1_faststr" |
| } |
| } |
| |
| var key *Node |
| if p != "" { |
| // fast versions take key by value |
| key = n.Right |
| } else { |
| // standard version takes key by reference. |
| // orderexpr made sure key is addressable. |
| key = Nod(OADDR, n.Right, nil) |
| |
| p = "mapaccess1" |
| } |
| |
| n = mkcall1(mapfn(p, t), Ptrto(t.Type), init, typename(t), n.Left, key) |
| n = Nod(OIND, n, nil) |
| n.Type = t.Type |
| n.Typecheck = 1 |
| |
| // mapaccess needs a zero value to be at least this big. |
| if zerosize < t.Type.Width { |
| zerosize = t.Type.Width |
| } |
| goto ret |
| |
| case ORECV: |
| Fatal("walkexpr ORECV") // should see inside OAS only |
| |
| case OSLICE: |
| if n.Right != nil && n.Right.Left == nil && n.Right.Right == nil { // noop |
| walkexpr(&n.Left, init) |
| n = n.Left |
| goto ret |
| } |
| fallthrough |
| |
| // fallthrough |
| case OSLICEARR, |
| OSLICESTR: |
| if n.Right == nil { // already processed |
| goto ret |
| } |
| |
| walkexpr(&n.Left, init) |
| |
| // cgen_slice can't handle string literals as source |
| // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] |
| if (n.Op == OSLICESTR && n.Left.Op == OLITERAL) || (n.Left.Op == OINDEX) { |
| n.Left = copyexpr(n.Left, n.Left.Type, init) |
| } else { |
| n.Left = safeexpr(n.Left, init) |
| } |
| walkexpr(&n.Right.Left, init) |
| n.Right.Left = safeexpr(n.Right.Left, init) |
| walkexpr(&n.Right.Right, init) |
| n.Right.Right = safeexpr(n.Right.Right, init) |
| n = sliceany(n, init) // chops n->right, sets n->list |
| goto ret |
| |
| case OSLICE3, |
| OSLICE3ARR: |
| if n.Right == nil { // already processed |
| goto ret |
| } |
| |
| walkexpr(&n.Left, init) |
| |
| // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] |
| // TODO the comment on the previous line was copied from case OSLICE. it might not even be true. |
| if n.Left.Op == OINDEX { |
| n.Left = copyexpr(n.Left, n.Left.Type, init) |
| } else { |
| n.Left = safeexpr(n.Left, init) |
| } |
| walkexpr(&n.Right.Left, init) |
| n.Right.Left = safeexpr(n.Right.Left, init) |
| walkexpr(&n.Right.Right.Left, init) |
| n.Right.Right.Left = safeexpr(n.Right.Right.Left, init) |
| walkexpr(&n.Right.Right.Right, init) |
| n.Right.Right.Right = safeexpr(n.Right.Right.Right, init) |
| n = sliceany(n, init) // chops n->right, sets n->list |
| goto ret |
| |
| case OADDR: |
| walkexpr(&n.Left, init) |
| goto ret |
| |
| case ONEW: |
| if n.Esc == EscNone && n.Type.Type.Width < 1<<16 { |
| r := temp(n.Type.Type) |
| r = Nod(OAS, r, nil) // zero temp |
| typecheck(&r, Etop) |
| *init = list(*init, r) |
| r = Nod(OADDR, r.Left, nil) |
| typecheck(&r, Erv) |
| n = r |
| } else { |
| n = callnew(n.Type.Type) |
| } |
| |
| goto ret |
| |
| // If one argument to the comparison is an empty string, |
| // comparing the lengths instead will yield the same result |
| // without the function call. |
| case OCMPSTR: |
| if (Isconst(n.Left, CTSTR) && len(n.Left.Val.U.Sval.S) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val.U.Sval.S) == 0) { |
| r := Nod(int(n.Etype), Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)) |
| typecheck(&r, Erv) |
| walkexpr(&r, init) |
| r.Type = n.Type |
| n = r |
| goto ret |
| } |
| |
| // s + "badgerbadgerbadger" == "badgerbadgerbadger" |
| if (n.Etype == OEQ || n.Etype == ONE) && Isconst(n.Right, CTSTR) && n.Left.Op == OADDSTR && count(n.Left.List) == 2 && Isconst(n.Left.List.Next.N, CTSTR) && cmpslit(n.Right, n.Left.List.Next.N) == 0 { |
| r := Nod(int(n.Etype), Nod(OLEN, n.Left.List.N, nil), Nodintconst(0)) |
| typecheck(&r, Erv) |
| walkexpr(&r, init) |
| r.Type = n.Type |
| n = r |
| goto ret |
| } |
| |
| var r *Node |
| if n.Etype == OEQ || n.Etype == ONE { |
| // prepare for rewrite below |
| n.Left = cheapexpr(n.Left, init) |
| |
| n.Right = cheapexpr(n.Right, init) |
| |
| r = mkcall("eqstring", Types[TBOOL], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING])) |
| |
| // quick check of len before full compare for == or != |
| // eqstring assumes that the lengths are equal |
| if n.Etype == OEQ { |
| // len(left) == len(right) && eqstring(left, right) |
| r = Nod(OANDAND, Nod(OEQ, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r) |
| } else { |
| // len(left) != len(right) || !eqstring(left, right) |
| r = Nod(ONOT, r, nil) |
| |
| r = Nod(OOROR, Nod(ONE, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r) |
| } |
| |
| typecheck(&r, Erv) |
| walkexpr(&r, nil) |
| } else { |
| // sys_cmpstring(s1, s2) :: 0 |
| r = mkcall("cmpstring", Types[TINT], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING])) |
| |
| r = Nod(int(n.Etype), r, Nodintconst(0)) |
| } |
| |
| typecheck(&r, Erv) |
| if n.Type.Etype != TBOOL { |
| Fatal("cmp %v", Tconv(n.Type, 0)) |
| } |
| r.Type = n.Type |
| n = r |
| goto ret |
| |
| case OADDSTR: |
| n = addstr(n, init) |
| goto ret |
| |
| case OAPPEND: |
| if n.Isddd != 0 { |
| n = appendslice(n, init) // also works for append(slice, string). |
| } else { |
| n = walkappend(n, init) |
| } |
| goto ret |
| |
| case OCOPY: |
| n = copyany(n, init, flag_race) |
| goto ret |
| |
| // cannot use chanfn - closechan takes any, not chan any |
| case OCLOSE: |
| fn := syslook("closechan", 1) |
| |
| argtype(fn, n.Left.Type) |
| n = mkcall1(fn, nil, init, n.Left) |
| goto ret |
| |
| case OMAKECHAN: |
| n = mkcall1(chanfn("makechan", 1, n.Type), n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64])) |
| goto ret |
| |
| case OMAKEMAP: |
| t := n.Type |
| |
| fn := syslook("makemap", 1) |
| |
| a := nodnil() // hmap buffer |
| r := nodnil() // bucket buffer |
| if n.Esc == EscNone { |
| // Allocate hmap buffer on stack. |
| var_ := temp(hmap(t)) |
| |
| a = Nod(OAS, var_, nil) // zero temp |
| typecheck(&a, Etop) |
| *init = list(*init, a) |
| a = Nod(OADDR, var_, nil) |
| |
| // Allocate one bucket on stack. |
| // Maximum key/value size is 128 bytes, larger objects |
| // are stored with an indirection. So max bucket size is 2048+eps. |
| var_ = temp(mapbucket(t)) |
| |
| r = Nod(OAS, var_, nil) // zero temp |
| typecheck(&r, Etop) |
| *init = list(*init, r) |
| r = Nod(OADDR, var_, nil) |
| } |
| |
| argtype(fn, hmap(t)) // hmap buffer |
| argtype(fn, mapbucket(t)) // bucket buffer |
| argtype(fn, t.Down) // key type |
| argtype(fn, t.Type) // value type |
| n = mkcall1(fn, n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64]), a, r) |
| goto ret |
| |
| case OMAKESLICE: |
| l := n.Left |
| r := n.Right |
| if r == nil { |
| r = safeexpr(l, init) |
| l = r |
| } |
| t := n.Type |
| if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.Xval) < (1<<16)/t.Type.Width) { |
| // var arr [r]T |
| // n = arr[:l] |
| t = aindex(r, t.Type) // [r]T |
| var_ := temp(t) |
| a := Nod(OAS, var_, nil) // zero temp |
| typecheck(&a, Etop) |
| *init = list(*init, a) |
| r := Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l] |
| r = conv(r, n.Type) // in case n->type is named. |
| typecheck(&r, Erv) |
| walkexpr(&r, init) |
| n = r |
| } else { |
| // makeslice(t *Type, nel int64, max int64) (ary []any) |
| fn := syslook("makeslice", 1) |
| |
| argtype(fn, t.Type) // any-1 |
| n = mkcall1(fn, n.Type, init, typename(n.Type), conv(l, Types[TINT64]), conv(r, Types[TINT64])) |
| } |
| |
| goto ret |
| |
| case ORUNESTR: |
| a := nodnil() |
| if n.Esc == EscNone { |
| t := aindex(Nodintconst(4), Types[TUINT8]) |
| var_ := temp(t) |
| a = Nod(OADDR, var_, nil) |
| } |
| |
| // intstring(*[4]byte, rune) |
| n = mkcall("intstring", n.Type, init, a, conv(n.Left, Types[TINT64])) |
| |
| goto ret |
| |
| case OARRAYBYTESTR: |
| a := nodnil() |
| if n.Esc == EscNone { |
| // Create temporary buffer for string on stack. |
| t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) |
| |
| a = Nod(OADDR, temp(t), nil) |
| } |
| |
| // slicebytetostring(*[32]byte, []byte) string; |
| n = mkcall("slicebytetostring", n.Type, init, a, n.Left) |
| |
| goto ret |
| |
| // slicebytetostringtmp([]byte) string; |
| case OARRAYBYTESTRTMP: |
| n = mkcall("slicebytetostringtmp", n.Type, init, n.Left) |
| |
| goto ret |
| |
| // slicerunetostring(*[32]byte, []rune) string; |
| case OARRAYRUNESTR: |
| a := nodnil() |
| |
| if n.Esc == EscNone { |
| // Create temporary buffer for string on stack. |
| t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) |
| |
| a = Nod(OADDR, temp(t), nil) |
| } |
| |
| n = mkcall("slicerunetostring", n.Type, init, a, n.Left) |
| goto ret |
| |
| // stringtoslicebyte(*32[byte], string) []byte; |
| case OSTRARRAYBYTE: |
| a := nodnil() |
| |
| if n.Esc == EscNone { |
| // Create temporary buffer for slice on stack. |
| t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) |
| |
| a = Nod(OADDR, temp(t), nil) |
| } |
| |
| n = mkcall("stringtoslicebyte", n.Type, init, a, conv(n.Left, Types[TSTRING])) |
| goto ret |
| |
| // stringtoslicebytetmp(string) []byte; |
| case OSTRARRAYBYTETMP: |
| n = mkcall("stringtoslicebytetmp", n.Type, init, conv(n.Left, Types[TSTRING])) |
| |
| goto ret |
| |
| // stringtoslicerune(*[32]rune, string) []rune |
| case OSTRARRAYRUNE: |
| a := nodnil() |
| |
| if n.Esc == EscNone { |
| // Create temporary buffer for slice on stack. |
| t := aindex(Nodintconst(tmpstringbufsize), Types[TINT32]) |
| |
| a = Nod(OADDR, temp(t), nil) |
| } |
| |
| n = mkcall("stringtoslicerune", n.Type, init, a, n.Left) |
| goto ret |
| |
| // ifaceeq(i1 any-1, i2 any-2) (ret bool); |
| case OCMPIFACE: |
| if !Eqtype(n.Left.Type, n.Right.Type) { |
| Fatal("ifaceeq %v %v %v", Oconv(int(n.Op), 0), Tconv(n.Left.Type, 0), Tconv(n.Right.Type, 0)) |
| } |
| var fn *Node |
| if isnilinter(n.Left.Type) { |
| fn = syslook("efaceeq", 1) |
| } else { |
| fn = syslook("ifaceeq", 1) |
| } |
| |
| n.Right = cheapexpr(n.Right, init) |
| n.Left = cheapexpr(n.Left, init) |
| argtype(fn, n.Right.Type) |
| argtype(fn, n.Left.Type) |
| r := mkcall1(fn, n.Type, init, n.Left, n.Right) |
| if n.Etype == ONE { |
| r = Nod(ONOT, r, nil) |
| } |
| |
| // check itable/type before full compare. |
| if n.Etype == OEQ { |
| r = Nod(OANDAND, Nod(OEQ, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r) |
| } else { |
| r = Nod(OOROR, Nod(ONE, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r) |
| } |
| typecheck(&r, Erv) |
| walkexpr(&r, init) |
| r.Type = n.Type |
| n = r |
| goto ret |
| |
| case OARRAYLIT, |
| OMAPLIT, |
| OSTRUCTLIT, |
| OPTRLIT: |
| var_ := temp(n.Type) |
| anylit(0, n, var_, init) |
| n = var_ |
| goto ret |
| |
| case OSEND: |
| n1 := n.Right |
| n1 = assignconv(n1, n.Left.Type.Type, "chan send") |
| walkexpr(&n1, init) |
| n1 = Nod(OADDR, n1, nil) |
| n = mkcall1(chanfn("chansend1", 2, n.Left.Type), nil, init, typename(n.Left.Type), n.Left, n1) |
| goto ret |
| |
| case OCLOSURE: |
| n = walkclosure(n, init) |
| goto ret |
| |
| case OCALLPART: |
| n = walkpartialcall(n, init) |
| goto ret |
| } |
| |
| Fatal("missing switch %v", Oconv(int(n.Op), 0)) |
| |
| // Expressions that are constant at run time but not |
| // considered const by the language spec are not turned into |
| // constants until walk. For example, if n is y%1 == 0, the |
| // walk of y%1 may have replaced it by 0. |
| // Check whether n with its updated args is itself now a constant. |
| ret: |
| t := n.Type |
| |
| evconst(n) |
| n.Type = t |
| if n.Op == OLITERAL { |
| typecheck(&n, Erv) |
| } |
| |
| ullmancalc(n) |
| |
| if Debug['w'] != 0 && n != nil { |
| Dump("walk", n) |
| } |
| |
| lineno = lno |
| *np = n |
| } |
| |
| func ascompatee1(op int, l *Node, r *Node, init **NodeList) *Node { |
| // convas will turn map assigns into function calls, |
| // making it impossible for reorder3 to work. |
| n := Nod(OAS, l, r) |
| |
| if l.Op == OINDEXMAP { |
| return n |
| } |
| |
| return convas(n, init) |
| } |
| |
| func ascompatee(op int, nl *NodeList, nr *NodeList, init **NodeList) *NodeList { |
| var ll *NodeList |
| var lr *NodeList |
| |
| /* |
| * check assign expression list to |
| * a expression list. called in |
| * expr-list = expr-list |
| */ |
| |
| // ensure order of evaluation for function calls |
| for ll = nl; ll != nil; ll = ll.Next { |
| ll.N = safeexpr(ll.N, init) |
| } |
| for lr = nr; lr != nil; lr = lr.Next { |
| lr.N = safeexpr(lr.N, init) |
| } |
| |
| nn := (*NodeList)(nil) |
| ll = nl |
| lr = nr |
| for ; ll != nil && lr != nil; (func() { ll = ll.Next; lr = lr.Next })() { |
| // Do not generate 'x = x' during return. See issue 4014. |
| if op == ORETURN && ll.N == lr.N { |
| continue |
| } |
| nn = list(nn, ascompatee1(op, ll.N, lr.N, init)) |
| } |
| |
| // cannot happen: caller checked that lists had same length |
| if ll != nil || lr != nil { |
| Yyerror("error in shape across %v %v %v / %d %d [%s]", Hconv(nl, obj.FmtSign), Oconv(int(op), 0), Hconv(nr, obj.FmtSign), count(nl), count(nr), Curfn.Nname.Sym.Name) |
| } |
| return nn |
| } |
| |
| /* |
| * l is an lv and rt is the type of an rv |
| * return 1 if this implies a function call |
| * evaluating the lv or a function call |
| * in the conversion of the types |
| */ |
| func fncall(l *Node, rt *Type) bool { |
| if l.Ullman >= UINF || l.Op == OINDEXMAP { |
| return true |
| } |
| r := Node{} |
| if needwritebarrier(l, &r) { |
| return true |
| } |
| if Eqtype(l.Type, rt) { |
| return false |
| } |
| return true |
| } |
| |
| func ascompatet(op int, nl *NodeList, nr **Type, fp int, init **NodeList) *NodeList { |
| var l *Node |
| var tmp *Node |
| var a *Node |
| var ll *NodeList |
| var saver Iter |
| |
| /* |
| * check assign type list to |
| * a expression list. called in |
| * expr-list = func() |
| */ |
| r := Structfirst(&saver, nr) |
| |
| nn := (*NodeList)(nil) |
| mm := (*NodeList)(nil) |
| ucount := 0 |
| for ll = nl; ll != nil; ll = ll.Next { |
| if r == nil { |
| break |
| } |
| l = ll.N |
| if isblank(l) { |
| r = structnext(&saver) |
| continue |
| } |
| |
| // any lv that causes a fn call must be |
| // deferred until all the return arguments |
| // have been pulled from the output arguments |
| if fncall(l, r.Type) { |
| tmp = temp(r.Type) |
| typecheck(&tmp, Erv) |
| a = Nod(OAS, l, tmp) |
| a = convas(a, init) |
| mm = list(mm, a) |
| l = tmp |
| } |
| |
| a = Nod(OAS, l, nodarg(r, fp)) |
| a = convas(a, init) |
| ullmancalc(a) |
| if a.Ullman >= UINF { |
| Dump("ascompatet ucount", a) |
| ucount++ |
| } |
| |
| nn = list(nn, a) |
| r = structnext(&saver) |
| } |
| |
| if ll != nil || r != nil { |
| Yyerror("ascompatet: assignment count mismatch: %d = %d", count(nl), structcount(*nr)) |
| } |
| |
| if ucount != 0 { |
| Fatal("ascompatet: too many function calls evaluating parameters") |
| } |
| return concat(nn, mm) |
| } |
| |
| /* |
| * package all the arguments that match a ... T parameter into a []T. |
| */ |
| func mkdotargslice(lr0 *NodeList, nn *NodeList, l *Type, fp int, init **NodeList, ddd *Node) *NodeList { |
| esc := EscUnknown |
| if ddd != nil { |
| esc = int(ddd.Esc) |
| } |
| |
| tslice := typ(TARRAY) |
| tslice.Type = l.Type.Type |
| tslice.Bound = -1 |
| |
| var n *Node |
| if count(lr0) == 0 { |
| n = nodnil() |
| n.Type = tslice |
| } else { |
| n = Nod(OCOMPLIT, nil, typenod(tslice)) |
| if ddd != nil { |
| n.Alloc = ddd.Alloc // temporary to use |
| } |
| n.List = lr0 |
| n.Esc = uint(esc) |
| typecheck(&n, Erv) |
| if n.Type == nil { |
| Fatal("mkdotargslice: typecheck failed") |
| } |
| walkexpr(&n, init) |
| } |
| |
| a := Nod(OAS, nodarg(l, fp), n) |
| nn = list(nn, convas(a, init)) |
| return nn |
| } |
| |
| /* |
| * helpers for shape errors |
| */ |
| func dumptypes(nl **Type, what string) string { |
| var savel Iter |
| |
| fmt_ := "" |
| fmt_ += fmt.Sprintf("\t") |
| first := 1 |
| for l := Structfirst(&savel, nl); l != nil; l = structnext(&savel) { |
| if first != 0 { |
| first = 0 |
| } else { |
| fmt_ += fmt.Sprintf(", ") |
| } |
| fmt_ += fmt.Sprintf("%v", Tconv(l, 0)) |
| } |
| |
| if first != 0 { |
| fmt_ += fmt.Sprintf("[no arguments %s]", what) |
| } |
| return fmt_ |
| } |
| |
| func dumpnodetypes(l *NodeList, what string) string { |
| var r *Node |
| |
| fmt_ := "" |
| fmt_ += fmt.Sprintf("\t") |
| first := 1 |
| for ; l != nil; l = l.Next { |
| r = l.N |
| if first != 0 { |
| first = 0 |
| } else { |
| fmt_ += fmt.Sprintf(", ") |
| } |
| fmt_ += fmt.Sprintf("%v", Tconv(r.Type, 0)) |
| } |
| |
| if first != 0 { |
| fmt_ += fmt.Sprintf("[no arguments %s]", what) |
| } |
| return fmt_ |
| } |
| |
| /* |
| * check assign expression list to |
| * a type list. called in |
| * return expr-list |
| * func(expr-list) |
| */ |
| func ascompatte(op int, call *Node, isddd int, nl **Type, lr *NodeList, fp int, init **NodeList) *NodeList { |
| var savel Iter |
| |
| lr0 := lr |
| l := Structfirst(&savel, nl) |
| r := (*Node)(nil) |
| if lr != nil { |
| r = lr.N |
| } |
| nn := (*NodeList)(nil) |
| |
| // f(g()) where g has multiple return values |
| var a *Node |
| var l2 string |
| var ll *Type |
| var l1 string |
| if r != nil && lr.Next == nil && r.Type.Etype == TSTRUCT && r.Type.Funarg != 0 { |
| // optimization - can do block copy |
| if eqtypenoname(r.Type, *nl) { |
| a := nodarg(*nl, fp) |
| r = Nod(OCONVNOP, r, nil) |
| r.Type = a.Type |
| nn = list1(convas(Nod(OAS, a, r), init)) |
| goto ret |
| } |
| |
| // conversions involved. |
| // copy into temporaries. |
| alist := (*NodeList)(nil) |
| |
| for l := Structfirst(&savel, &r.Type); l != nil; l = structnext(&savel) { |
| a = temp(l.Type) |
| alist = list(alist, a) |
| } |
| |
| a = Nod(OAS2, nil, nil) |
| a.List = alist |
| a.Rlist = lr |
| typecheck(&a, Etop) |
| walkstmt(&a) |
| *init = list(*init, a) |
| lr = alist |
| r = lr.N |
| l = Structfirst(&savel, nl) |
| } |
| |
| loop: |
| if l != nil && l.Isddd != 0 { |
| // the ddd parameter must be last |
| ll = structnext(&savel) |
| |
| if ll != nil { |
| Yyerror("... must be last argument") |
| } |
| |
| // special case -- |
| // only if we are assigning a single ddd |
| // argument to a ddd parameter then it is |
| // passed thru unencapsulated |
| if r != nil && lr.Next == nil && isddd != 0 && Eqtype(l.Type, r.Type) { |
| a = Nod(OAS, nodarg(l, fp), r) |
| a = convas(a, init) |
| nn = list(nn, a) |
| goto ret |
| } |
| |
| // normal case -- make a slice of all |
| // remaining arguments and pass it to |
| // the ddd parameter. |
| nn = mkdotargslice(lr, nn, l, fp, init, call.Right) |
| |
| goto ret |
| } |
| |
| if l == nil || r == nil { |
| if l != nil || r != nil { |
| l1 = dumptypes(nl, "expected") |
| l2 = dumpnodetypes(lr0, "given") |
| if l != nil { |
| Yyerror("not enough arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2) |
| } else { |
| Yyerror("too many arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2) |
| } |
| } |
| |
| goto ret |
| } |
| |
| a = Nod(OAS, nodarg(l, fp), r) |
| a = convas(a, init) |
| nn = list(nn, a) |
| |
| l = structnext(&savel) |
| r = nil |
| lr = lr.Next |
| if lr != nil { |
| r = lr.N |
| } |
| goto loop |
| |
| ret: |
| for lr = nn; lr != nil; lr = lr.Next { |
| lr.N.Typecheck = 1 |
| } |
| return nn |
| } |
| |
| // generate code for print |
| func walkprint(nn *Node, init **NodeList) *Node { |
| var r *Node |
| var n *Node |
| var on *Node |
| var t *Type |
| var et int |
| |
| op := int(nn.Op) |
| all := nn.List |
| calls := (*NodeList)(nil) |
| notfirst := false |
| |
| // Hoist all the argument evaluation up before the lock. |
| walkexprlistcheap(all, init) |
| |
| calls = list(calls, mkcall("printlock", nil, init)) |
| |
| for l := all; l != nil; l = l.Next { |
| if notfirst { |
| calls = list(calls, mkcall("printsp", nil, init)) |
| } |
| |
| notfirst = op == OPRINTN |
| |
| n = l.N |
| if n.Op == OLITERAL { |
| switch n.Val.Ctype { |
| case CTRUNE: |
| defaultlit(&n, runetype) |
| |
| case CTINT: |
| defaultlit(&n, Types[TINT64]) |
| |
| case CTFLT: |
| defaultlit(&n, Types[TFLOAT64]) |
| } |
| } |
| |
| if n.Op != OLITERAL && n.Type != nil && n.Type.Etype == TIDEAL { |
| defaultlit(&n, Types[TINT64]) |
| } |
| defaultlit(&n, nil) |
| l.N = n |
| if n.Type == nil || n.Type.Etype == TFORW { |
| continue |
| } |
| |
| t = n.Type |
| et = int(n.Type.Etype) |
| if Isinter(n.Type) { |
| if isnilinter(n.Type) { |
| on = syslook("printeface", 1) |
| } else { |
| on = syslook("printiface", 1) |
| } |
| argtype(on, n.Type) // any-1 |
| } else if Isptr[et] != 0 || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR { |
| on = syslook("printpointer", 1) |
| argtype(on, n.Type) // any-1 |
| } else if Isslice(n.Type) { |
| on = syslook("printslice", 1) |
| argtype(on, n.Type) // any-1 |
| } else if Isint[et] != 0 { |
| if et == TUINT64 { |
| if (t.Sym.Pkg == Runtimepkg || compiling_runtime != 0) && t.Sym.Name == "hex" { |
| on = syslook("printhex", 0) |
| } else { |
| on = syslook("printuint", 0) |
| } |
| } else { |
| on = syslook("printint", 0) |
| } |
| } else if Isfloat[et] != 0 { |
| on = syslook("printfloat", 0) |
| } else if Iscomplex[et] != 0 { |
| on = syslook("printcomplex", 0) |
| } else if et == TBOOL { |
| on = syslook("printbool", 0) |
| } else if et == TSTRING { |
| on = syslook("printstring", 0) |
| } else { |
| badtype(OPRINT, n.Type, nil) |
| continue |
| } |
| |
| t = *getinarg(on.Type) |
| if t != nil { |
| t = t.Type |
| } |
| if t != nil { |
| t = t.Type |
| } |
| |
| if !Eqtype(t, n.Type) { |
| n = Nod(OCONV, n, nil) |
| n.Type = t |
| } |
| |
| r = Nod(OCALL, on, nil) |
| r.List = list1(n) |
| calls = list(calls, r) |
| } |
| |
| if op == OPRINTN { |
| calls = list(calls, mkcall("printnl", nil, nil)) |
| } |
| |
| calls = list(calls, mkcall("printunlock", nil, init)) |
| |
| typechecklist(calls, Etop) |
| walkexprlist(calls, init) |
| |
| r = Nod(OEMPTY, nil, nil) |
| typecheck(&r, Etop) |
| walkexpr(&r, init) |
| r.Ninit = calls |
| return r |
| } |
| |
| func callnew(t *Type) *Node { |
| dowidth(t) |
| fn := syslook("newobject", 1) |
| argtype(fn, t) |
| return mkcall1(fn, Ptrto(t), nil, typename(t)) |
| } |
| |
| func isstack(n *Node) bool { |
| n = outervalue(n) |
| |
| // If n is *autotmp and autotmp = &foo, replace n with foo. |
| // We introduce such temps when initializing struct literals. |
| if n.Op == OIND && n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { |
| defn := n.Left.Defn |
| if defn != nil && defn.Op == OAS && defn.Right.Op == OADDR { |
| n = defn.Right.Left |
| } |
| } |
| |
| switch n.Op { |
| // OINDREG only ends up in walk if it's indirect of SP. |
| case OINDREG: |
| return true |
| |
| case ONAME: |
| switch n.Class { |
| case PAUTO, |
| PPARAM, |
| PPARAMOUT: |
| return true |
| } |
| } |
| |
| return false |
| } |
| |
| func isglobal(n *Node) bool { |
| n = outervalue(n) |
| |
| switch n.Op { |
| case ONAME: |
| switch n.Class { |
| case PEXTERN: |
| return true |
| } |
| } |
| |
| return false |
| } |
| |
| // Do we need a write barrier for the assignment l = r? |
| func needwritebarrier(l *Node, r *Node) bool { |
| if use_writebarrier == 0 { |
| return false |
| } |
| |
| if l == nil || isblank(l) { |
| return false |
| } |
| |
| // No write barrier for write of non-pointers. |
| dowidth(l.Type) |
| |
| if !haspointers(l.Type) { |
| return false |
| } |
| |
| // No write barrier for write to stack. |
| if isstack(l) { |
| return false |
| } |
| |
| // No write barrier for implicit or explicit zeroing. |
| if r == nil || iszero(r) { |
| return false |
| } |
| |
| // No write barrier for initialization to constant. |
| if r.Op == OLITERAL { |
| return false |
| } |
| |
| // No write barrier for storing static (read-only) data. |
| if r.Op == ONAME && strings.HasPrefix(r.Sym.Name, "statictmp_") { |
| return false |
| } |
| |
| // No write barrier for storing address of stack values, |
| // which are guaranteed only to be written to the stack. |
| if r.Op == OADDR && isstack(r.Left) { |
| return false |
| } |
| |
| // No write barrier for storing address of global, which |
| // is live no matter what. |
| if r.Op == OADDR && isglobal(r.Left) { |
| return false |
| } |
| |
| // No write barrier for reslice: x = x[0:y] or x = append(x, ...). |
| // Both are compiled to modify x directly. |
| // In the case of append, a write barrier may still be needed |
| // if the underlying array grows, but the append code can |
| // generate the write barrier directly in that case. |
| // (It does not yet, but the cost of the write barrier will be |
| // small compared to the cost of the allocation.) |
| if r.Reslice != 0 { |
| switch r.Op { |
| case OSLICE, |
| OSLICE3, |
| OSLICESTR, |
| OAPPEND: |
| break |
| |
| default: |
| Dump("bad reslice-l", l) |
| Dump("bad reslice-r", r) |
| } |
| |
| return false |
| } |
| |
| // Otherwise, be conservative and use write barrier. |
| return true |
| } |
| |
| // TODO(rsc): Perhaps componentgen should run before this. |
| |
| var applywritebarrier_bv *Bvec |
| |
| func applywritebarrier(n *Node, init **NodeList) *Node { |
| if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) { |
| if Curfn != nil && Curfn.Nowritebarrier { |
| Yyerror("write barrier prohibited") |
| } |
| t := n.Left.Type |
| l := Nod(OADDR, n.Left, nil) |
| l.Etype = 1 // addr does not escape |
| if t.Width == int64(Widthptr) { |
| n = mkcall1(writebarrierfn("writebarrierptr", t, n.Right.Type), nil, init, l, n.Right) |
| } else if t.Etype == TSTRING { |
| n = mkcall1(writebarrierfn("writebarrierstring", t, n.Right.Type), nil, init, l, n.Right) |
| } else if Isslice(t) { |
| n = mkcall1(writebarrierfn("writebarrierslice", t, n.Right.Type), nil, init, l, n.Right) |
| } else if Isinter(t) { |
| n = mkcall1(writebarrierfn("writebarrieriface", t, n.Right.Type), nil, init, l, n.Right) |
| } else if t.Width <= int64(4*Widthptr) { |
| x := int64(0) |
| if applywritebarrier_bv == nil { |
| applywritebarrier_bv = bvalloc(obj.BitsPerPointer * 4) |
| } |
| bvresetall(applywritebarrier_bv) |
| twobitwalktype1(t, &x, applywritebarrier_bv) |
| const ( |
| PtrBit = 1 |
| ) |
| // The bvgets are looking for BitsPointer in successive slots. |
| if obj.BitsPointer != 1<<PtrBit { |
| Fatal("wrong PtrBit") |
| } |
| var name string |
| switch t.Width / int64(Widthptr) { |
| default: |
| Fatal("found writebarrierfat for %d-byte object of type %v", int(t.Width), Tconv(t, 0)) |
| |
| case 2: |
| name = fmt.Sprintf("writebarrierfat%d%d", bvget(applywritebarrier_bv, PtrBit), bvget(applywritebarrier_bv, obj.BitsPerPointer+PtrBit)) |
| |
| case 3: |
| name = fmt.Sprintf("writebarrierfat%d%d%d", bvget(applywritebarrier_bv, PtrBit), bvget(applywritebarrier_bv, obj.BitsPerPointer+PtrBit), bvget(applywritebarrier_bv, 2*obj.BitsPerPointer+PtrBit)) |
| |
| case 4: |
| name = fmt.Sprintf("writebarrierfat%d%d%d%d", bvget(applywritebarrier_bv, PtrBit), bvget(applywritebarrier_bv, obj.BitsPerPointer+PtrBit), bvget(applywritebarrier_bv, 2*obj.BitsPerPointer+PtrBit), bvget(applywritebarrier_bv, 3*obj.BitsPerPointer+PtrBit)) |
| } |
| |
| n = mkcall1(writebarrierfn(name, t, n.Right.Type), nil, init, l, nodnil(), n.Right) |
| } else { |
| r := n.Right |
| for r.Op == OCONVNOP { |
| r = r.Left |
| } |
| r = Nod(OADDR, r, nil) |
| r.Etype = 1 // addr does not escape |
| |
| //warnl(n->lineno, "typedmemmove %T %N", t, r); |
| n = mkcall1(writebarrierfn("typedmemmove", t, r.Left.Type), nil, init, typename(t), l, r) |
| } |
| } |
| |
| return n |
| } |
| |
| func convas(n *Node, init **NodeList) *Node { |
| if n.Op != OAS { |
| Fatal("convas: not OAS %v", Oconv(int(n.Op), 0)) |
| } |
| |
| n.Typecheck = 1 |
| |
| var lt *Type |
| var rt *Type |
| if n.Left == nil || n.Right == nil { |
| goto out |
| } |
| |
| lt = n.Left.Type |
| rt = n.Right.Type |
| if lt == nil || rt == nil { |
| goto out |
| } |
| |
| if isblank(n.Left) { |
| defaultlit(&n.Right, nil) |
| goto out |
| } |
| |
| if n.Left.Op == OINDEXMAP { |
| map_ := n.Left.Left |
| key := n.Left.Right |
| val := n.Right |
| walkexpr(&map_, init) |
| walkexpr(&key, init) |
| walkexpr(&val, init) |
| |
| // orderexpr made sure key and val are addressable. |
| key = Nod(OADDR, key, nil) |
| |
| val = Nod(OADDR, val, nil) |
| n = mkcall1(mapfn("mapassign1", map_.Type), nil, init, typename(map_.Type), map_, key, val) |
| goto out |
| } |
| |
| if !Eqtype(lt, rt) { |
| n.Right = assignconv(n.Right, lt, "assignment") |
| walkexpr(&n.Right, init) |
| } |
| |
| out: |
| ullmancalc(n) |
| return n |
| } |
| |
| /* |
| * from ascompat[te] |
| * evaluating actual function arguments. |
| * f(a,b) |
| * if there is exactly one function expr, |
| * then it is done first. otherwise must |
| * make temp variables |
| */ |
| func reorder1(all *NodeList) *NodeList { |
| var n *Node |
| |
| c := 0 // function calls |
| t := 0 // total parameters |
| |
| for l := all; l != nil; l = l.Next { |
| n = l.N |
| t++ |
| ullmancalc(n) |
| if n.Ullman >= UINF { |
| c++ |
| } |
| } |
| |
| if c == 0 || t == 1 { |
| return all |
| } |
| |
| g := (*NodeList)(nil) // fncalls assigned to tempnames |
| f := (*Node)(nil) // last fncall assigned to stack |
| r := (*NodeList)(nil) // non fncalls and tempnames assigned to stack |
| d := 0 |
| var a *Node |
| for l := all; l != nil; l = l.Next { |
| n = l.N |
| if n.Ullman < UINF { |
| r = list(r, n) |
| continue |
| } |
| |
| d++ |
| if d == c { |
| f = n |
| continue |
| } |
| |
| // make assignment of fncall to tempname |
| a = temp(n.Right.Type) |
| |
| a = Nod(OAS, a, n.Right) |
| g = list(g, a) |
| |
| // put normal arg assignment on list |
| // with fncall replaced by tempname |
| n.Right = a.Left |
| |
| r = list(r, n) |
| } |
| |
| if f != nil { |
| g = list(g, f) |
| } |
| return concat(g, r) |
| } |
| |
| /* |
| * from ascompat[ee] |
| * a,b = c,d |
| * simultaneous assignment. there cannot |
| * be later use of an earlier lvalue. |
| * |
| * function calls have been removed. |
| */ |
| func reorder3(all *NodeList) *NodeList { |
| var l *Node |
| |
| // If a needed expression may be affected by an |
| // earlier assignment, make an early copy of that |
| // expression and use the copy instead. |
| early := (*NodeList)(nil) |
| |
| mapinit := (*NodeList)(nil) |
| for list := all; list != nil; list = list.Next { |
| l = list.N.Left |
| |
| // Save subexpressions needed on left side. |
| // Drill through non-dereferences. |
| for { |
| if l.Op == ODOT || l.Op == OPAREN { |
| l = l.Left |
| continue |
| } |
| |
| if l.Op == OINDEX && Isfixedarray(l.Left.Type) { |
| reorder3save(&l.Right, all, list, &early) |
| l = l.Left |
| continue |
| } |
| |
| break |
| } |
| |
| switch l.Op { |
| default: |
| Fatal("reorder3 unexpected lvalue %v", Oconv(int(l.Op), obj.FmtSharp)) |
| |
| case ONAME: |
| break |
| |
| case OINDEX, |
| OINDEXMAP: |
| reorder3save(&l.Left, all, list, &early) |
| reorder3save(&l.Right, all, list, &early) |
| if l.Op == OINDEXMAP { |
| list.N = convas(list.N, &mapinit) |
| } |
| |
| case OIND, |
| ODOTPTR: |
| reorder3save(&l.Left, all, list, &early) |
| } |
| |
| // Save expression on right side. |
| reorder3save(&list.N.Right, all, list, &early) |
| } |
| |
| early = concat(mapinit, early) |
| return concat(early, all) |
| } |
| |
| /* |
| * if the evaluation of *np would be affected by the |
| * assignments in all up to but not including stop, |
| * copy into a temporary during *early and |
| * replace *np with that temp. |
| */ |
| func reorder3save(np **Node, all *NodeList, stop *NodeList, early **NodeList) { |
| n := *np |
| if !aliased(n, all, stop) { |
| return |
| } |
| |
| q := temp(n.Type) |
| q = Nod(OAS, q, n) |
| typecheck(&q, Etop) |
| *early = list(*early, q) |
| *np = q.Left |
| } |
| |
| /* |
| * what's the outer value that a write to n affects? |
| * outer value means containing struct or array. |
| */ |
| func outervalue(n *Node) *Node { |
| for { |
| if n.Op == OXDOT { |
| Fatal("OXDOT in walk") |
| } |
| if n.Op == ODOT || n.Op == OPAREN || n.Op == OCONVNOP { |
| n = n.Left |
| continue |
| } |
| |
| if n.Op == OINDEX && Isfixedarray(n.Left.Type) { |
| n = n.Left |
| continue |
| } |
| |
| break |
| } |
| |
| return n |
| } |
| |
| /* |
| * Is it possible that the computation of n might be |
| * affected by writes in as up to but not including stop? |
| */ |
| func aliased(n *Node, all *NodeList, stop *NodeList) bool { |
| if n == nil { |
| return false |
| } |
| |
| // Look for obvious aliasing: a variable being assigned |
| // during the all list and appearing in n. |
| // Also record whether there are any writes to main memory. |
| // Also record whether there are any writes to variables |
| // whose addresses have been taken. |
| memwrite := 0 |
| |
| varwrite := 0 |
| var a *Node |
| for l := all; l != stop; l = l.Next { |
| a = outervalue(l.N.Left) |
| if a.Op != ONAME { |
| memwrite = 1 |
| continue |
| } |
| |
| switch n.Class { |
| default: |
| varwrite = 1 |
| continue |
| |
| case PAUTO, |
| PPARAM, |
| PPARAMOUT: |
| if n.Addrtaken != 0 { |
| varwrite = 1 |
| continue |
| } |
| |
| if vmatch2(a, n) { |
| // Direct hit. |
| return true |
| } |
| } |
| } |
| |
| // The variables being written do not appear in n. |
| // However, n might refer to computed addresses |
| // that are being written. |
| |
| // If no computed addresses are affected by the writes, no aliasing. |
| if memwrite == 0 && varwrite == 0 { |
| return false |
| } |
| |
| // If n does not refer to computed addresses |
| // (that is, if n only refers to variables whose addresses |
| // have not been taken), no aliasing. |
| if varexpr(n) { |
| return false |
| } |
| |
| // Otherwise, both the writes and n refer to computed memory addresses. |
| // Assume that they might conflict. |
| return true |
| } |
| |
| /* |
| * does the evaluation of n only refer to variables |
| * whose addresses have not been taken? |
| * (and no other memory) |
| */ |
| func varexpr(n *Node) bool { |
| if n == nil { |
| return true |
| } |
| |
| switch n.Op { |
| case OLITERAL: |
| return true |
| |
| case ONAME: |
| switch n.Class { |
| case PAUTO, |
| PPARAM, |
| PPARAMOUT: |
| if n.Addrtaken == 0 { |
| return true |
| } |
| } |
| |
| return false |
| |
| case OADD, |
| OSUB, |
| OOR, |
| OXOR, |
| OMUL, |
| ODIV, |
| OMOD, |
| OLSH, |
| ORSH, |
| OAND, |
| OANDNOT, |
| OPLUS, |
| OMINUS, |
| OCOM, |
| OPAREN, |
| OANDAND, |
| OOROR, |
| ODOT, // but not ODOTPTR |
| OCONV, |
| OCONVNOP, |
| OCONVIFACE, |
| ODOTTYPE: |
| return varexpr(n.Left) && varexpr(n.Right) |
| } |
| |
| // Be conservative. |
| return false |
| } |
| |
| /* |
| * is the name l mentioned in r? |
| */ |
| func vmatch2(l *Node, r *Node) bool { |
| if r == nil { |
| return false |
| } |
| switch r.Op { |
| // match each right given left |
| case ONAME: |
| return l == r |
| |
| case OLITERAL: |
| return false |
| } |
| |
| if vmatch2(l, r.Left) { |
| return true |
| } |
| if vmatch2(l, r.Right) { |
| return true |
| } |
| for ll := r.List; ll != nil; ll = ll.Next { |
| if vmatch2(l, ll.N) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| /* |
| * is any name mentioned in l also mentioned in r? |
| * called by sinit.c |
| */ |
| func vmatch1(l *Node, r *Node) bool { |
| /* |
| * isolate all left sides |
| */ |
| if l == nil || r == nil { |
| return false |
| } |
| switch l.Op { |
| case ONAME: |
| switch l.Class { |
| case PPARAM, |
| PPARAMREF, |
| PAUTO: |
| break |
| |
| // assignment to non-stack variable |
| // must be delayed if right has function calls. |
| default: |
| if r.Ullman >= UINF { |
| return true |
| } |
| } |
| |
| return vmatch2(l, r) |
| |
| case OLITERAL: |
| return false |
| } |
| |
| if vmatch1(l.Left, r) { |
| return true |
| } |
| if vmatch1(l.Right, r) { |
| return true |
| } |
| for ll := l.List; ll != nil; ll = ll.Next { |
| if vmatch1(ll.N, r) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| /* |
| * walk through argin parameters. |
| * generate and return code to allocate |
| * copies of escaped parameters to the heap. |
| */ |
| func paramstoheap(argin **Type, out int) *NodeList { |
| var savet Iter |
| var v *Node |
| var as *Node |
| |
| nn := (*NodeList)(nil) |
| for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { |
| v = t.Nname |
| if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result |
| v = nil |
| } |
| |
| // For precise stacks, the garbage collector assumes results |
| // are always live, so zero them always. |
| if out != 0 { |
| // Defer might stop a panic and show the |
| // return values as they exist at the time of panic. |
| // Make sure to zero them on entry to the function. |
| nn = list(nn, Nod(OAS, nodarg(t, 1), nil)) |
| } |
| |
| if v == nil || v.Class&PHEAP == 0 { |
| continue |
| } |
| |
| // generate allocation & copying code |
| if compiling_runtime != 0 { |
| Yyerror("%v escapes to heap, not allowed in runtime.", Nconv(v, 0)) |
| } |
| if v.Alloc == nil { |
| v.Alloc = callnew(v.Type) |
| } |
| nn = list(nn, Nod(OAS, v.Heapaddr, v.Alloc)) |
| if v.Class&^PHEAP != PPARAMOUT { |
| as = Nod(OAS, v, v.Stackparam) |
| v.Stackparam.Typecheck = 1 |
| typecheck(&as, Etop) |
| as = applywritebarrier(as, &nn) |
| nn = list(nn, as) |
| } |
| } |
| |
| return nn |
| } |
| |
| /* |
| * walk through argout parameters copying back to stack |
| */ |
| func returnsfromheap(argin **Type) *NodeList { |
| var savet Iter |
| var v *Node |
| |
| nn := (*NodeList)(nil) |
| for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { |
| v = t.Nname |
| if v == nil || v.Class != PHEAP|PPARAMOUT { |
| continue |
| } |
| nn = list(nn, Nod(OAS, v.Stackparam, v)) |
| } |
| |
| return nn |
| } |
| |
| /* |
| * take care of migrating any function in/out args |
| * between the stack and the heap. adds code to |
| * curfn's before and after lists. |
| */ |
| func heapmoves() { |
| lno := lineno |
| lineno = Curfn.Lineno |
| nn := paramstoheap(getthis(Curfn.Type), 0) |
| nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0)) |
| nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1)) |
| Curfn.Enter = concat(Curfn.Enter, nn) |
| lineno = Curfn.Endlineno |
| Curfn.Exit = returnsfromheap(Getoutarg(Curfn.Type)) |
| lineno = lno |
| } |
| |
| func vmkcall(fn *Node, t *Type, init **NodeList, va []*Node) *Node { |
| if fn.Type == nil || fn.Type.Etype != TFUNC { |
| Fatal("mkcall %v %v", Nconv(fn, 0), Tconv(fn.Type, 0)) |
| } |
| |
| args := (*NodeList)(nil) |
| n := fn.Type.Intuple |
| for i := 0; i < n; i++ { |
| args = list(args, va[i]) |
| } |
| |
| r := Nod(OCALL, fn, nil) |
| r.List = args |
| if fn.Type.Outtuple > 0 { |
| typecheck(&r, Erv|Efnstruct) |
| } else { |
| typecheck(&r, Etop) |
| } |
| walkexpr(&r, init) |
| r.Type = t |
| return r |
| } |
| |
| func mkcall(name string, t *Type, init **NodeList, args ...*Node) *Node { |
| return vmkcall(syslook(name, 0), t, init, args) |
| } |
| |
| func mkcall1(fn *Node, t *Type, init **NodeList, args ...*Node) *Node { |
| return vmkcall(fn, t, init, args) |
| } |
| |
| func conv(n *Node, t *Type) *Node { |
| if Eqtype(n.Type, t) { |
| return n |
| } |
| n = Nod(OCONV, n, nil) |
| n.Type = t |
| typecheck(&n, Erv) |
| return n |
| } |
| |
| func chanfn(name string, n int, t *Type) *Node { |
| if t.Etype != TCHAN { |
| Fatal("chanfn %v", Tconv(t, 0)) |
| } |
| fn := syslook(name, 1) |
| for i := 0; i < n; i++ { |
| argtype(fn, t.Type) |
| } |
| return fn |
| } |
| |
| func mapfn(name string, t *Type) *Node { |
| if t.Etype != TMAP { |
| Fatal("mapfn %v", Tconv(t, 0)) |
| } |
| fn := syslook(name, 1) |
| argtype(fn, t.Down) |
| argtype(fn, t.Type) |
| argtype(fn, t.Down) |
| argtype(fn, t.Type) |
| return fn |
| } |
| |
| func mapfndel(name string, t *Type) *Node { |
| if t.Etype != TMAP { |
| Fatal("mapfn %v", Tconv(t, 0)) |
| } |
| fn := syslook(name, 1) |
| argtype(fn, t.Down) |
| argtype(fn, t.Type) |
| argtype(fn, t.Down) |
| return fn |
| } |
| |
| func writebarrierfn(name string, l *Type, r *Type) *Node { |
| fn := syslook(name, 1) |
| argtype(fn, l) |
| argtype(fn, r) |
| return fn |
| } |
| |
| func addstr(n *Node, init **NodeList) *Node { |
| // orderexpr rewrote OADDSTR to have a list of strings. |
| c := count(n.List) |
| |
| if c < 2 { |
| Yyerror("addstr count %d too small", c) |
| } |
| |
| buf := nodnil() |
| if n.Esc == EscNone { |
| sz := int64(0) |
| for l := n.List; l != nil; l = l.Next { |
| if n.Op == OLITERAL { |
| sz += int64(len(n.Val.U.Sval.S)) |
| } |
| } |
| |
| // Don't allocate the buffer if the result won't fit. |
| if sz < tmpstringbufsize { |
| // Create temporary buffer for result string on stack. |
| t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) |
| |
| buf = Nod(OADDR, temp(t), nil) |
| } |
| } |
| |
| // build list of string arguments |
| args := list1(buf) |
| |
| for l := n.List; l != nil; l = l.Next { |
| args = list(args, conv(l.N, Types[TSTRING])) |
| } |
| |
| if c <= 5 { |
| // small numbers of strings use direct runtime helpers. |
| // note: orderexpr knows this cutoff too. |
| namebuf = fmt.Sprintf("concatstring%d", c) |
| } else { |
| // large numbers of strings are passed to the runtime as a slice. |
| namebuf = "concatstrings" |
| |
| t := typ(TARRAY) |
| t.Type = Types[TSTRING] |
| t.Bound = -1 |
| slice := Nod(OCOMPLIT, nil, typenod(t)) |
| slice.Alloc = n.Alloc |
| slice.List = args.Next // skip buf arg |
| args = list1(buf) |
| args = list(args, slice) |
| slice.Esc = EscNone |
| } |
| |
| cat := syslook(namebuf, 1) |
| r := Nod(OCALL, cat, nil) |
| r.List = args |
| typecheck(&r, Erv) |
| walkexpr(&r, init) |
| r.Type = n.Type |
| |
| return r |
| } |
| |
| // expand append(l1, l2...) to |
| // init { |
| // s := l1 |
| // if n := len(l1) + len(l2) - cap(s); n > 0 { |
| // s = growslice(s, n) |
| // } |
| // s = s[:len(l1)+len(l2)] |
| // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) |
| // } |
| // s |
| // |
| // l2 is allowed to be a string. |
| func appendslice(n *Node, init **NodeList) *Node { |
| walkexprlistsafe(n.List, init) |
| |
| // walkexprlistsafe will leave OINDEX (s[n]) alone if both s |
| // and n are name or literal, but those may index the slice we're |
| // modifying here. Fix explicitly. |
| for l := n.List; l != nil; l = l.Next { |
| l.N = cheapexpr(l.N, init) |
| } |
| |
| l1 := n.List.N |
| l2 := n.List.Next.N |
| |
| s := temp(l1.Type) // var s []T |
| l := (*NodeList)(nil) |
| l = list(l, Nod(OAS, s, l1)) // s = l1 |
| |
| nt := temp(Types[TINT]) |
| |
| nif := Nod(OIF, nil, nil) |
| |
| // n := len(s) + len(l2) - cap(s) |
| nif.Ninit = list1(Nod(OAS, nt, Nod(OSUB, Nod(OADD, Nod(OLEN, s, nil), Nod(OLEN, l2, nil)), Nod(OCAP, s, nil)))) |
| |
| nif.Ntest = Nod(OGT, nt, Nodintconst(0)) |
| |
| // instantiate growslice(Type*, []any, int64) []any |
| fn := syslook("growslice", 1) |
| |
| argtype(fn, s.Type.Type) |
| argtype(fn, s.Type.Type) |
| |
| // s = growslice(T, s, n) |
| nif.Nbody = list1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, conv(nt, Types[TINT64])))) |
| |
| l = list(l, nif) |
| |
| if haspointers(l1.Type.Type) { |
| // copy(s[len(l1):len(l1)+len(l2)], l2) |
| nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)))) |
| |
| nptr1.Etype = 1 |
| nptr2 := l2 |
| fn := syslook("typedslicecopy", 1) |
| argtype(fn, l1.Type) |
| argtype(fn, l2.Type) |
| nt := mkcall1(fn, Types[TINT], &l, typename(l1.Type.Type), nptr1, nptr2) |
| l = list(l, nt) |
| } else if flag_race != 0 { |
| // rely on runtime to instrument copy. |
| // copy(s[len(l1):len(l1)+len(l2)], l2) |
| nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)))) |
| |
| nptr1.Etype = 1 |
| nptr2 := l2 |
| var fn *Node |
| if l2.Type.Etype == TSTRING { |
| fn = syslook("slicestringcopy", 1) |
| } else { |
| fn = syslook("slicecopy", 1) |
| } |
| argtype(fn, l1.Type) |
| argtype(fn, l2.Type) |
| nt := mkcall1(fn, Types[TINT], &l, nptr1, nptr2, Nodintconst(s.Type.Type.Width)) |
| l = list(l, nt) |
| } else { |
| // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) |
| nptr1 := Nod(OINDEX, s, Nod(OLEN, l1, nil)) |
| |
| nptr1.Bounded = true |
| nptr1 = Nod(OADDR, nptr1, nil) |
| |
| nptr2 := Nod(OSPTR, l2, nil) |
| |
| fn := syslook("memmove", 1) |
| argtype(fn, s.Type.Type) // 1 old []any |
| argtype(fn, s.Type.Type) // 2 ret []any |
| |
| nwid := cheapexpr(conv(Nod(OLEN, l2, nil), Types[TUINTPTR]), &l) |
| |
| nwid = Nod(OMUL, nwid, Nodintconst(s.Type.Type.Width)) |
| nt := mkcall1(fn, nil, &l, nptr1, nptr2, nwid) |
| l = list(l, nt) |
| } |
| |
| // s = s[:len(l1)+len(l2)] |
| nt = Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)) |
| |
| nt = Nod(OSLICE, s, Nod(OKEY, nil, nt)) |
| nt.Etype = 1 |
| l = list(l, Nod(OAS, s, nt)) |
| |
| typechecklist(l, Etop) |
| walkstmtlist(l) |
| *init = concat(*init, l) |
| return s |
| } |
| |
| // expand append(src, a [, b]* ) to |
| // |
| // init { |
| // s := src |
| // const argc = len(args) - 1 |
| // if cap(s) - len(s) < argc { |
| // s = growslice(s, argc) |
| // } |
| // n := len(s) |
| // s = s[:n+argc] |
| // s[n] = a |
| // s[n+1] = b |
| // ... |
| // } |
| // s |
| func walkappend(n *Node, init **NodeList) *Node { |
| walkexprlistsafe(n.List, init) |
| |
| // walkexprlistsafe will leave OINDEX (s[n]) alone if both s |
| // and n are name or literal, but those may index the slice we're |
| // modifying here. Fix explicitly. |
| for l := n.List; l != nil; l = l.Next { |
| l.N = cheapexpr(l.N, init) |
| } |
| |
| nsrc := n.List.N |
| |
| // Resolve slice type of multi-valued return. |
| if Istype(nsrc.Type, TSTRUCT) { |
| nsrc.Type = nsrc.Type.Type.Type |
| } |
| argc := count(n.List) - 1 |
| if argc < 1 { |
| return nsrc |
| } |
| |
| l := (*NodeList)(nil) |
| |
| ns := temp(nsrc.Type) |
| l = list(l, Nod(OAS, ns, nsrc)) // s = src |
| |
| na := Nodintconst(int64(argc)) // const argc |
| nx := Nod(OIF, nil, nil) // if cap(s) - len(s) < argc |
| nx.Ntest = Nod(OLT, Nod(OSUB, Nod(OCAP, ns, nil), Nod(OLEN, ns, nil)), na) |
| |
| fn := syslook("growslice", 1) // growslice(<type>, old []T, n int64) (ret []T) |
| argtype(fn, ns.Type.Type) // 1 old []any |
| argtype(fn, ns.Type.Type) // 2 ret []any |
| |
| nx.Nbody = list1(Nod(OAS, ns, mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns, conv(na, Types[TINT64])))) |
| |
| l = list(l, nx) |
| |
| nn := temp(Types[TINT]) |
| l = list(l, Nod(OAS, nn, Nod(OLEN, ns, nil))) // n = len(s) |
| |
| nx = Nod(OSLICE, ns, Nod(OKEY, nil, Nod(OADD, nn, na))) // ...s[:n+argc] |
| nx.Etype = 1 |
| l = list(l, Nod(OAS, ns, nx)) // s = s[:n+argc] |
| |
| for a := n.List.Next; a != nil; a = a.Next { |
| nx = Nod(OINDEX, ns, nn) // s[n] ... |
| nx.Bounded = true |
| l = list(l, Nod(OAS, nx, a.N)) // s[n] = arg |
| if a.Next != nil { |
| l = list(l, Nod(OAS, nn, Nod(OADD, nn, Nodintconst(1)))) // n = n + 1 |
| } |
| } |
| |
| typechecklist(l, Etop) |
| walkstmtlist(l) |
| *init = concat(*init, l) |
| return ns |
| } |
| |
| // Lower copy(a, b) to a memmove call or a runtime call. |
| // |
| // init { |
| // n := len(a) |
| // if n > len(b) { n = len(b) } |
| // memmove(a.ptr, b.ptr, n*sizeof(elem(a))) |
| // } |
| // n; |
| // |
| // Also works if b is a string. |
| // |
| func copyany(n *Node, init **NodeList, runtimecall int) *Node { |
| if haspointers(n.Left.Type.Type) { |
| fn := writebarrierfn("typedslicecopy", n.Left.Type, n.Right.Type) |
| return mkcall1(fn, n.Type, init, typename(n.Left.Type.Type), n.Left, n.Right) |
| } |
| |
| if runtimecall != 0 { |
| var fn *Node |
| if n.Right.Type.Etype == TSTRING { |
| fn = syslook("slicestringcopy", 1) |
| } else { |
| fn = syslook("slicecopy", 1) |
| } |
| argtype(fn, n.Left.Type) |
| argtype(fn, n.Right.Type) |
| return mkcall1(fn, n.Type, init, n.Left, n.Right, Nodintconst(n.Left.Type.Type.Width)) |
| } |
| |
| walkexpr(&n.Left, init) |
| walkexpr(&n.Right, init) |
| nl := temp(n.Left.Type) |
| nr := temp(n.Right.Type) |
| l := (*NodeList)(nil) |
| l = list(l, Nod(OAS, nl, n.Left)) |
| l = list(l, Nod(OAS, nr, n.Right)) |
| |
| nfrm := Nod(OSPTR, nr, nil) |
| nto := Nod(OSPTR, nl, nil) |
| |
| nlen := temp(Types[TINT]) |
| |
| // n = len(to) |
| l = list(l, Nod(OAS, nlen, Nod(OLEN, nl, nil))) |
| |
| // if n > len(frm) { n = len(frm) } |
| nif := Nod(OIF, nil, nil) |
| |
| nif.Ntest = Nod(OGT, nlen, Nod(OLEN, nr, nil)) |
| nif.Nbody = list(nif.Nbody, Nod(OAS, nlen, Nod(OLEN, nr, nil))) |
| l = list(l, nif) |
| |
| // Call memmove. |
| fn := syslook("memmove", 1) |
| |
| argtype(fn, nl.Type.Type) |
| argtype(fn, nl.Type.Type) |
| nwid := temp(Types[TUINTPTR]) |
| l = list(l, Nod(OAS, nwid, conv(nlen, Types[TUINTPTR]))) |
| nwid = Nod(OMUL, nwid, Nodintconst(nl.Type.Type.Width)) |
| l = list(l, mkcall1(fn, nil, init, nto, nfrm, nwid)) |
| |
| typechecklist(l, Etop) |
| walkstmtlist(l) |
| *init = concat(*init, l) |
| return nlen |
| } |
| |
| // Generate frontend part for OSLICE[3][ARR|STR] |
| // |
| func sliceany(n *Node, init **NodeList) *Node { |
| var hb *Node |
| var cb *Node |
| |
| // print("before sliceany: %+N\n", n); |
| |
| src := n.Left |
| |
| lb := n.Right.Left |
| slice3 := n.Op == OSLICE3 || n.Op == OSLICE3ARR |
| if slice3 { |
| hb = n.Right.Right.Left |
| cb = n.Right.Right.Right |
| } else { |
| hb = n.Right.Right |
| cb = nil |
| } |
| |
| bounded := int(n.Etype) |
| |
| var bound *Node |
| if n.Op == OSLICESTR { |
| bound = Nod(OLEN, src, nil) |
| } else { |
| bound = Nod(OCAP, src, nil) |
| } |
| |
| typecheck(&bound, Erv) |
| walkexpr(&bound, init) // if src is an array, bound will be a const now. |
| |
| // static checks if possible |
| bv := int64(1 << 50) |
| |
| if Isconst(bound, CTINT) { |
| if !Smallintconst(bound) { |
| Yyerror("array len too large") |
| } else { |
| bv = Mpgetfix(bound.Val.U.Xval) |
| } |
| } |
| |
| if Isconst(cb, CTINT) { |
| cbv := Mpgetfix(cb.Val.U.Xval) |
| if cbv < 0 || cbv > bv { |
| Yyerror("slice index out of bounds") |
| } |
| } |
| |
| if Isconst(hb, CTINT) { |
| hbv := Mpgetfix(hb.Val.U.Xval) |
| if hbv < 0 || hbv > bv { |
| Yyerror("slice index out of bounds") |
| } |
| } |
| |
| if Isconst(lb, CTINT) { |
| lbv := Mpgetfix(lb.Val.U.Xval) |
| if lbv < 0 || lbv > bv { |
| Yyerror("slice index out of bounds") |
| lbv = -1 |
| } |
| |
| if lbv == 0 { |
| lb = nil |
| } |
| } |
| |
| // Checking src[lb:hb:cb] or src[lb:hb]. |
| // if chk0 || chk1 || chk2 { panicslice() } |
| chk0 := (*Node)(nil) // cap(src) < cb |
| chk1 := (*Node)(nil) // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb] |
| chk2 := (*Node)(nil) // hb < lb |
| |
| // All comparisons are unsigned to avoid testing < 0. |
| bt := Types[Simtype[TUINT]] |
| |
| if cb != nil && cb.Type.Width > 4 { |
| bt = Types[TUINT64] |
| } |
| if hb != nil && hb.Type.Width > 4 { |
| bt = Types[TUINT64] |
| } |
| if lb != nil && lb.Type.Width > 4 { |
| bt = Types[TUINT64] |
| } |
| |
| bound = cheapexpr(conv(bound, bt), init) |
| |
| if cb != nil { |
| cb = cheapexpr(conv(cb, bt), init) |
| if bounded == 0 { |
| chk0 = Nod(OLT, bound, cb) |
| } |
| } else if slice3 { |
| // When we figure out what this means, implement it. |
| Fatal("slice3 with cb == N") // rejected by parser |
| } |
| |
| if hb != nil { |
| hb = cheapexpr(conv(hb, bt), init) |
| if bounded == 0 { |
| if cb != nil { |
| chk1 = Nod(OLT, cb, hb) |
| } else { |
| chk1 = Nod(OLT, bound, hb) |
| } |
| } |
| } else if slice3 { |
| // When we figure out what this means, implement it. |
| Fatal("slice3 with hb == N") // rejected by parser |
| } else if n.Op == OSLICEARR { |
| hb = bound |
| } else { |
| hb = Nod(OLEN, src, nil) |
| typecheck(&hb, Erv) |
| walkexpr(&hb, init) |
| hb = cheapexpr(conv(hb, bt), init) |
| } |
| |
| if lb != nil { |
| lb = cheapexpr(conv(lb, bt), init) |
| if bounded == 0 { |
| chk2 = Nod(OLT, hb, lb) |
| } |
| } |
| |
| if chk0 != nil || chk1 != nil || chk2 != nil { |
| chk := Nod(OIF, nil, nil) |
| chk.Nbody = list1(mkcall("panicslice", nil, init)) |
| chk.Likely = -1 |
| if chk0 != nil { |
| chk.Ntest = chk0 |
| } |
| if chk1 != nil { |
| if chk.Ntest == nil { |
| chk.Ntest = chk1 |
| } else { |
| chk.Ntest = Nod(OOROR, chk.Ntest, chk1) |
| } |
| } |
| |
| if chk2 != nil { |
| if chk.Ntest == nil { |
| chk.Ntest = chk2 |
| } else { |
| chk.Ntest = Nod(OOROR, chk.Ntest, chk2) |
| } |
| } |
| |
| typecheck(&chk, Etop) |
| walkstmt(&chk) |
| *init = concat(*init, chk.Ninit) |
| chk.Ninit = nil |
| *init = list(*init, chk) |
| } |
| |
| // prepare new cap, len and offs for backend cgen_slice |
| // cap = bound [ - lo ] |
| n.Right = nil |
| |
| n.List = nil |
| if !slice3 { |
| cb = bound |
| } |
| if lb == nil { |
| bound = conv(cb, Types[Simtype[TUINT]]) |
| } else { |
| bound = Nod(OSUB, conv(cb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]])) |
| } |
| typecheck(&bound, Erv) |
| walkexpr(&bound, init) |
| n.List = list(n.List, bound) |
| |
| // len = hi [ - lo] |
| if lb == nil { |
| hb = conv(hb, Types[Simtype[TUINT]]) |
| } else { |
| hb = Nod(OSUB, conv(hb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]])) |
| } |
| typecheck(&hb, Erv) |
| walkexpr(&hb, init) |
| n.List = list(n.List, hb) |
| |
| // offs = [width *] lo, but omit if zero |
| if lb != nil { |
| var w int64 |
| if n.Op == OSLICESTR { |
| w = 1 |
| } else { |
| w = n.Type.Type.Width |
| } |
| lb = conv(lb, Types[TUINTPTR]) |
| if w > 1 { |
| lb = Nod(OMUL, Nodintconst(w), lb) |
| } |
| typecheck(&lb, Erv) |
| walkexpr(&lb, init) |
| n.List = list(n.List, lb) |
| } |
| |
| // print("after sliceany: %+N\n", n); |
| |
| return n |
| } |
| |
| func eqfor(t *Type, needsize *int) *Node { |
| // Should only arrive here with large memory or |
| // a struct/array containing a non-memory field/element. |
| // Small memory is handled inline, and single non-memory |
| // is handled during type check (OCMPSTR etc). |
| a := algtype1(t, nil) |
| |
| if a != AMEM && a != -1 { |
| Fatal("eqfor %v", Tconv(t, 0)) |
| } |
| |
| if a == AMEM { |
| n := syslook("memequal", 1) |
| argtype(n, t) |
| argtype(n, t) |
| *needsize = 1 |
| return n |
| } |
| |
| sym := typesymprefix(".eq", t) |
| n := newname(sym) |
| n.Class = PFUNC |
| ntype := Nod(OTFUNC, nil, nil) |
| ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) |
| ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) |
| ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))) |
| typecheck(&ntype, Etype) |
| n.Type = ntype.Type |
| *needsize = 0 |
| return n |
| } |
| |
| func countfield(t *Type) int { |
| n := 0 |
| for t1 := t.Type; t1 != nil; t1 = t1.Down { |
| n++ |
| } |
| return n |
| } |
| |
| func walkcompare(np **Node, init **NodeList) { |
| n := *np |
| |
| // Given interface value l and concrete value r, rewrite |
| // l == r |
| // to |
| // x, ok := l.(type(r)); ok && x == r |
| // Handle != similarly. |
| // This avoids the allocation that would be required |
| // to convert r to l for comparison. |
| l := (*Node)(nil) |
| |
| r := (*Node)(nil) |
| if Isinter(n.Left.Type) && !Isinter(n.Right.Type) { |
| l = n.Left |
| r = n.Right |
| } else if !Isinter(n.Left.Type) && Isinter(n.Right.Type) { |
| l = n.Right |
| r = n.Left |
| } |
| |
| var call *Node |
| var a *Node |
| var cmpl *Node |
| var cmpr *Node |
| var andor int |
| var expr *Node |
| var needsize int |
| var t *Type |
| if l != nil { |
| x := temp(r.Type) |
| ok := temp(Types[TBOOL]) |
| |
| // l.(type(r)) |
| a := Nod(ODOTTYPE, l, nil) |
| |
| a.Type = r.Type |
| |
| // x, ok := l.(type(r)) |
| expr := Nod(OAS2, nil, nil) |
| |
| expr.List = list1(x) |
| expr.List = list(expr.List, ok) |
| expr.Rlist = list1(a) |
| typecheck(&expr, Etop) |
| walkexpr(&expr, init) |
| |
| if n.Op == OEQ { |
| r = Nod(OANDAND, ok, Nod(OEQ, x, r)) |
| } else { |
| r = Nod(OOROR, Nod(ONOT, ok, nil), Nod(ONE, x, r)) |
| } |
| *init = list(*init, expr) |
| goto ret |
| } |
| |
| // Must be comparison of array or struct. |
| // Otherwise back end handles it. |
| t = n.Left.Type |
| |
| switch t.Etype { |
| default: |
| return |
| |
| case TARRAY: |
| if Isslice(t) { |
| return |
| } |
| |
| case TSTRUCT: |
| break |
| } |
| |
| cmpl = n.Left |
| for cmpl != nil && cmpl.Op == OCONVNOP { |
| cmpl = cmpl.Left |
| } |
| cmpr = n.Right |
| for cmpr != nil && cmpr.Op == OCONVNOP { |
| cmpr = cmpr.Left |
| } |
| |
| if !islvalue(cmpl) || !islvalue(cmpr) { |
| Fatal("arguments of comparison must be lvalues - %v %v", Nconv(cmpl, 0), Nconv(cmpr, 0)) |
| } |
| |
| l = temp(Ptrto(t)) |
| a = Nod(OAS, l, Nod(OADDR, cmpl, nil)) |
| a.Right.Etype = 1 // addr does not escape |
| typecheck(&a, Etop) |
| *init = list(*init, a) |
| |
| r = temp(Ptrto(t)) |
| a = Nod(OAS, r, Nod(OADDR, cmpr, nil)) |
| a.Right.Etype = 1 // addr does not escape |
| typecheck(&a, Etop) |
| *init = list(*init, a) |
| |
| expr = nil |
| andor = OANDAND |
| if n.Op == ONE { |
| andor = OOROR |
| } |
| |
| if t.Etype == TARRAY && t.Bound <= 4 && issimple[t.Type.Etype] != 0 { |
| // Four or fewer elements of a basic type. |
| // Unroll comparisons. |
| var li *Node |
| var ri *Node |
| for i := 0; int64(i) < t.Bound; i++ { |
| li = Nod(OINDEX, l, Nodintconst(int64(i))) |
| ri = Nod(OINDEX, r, Nodintconst(int64(i))) |
| a = Nod(int(n.Op), li, ri) |
| if expr == nil { |
| expr = a |
| } else { |
| expr = Nod(andor, expr, a) |
| } |
| } |
| |
| if expr == nil { |
| expr = Nodbool(n.Op == OEQ) |
| } |
| r = expr |
| goto ret |
| } |
| |
| if t.Etype == TSTRUCT && countfield(t) <= 4 { |
| // Struct of four or fewer fields. |
| // Inline comparisons. |
| var li *Node |
| var ri *Node |
| for t1 := t.Type; t1 != nil; t1 = t1.Down { |
| if isblanksym(t1.Sym) { |
| continue |
| } |
| li = Nod(OXDOT, l, newname(t1.Sym)) |
| ri = Nod(OXDOT, r, newname(t1.Sym)) |
| a = Nod(int(n.Op), li, ri) |
| if expr == nil { |
| expr = a |
| } else { |
| expr = Nod(andor, expr, a) |
| } |
| } |
| |
| if expr == nil { |
| expr = Nodbool(n.Op == OEQ) |
| } |
| r = expr |
| goto ret |
| } |
| |
| // Chose not to inline. Call equality function directly. |
| call = Nod(OCALL, eqfor(t, &needsize), nil) |
| |
| call.List = list(call.List, l) |
| call.List = list(call.List, r) |
| if needsize != 0 { |
| call.List = list(call.List, Nodintconst(t.Width)) |
| } |
| r = call |
| if n.Op != OEQ { |
| r = Nod(ONOT, r, nil) |
| } |
| goto ret |
| |
| ret: |
| typecheck(&r, Erv) |
| walkexpr(&r, init) |
| if r.Type != n.Type { |
| r = Nod(OCONVNOP, r, nil) |
| r.Type = n.Type |
| r.Typecheck = 1 |
| } |
| |
| *np = r |
| return |
| } |
| |
| func samecheap(a *Node, b *Node) bool { |
| var ar *Node |
| var br *Node |
| for a != nil && b != nil && a.Op == b.Op { |
| switch a.Op { |
| default: |
| return false |
| |
| case ONAME: |
| return a == b |
| |
| case ODOT, |
| ODOTPTR: |
| ar = a.Right |
| br = b.Right |
| if ar.Op != ONAME || br.Op != ONAME || ar.Sym != br.Sym { |
| return false |
| } |
| |
| case OINDEX: |
| ar = a.Right |
| br = b.Right |
| if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.Xval, br.Val.U.Xval) != 0 { |
| return false |
| } |
| } |
| |
| a = a.Left |
| b = b.Left |
| } |
| |
| return false |
| } |
| |
| func walkrotate(np **Node) { |
| if Thearch.Thechar == '9' { |
| return |
| } |
| |
| n := *np |
| |
| // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value. |
| l := n.Left |
| |
| r := n.Right |
| if (n.Op != OOR && n.Op != OXOR) || (l.Op != OLSH && l.Op != ORSH) || (r.Op != OLSH && r.Op != ORSH) || n.Type == nil || Issigned[n.Type.Etype] != 0 || l.Op == r.Op { |
| return |
| } |
| |
| // Want same, side effect-free expression on lhs of both shifts. |
| if !samecheap(l.Left, r.Left) { |
| return |
| } |
| |
| // Constants adding to width? |
| w := int(l.Type.Width * 8) |
| |
| if Smallintconst(l.Right) && Smallintconst(r.Right) { |
| sl := int(Mpgetfix(l.Right.Val.U.Xval)) |
| if sl >= 0 { |
| sr := int(Mpgetfix(r.Right.Val.U.Xval)) |
| if sr >= 0 && sl+sr == w { |
| goto yes |
| } |
| } |
| return |
| } |
| |
| // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31). |
| return |
| |
| // Rewrite left shift half to left rotate. |
| yes: |
| if l.Op == OLSH { |
| n = l |
| } else { |
| n = r |
| } |
| n.Op = OLROT |
| |
| // Remove rotate 0 and rotate w. |
| s := int(Mpgetfix(n.Right.Val.U.Xval)) |
| |
| if s == 0 || s == w { |
| n = n.Left |
| } |
| |
| *np = n |
| return |
| } |
| |
| /* |
| * walkmul rewrites integer multiplication by powers of two as shifts. |
| */ |
| func walkmul(np **Node, init **NodeList) { |
| n := *np |
| if Isint[n.Type.Etype] == 0 { |
| return |
| } |
| |
| var nr *Node |
| var nl *Node |
| if n.Right.Op == OLITERAL { |
| nl = n.Left |
| nr = n.Right |
| } else if n.Left.Op == OLITERAL { |
| nl = n.Right |
| nr = n.Left |
| } else { |
| return |
| } |
| |
| neg := 0 |
| |
| // x*0 is 0 (and side effects of x). |
| var pow int |
| var w int |
| if Mpgetfix(nr.Val.U.Xval) == 0 { |
| cheapexpr(nl, init) |
| Nodconst(n, n.Type, 0) |
| goto ret |
| } |
| |
| // nr is a constant. |
| pow = powtwo(nr) |
| |
| if pow < 0 { |
| return |
| } |
| if pow >= 1000 { |
| // negative power of 2, like -16 |
| neg = 1 |
| |
| pow -= 1000 |
| } |
| |
| w = int(nl.Type.Width * 8) |
| if pow+1 >= w { // too big, shouldn't happen |
| return |
| } |
| |
| nl = cheapexpr(nl, init) |
| |
| if pow == 0 { |
| // x*1 is x |
| n = nl |
| |
| goto ret |
| } |
| |
| n = Nod(OLSH, nl, Nodintconst(int64(pow))) |
| |
| ret: |
| if neg != 0 { |
| n = Nod(OMINUS, n, nil) |
| } |
| |
| typecheck(&n, Erv) |
| walkexpr(&n, init) |
| *np = n |
| } |
| |
| /* |
| * walkdiv rewrites division by a constant as less expensive |
| * operations. |
| */ |
| func walkdiv(np **Node, init **NodeList) { |
| // if >= 0, nr is 1<<pow // 1 if nr is negative. |
| |
| // TODO(minux) |
| if Thearch.Thechar == '9' { |
| return |
| } |
| |
| n := *np |
| if n.Right.Op != OLITERAL { |
| return |
| } |
| |
| // nr is a constant. |
| nl := cheapexpr(n.Left, init) |
| |
| nr := n.Right |
| |
| // special cases of mod/div |
| // by a constant |
| w := int(nl.Type.Width * 8) |
| |
| s := 0 |
| pow := powtwo(nr) |
| if pow >= 1000 { |
| // negative power of 2 |
| s = 1 |
| |
| pow -= 1000 |
| } |
| |
| if pow+1 >= w { |
| // divisor too large. |
| return |
| } |
| |
| var n1 *Node |
| var m Magic |
| var n2 *Node |
| if pow < 0 { |
| goto divbymul |
| } |
| |
| switch pow { |
| case 0: |
| if n.Op == OMOD { |
| // nl % 1 is zero. |
| Nodconst(n, n.Type, 0) |
| } else if s != 0 { |
| // divide by -1 |
| n.Op = OMINUS |
| |
| n.Right = nil |
| } else { |
| // divide by 1 |
| n = nl |
| } |
| |
| default: |
| if Issigned[n.Type.Etype] != 0 { |
| if n.Op == OMOD { |
| // signed modulo 2^pow is like ANDing |
| // with the last pow bits, but if nl < 0, |
| // nl & (2^pow-1) is (nl+1)%2^pow - 1. |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1) |
| n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0. |
| if pow == 1 { |
| typecheck(&n1, Erv) |
| n1 = cheapexpr(n1, init) |
| |
| // n = (nl+ε)&1 -ε where ε=1 iff nl<0. |
| n2 := Nod(OSUB, nl, n1) |
| |
| nc := Nod(OXXX, nil, nil) |
| Nodconst(nc, nl.Type, 1) |
| n3 := Nod(OAND, n2, nc) |
| n = Nod(OADD, n3, n1) |
| } else { |
| // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, nl.Type, (1<<uint(pow))-1) |
| n2 := Nod(OAND, n1, nc) // n2 = 2^pow-1 iff nl<0. |
| typecheck(&n2, Erv) |
| n2 = cheapexpr(n2, init) |
| |
| n3 := Nod(OADD, nl, n2) |
| n4 := Nod(OAND, n3, nc) |
| n = Nod(OSUB, n4, n2) |
| } |
| |
| break |
| } else { |
| // arithmetic right shift does not give the correct rounding. |
| // if nl >= 0, nl >> n == nl / nr |
| // if nl < 0, we want to add 2^n-1 first. |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1) |
| n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0. |
| if pow == 1 { |
| // nl+1 is nl-(-1) |
| n.Left = Nod(OSUB, nl, n1) |
| } else { |
| // Do a logical right right on -1 to keep pow bits. |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, Types[Simtype[TUINT]], int64(w)-int64(pow)) |
| n2 := Nod(ORSH, conv(n1, tounsigned(nl.Type)), nc) |
| n.Left = Nod(OADD, nl, conv(n2, nl.Type)) |
| } |
| |
| // n = (nl + 2^pow-1) >> pow |
| n.Op = ORSH |
| |
| nc = Nod(OXXX, nil, nil) |
| Nodconst(nc, Types[Simtype[TUINT]], int64(pow)) |
| n.Right = nc |
| n.Typecheck = 0 |
| } |
| |
| if s != 0 { |
| n = Nod(OMINUS, n, nil) |
| } |
| break |
| } |
| |
| nc := Nod(OXXX, nil, nil) |
| if n.Op == OMOD { |
| // n = nl & (nr-1) |
| n.Op = OAND |
| |
| Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.Xval)-1) |
| } else { |
| // n = nl >> pow |
| n.Op = ORSH |
| |
| Nodconst(nc, Types[Simtype[TUINT]], int64(pow)) |
| } |
| |
| n.Typecheck = 0 |
| n.Right = nc |
| } |
| |
| goto ret |
| |
| // try to do division by multiply by (2^w)/d |
| // see hacker's delight chapter 10 |
| // TODO: support 64-bit magic multiply here. |
| divbymul: |
| m.W = w |
| |
| if Issigned[nl.Type.Etype] != 0 { |
| m.Sd = Mpgetfix(nr.Val.U.Xval) |
| Smagic(&m) |
| } else { |
| m.Ud = uint64(Mpgetfix(nr.Val.U.Xval)) |
| Umagic(&m) |
| } |
| |
| if m.Bad != 0 { |
| return |
| } |
| |
| // We have a quick division method so use it |
| // for modulo too. |
| if n.Op == OMOD { |
| goto longmod |
| } |
| |
| switch Simtype[nl.Type.Etype] { |
| default: |
| return |
| |
| // n1 = nl * magic >> w (HMUL) |
| case TUINT8, |
| TUINT16, |
| TUINT32: |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, nl.Type, int64(m.Um)) |
| n1 := Nod(OMUL, nl, nc) |
| typecheck(&n1, Erv) |
| n1.Op = OHMUL |
| if m.Ua != 0 { |
| // Select a Go type with (at least) twice the width. |
| var twide *Type |
| switch Simtype[nl.Type.Etype] { |
| default: |
| return |
| |
| case TUINT8, |
| TUINT16: |
| twide = Types[TUINT32] |
| |
| case TUINT32: |
| twide = Types[TUINT64] |
| |
| case TINT8, |
| TINT16: |
| twide = Types[TINT32] |
| |
| case TINT32: |
| twide = Types[TINT64] |
| } |
| |
| // add numerator (might overflow). |
| // n2 = (n1 + nl) |
| n2 := Nod(OADD, conv(n1, twide), conv(nl, twide)) |
| |
| // shift by m.s |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, Types[TUINT], int64(m.S)) |
| n = conv(Nod(ORSH, n2, nc), nl.Type) |
| } else { |
| // n = n1 >> m.s |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, Types[TUINT], int64(m.S)) |
| n = Nod(ORSH, n1, nc) |
| } |
| |
| // n1 = nl * magic >> w |
| case TINT8, |
| TINT16, |
| TINT32: |
| nc := Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, nl.Type, m.Sm) |
| n1 := Nod(OMUL, nl, nc) |
| typecheck(&n1, Erv) |
| n1.Op = OHMUL |
| if m.Sm < 0 { |
| // add the numerator. |
| n1 = Nod(OADD, n1, nl) |
| } |
| |
| // shift by m.s |
| nc = Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, Types[TUINT], int64(m.S)) |
| n2 := conv(Nod(ORSH, n1, nc), nl.Type) |
| |
| // add 1 iff n1 is negative. |
| nc = Nod(OXXX, nil, nil) |
| |
| Nodconst(nc, Types[TUINT], int64(w)-1) |
| n3 := Nod(ORSH, nl, nc) // n4 = -1 iff n1 is negative. |
| n = Nod(OSUB, n2, n3) |
| |
| // apply sign. |
| if m.Sd < 0 { |
| n = Nod(OMINUS, n, nil) |
| } |
| } |
| |
| goto ret |
| |
| // rewrite as A%B = A - (A/B*B). |
| longmod: |
| n1 = Nod(ODIV, nl, nr) |
| |
| n2 = Nod(OMUL, n1, nr) |
| n = Nod(OSUB, nl, n2) |
| goto ret |
| |
| ret: |
| typecheck(&n, Erv) |
| walkexpr(&n, init) |
| *np = n |
| } |
| |
| // return 1 if integer n must be in range [0, max), 0 otherwise |
| func bounded(n *Node, max int64) bool { |
| if n.Type == nil || Isint[n.Type.Etype] == 0 { |
| return false |
| } |
| |
| sign := int(Issigned[n.Type.Etype]) |
| bits := int32(8 * n.Type.Width) |
| |
| if Smallintconst(n) { |
| v := Mpgetfix(n.Val.U.Xval) |
| return 0 <= v && v < max |
| } |
| |
| switch n.Op { |
| case OAND: |
| v := int64(-1) |
| if Smallintconst(n.Left) { |
| v = Mpgetfix(n.Left.Val.U.Xval) |
| } else if Smallintconst(n.Right) { |
| v = Mpgetfix(n.Right.Val.U.Xval) |
| } |
| |
| if 0 <= v && v < max { |
| return true |
| } |
| |
| case OMOD: |
| if sign == 0 && Smallintconst(n.Right) { |
| v := Mpgetfix(n.Right.Val.U.Xval) |
| if 0 <= v && v <= max { |
| return true |
| } |
| } |
| |
| case ODIV: |
| if sign == 0 && Smallintconst(n.Right) { |
| v := Mpgetfix(n.Right.Val.U.Xval) |
| for bits > 0 && v >= 2 { |
| bits-- |
| v >>= 1 |
| } |
| } |
| |
| case ORSH: |
| if sign == 0 && Smallintconst(n.Right) { |
| v := Mpgetfix(n.Right.Val.U.Xval) |
| if v > int64(bits) { |
| return true |
| } |
| bits -= int32(v) |
| } |
| } |
| |
| if sign == 0 && bits <= 62 && 1<<uint(bits) <= max { |
| return true |
| } |
| |
| return false |
| } |
| |
| func usefield(n *Node) { |
| if obj.Fieldtrack_enabled == 0 { |
| return |
| } |
| |
| switch n.Op { |
| default: |
| Fatal("usefield %v", Oconv(int(n.Op), 0)) |
| |
| case ODOT, |
| ODOTPTR: |
| break |
| } |
| |
| field := n.Paramfld |
| if field == nil { |
| Fatal("usefield %v %v without paramfld", Tconv(n.Left.Type, 0), Sconv(n.Right.Sym, 0)) |
| } |
| if field.Note == nil || !strings.Contains(field.Note.S, "go:\"track\"") { |
| return |
| } |
| |
| // dedup on list |
| if field.Lastfn == Curfn { |
| return |
| } |
| field.Lastfn = Curfn |
| field.Outer = n.Left.Type |
| if Isptr[field.Outer.Etype] != 0 { |
| field.Outer = field.Outer.Type |
| } |
| if field.Outer.Sym == nil { |
| Yyerror("tracked field must be in named struct type") |
| } |
| if !exportname(field.Sym.Name) { |
| Yyerror("tracked field must be exported (upper case)") |
| } |
| |
| l := typ(0) |
| l.Type = field |
| l.Down = Curfn.Paramfld |
| Curfn.Paramfld = l |
| } |
| |
| func candiscardlist(l *NodeList) bool { |
| for ; l != nil; l = l.Next { |
| if !candiscard(l.N) { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func candiscard(n *Node) bool { |
| if n == nil { |
| return true |
| } |
| |
| switch n.Op { |
| default: |
| return false |
| |
| // Discardable as long as the subpieces are. |
| case ONAME, |
| ONONAME, |
| OTYPE, |
| OPACK, |
| OLITERAL, |
| OADD, |
| OSUB, |
| OOR, |
| OXOR, |
| OADDSTR, |
| OADDR, |
| OANDAND, |
| OARRAYBYTESTR, |
| OARRAYRUNESTR, |
| OSTRARRAYBYTE, |
| OSTRARRAYRUNE, |
| OCAP, |
| OCMPIFACE, |
| OCMPSTR, |
| OCOMPLIT, |
| OMAPLIT, |
| OSTRUCTLIT, |
| OARRAYLIT, |
| OPTRLIT, |
| OCONV, |
| OCONVIFACE, |
| OCONVNOP, |
| ODOT, |
| OEQ, |
| ONE, |
| OLT, |
| OLE, |
| OGT, |
| OGE, |
| OKEY, |
| OLEN, |
| OMUL, |
| OLSH, |
| ORSH, |
| OAND, |
| OANDNOT, |
| ONEW, |
| ONOT, |
| OCOM, |
| OPLUS, |
| OMINUS, |
| OOROR, |
| OPAREN, |
| ORUNESTR, |
| OREAL, |
| OIMAG, |
| OCOMPLEX: |
| break |
| |
| // Discardable as long as we know it's not division by zero. |
| case ODIV, |
| OMOD: |
| if Isconst(n.Right, CTINT) && mpcmpfixc(n.Right.Val.U.Xval, 0) != 0 { |
| break |
| } |
| if Isconst(n.Right, CTFLT) && mpcmpfltc(n.Right.Val.U.Fval, 0) != 0 { |
| break |
| } |
| return false |
| |
| // Discardable as long as we know it won't fail because of a bad size. |
| case OMAKECHAN, |
| OMAKEMAP: |
| if Isconst(n.Left, CTINT) && mpcmpfixc(n.Left.Val.U.Xval, 0) == 0 { |
| break |
| } |
| return false |
| |
| // Difficult to tell what sizes are okay. |
| case OMAKESLICE: |
| return false |
| } |
| |
| if !candiscard(n.Left) || !candiscard(n.Right) || !candiscard(n.Ntest) || !candiscard(n.Nincr) || !candiscardlist(n.Ninit) || !candiscardlist(n.Nbody) || !candiscardlist(n.Nelse) || !candiscardlist(n.List) || !candiscardlist(n.Rlist) { |
| return false |
| } |
| |
| return true |
| } |
| |
| // rewrite |
| // print(x, y, z) |
| // into |
| // func(a1, a2, a3) { |
| // print(a1, a2, a3) |
| // }(x, y, z) |
| // and same for println. |
| |
| var walkprintfunc_prgen int |
| |
| func walkprintfunc(np **Node, init **NodeList) { |
| n := *np |
| |
| if n.Ninit != nil { |
| walkstmtlist(n.Ninit) |
| *init = concat(*init, n.Ninit) |
| n.Ninit = nil |
| } |
| |
| t := Nod(OTFUNC, nil, nil) |
| num := 0 |
| printargs := (*NodeList)(nil) |
| var a *Node |
| var buf string |
| for l := n.List; l != nil; l = l.Next { |
| buf = fmt.Sprintf("a%d", num) |
| num++ |
| a = Nod(ODCLFIELD, newname(Lookup(buf)), typenod(l.N.Type)) |
| t.List = list(t.List, a) |
| printargs = list(printargs, a.Left) |
| } |
| |
| fn := Nod(ODCLFUNC, nil, nil) |
| walkprintfunc_prgen++ |
| buf = fmt.Sprintf("print·%d", walkprintfunc_prgen) |
| fn.Nname = newname(Lookup(buf)) |
| fn.Nname.Defn = fn |
| fn.Nname.Ntype = t |
| declare(fn.Nname, PFUNC) |
| |
| oldfn := Curfn |
| Curfn = nil |
| funchdr(fn) |
| |
| a = Nod(int(n.Op), nil, nil) |
| a.List = printargs |
| typecheck(&a, Etop) |
| walkstmt(&a) |
| |
| fn.Nbody = list1(a) |
| |
| funcbody(fn) |
| |
| typecheck(&fn, Etop) |
| typechecklist(fn.Nbody, Etop) |
| xtop = list(xtop, fn) |
| Curfn = oldfn |
| |
| a = Nod(OCALL, nil, nil) |
| a.Left = fn.Nname |
| a.List = n.List |
| typecheck(&a, Etop) |
| walkexpr(&a, init) |
| *np = a |
| } |