github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/compile/internal/gc/gen.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Portable half of code generator; mainly statements and control flow.
     6  
     7  package gc
     8  
     9  import (
    10  	"cmd/compile/internal/types"
    11  	"cmd/internal/obj"
    12  	"cmd/internal/src"
    13  	"fmt"
    14  	"strconv"
    15  )
    16  
    17  func Sysfunc(name string) *obj.LSym {
    18  	return Linksym(Runtimepkg.Lookup(name))
    19  }
    20  
    21  // addrescapes tags node n as having had its address taken
    22  // by "increasing" the "value" of n.Esc to EscHeap.
    23  // Storage is allocated as necessary to allow the address
    24  // to be taken.
    25  func addrescapes(n *Node) {
    26  	switch n.Op {
    27  	// probably a type error already.
    28  	// dump("addrescapes", n);
    29  	default:
    30  		break
    31  
    32  	case ONAME:
    33  		if n == nodfp {
    34  			break
    35  		}
    36  
    37  		// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
    38  		// on PPARAM it means something different.
    39  		if n.Class == PAUTO && n.Esc == EscNever {
    40  			break
    41  		}
    42  
    43  		// If a closure reference escapes, mark the outer variable as escaping.
    44  		if n.IsClosureVar() {
    45  			addrescapes(n.Name.Defn)
    46  			break
    47  		}
    48  
    49  		if n.Class != PPARAM && n.Class != PPARAMOUT && n.Class != PAUTO {
    50  			break
    51  		}
    52  
    53  		// This is a plain parameter or local variable that needs to move to the heap,
    54  		// but possibly for the function outside the one we're compiling.
    55  		// That is, if we have:
    56  		//
    57  		//	func f(x int) {
    58  		//		func() {
    59  		//			global = &x
    60  		//		}
    61  		//	}
    62  		//
    63  		// then we're analyzing the inner closure but we need to move x to the
    64  		// heap in f, not in the inner closure. Flip over to f before calling moveToHeap.
    65  		oldfn := Curfn
    66  		Curfn = n.Name.Curfn
    67  		if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
    68  			Curfn = Curfn.Func.Closure
    69  		}
    70  		ln := lineno
    71  		lineno = Curfn.Pos
    72  		moveToHeap(n)
    73  		Curfn = oldfn
    74  		lineno = ln
    75  
    76  	case OIND, ODOTPTR:
    77  		break
    78  
    79  	// ODOTPTR has already been introduced,
    80  	// so these are the non-pointer ODOT and OINDEX.
    81  	// In &x[0], if x is a slice, then x does not
    82  	// escape--the pointer inside x does, but that
    83  	// is always a heap pointer anyway.
    84  	case ODOT, OINDEX, OPAREN, OCONVNOP:
    85  		if !n.Left.Type.IsSlice() {
    86  			addrescapes(n.Left)
    87  		}
    88  	}
    89  }
    90  
    91  // isParamStackCopy reports whether this is the on-stack copy of a
    92  // function parameter that moved to the heap.
    93  func (n *Node) isParamStackCopy() bool {
    94  	return n.Op == ONAME && (n.Class == PPARAM || n.Class == PPARAMOUT) && n.Name.Param.Heapaddr != nil
    95  }
    96  
    97  // isParamHeapCopy reports whether this is the on-heap copy of
    98  // a function parameter that moved to the heap.
    99  func (n *Node) isParamHeapCopy() bool {
   100  	return n.Op == ONAME && n.Class == PAUTOHEAP && n.Name.Param.Stackcopy != nil
   101  }
   102  
   103  // moveToHeap records the parameter or local variable n as moved to the heap.
   104  func moveToHeap(n *Node) {
   105  	if Debug['r'] != 0 {
   106  		Dump("MOVE", n)
   107  	}
   108  	if compiling_runtime {
   109  		yyerror("%v escapes to heap, not allowed in runtime.", n)
   110  	}
   111  	if n.Class == PAUTOHEAP {
   112  		Dump("n", n)
   113  		Fatalf("double move to heap")
   114  	}
   115  
   116  	// Allocate a local stack variable to hold the pointer to the heap copy.
   117  	// temp will add it to the function declaration list automatically.
   118  	heapaddr := temp(types.NewPtr(n.Type))
   119  	heapaddr.Sym = lookup("&" + n.Sym.Name)
   120  	heapaddr.Orig.Sym = heapaddr.Sym
   121  
   122  	// Unset AutoTemp to persist the &foo variable name through SSA to
   123  	// liveness analysis.
   124  	// TODO(mdempsky/drchase): Cleaner solution?
   125  	heapaddr.Name.SetAutoTemp(false)
   126  
   127  	// Parameters have a local stack copy used at function start/end
   128  	// in addition to the copy in the heap that may live longer than
   129  	// the function.
   130  	if n.Class == PPARAM || n.Class == PPARAMOUT {
   131  		if n.Xoffset == BADWIDTH {
   132  			Fatalf("addrescapes before param assignment")
   133  		}
   134  
   135  		// We rewrite n below to be a heap variable (indirection of heapaddr).
   136  		// Preserve a copy so we can still write code referring to the original,
   137  		// and substitute that copy into the function declaration list
   138  		// so that analyses of the local (on-stack) variables use it.
   139  		stackcopy := newname(n.Sym)
   140  		stackcopy.SetAddable(false)
   141  		stackcopy.Type = n.Type
   142  		stackcopy.Xoffset = n.Xoffset
   143  		stackcopy.Class = n.Class
   144  		stackcopy.Name.Param.Heapaddr = heapaddr
   145  		if n.Class == PPARAMOUT {
   146  			// Make sure the pointer to the heap copy is kept live throughout the function.
   147  			// The function could panic at any point, and then a defer could recover.
   148  			// Thus, we need the pointer to the heap copy always available so the
   149  			// post-deferreturn code can copy the return value back to the stack.
   150  			// See issue 16095.
   151  			heapaddr.SetIsOutputParamHeapAddr(true)
   152  		}
   153  		n.Name.Param.Stackcopy = stackcopy
   154  
   155  		// Substitute the stackcopy into the function variable list so that
   156  		// liveness and other analyses use the underlying stack slot
   157  		// and not the now-pseudo-variable n.
   158  		found := false
   159  		for i, d := range Curfn.Func.Dcl {
   160  			if d == n {
   161  				Curfn.Func.Dcl[i] = stackcopy
   162  				found = true
   163  				break
   164  			}
   165  			// Parameters are before locals, so can stop early.
   166  			// This limits the search even in functions with many local variables.
   167  			if d.Class == PAUTO {
   168  				break
   169  			}
   170  		}
   171  		if !found {
   172  			Fatalf("cannot find %v in local variable list", n)
   173  		}
   174  		Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   175  	}
   176  
   177  	// Modify n in place so that uses of n now mean indirection of the heapaddr.
   178  	n.Class = PAUTOHEAP
   179  	n.Xoffset = 0
   180  	n.Name.Param.Heapaddr = heapaddr
   181  	n.Esc = EscHeap
   182  	if Debug['m'] != 0 {
   183  		fmt.Printf("%v: moved to heap: %v\n", n.Line(), n)
   184  	}
   185  }
   186  
   187  // autotmpname returns the name for an autotmp variable numbered n.
   188  func autotmpname(n int) string {
   189  	// Give each tmp a different name so that they can be registerized.
   190  	// Add a preceding . to avoid clashing with legal names.
   191  	const prefix = ".autotmp_"
   192  	// Start with a buffer big enough to hold a large n.
   193  	b := []byte(prefix + "      ")[:len(prefix)]
   194  	b = strconv.AppendInt(b, int64(n), 10)
   195  	_ = b
   196  	return types.InternString(b)
   197  }
   198  
   199  // make a new Node off the books
   200  func tempnamel(pos src.XPos, curfn *Node, nn *Node, t *types.Type) {
   201  	if curfn == nil {
   202  		Fatalf("no curfn for tempname")
   203  	}
   204  	if curfn.Func.Closure != nil && curfn.Op == OCLOSURE {
   205  		Dump("tempname", curfn)
   206  		Fatalf("adding tempname to wrong closure function")
   207  	}
   208  	if t == nil {
   209  		Fatalf("tempname called with nil type")
   210  	}
   211  
   212  	s := &types.Sym{
   213  		Name: autotmpname(len(curfn.Func.Dcl)),
   214  		Pkg:  localpkg,
   215  	}
   216  	n := newnamel(pos, s)
   217  	s.Def = asTypesNode(n)
   218  	n.Type = t
   219  	n.Class = PAUTO
   220  	n.Esc = EscNever
   221  	n.Name.Curfn = curfn
   222  	n.Name.SetAutoTemp(true)
   223  	curfn.Func.Dcl = append(curfn.Func.Dcl, n)
   224  
   225  	dowidth(t)
   226  	*nn = *n
   227  }
   228  
   229  func temp(t *types.Type) *Node {
   230  	var n Node
   231  	tempnamel(lineno, Curfn, &n, t)
   232  	asNode(n.Sym.Def).SetUsed(true)
   233  	return n.Orig
   234  }
   235  
   236  func tempAt(pos src.XPos, curfn *Node, t *types.Type) *Node {
   237  	var n Node
   238  	tempnamel(pos, curfn, &n, t)
   239  	asNode(n.Sym.Def).SetUsed(true)
   240  	return n.Orig
   241  }