// 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 } /* * the address of n has been taken and might be used after * the current function returns. mark any local vars * as needing to move to the heap. */ 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.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.Stackparam = Nod(OPARAM, n, nil) n.Stackparam.Type = n.Type n.Stackparam.Addable = 1 if n.Xoffset == BADWIDTH { Fatal("addrescapes before param assignment") } n.Stackparam.Xoffset = n.Xoffset fallthrough // fallthrough case PAUTO: n.Class |= PHEAP n.Addable = 0 n.Ullman = 2 n.Xoffset = 0 // create stack variable to hold pointer to heap oldfn := Curfn Curfn = n.Curfn n.Heapaddr = temp(Ptrto(n.Type)) buf := fmt.Sprintf("&%v", Sconv(n.Sym, 0)) n.Heapaddr.Sym = Lookup(buf) n.Heapaddr.Orig.Sym = n.Heapaddr.Sym n.Esc = EscHeap if Debug['m'] != 0 { fmt.Printf("%v: moved to heap: %v\n", n.Line(), Nconv(n, 0)) } 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: 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", Sconv(s, 0), lab.Def.Line()) } else { lab.Def = n } } else { lab.Use = list(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. block := (*Sym)(nil) dcl := (*Sym)(nil) 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", Sconv(from.Left.Sym, 0), Ctxt.Line(int(block.Lastlineno))) } else { Yyerror("goto %v jumps over declaration of %v at %v", Sconv(from.Left.Sym, 0), Sconv(dcl, 0), 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.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: Fatal("cgen_proc: unknown call %v", Oconv(int(n.Left.Op), 0)) case OCALLMETH: Cgen_callmeth(n.Left, proc) case OCALLINTER: Thearch.Cgen_callinter(n.Left, nil, proc) case OCALLFUNC: Thearch.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) Fatal("cgen_dcl") } if n.Class&PHEAP == 0 { return } if compiling_runtime != 0 { Yyerror("%v escapes to heap, not allowed in runtime.", Nconv(n, 0)) } if n.Alloc == nil { n.Alloc = callnew(n.Type) } Cgen_as(n.Heapaddr, n.Alloc) } /* * 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) { z := Node{} z.Op = OLITERAL z.Type = n.Type z.Addable = 1 switch Simtype[n.Type.Etype] { case TCOMPLEX64, TCOMPLEX128: z.Val.U.Cval = new(Mpcplx) Mpmovecflt(&z.Val.U.Cval.Real, 0.0) Mpmovecflt(&z.Val.U.Cval.Imag, 0.0) case TFLOAT32, TFLOAT64: var zero Mpflt Mpmovecflt(&zero, 0.0) z.Val.Ctype = CTFLT z.Val.U.Fval = &zero case TPTR32, TPTR64, TCHAN, TMAP: z.Val.Ctype = CTNIL case TBOOL: z.Val.Ctype = CTBOOL case TINT8, TINT16, TINT32, TINT64, TUINT8, TUINT16, TUINT32, TUINT64: z.Val.Ctype = CTINT z.Val.U.Xval = new(Mpint) Mpmovecfix(z.Val.U.Xval, 0) default: Fatal("clearslim called on type %v", Tconv(n.Type, 0)) } ullmancalc(&z) Thearch.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]) Thearch.Cgen(n.Right, tmp) Gvardef(res) dst := *res dst.Type = Types[Tptr] dst.Xoffset += int64(Widthptr) Thearch.Cgen(tmp, &dst) dst.Xoffset -= int64(Widthptr) Thearch.Cgen(n.Left, &dst) } /* * generate: * res = s[lo, hi]; * n->left is s * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)]) * caller (cgen) guarantees res is an addable ONAME. * * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR. */ func Cgen_slice(n *Node, res *Node) { cap := n.List.N len := n.List.Next.N offs := (*Node)(nil) if n.List.Next.Next != nil { offs = n.List.Next.Next.N } // evaluate base pointer first, because it is the only // possibly complex expression. once that is evaluated // and stored, updating the len and cap can be done // without making any calls, so without doing anything that // might cause preemption or garbage collection. // this makes the whole slice update atomic as far as the // garbage collector can see. base := temp(Types[TUINTPTR]) tmplen := temp(Types[TINT]) var tmpcap *Node if n.Op != OSLICESTR { tmpcap = temp(Types[TINT]) } else { tmpcap = tmplen } var src Node if isnil(n.Left) { Tempname(&src, n.Left.Type) Thearch.Cgen(n.Left, &src) } else { src = *n.Left } if n.Op == OSLICE || n.Op == OSLICE3 || n.Op == OSLICESTR { src.Xoffset += int64(Array_array) } if n.Op == OSLICEARR || n.Op == OSLICE3ARR { if Isptr[n.Left.Type.Etype] == 0 { Fatal("slicearr is supposed to work on pointer: %v\n", Nconv(n, obj.FmtSign)) } Thearch.Cgen(&src, base) Cgen_checknil(base) } else { src.Type = Types[Tptr] Thearch.Cgen(&src, base) } // committed to the update Gvardef(res) // compute len and cap. // len = n-i, cap = m-i, and offs = i*width. // computing offs last lets the multiply overwrite i. Thearch.Cgen((*Node)(len), tmplen) if n.Op != OSLICESTR { Thearch.Cgen(cap, tmpcap) } // if new cap != 0 { base += add } // This avoids advancing base past the end of the underlying array/string, // so that it cannot point at the next object in memory. // If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero. // In essence we are replacing x[i:j:k] where i == j == k // or x[i:j] where i == j == cap(x) with x[0:0:0]. if offs != nil { p1 := gjmp(nil) p2 := gjmp(nil) Patch(p1, Pc) var con Node Nodconst(&con, tmpcap.Type, 0) cmp := Nod(OEQ, tmpcap, &con) typecheck(&cmp, Erv) Thearch.Bgen(cmp, true, -1, p2) add := Nod(OADD, base, offs) typecheck(&add, Erv) Thearch.Cgen(add, base) Patch(p2, Pc) } // dst.array = src.array [ + lo *width ] dst := *res dst.Xoffset += int64(Array_array) dst.Type = Types[Tptr] Thearch.Cgen(base, &dst) // dst.len = hi [ - lo ] dst = *res dst.Xoffset += int64(Array_nel) dst.Type = Types[Simtype[TUINT]] Thearch.Cgen(tmplen, &dst) if n.Op != OSLICESTR { // dst.cap = cap [ - lo ] dst = *res dst.Xoffset += int64(Array_cap) dst.Type = Types[Simtype[TUINT]] Thearch.Cgen(tmpcap, &dst) } } /* * 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) Fatal("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) Fatal("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 { Fatal("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 namebuf = fmt.Sprintf("autotmp_%.4d", statuniqgen) statuniqgen++ s := Lookup(namebuf) n := Nod(ONAME, nil, nil) n.Sym = s s.Def = n n.Type = t n.Class = PAUTO n.Addable = 1 n.Ullman = 1 n.Esc = EscNever n.Curfn = Curfn Curfn.Dcl = list(Curfn.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 = 1 return n.Orig } func gen(n *Node) { //dump("gen", n); lno := setlineno(n) wasregalloc := Thearch.Anyregalloc() if n == nil { goto ret } if n.Ninit != nil { Genlist(n.Ninit) } setlineno(n) switch n.Op { default: Fatal("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.Defn != nil { switch n.Defn.Op { // so stmtlabel can find the label case OFOR, OSWITCH, OSELECT: n.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", Sconv(n.Left.Sym, 0)) break } lab.Used = 1 if lab.Breakpc == nil { Yyerror("invalid break label %v", Sconv(n.Left.Sym, 0)) 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", Sconv(n.Left.Sym, 0)) break } lab.Used = 1 if lab.Continpc == nil { Yyerror("invalid continue label %v", Sconv(n.Left.Sym, 0)) 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.Nincr) // contin: incr Patch(p1, Pc) // test: Thearch.Bgen(n.Ntest, 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: Thearch.Bgen(n.Ntest, false, int(-n.Likely), p2) // if(!test) goto p2 Genlist(n.Nbody) // then p3 := gjmp(nil) // goto done Patch(p2, Pc) // else: Genlist(n.Nelse) // 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 OCALLMETH: Cgen_callmeth(n, 0) case OCALLINTER: Thearch.Cgen_callinter(n, nil, 0) case OCALLFUNC: Thearch.Cgen_call(n, 0) case OPROC: cgen_proc(n, 1) case ODEFER: cgen_proc(n, 2) case ORETURN, ORETJMP: Thearch.Cgen_ret(n) case OCHECKNIL: Cgen_checknil(n.Left) case OVARKILL: gvarkill(n.Left) } ret: if Thearch.Anyregalloc() != wasregalloc { Dump("node", n) Fatal("registers left allocated") } lineno = lno } func Cgen_as(nl *Node, nr *Node) { if Debug['g'] != 0 { Dump("cgen_as", nl) Dump("cgen_as = ", 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 } Thearch.Cgen(nr, nl) } 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 { Fatal("cgen_callmeth: not dotmethod: %v") } n2 := *n n2.Op = OCALLFUNC n2.Left = l.Right n2.Left.Type = l.Type if n2.Left.Op == ONAME { n2.Left.Class = PFUNC } Thearch.Cgen_call(&n2, proc) } func checklabels() { var l *NodeList for lab := labellist; lab != nil; lab = lab.Link { if lab.Def == nil { for l = lab.Use; l != nil; l = l.Next { yyerrorl(int(l.N.Lineno), "label %v not defined", Sconv(lab.Sym, 0)) } continue } if lab.Use == nil && lab.Used == 0 { yyerrorl(int(lab.Def.Lineno), "label %v defined and not used", Sconv(lab.Sym, 0)) continue } if lab.Gotopc != nil { Fatal("label %v never resolved", Sconv(lab.Sym, 0)) } for l = lab.Use; l != nil; l = l.Next { checkgoto(l.N, lab.Def) } } }