// 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. // Portable half of code generator; mainly statements and control flow. package gc import ( "cmd/internal/obj" "cmd/internal/src" "fmt" "strconv" ) func Sysfunc(name string) *obj.LSym { return Linksym(Runtimepkg.Lookup(name)) } // 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 } // If a closure reference escapes, mark the outer variable as escaping. if n.IsClosureVar() { addrescapes(n.Name.Defn) break } if n.Class != PPARAM && n.Class != PPARAMOUT && n.Class != PAUTO { break } // This is a plain parameter or local variable that needs to move to the heap, // but possibly for the function outside the one we're compiling. // That is, if we have: // // func f(x int) { // func() { // global = &x // } // } // // then we're analyzing the inner closure but we need to move x to the // heap in f, not in the inner closure. Flip over to f before calling moveToHeap. oldfn := Curfn Curfn = n.Name.Curfn if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE { Curfn = Curfn.Func.Closure } ln := lineno lineno = Curfn.Pos moveToHeap(n) Curfn = oldfn lineno = ln 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 !n.Left.Type.IsSlice() { addrescapes(n.Left) } } } // isParamStackCopy reports whether this is the on-stack copy of a // function parameter that moved to the heap. func (n *Node) isParamStackCopy() bool { return n.Op == ONAME && (n.Class == PPARAM || n.Class == PPARAMOUT) && n.Name.Param.Heapaddr != nil } // isParamHeapCopy reports whether this is the on-heap copy of // a function parameter that moved to the heap. func (n *Node) isParamHeapCopy() bool { return n.Op == ONAME && n.Class == PAUTOHEAP && n.Name.Param.Stackcopy != nil } // moveToHeap records the parameter or local variable n as moved to the heap. func moveToHeap(n *Node) { if Debug['r'] != 0 { Dump("MOVE", n) } if compiling_runtime { yyerror("%v escapes to heap, not allowed in runtime.", n) } if n.Class == PAUTOHEAP { Dump("n", n) Fatalf("double move to heap") } // Allocate a local stack variable to hold the pointer to the heap copy. // temp will add it to the function declaration list automatically. heapaddr := temp(typPtr(n.Type)) heapaddr.Sym = lookup("&" + n.Sym.Name) heapaddr.Orig.Sym = heapaddr.Sym // Unset AutoTemp to persist the &foo variable name through SSA to // liveness analysis. // TODO(mdempsky/drchase): Cleaner solution? heapaddr.Name.SetAutoTemp(false) // Parameters have a local stack copy used at function start/end // in addition to the copy in the heap that may live longer than // the function. if n.Class == PPARAM || n.Class == PPARAMOUT { if n.Xoffset == BADWIDTH { Fatalf("addrescapes before param assignment") } // We rewrite n below to be a heap variable (indirection of heapaddr). // Preserve a copy so we can still write code referring to the original, // and substitute that copy into the function declaration list // so that analyses of the local (on-stack) variables use it. stackcopy := newname(n.Sym) stackcopy.SetAddable(false) stackcopy.Type = n.Type stackcopy.Xoffset = n.Xoffset stackcopy.Class = n.Class stackcopy.Name.Param.Heapaddr = heapaddr if n.Class == PPARAMOUT { // Make sure the pointer to the heap copy is kept live throughout the function. // The function could panic at any point, and then a defer could recover. // Thus, we need the pointer to the heap copy always available so the // post-deferreturn code can copy the return value back to the stack. // See issue 16095. heapaddr.SetIsOutputParamHeapAddr(true) } n.Name.Param.Stackcopy = stackcopy // Substitute the stackcopy into the function variable list so that // liveness and other analyses use the underlying stack slot // and not the now-pseudo-variable n. found := false for i, d := range Curfn.Func.Dcl { if d == n { Curfn.Func.Dcl[i] = stackcopy found = true break } // Parameters are before locals, so can stop early. // This limits the search even in functions with many local variables. if d.Class == PAUTO { break } } if !found { Fatalf("cannot find %v in local variable list", n) } Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) } // Modify n in place so that uses of n now mean indirection of the heapaddr. n.Class = PAUTOHEAP n.Xoffset = 0 n.Name.Param.Heapaddr = heapaddr n.Esc = EscHeap if Debug['m'] != 0 { fmt.Printf("%v: moved to heap: %v\n", n.Line(), n) } } // autotmpname returns the name for an autotmp variable numbered n. func autotmpname(n int) string { // Give each tmp a different name so that they can be registerized. // Add a preceding . to avoid clashing with legal names. const prefix = ".autotmp_" // Start with a buffer big enough to hold a large n. b := []byte(prefix + " ")[:len(prefix)] b = strconv.AppendInt(b, int64(n), 10) return internString(b) } // make a new Node off the books func tempname(nn *Node, t *Type) { if Curfn == nil { Fatalf("no curfn for tempname") } if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE { Dump("tempname", Curfn) Fatalf("adding tempname to wrong closure function") } if t == nil { Fatalf("tempname called with nil type") } s := &Sym{ Name: autotmpname(len(Curfn.Func.Dcl)), Pkg: localpkg, } n := newname(s) s.Def = n n.Type = t n.Class = PAUTO n.Esc = EscNever n.Name.Curfn = Curfn n.Name.SetAutoTemp(true) Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) dowidth(t) *nn = *n } func temp(t *Type) *Node { var n Node tempname(&n, t) n.Sym.Def.SetUsed(true) return n.Orig } func tempAt(pos src.XPos, curfn *Node, t *Type) *Node { // TODO(mdempsky/josharian): Remove all reads and writes of lineno and Curfn. lineno = pos Curfn = curfn n := temp(t) Curfn = nil return n }