// Copyright 2011 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" "crypto/md5" "fmt" "strings" ) // "Portable" code generation. var makefuncdatasym_nsym int32 func makefuncdatasym(namefmt string, funcdatakind int64) *Sym { var nod Node sym := Lookupf(namefmt, makefuncdatasym_nsym) makefuncdatasym_nsym++ pnod := newname(sym) pnod.Class = PEXTERN Nodconst(&nod, Types[TINT32], funcdatakind) Thearch.Gins(obj.AFUNCDATA, &nod, pnod) return sym } // gvardef inserts a VARDEF for n into the instruction stream. // VARDEF is an annotation for the liveness analysis, marking a place // where a complete initialization (definition) of a variable begins. // Since the liveness analysis can see initialization of single-word // variables quite easy, gvardef is usually only called for multi-word // or 'fat' variables, those satisfying isfat(n->type). // However, gvardef is also called when a non-fat variable is initialized // via a block move; the only time this happens is when you have // return f() // for a function with multiple return values exactly matching the return // types of the current function. // // A 'VARDEF x' annotation in the instruction stream tells the liveness // analysis to behave as though the variable x is being initialized at that // point in the instruction stream. The VARDEF must appear before the // actual (multi-instruction) initialization, and it must also appear after // any uses of the previous value, if any. For example, if compiling: // // x = x[1:] // // it is important to generate code like: // // base, len, cap = pieces of x[1:] // VARDEF x // x = {base, len, cap} // // If instead the generated code looked like: // // VARDEF x // base, len, cap = pieces of x[1:] // x = {base, len, cap} // // then the liveness analysis would decide the previous value of x was // unnecessary even though it is about to be used by the x[1:] computation. // Similarly, if the generated code looked like: // // base, len, cap = pieces of x[1:] // x = {base, len, cap} // VARDEF x // // then the liveness analysis will not preserve the new value of x, because // the VARDEF appears to have "overwritten" it. // // VARDEF is a bit of a kludge to work around the fact that the instruction // stream is working on single-word values but the liveness analysis // wants to work on individual variables, which might be multi-word // aggregates. It might make sense at some point to look into letting // the liveness analysis work on single-word values as well, although // there are complications around interface values, slices, and strings, // all of which cannot be treated as individual words. // // VARKILL is the opposite of VARDEF: it marks a value as no longer needed, // even if its address has been taken. That is, a VARKILL annotation asserts // that its argument is certainly dead, for use when the liveness analysis // would not otherwise be able to deduce that fact. func gvardefx(n *Node, as int) { if n == nil { Fatal("gvardef nil") } if n.Op != ONAME { Yyerror("gvardef %v; %v", Oconv(int(n.Op), obj.FmtSharp), n) return } switch n.Class { case PAUTO, PPARAM, PPARAMOUT: Thearch.Gins(as, nil, n) } } func Gvardef(n *Node) { gvardefx(n, obj.AVARDEF) } func gvarkill(n *Node) { gvardefx(n, obj.AVARKILL) } func removevardef(firstp *obj.Prog) { for p := firstp; p != nil; p = p.Link { for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) { p.Link = p.Link.Link } if p.To.Type == obj.TYPE_BRANCH { for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL) { p.To.Val = p.To.Val.(*obj.Prog).Link } } } } func gcsymdup(s *Sym) { ls := Linksym(s) if len(ls.R) > 0 { Fatal("cannot rosymdup %s with relocations", ls.Name) } ls.Name = fmt.Sprintf("gclocals·%x", md5.Sum(ls.P)) ls.Dupok = 1 } func emitptrargsmap() { sym := Lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Func.Nname.Sym.Name)) nptr := int(Curfn.Type.Argwid / int64(Widthptr)) bv := bvalloc(int32(nptr) * 2) nbitmap := 1 if Curfn.Type.Outtuple > 0 { nbitmap = 2 } off := duint32(sym, 0, uint32(nbitmap)) off = duint32(sym, off, uint32(bv.n)) var xoffset int64 if Curfn.Type.Thistuple > 0 { xoffset = 0 onebitwalktype1(getthisx(Curfn.Type), &xoffset, bv) } if Curfn.Type.Intuple > 0 { xoffset = 0 onebitwalktype1(getinargx(Curfn.Type), &xoffset, bv) } for j := 0; int32(j) < bv.n; j += 32 { off = duint32(sym, off, bv.b[j/32]) } if Curfn.Type.Outtuple > 0 { xoffset = 0 onebitwalktype1(getoutargx(Curfn.Type), &xoffset, bv) for j := 0; int32(j) < bv.n; j += 32 { off = duint32(sym, off, bv.b[j/32]) } } ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL) } // Sort the list of stack variables. Autos after anything else, // within autos, unused after used, within used, things with // pointers first, zeroed things first, and then decreasing size. // Because autos are laid out in decreasing addresses // on the stack, pointers first, zeroed things first and decreasing size // really means, in memory, things with pointers needing zeroing at // the top of the stack and increasing in size. // Non-autos sort on offset. func cmpstackvar(a *Node, b *Node) int { if a.Class != b.Class { if a.Class == PAUTO { return +1 } return -1 } if a.Class != PAUTO { if a.Xoffset < b.Xoffset { return -1 } if a.Xoffset > b.Xoffset { return +1 } return 0 } if a.Used != b.Used { return obj.Bool2int(b.Used) - obj.Bool2int(a.Used) } ap := obj.Bool2int(haspointers(a.Type)) bp := obj.Bool2int(haspointers(b.Type)) if ap != bp { return bp - ap } ap = obj.Bool2int(a.Name.Needzero) bp = obj.Bool2int(b.Name.Needzero) if ap != bp { return bp - ap } if a.Type.Width < b.Type.Width { return +1 } if a.Type.Width > b.Type.Width { return -1 } return stringsCompare(a.Sym.Name, b.Sym.Name) } // stkdelta records the stack offset delta for a node // during the compaction of the stack frame to remove // unused stack slots. var stkdelta = map[*Node]int64{} // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. func allocauto(ptxt *obj.Prog) { Stksize = 0 stkptrsize = 0 if Curfn.Func.Dcl == nil { return } // Mark the PAUTO's unused. for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { if ll.N.Class == PAUTO { ll.N.Used = false } } markautoused(ptxt) listsort(&Curfn.Func.Dcl, cmpstackvar) // Unused autos are at the end, chop 'em off. ll := Curfn.Func.Dcl n := ll.N if n.Class == PAUTO && n.Op == ONAME && !n.Used { // No locals used at all Curfn.Func.Dcl = nil fixautoused(ptxt) return } for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next { n = ll.Next.N if n.Class == PAUTO && n.Op == ONAME && !n.Used { ll.Next = nil Curfn.Func.Dcl.End = ll break } } // Reassign stack offsets of the locals that are still there. var w int64 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { n = ll.N if n.Class != PAUTO || n.Op != ONAME { continue } dowidth(n.Type) w = n.Type.Width if w >= Thearch.MAXWIDTH || w < 0 { Fatal("bad width") } Stksize += w Stksize = Rnd(Stksize, int64(n.Type.Align)) if haspointers(n.Type) { stkptrsize = Stksize } if Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { Stksize = Rnd(Stksize, int64(Widthptr)) } if Stksize >= 1<<31 { setlineno(Curfn) Yyerror("stack frame too large (>2GB)") } stkdelta[n] = -Stksize - n.Xoffset } Stksize = Rnd(Stksize, int64(Widthreg)) stkptrsize = Rnd(stkptrsize, int64(Widthreg)) fixautoused(ptxt) // The debug information needs accurate offsets on the symbols. for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { if ll.N.Class != PAUTO || ll.N.Op != ONAME { continue } ll.N.Xoffset += stkdelta[ll.N] delete(stkdelta, ll.N) } } func Cgen_checknil(n *Node) { if Disable_checknil != 0 { return } // Ideally we wouldn't see any integer types here, but we do. if n.Type == nil || (!Isptr[n.Type.Etype] && !Isint[n.Type.Etype] && n.Type.Etype != TUNSAFEPTR) { Dump("checknil", n) Fatal("bad checknil") } if ((Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { var reg Node Regalloc(®, Types[Tptr], n) Cgen(n, ®) Thearch.Gins(obj.ACHECKNIL, ®, nil) Regfree(®) return } Thearch.Gins(obj.ACHECKNIL, n, nil) } func compile(fn *Node) { if Newproc == nil { Newproc = Sysfunc("newproc") Deferproc = Sysfunc("deferproc") Deferreturn = Sysfunc("deferreturn") Panicindex = Sysfunc("panicindex") panicslice = Sysfunc("panicslice") throwreturn = Sysfunc("throwreturn") } lno := setlineno(fn) Curfn = fn dowidth(Curfn.Type) var oldstksize int64 var nod1 Node var ptxt *obj.Prog var pl *obj.Plist var p *obj.Prog var n *Node var nam *Node var gcargs *Sym var gclocals *Sym if fn.Nbody == nil { if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") { Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name) goto ret } if Debug['A'] != 0 { goto ret } emitptrargsmap() goto ret } saveerrors() // set up domain for labels clearlabels() if Curfn.Type.Outnamed != 0 { // add clearing of the output parameters var save Iter t := Structfirst(&save, Getoutarg(Curfn.Type)) for t != nil { if t.Nname != nil { n = Nod(OAS, t.Nname, nil) typecheck(&n, Etop) Curfn.Nbody = concat(list1(n), Curfn.Nbody) } t = structnext(&save) } } order(Curfn) if nerrors != 0 { goto ret } Hasdefer = 0 walk(Curfn) if nerrors != 0 { goto ret } if flag_race != 0 { racewalk(Curfn) } if nerrors != 0 { goto ret } continpc = nil breakpc = nil pl = newplist() pl.Name = Linksym(Curfn.Func.Nname.Sym) setlineno(Curfn) Nodconst(&nod1, Types[TINT32], 0) nam = Curfn.Func.Nname if isblank(nam) { nam = nil } ptxt = Thearch.Gins(obj.ATEXT, nam, &nod1) Afunclit(&ptxt.From, Curfn.Func.Nname) ptxt.From3 = new(obj.Addr) if fn.Func.Dupok { ptxt.From3.Offset |= obj.DUPOK } if fn.Func.Wrapper { ptxt.From3.Offset |= obj.WRAPPER } if fn.Func.Needctxt { ptxt.From3.Offset |= obj.NEEDCTXT } if fn.Func.Nosplit { ptxt.From3.Offset |= obj.NOSPLIT } if fn.Func.Systemstack { ptxt.From.Sym.Cfunc = 1 } // Clumsy but important. // See test/recover.go for test cases and src/reflect/value.go // for the actual functions being considered. if myimportpath != "" && myimportpath == "reflect" { if Curfn.Func.Nname.Sym.Name == "callReflect" || Curfn.Func.Nname.Sym.Name == "callMethod" { ptxt.From3.Offset |= obj.WRAPPER } } ginit() gcargs = makefuncdatasym("gcargs·%d", obj.FUNCDATA_ArgsPointerMaps) gclocals = makefuncdatasym("gclocals·%d", obj.FUNCDATA_LocalsPointerMaps) for _, t := range Curfn.Func.Fieldtrack { gtrack(tracksym(t)) } for l := fn.Func.Dcl; l != nil; l = l.Next { n = l.N if n.Op != ONAME { // might be OTYPE or OLITERAL continue } switch n.Class { case PAUTO, PPARAM, PPARAMOUT: Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width) p = Thearch.Gins(obj.ATYPE, l.N, &nod1) p.From.Gotype = Linksym(ngotype(l.N)) } } Genlist(Curfn.Func.Enter) Genlist(Curfn.Nbody) gclean() checklabels() if nerrors != 0 { goto ret } if Curfn.Func.Endlineno != 0 { lineno = Curfn.Func.Endlineno } if Curfn.Type.Outtuple != 0 { Ginscall(throwreturn, 0) } ginit() // TODO: Determine when the final cgen_ret can be omitted. Perhaps always? cgen_ret(nil) if Hasdefer != 0 { // deferreturn pretends to have one uintptr argument. // Reserve space for it so stack scanner is happy. if Maxarg < int64(Widthptr) { Maxarg = int64(Widthptr) } } gclean() if nerrors != 0 { goto ret } Pc.As = obj.ARET // overwrite AEND Pc.Lineno = lineno fixjmp(ptxt) if Debug['N'] == 0 || Debug['R'] != 0 || Debug['P'] != 0 { regopt(ptxt) nilopt(ptxt) } Thearch.Expandchecks(ptxt) oldstksize = Stksize allocauto(ptxt) if false { fmt.Printf("allocauto: %d to %d\n", oldstksize, int64(Stksize)) } setlineno(Curfn) if int64(Stksize)+Maxarg > 1<<31 { Yyerror("stack frame too large (>2GB)") goto ret } // Emit garbage collection symbols. liveness(Curfn, ptxt, gcargs, gclocals) gcsymdup(gcargs) gcsymdup(gclocals) Thearch.Defframe(ptxt) if Debug['f'] != 0 { frame(0) } // Remove leftover instrumentation from the instruction stream. removevardef(ptxt) ret: lineno = lno }