| // 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" |
| ) |
| |
| // portable half of code generator. |
| // mainly statements and control flow. |
| var labellist *Label |
| |
| var lastlabel *Label |
| |
| func Sysfunc(name string) *Node { |
| n := newname(Pkglookup(name, Runtimepkg)) |
| n.Class = PFUNC |
| return n |
| } |
| |
| // addrescapes tags node n as having had its address taken |
| // by "increasing" the "value" of n.Esc to EscHeap. |
| // Storage is allocated as necessary to allow the address |
| // to be taken. |
| func addrescapes(n *Node) { |
| switch n.Op { |
| // probably a type error already. |
| // dump("addrescapes", n); |
| default: |
| break |
| |
| case ONAME: |
| if n == nodfp { |
| break |
| } |
| |
| // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping. |
| // on PPARAM it means something different. |
| if n.Class == PAUTO && n.Esc == EscNever { |
| break |
| } |
| |
| switch n.Class { |
| case PPARAMREF: |
| addrescapes(n.Name.Defn) |
| |
| // if func param, need separate temporary |
| // to hold heap pointer. |
| // the function type has already been checked |
| // (we're in the function body) |
| // so the param already has a valid xoffset. |
| |
| // expression to refer to stack copy |
| case PPARAM, PPARAMOUT: |
| n.Name.Param.Stackparam = Nod(OPARAM, n, nil) |
| |
| n.Name.Param.Stackparam.Type = n.Type |
| n.Name.Param.Stackparam.Addable = true |
| if n.Xoffset == BADWIDTH { |
| Fatalf("addrescapes before param assignment") |
| } |
| n.Name.Param.Stackparam.Xoffset = n.Xoffset |
| fallthrough |
| |
| case PAUTO: |
| n.Class |= PHEAP |
| |
| n.Addable = false |
| n.Ullman = 2 |
| n.Xoffset = 0 |
| |
| // create stack variable to hold pointer to heap |
| oldfn := Curfn |
| |
| Curfn = n.Name.Curfn |
| n.Name.Heapaddr = temp(Ptrto(n.Type)) |
| buf := fmt.Sprintf("&%v", n.Sym) |
| n.Name.Heapaddr.Sym = Lookup(buf) |
| n.Name.Heapaddr.Orig.Sym = n.Name.Heapaddr.Sym |
| n.Esc = EscHeap |
| if Debug['m'] != 0 { |
| fmt.Printf("%v: moved to heap: %v\n", n.Line(), n) |
| } |
| Curfn = oldfn |
| } |
| |
| case OIND, ODOTPTR: |
| break |
| |
| // ODOTPTR has already been introduced, |
| // so these are the non-pointer ODOT and OINDEX. |
| // In &x[0], if x is a slice, then x does not |
| // escape--the pointer inside x does, but that |
| // is always a heap pointer anyway. |
| case ODOT, OINDEX, OPAREN, OCONVNOP: |
| if !Isslice(n.Left.Type) { |
| addrescapes(n.Left) |
| } |
| } |
| } |
| |
| func clearlabels() { |
| for l := labellist; l != nil; l = l.Link { |
| l.Sym.Label = nil |
| } |
| |
| labellist = nil |
| lastlabel = nil |
| } |
| |
| func newlab(n *Node) *Label { |
| s := n.Left.Sym |
| lab := s.Label |
| if lab == nil { |
| lab = new(Label) |
| if lastlabel == nil { |
| labellist = lab |
| } else { |
| lastlabel.Link = lab |
| } |
| lastlabel = lab |
| lab.Sym = s |
| s.Label = lab |
| } |
| |
| if n.Op == OLABEL { |
| if lab.Def != nil { |
| Yyerror("label %v already defined at %v", s, lab.Def.Line()) |
| } else { |
| lab.Def = n |
| } |
| } else { |
| lab.Use = append(lab.Use, n) |
| } |
| |
| return lab |
| } |
| |
| func checkgoto(from *Node, to *Node) { |
| if from.Sym == to.Sym { |
| return |
| } |
| |
| nf := 0 |
| for fs := from.Sym; fs != nil; fs = fs.Link { |
| nf++ |
| } |
| nt := 0 |
| for fs := to.Sym; fs != nil; fs = fs.Link { |
| nt++ |
| } |
| fs := from.Sym |
| for ; nf > nt; nf-- { |
| fs = fs.Link |
| } |
| if fs != to.Sym { |
| lno := int(lineno) |
| setlineno(from) |
| |
| // decide what to complain about. |
| // prefer to complain about 'into block' over declarations, |
| // so scan backward to find most recent block or else dcl. |
| var block *Sym |
| |
| var dcl *Sym |
| ts := to.Sym |
| for ; nt > nf; nt-- { |
| if ts.Pkg == nil { |
| block = ts |
| } else { |
| dcl = ts |
| } |
| ts = ts.Link |
| } |
| |
| for ts != fs { |
| if ts.Pkg == nil { |
| block = ts |
| } else { |
| dcl = ts |
| } |
| ts = ts.Link |
| fs = fs.Link |
| } |
| |
| if block != nil { |
| Yyerror("goto %v jumps into block starting at %v", from.Left.Sym, Ctxt.Line(int(block.Lastlineno))) |
| } else { |
| Yyerror("goto %v jumps over declaration of %v at %v", from.Left.Sym, dcl, Ctxt.Line(int(dcl.Lastlineno))) |
| } |
| lineno = int32(lno) |
| } |
| } |
| |
| func stmtlabel(n *Node) *Label { |
| if n.Sym != nil { |
| lab := n.Sym.Label |
| if lab != nil { |
| if lab.Def != nil { |
| if lab.Def.Name.Defn == n { |
| return lab |
| } |
| } |
| } |
| } |
| return nil |
| } |
| |
| // compile statements |
| func Genlist(l *NodeList) { |
| for ; l != nil; l = l.Next { |
| gen(l.N) |
| } |
| } |
| |
| // generate code to start new proc running call n. |
| func cgen_proc(n *Node, proc int) { |
| switch n.Left.Op { |
| default: |
| Fatalf("cgen_proc: unknown call %v", Oconv(int(n.Left.Op), 0)) |
| |
| case OCALLMETH: |
| cgen_callmeth(n.Left, proc) |
| |
| case OCALLINTER: |
| cgen_callinter(n.Left, nil, proc) |
| |
| case OCALLFUNC: |
| cgen_call(n.Left, proc) |
| } |
| } |
| |
| // generate declaration. |
| // have to allocate heap copy |
| // for escaped variables. |
| func cgen_dcl(n *Node) { |
| if Debug['g'] != 0 { |
| Dump("\ncgen-dcl", n) |
| } |
| if n.Op != ONAME { |
| Dump("cgen_dcl", n) |
| Fatalf("cgen_dcl") |
| } |
| |
| if n.Class&PHEAP == 0 { |
| return |
| } |
| if compiling_runtime != 0 { |
| Yyerror("%v escapes to heap, not allowed in runtime.", n) |
| } |
| if prealloc[n] == nil { |
| prealloc[n] = callnew(n.Type) |
| } |
| Cgen_as(n.Name.Heapaddr, prealloc[n]) |
| } |
| |
| // generate discard of value |
| func cgen_discard(nr *Node) { |
| if nr == nil { |
| return |
| } |
| |
| switch nr.Op { |
| case ONAME: |
| if nr.Class&PHEAP == 0 && nr.Class != PEXTERN && nr.Class != PFUNC && nr.Class != PPARAMREF { |
| gused(nr) |
| } |
| |
| // unary |
| case OADD, |
| OAND, |
| ODIV, |
| OEQ, |
| OGE, |
| OGT, |
| OLE, |
| OLSH, |
| OLT, |
| OMOD, |
| OMUL, |
| ONE, |
| OOR, |
| ORSH, |
| OSUB, |
| OXOR: |
| cgen_discard(nr.Left) |
| |
| cgen_discard(nr.Right) |
| |
| // binary |
| case OCAP, |
| OCOM, |
| OLEN, |
| OMINUS, |
| ONOT, |
| OPLUS: |
| cgen_discard(nr.Left) |
| |
| case OIND: |
| Cgen_checknil(nr.Left) |
| |
| // special enough to just evaluate |
| default: |
| var tmp Node |
| Tempname(&tmp, nr.Type) |
| |
| Cgen_as(&tmp, nr) |
| gused(&tmp) |
| } |
| } |
| |
| // clearslim generates code to zero a slim node. |
| func Clearslim(n *Node) { |
| var z Node |
| z.Op = OLITERAL |
| z.Type = n.Type |
| z.Addable = true |
| |
| switch Simtype[n.Type.Etype] { |
| case TCOMPLEX64, TCOMPLEX128: |
| z.SetVal(Val{new(Mpcplx)}) |
| Mpmovecflt(&z.Val().U.(*Mpcplx).Real, 0.0) |
| Mpmovecflt(&z.Val().U.(*Mpcplx).Imag, 0.0) |
| |
| case TFLOAT32, TFLOAT64: |
| var zero Mpflt |
| Mpmovecflt(&zero, 0.0) |
| z.SetVal(Val{&zero}) |
| |
| case TPTR32, TPTR64, TCHAN, TMAP: |
| z.SetVal(Val{new(NilVal)}) |
| |
| case TBOOL: |
| z.SetVal(Val{false}) |
| |
| case TINT8, |
| TINT16, |
| TINT32, |
| TINT64, |
| TUINT8, |
| TUINT16, |
| TUINT32, |
| TUINT64: |
| z.SetVal(Val{new(Mpint)}) |
| Mpmovecfix(z.Val().U.(*Mpint), 0) |
| |
| default: |
| Fatalf("clearslim called on type %v", n.Type) |
| } |
| |
| ullmancalc(&z) |
| Cgen(&z, n) |
| } |
| |
| // generate: |
| // res = iface{typ, data} |
| // n->left is typ |
| // n->right is data |
| func Cgen_eface(n *Node, res *Node) { |
| // the right node of an eface may contain function calls that uses res as an argument, |
| // so it's important that it is done first |
| |
| tmp := temp(Types[Tptr]) |
| Cgen(n.Right, tmp) |
| |
| Gvardef(res) |
| |
| dst := *res |
| dst.Type = Types[Tptr] |
| dst.Xoffset += int64(Widthptr) |
| Cgen(tmp, &dst) |
| |
| dst.Xoffset -= int64(Widthptr) |
| Cgen(n.Left, &dst) |
| } |
| |
| // generate one of: |
| // res, resok = x.(T) |
| // res = x.(T) (when resok == nil) |
| // n.Left is x |
| // n.Type is T |
| func cgen_dottype(n *Node, res, resok *Node, wb bool) { |
| if Debug_typeassert > 0 { |
| Warn("type assertion inlined") |
| } |
| // iface := n.Left |
| // r1 := iword(iface) |
| // if n.Left is non-empty interface { |
| // r1 = *r1 |
| // } |
| // if r1 == T { |
| // res = idata(iface) |
| // resok = true |
| // } else { |
| // assert[EI]2T(x, T, nil) // (when resok == nil; does not return) |
| // resok = false // (when resok != nil) |
| // } |
| // |
| var iface Node |
| Igen(n.Left, &iface, res) |
| var r1, r2 Node |
| byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) |
| Regalloc(&r1, byteptr, nil) |
| iface.Type = byteptr |
| Cgen(&iface, &r1) |
| if !isnilinter(n.Left.Type) { |
| // Holding itab, want concrete type in second word. |
| p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) |
| r2 = r1 |
| r2.Op = OINDREG |
| r2.Xoffset = int64(Widthptr) |
| Cgen(&r2, &r1) |
| Patch(p, Pc) |
| } |
| Regalloc(&r2, byteptr, nil) |
| Cgen(typename(n.Type), &r2) |
| p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) |
| Regfree(&r2) // not needed for success path; reclaimed on one failure path |
| iface.Xoffset += int64(Widthptr) |
| Cgen(&iface, &r1) |
| Regfree(&iface) |
| |
| if resok == nil { |
| r1.Type = res.Type |
| cgen_wb(&r1, res, wb) |
| q := Gbranch(obj.AJMP, nil, 0) |
| Patch(p, Pc) |
| Regrealloc(&r2) // reclaim from above, for this failure path |
| fn := syslook("panicdottype", 0) |
| dowidth(fn.Type) |
| call := Nod(OCALLFUNC, fn, nil) |
| r1.Type = byteptr |
| r2.Type = byteptr |
| call.List = list(list(list1(&r1), &r2), typename(n.Left.Type)) |
| call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil) |
| gen(call) |
| Regfree(&r1) |
| Regfree(&r2) |
| Thearch.Gins(obj.AUNDEF, nil, nil) |
| Patch(q, Pc) |
| } else { |
| // This half is handling the res, resok = x.(T) case, |
| // which is called from gen, not cgen, and is consequently fussier |
| // about blank assignments. We have to avoid calling cgen for those. |
| r1.Type = res.Type |
| if !isblank(res) { |
| cgen_wb(&r1, res, wb) |
| } |
| Regfree(&r1) |
| if !isblank(resok) { |
| Cgen(Nodbool(true), resok) |
| } |
| q := Gbranch(obj.AJMP, nil, 0) |
| Patch(p, Pc) |
| if !isblank(res) { |
| n := nodnil() |
| n.Type = res.Type |
| Cgen(n, res) |
| } |
| if !isblank(resok) { |
| Cgen(Nodbool(false), resok) |
| } |
| Patch(q, Pc) |
| } |
| } |
| |
| // generate: |
| // res, resok = x.(T) |
| // n.Left is x |
| // n.Type is T |
| func Cgen_As2dottype(n, res, resok *Node) { |
| if Debug_typeassert > 0 { |
| Warn("type assertion inlined") |
| } |
| // iface := n.Left |
| // r1 := iword(iface) |
| // if n.Left is non-empty interface { |
| // r1 = *r1 |
| // } |
| // if r1 == T { |
| // res = idata(iface) |
| // resok = true |
| // } else { |
| // res = nil |
| // resok = false |
| // } |
| // |
| var iface Node |
| Igen(n.Left, &iface, nil) |
| var r1, r2 Node |
| byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) |
| Regalloc(&r1, byteptr, res) |
| iface.Type = byteptr |
| Cgen(&iface, &r1) |
| if !isnilinter(n.Left.Type) { |
| // Holding itab, want concrete type in second word. |
| p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) |
| r2 = r1 |
| r2.Op = OINDREG |
| r2.Xoffset = int64(Widthptr) |
| Cgen(&r2, &r1) |
| Patch(p, Pc) |
| } |
| Regalloc(&r2, byteptr, nil) |
| Cgen(typename(n.Type), &r2) |
| p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) |
| iface.Type = n.Type |
| iface.Xoffset += int64(Widthptr) |
| Cgen(&iface, &r1) |
| if iface.Op != 0 { |
| Regfree(&iface) |
| } |
| Cgen(&r1, res) |
| q := Gbranch(obj.AJMP, nil, 0) |
| Patch(p, Pc) |
| |
| fn := syslook("panicdottype", 0) |
| dowidth(fn.Type) |
| call := Nod(OCALLFUNC, fn, nil) |
| call.List = list(list(list1(&r1), &r2), typename(n.Left.Type)) |
| call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil) |
| gen(call) |
| Regfree(&r1) |
| Regfree(&r2) |
| Thearch.Gins(obj.AUNDEF, nil, nil) |
| Patch(q, Pc) |
| } |
| |
| // gather series of offsets |
| // >=0 is direct addressed field |
| // <0 is pointer to next field (+1) |
| func Dotoffset(n *Node, oary []int64, nn **Node) int { |
| var i int |
| |
| switch n.Op { |
| case ODOT: |
| if n.Xoffset == BADWIDTH { |
| Dump("bad width in dotoffset", n) |
| Fatalf("bad width in dotoffset") |
| } |
| |
| i = Dotoffset(n.Left, oary, nn) |
| if i > 0 { |
| if oary[i-1] >= 0 { |
| oary[i-1] += n.Xoffset |
| } else { |
| oary[i-1] -= n.Xoffset |
| } |
| break |
| } |
| |
| if i < 10 { |
| oary[i] = n.Xoffset |
| i++ |
| } |
| |
| case ODOTPTR: |
| if n.Xoffset == BADWIDTH { |
| Dump("bad width in dotoffset", n) |
| Fatalf("bad width in dotoffset") |
| } |
| |
| i = Dotoffset(n.Left, oary, nn) |
| if i < 10 { |
| oary[i] = -(n.Xoffset + 1) |
| i++ |
| } |
| |
| default: |
| *nn = n |
| return 0 |
| } |
| |
| if i >= 10 { |
| *nn = nil |
| } |
| return i |
| } |
| |
| // make a new off the books |
| func Tempname(nn *Node, t *Type) { |
| if Curfn == nil { |
| Fatalf("no curfn for tempname") |
| } |
| |
| if t == nil { |
| Yyerror("tempname called with nil type") |
| t = Types[TINT32] |
| } |
| |
| // give each tmp a different name so that there |
| // a chance to registerizer them |
| s := Lookupf("autotmp_%.4d", statuniqgen) |
| statuniqgen++ |
| n := Nod(ONAME, nil, nil) |
| n.Sym = s |
| s.Def = n |
| n.Type = t |
| n.Class = PAUTO |
| n.Addable = true |
| n.Ullman = 1 |
| n.Esc = EscNever |
| n.Name.Curfn = Curfn |
| Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) |
| |
| dowidth(t) |
| n.Xoffset = 0 |
| *nn = *n |
| } |
| |
| func temp(t *Type) *Node { |
| n := Nod(OXXX, nil, nil) |
| Tempname(n, t) |
| n.Sym.Def.Used = true |
| return n.Orig |
| } |
| |
| func gen(n *Node) { |
| //dump("gen", n); |
| |
| lno := setlineno(n) |
| |
| wasregalloc := Anyregalloc() |
| |
| if n == nil { |
| goto ret |
| } |
| |
| if n.Ninit != nil { |
| Genlist(n.Ninit) |
| } |
| |
| setlineno(n) |
| |
| switch n.Op { |
| default: |
| Fatalf("gen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) |
| |
| case OCASE, |
| OFALL, |
| OXCASE, |
| OXFALL, |
| ODCLCONST, |
| ODCLFUNC, |
| ODCLTYPE: |
| break |
| |
| case OEMPTY: |
| break |
| |
| case OBLOCK: |
| Genlist(n.List) |
| |
| case OLABEL: |
| if isblanksym(n.Left.Sym) { |
| break |
| } |
| |
| lab := newlab(n) |
| |
| // if there are pending gotos, resolve them all to the current pc. |
| var p2 *obj.Prog |
| for p1 := lab.Gotopc; p1 != nil; p1 = p2 { |
| p2 = unpatch(p1) |
| Patch(p1, Pc) |
| } |
| |
| lab.Gotopc = nil |
| if lab.Labelpc == nil { |
| lab.Labelpc = Pc |
| } |
| |
| if n.Name.Defn != nil { |
| switch n.Name.Defn.Op { |
| // so stmtlabel can find the label |
| case OFOR, OSWITCH, OSELECT: |
| n.Name.Defn.Sym = lab.Sym |
| } |
| } |
| |
| // if label is defined, emit jump to it. |
| // otherwise save list of pending gotos in lab->gotopc. |
| // the list is linked through the normal jump target field |
| // to avoid a second list. (the jumps are actually still |
| // valid code, since they're just going to another goto |
| // to the same label. we'll unwind it when we learn the pc |
| // of the label in the OLABEL case above.) |
| case OGOTO: |
| lab := newlab(n) |
| |
| if lab.Labelpc != nil { |
| gjmp(lab.Labelpc) |
| } else { |
| lab.Gotopc = gjmp(lab.Gotopc) |
| } |
| |
| case OBREAK: |
| if n.Left != nil { |
| lab := n.Left.Sym.Label |
| if lab == nil { |
| Yyerror("break label not defined: %v", n.Left.Sym) |
| break |
| } |
| |
| lab.Used = true |
| if lab.Breakpc == nil { |
| Yyerror("invalid break label %v", n.Left.Sym) |
| break |
| } |
| |
| gjmp(lab.Breakpc) |
| break |
| } |
| |
| if breakpc == nil { |
| Yyerror("break is not in a loop") |
| break |
| } |
| |
| gjmp(breakpc) |
| |
| case OCONTINUE: |
| if n.Left != nil { |
| lab := n.Left.Sym.Label |
| if lab == nil { |
| Yyerror("continue label not defined: %v", n.Left.Sym) |
| break |
| } |
| |
| lab.Used = true |
| if lab.Continpc == nil { |
| Yyerror("invalid continue label %v", n.Left.Sym) |
| break |
| } |
| |
| gjmp(lab.Continpc) |
| break |
| } |
| |
| if continpc == nil { |
| Yyerror("continue is not in a loop") |
| break |
| } |
| |
| gjmp(continpc) |
| |
| case OFOR: |
| sbreak := breakpc |
| p1 := gjmp(nil) // goto test |
| breakpc = gjmp(nil) // break: goto done |
| scontin := continpc |
| continpc = Pc |
| |
| // define break and continue labels |
| lab := stmtlabel(n) |
| if lab != nil { |
| lab.Breakpc = breakpc |
| lab.Continpc = continpc |
| } |
| |
| gen(n.Right) // contin: incr |
| Patch(p1, Pc) // test: |
| Bgen(n.Left, false, -1, breakpc) // if(!test) goto break |
| Genlist(n.Nbody) // body |
| gjmp(continpc) |
| Patch(breakpc, Pc) // done: |
| continpc = scontin |
| breakpc = sbreak |
| if lab != nil { |
| lab.Breakpc = nil |
| lab.Continpc = nil |
| } |
| |
| case OIF: |
| p1 := gjmp(nil) // goto test |
| p2 := gjmp(nil) // p2: goto else |
| Patch(p1, Pc) // test: |
| Bgen(n.Left, false, int(-n.Likely), p2) // if(!test) goto p2 |
| Genlist(n.Nbody) // then |
| p3 := gjmp(nil) // goto done |
| Patch(p2, Pc) // else: |
| Genlist(n.Rlist) // else |
| Patch(p3, Pc) // done: |
| |
| case OSWITCH: |
| sbreak := breakpc |
| p1 := gjmp(nil) // goto test |
| breakpc = gjmp(nil) // break: goto done |
| |
| // define break label |
| lab := stmtlabel(n) |
| if lab != nil { |
| lab.Breakpc = breakpc |
| } |
| |
| Patch(p1, Pc) // test: |
| Genlist(n.Nbody) // switch(test) body |
| Patch(breakpc, Pc) // done: |
| breakpc = sbreak |
| if lab != nil { |
| lab.Breakpc = nil |
| } |
| |
| case OSELECT: |
| sbreak := breakpc |
| p1 := gjmp(nil) // goto test |
| breakpc = gjmp(nil) // break: goto done |
| |
| // define break label |
| lab := stmtlabel(n) |
| if lab != nil { |
| lab.Breakpc = breakpc |
| } |
| |
| Patch(p1, Pc) // test: |
| Genlist(n.Nbody) // select() body |
| Patch(breakpc, Pc) // done: |
| breakpc = sbreak |
| if lab != nil { |
| lab.Breakpc = nil |
| } |
| |
| case ODCL: |
| cgen_dcl(n.Left) |
| |
| case OAS: |
| if gen_as_init(n) { |
| break |
| } |
| Cgen_as(n.Left, n.Right) |
| |
| case OASWB: |
| Cgen_as_wb(n.Left, n.Right, true) |
| |
| case OAS2DOTTYPE: |
| cgen_dottype(n.Rlist.N, n.List.N, n.List.Next.N, false) |
| |
| case OCALLMETH: |
| cgen_callmeth(n, 0) |
| |
| case OCALLINTER: |
| cgen_callinter(n, nil, 0) |
| |
| case OCALLFUNC: |
| cgen_call(n, 0) |
| |
| case OPROC: |
| cgen_proc(n, 1) |
| |
| case ODEFER: |
| cgen_proc(n, 2) |
| |
| case ORETURN, ORETJMP: |
| cgen_ret(n) |
| |
| // Function calls turned into compiler intrinsics. |
| // At top level, can just ignore the call and make sure to preserve side effects in the argument, if any. |
| case OGETG: |
| // nothing |
| case OSQRT: |
| cgen_discard(n.Left) |
| |
| case OCHECKNIL: |
| Cgen_checknil(n.Left) |
| |
| case OVARKILL: |
| gvarkill(n.Left) |
| } |
| |
| ret: |
| if Anyregalloc() != wasregalloc { |
| Dump("node", n) |
| Fatalf("registers left allocated") |
| } |
| |
| lineno = lno |
| } |
| |
| func Cgen_as(nl, nr *Node) { |
| Cgen_as_wb(nl, nr, false) |
| } |
| |
| func Cgen_as_wb(nl, nr *Node, wb bool) { |
| if Debug['g'] != 0 { |
| op := "cgen_as" |
| if wb { |
| op = "cgen_as_wb" |
| } |
| Dump(op, nl) |
| Dump(op+" = ", nr) |
| } |
| |
| for nr != nil && nr.Op == OCONVNOP { |
| nr = nr.Left |
| } |
| |
| if nl == nil || isblank(nl) { |
| cgen_discard(nr) |
| return |
| } |
| |
| if nr == nil || iszero(nr) { |
| // heaps should already be clear |
| if nr == nil && (nl.Class&PHEAP != 0) { |
| return |
| } |
| |
| tl := nl.Type |
| if tl == nil { |
| return |
| } |
| if Isfat(tl) { |
| if nl.Op == ONAME { |
| Gvardef(nl) |
| } |
| Thearch.Clearfat(nl) |
| return |
| } |
| |
| Clearslim(nl) |
| return |
| } |
| |
| tl := nl.Type |
| if tl == nil { |
| return |
| } |
| |
| cgen_wb(nr, nl, wb) |
| } |
| |
| func cgen_callmeth(n *Node, proc int) { |
| // generate a rewrite in n2 for the method call |
| // (p.f)(...) goes to (f)(p,...) |
| |
| l := n.Left |
| |
| if l.Op != ODOTMETH { |
| Fatalf("cgen_callmeth: not dotmethod: %v", l) |
| } |
| |
| n2 := *n |
| n2.Op = OCALLFUNC |
| n2.Left = l.Right |
| n2.Left.Type = l.Type |
| |
| if n2.Left.Op == ONAME { |
| n2.Left.Class = PFUNC |
| } |
| cgen_call(&n2, proc) |
| } |
| |
| // CgenTemp creates a temporary node, assigns n to it, and returns it. |
| func CgenTemp(n *Node) *Node { |
| var tmp Node |
| Tempname(&tmp, n.Type) |
| Cgen(n, &tmp) |
| return &tmp |
| } |
| |
| func checklabels() { |
| for lab := labellist; lab != nil; lab = lab.Link { |
| if lab.Def == nil { |
| for _, n := range lab.Use { |
| yyerrorl(int(n.Lineno), "label %v not defined", lab.Sym) |
| } |
| continue |
| } |
| |
| if lab.Use == nil && !lab.Used { |
| yyerrorl(int(lab.Def.Lineno), "label %v defined and not used", lab.Sym) |
| continue |
| } |
| |
| if lab.Gotopc != nil { |
| Fatalf("label %v never resolved", lab.Sym) |
| } |
| for _, n := range lab.Use { |
| checkgoto(n, lab.Def) |
| } |
| } |
| } |
| |
| // Componentgen copies a composite value by moving its individual components. |
| // Slices, strings and interfaces are supported. Small structs or arrays with |
| // elements of basic type are also supported. |
| // nr is nil when assigning a zero value. |
| func Componentgen(nr, nl *Node) bool { |
| return componentgen_wb(nr, nl, false) |
| } |
| |
| // componentgen_wb is like componentgen but if wb==true emits write barriers for pointer updates. |
| func componentgen_wb(nr, nl *Node, wb bool) bool { |
| // Don't generate any code for complete copy of a variable into itself. |
| // It's useless, and the VARDEF will incorrectly mark the old value as dead. |
| // (This check assumes that the arguments passed to componentgen did not |
| // themselves come from Igen, or else we could have Op==ONAME but |
| // with a Type and Xoffset describing an individual field, not the entire |
| // variable.) |
| if nl.Op == ONAME && nl == nr { |
| return true |
| } |
| |
| // Count number of moves required to move components. |
| // If using write barrier, can only emit one pointer. |
| // TODO(rsc): Allow more pointers, for reflect.Value. |
| const maxMoves = 8 |
| n := 0 |
| numPtr := 0 |
| visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { |
| n++ |
| if Simtype[t.Etype] == Tptr && t != itable { |
| numPtr++ |
| } |
| return n <= maxMoves && (!wb || numPtr <= 1) |
| }) |
| if n > maxMoves || wb && numPtr > 1 { |
| return false |
| } |
| |
| // Must call emitVardef after evaluating rhs but before writing to lhs. |
| emitVardef := func() { |
| // Emit vardef if needed. |
| if nl.Op == ONAME { |
| switch nl.Type.Etype { |
| case TARRAY, TSTRING, TINTER, TSTRUCT: |
| Gvardef(nl) |
| } |
| } |
| } |
| |
| isConstString := Isconst(nr, CTSTR) |
| |
| if !cadable(nl) && nr != nil && !cadable(nr) && !isConstString { |
| return false |
| } |
| |
| var nodl Node |
| if cadable(nl) { |
| nodl = *nl |
| } else { |
| if nr != nil && !cadable(nr) && !isConstString { |
| return false |
| } |
| if nr == nil || isConstString || nl.Ullman >= nr.Ullman { |
| Igen(nl, &nodl, nil) |
| defer Regfree(&nodl) |
| } |
| } |
| lbase := nodl.Xoffset |
| |
| // Special case: zeroing. |
| var nodr Node |
| if nr == nil { |
| // When zeroing, prepare a register containing zero. |
| // TODO(rsc): Check that this is actually generating the best code. |
| if Thearch.REGZERO != 0 { |
| // cpu has a dedicated zero register |
| Nodreg(&nodr, Types[TUINT], Thearch.REGZERO) |
| } else { |
| // no dedicated zero register |
| var zero Node |
| Nodconst(&zero, nl.Type, 0) |
| Regalloc(&nodr, Types[TUINT], nil) |
| Thearch.Gmove(&zero, &nodr) |
| defer Regfree(&nodr) |
| } |
| |
| emitVardef() |
| visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { |
| nodl.Type = t |
| nodl.Xoffset = lbase + offset |
| nodr.Type = t |
| if Isfloat[t.Etype] { |
| // TODO(rsc): Cache zero register like we do for integers? |
| Clearslim(&nodl) |
| } else { |
| Thearch.Gmove(&nodr, &nodl) |
| } |
| return true |
| }) |
| return true |
| } |
| |
| // Special case: assignment of string constant. |
| if isConstString { |
| emitVardef() |
| |
| // base |
| nodl.Type = Ptrto(Types[TUINT8]) |
| Regalloc(&nodr, Types[Tptr], nil) |
| p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr) |
| Datastring(nr.Val().U.(string), &p.From) |
| p.From.Type = obj.TYPE_ADDR |
| Thearch.Gmove(&nodr, &nodl) |
| Regfree(&nodr) |
| |
| // length |
| nodl.Type = Types[Simtype[TUINT]] |
| nodl.Xoffset += int64(Array_nel) - int64(Array_array) |
| Nodconst(&nodr, nodl.Type, int64(len(nr.Val().U.(string)))) |
| Thearch.Gmove(&nodr, &nodl) |
| return true |
| } |
| |
| // General case: copy nl = nr. |
| nodr = *nr |
| if !cadable(nr) { |
| if nr.Ullman >= UINF && nodl.Op == OINDREG { |
| Fatalf("miscompile") |
| } |
| Igen(nr, &nodr, nil) |
| defer Regfree(&nodr) |
| } |
| rbase := nodr.Xoffset |
| |
| if nodl.Op == 0 { |
| Igen(nl, &nodl, nil) |
| defer Regfree(&nodl) |
| lbase = nodl.Xoffset |
| } |
| |
| emitVardef() |
| var ( |
| ptrType *Type |
| ptrOffset int64 |
| ) |
| visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { |
| if wb && Simtype[t.Etype] == Tptr && t != itable { |
| if ptrType != nil { |
| Fatalf("componentgen_wb %v", Tconv(nl.Type, 0)) |
| } |
| ptrType = t |
| ptrOffset = offset |
| return true |
| } |
| nodl.Type = t |
| nodl.Xoffset = lbase + offset |
| nodr.Type = t |
| nodr.Xoffset = rbase + offset |
| Thearch.Gmove(&nodr, &nodl) |
| return true |
| }) |
| if ptrType != nil { |
| nodl.Type = ptrType |
| nodl.Xoffset = lbase + ptrOffset |
| nodr.Type = ptrType |
| nodr.Xoffset = rbase + ptrOffset |
| cgen_wbptr(&nodr, &nodl) |
| } |
| return true |
| } |
| |
| // visitComponents walks the individual components of the type t, |
| // walking into array elements, struct fields, the real and imaginary |
| // parts of complex numbers, and on 32-bit systems the high and |
| // low halves of 64-bit integers. |
| // It calls f for each such component, passing the component (aka element) |
| // type and memory offset, assuming t starts at startOffset. |
| // If f ever returns false, visitComponents returns false without any more |
| // calls to f. Otherwise visitComponents returns true. |
| func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset int64) bool) bool { |
| switch t.Etype { |
| case TINT64: |
| if Widthreg == 8 { |
| break |
| } |
| // NOTE: Assuming little endian (signed top half at offset 4). |
| // We don't have any 32-bit big-endian systems. |
| if Thearch.Thechar != '5' && Thearch.Thechar != '8' { |
| Fatalf("unknown 32-bit architecture") |
| } |
| return f(Types[TUINT32], startOffset) && |
| f(Types[TINT32], startOffset+4) |
| |
| case TUINT64: |
| if Widthreg == 8 { |
| break |
| } |
| return f(Types[TUINT32], startOffset) && |
| f(Types[TUINT32], startOffset+4) |
| |
| case TCOMPLEX64: |
| return f(Types[TFLOAT32], startOffset) && |
| f(Types[TFLOAT32], startOffset+4) |
| |
| case TCOMPLEX128: |
| return f(Types[TFLOAT64], startOffset) && |
| f(Types[TFLOAT64], startOffset+8) |
| |
| case TINTER: |
| return f(itable, startOffset) && |
| f(Ptrto(Types[TUINT8]), startOffset+int64(Widthptr)) |
| |
| case TSTRING: |
| return f(Ptrto(Types[TUINT8]), startOffset) && |
| f(Types[Simtype[TUINT]], startOffset+int64(Widthptr)) |
| |
| case TARRAY: |
| if Isslice(t) { |
| return f(Ptrto(t.Type), startOffset+int64(Array_array)) && |
| f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) && |
| f(Types[Simtype[TUINT]], startOffset+int64(Array_cap)) |
| } |
| |
| // Short-circuit [1e6]struct{}. |
| if t.Type.Width == 0 { |
| return true |
| } |
| |
| for i := int64(0); i < t.Bound; i++ { |
| if !visitComponents(t.Type, startOffset+i*t.Type.Width, f) { |
| return false |
| } |
| } |
| return true |
| |
| case TSTRUCT: |
| if t.Type != nil && t.Type.Width != 0 { |
| // NOTE(rsc): If this happens, the right thing to do is to say |
| // startOffset -= t.Type.Width |
| // but I want to see if it does. |
| // The old version of componentgen handled this, |
| // in code introduced in CL 6932045 to fix issue #4518. |
| // But the test case in issue 4518 does not trigger this anymore, |
| // so maybe this complication is no longer needed. |
| Fatalf("struct not at offset 0") |
| } |
| |
| for field := t.Type; field != nil; field = field.Down { |
| if field.Etype != TFIELD { |
| Fatalf("bad struct") |
| } |
| if !visitComponents(field.Type, startOffset+field.Width, f) { |
| return false |
| } |
| } |
| return true |
| } |
| return f(t, startOffset) |
| } |
| |
| func cadable(n *Node) bool { |
| // Note: Not sure why you can have n.Op == ONAME without n.Addable, but you can. |
| return n.Addable && n.Op == ONAME |
| } |