github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/pgen.go (about)

     1  // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/gc/pgen.go
     2  
     3  // Copyright 2011 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package gc
     8  
     9  import (
    10  	"rsc.io/tmp/bootstrap/internal/obj"
    11  	"crypto/md5"
    12  	"fmt"
    13  	"strings"
    14  )
    15  
    16  // "Portable" code generation.
    17  
    18  var makefuncdatasym_nsym int32
    19  
    20  func makefuncdatasym(namefmt string, funcdatakind int64) *Sym {
    21  	var nod Node
    22  
    23  	sym := Lookupf(namefmt, makefuncdatasym_nsym)
    24  	makefuncdatasym_nsym++
    25  	pnod := newname(sym)
    26  	pnod.Class = PEXTERN
    27  	Nodconst(&nod, Types[TINT32], funcdatakind)
    28  	Thearch.Gins(obj.AFUNCDATA, &nod, pnod)
    29  	return sym
    30  }
    31  
    32  // gvardef inserts a VARDEF for n into the instruction stream.
    33  // VARDEF is an annotation for the liveness analysis, marking a place
    34  // where a complete initialization (definition) of a variable begins.
    35  // Since the liveness analysis can see initialization of single-word
    36  // variables quite easy, gvardef is usually only called for multi-word
    37  // or 'fat' variables, those satisfying isfat(n->type).
    38  // However, gvardef is also called when a non-fat variable is initialized
    39  // via a block move; the only time this happens is when you have
    40  //	return f()
    41  // for a function with multiple return values exactly matching the return
    42  // types of the current function.
    43  //
    44  // A 'VARDEF x' annotation in the instruction stream tells the liveness
    45  // analysis to behave as though the variable x is being initialized at that
    46  // point in the instruction stream. The VARDEF must appear before the
    47  // actual (multi-instruction) initialization, and it must also appear after
    48  // any uses of the previous value, if any. For example, if compiling:
    49  //
    50  //	x = x[1:]
    51  //
    52  // it is important to generate code like:
    53  //
    54  //	base, len, cap = pieces of x[1:]
    55  //	VARDEF x
    56  //	x = {base, len, cap}
    57  //
    58  // If instead the generated code looked like:
    59  //
    60  //	VARDEF x
    61  //	base, len, cap = pieces of x[1:]
    62  //	x = {base, len, cap}
    63  //
    64  // then the liveness analysis would decide the previous value of x was
    65  // unnecessary even though it is about to be used by the x[1:] computation.
    66  // Similarly, if the generated code looked like:
    67  //
    68  //	base, len, cap = pieces of x[1:]
    69  //	x = {base, len, cap}
    70  //	VARDEF x
    71  //
    72  // then the liveness analysis will not preserve the new value of x, because
    73  // the VARDEF appears to have "overwritten" it.
    74  //
    75  // VARDEF is a bit of a kludge to work around the fact that the instruction
    76  // stream is working on single-word values but the liveness analysis
    77  // wants to work on individual variables, which might be multi-word
    78  // aggregates. It might make sense at some point to look into letting
    79  // the liveness analysis work on single-word values as well, although
    80  // there are complications around interface values, slices, and strings,
    81  // all of which cannot be treated as individual words.
    82  //
    83  // VARKILL is the opposite of VARDEF: it marks a value as no longer needed,
    84  // even if its address has been taken. That is, a VARKILL annotation asserts
    85  // that its argument is certainly dead, for use when the liveness analysis
    86  // would not otherwise be able to deduce that fact.
    87  
    88  func gvardefx(n *Node, as int) {
    89  	if n == nil {
    90  		Fatal("gvardef nil")
    91  	}
    92  	if n.Op != ONAME {
    93  		Yyerror("gvardef %v; %v", Oconv(int(n.Op), obj.FmtSharp), n)
    94  		return
    95  	}
    96  
    97  	switch n.Class {
    98  	case PAUTO, PPARAM, PPARAMOUT:
    99  		Thearch.Gins(as, nil, n)
   100  	}
   101  }
   102  
   103  func Gvardef(n *Node) {
   104  	gvardefx(n, obj.AVARDEF)
   105  }
   106  
   107  func gvarkill(n *Node) {
   108  	gvardefx(n, obj.AVARKILL)
   109  }
   110  
   111  func removevardef(firstp *obj.Prog) {
   112  	for p := firstp; p != nil; p = p.Link {
   113  		for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) {
   114  			p.Link = p.Link.Link
   115  		}
   116  		if p.To.Type == obj.TYPE_BRANCH {
   117  			for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL) {
   118  				p.To.Val = p.To.Val.(*obj.Prog).Link
   119  			}
   120  		}
   121  	}
   122  }
   123  
   124  func gcsymdup(s *Sym) {
   125  	ls := Linksym(s)
   126  	if len(ls.R) > 0 {
   127  		Fatal("cannot rosymdup %s with relocations", ls.Name)
   128  	}
   129  	ls.Name = fmt.Sprintf("gclocals·%x", md5.Sum(ls.P))
   130  	ls.Dupok = 1
   131  }
   132  
   133  func emitptrargsmap() {
   134  	sym := Lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Nname.Sym.Name))
   135  
   136  	nptr := int(Curfn.Type.Argwid / int64(Widthptr))
   137  	bv := bvalloc(int32(nptr) * 2)
   138  	nbitmap := 1
   139  	if Curfn.Type.Outtuple > 0 {
   140  		nbitmap = 2
   141  	}
   142  	off := duint32(sym, 0, uint32(nbitmap))
   143  	off = duint32(sym, off, uint32(bv.n))
   144  	var xoffset int64
   145  	if Curfn.Type.Thistuple > 0 {
   146  		xoffset = 0
   147  		onebitwalktype1(getthisx(Curfn.Type), &xoffset, bv)
   148  	}
   149  
   150  	if Curfn.Type.Intuple > 0 {
   151  		xoffset = 0
   152  		onebitwalktype1(getinargx(Curfn.Type), &xoffset, bv)
   153  	}
   154  
   155  	for j := 0; int32(j) < bv.n; j += 32 {
   156  		off = duint32(sym, off, bv.b[j/32])
   157  	}
   158  	if Curfn.Type.Outtuple > 0 {
   159  		xoffset = 0
   160  		onebitwalktype1(getoutargx(Curfn.Type), &xoffset, bv)
   161  		for j := 0; int32(j) < bv.n; j += 32 {
   162  			off = duint32(sym, off, bv.b[j/32])
   163  		}
   164  	}
   165  
   166  	ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL)
   167  }
   168  
   169  // Sort the list of stack variables. Autos after anything else,
   170  // within autos, unused after used, within used, things with
   171  // pointers first, zeroed things first, and then decreasing size.
   172  // Because autos are laid out in decreasing addresses
   173  // on the stack, pointers first, zeroed things first and decreasing size
   174  // really means, in memory, things with pointers needing zeroing at
   175  // the top of the stack and increasing in size.
   176  // Non-autos sort on offset.
   177  func cmpstackvar(a *Node, b *Node) int {
   178  	if a.Class != b.Class {
   179  		if a.Class == PAUTO {
   180  			return +1
   181  		}
   182  		return -1
   183  	}
   184  
   185  	if a.Class != PAUTO {
   186  		if a.Xoffset < b.Xoffset {
   187  			return -1
   188  		}
   189  		if a.Xoffset > b.Xoffset {
   190  			return +1
   191  		}
   192  		return 0
   193  	}
   194  
   195  	if a.Used != b.Used {
   196  		return obj.Bool2int(b.Used) - obj.Bool2int(a.Used)
   197  	}
   198  
   199  	ap := obj.Bool2int(haspointers(a.Type))
   200  	bp := obj.Bool2int(haspointers(b.Type))
   201  	if ap != bp {
   202  		return bp - ap
   203  	}
   204  
   205  	ap = obj.Bool2int(a.Needzero)
   206  	bp = obj.Bool2int(b.Needzero)
   207  	if ap != bp {
   208  		return bp - ap
   209  	}
   210  
   211  	if a.Type.Width < b.Type.Width {
   212  		return +1
   213  	}
   214  	if a.Type.Width > b.Type.Width {
   215  		return -1
   216  	}
   217  
   218  	return stringsCompare(a.Sym.Name, b.Sym.Name)
   219  }
   220  
   221  // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
   222  func allocauto(ptxt *obj.Prog) {
   223  	Stksize = 0
   224  	stkptrsize = 0
   225  
   226  	if Curfn.Func.Dcl == nil {
   227  		return
   228  	}
   229  
   230  	// Mark the PAUTO's unused.
   231  	for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
   232  		if ll.N.Class == PAUTO {
   233  			ll.N.Used = false
   234  		}
   235  	}
   236  
   237  	markautoused(ptxt)
   238  
   239  	listsort(&Curfn.Func.Dcl, cmpstackvar)
   240  
   241  	// Unused autos are at the end, chop 'em off.
   242  	ll := Curfn.Func.Dcl
   243  
   244  	n := ll.N
   245  	if n.Class == PAUTO && n.Op == ONAME && !n.Used {
   246  		// No locals used at all
   247  		Curfn.Func.Dcl = nil
   248  
   249  		fixautoused(ptxt)
   250  		return
   251  	}
   252  
   253  	for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next {
   254  		n = ll.Next.N
   255  		if n.Class == PAUTO && n.Op == ONAME && !n.Used {
   256  			ll.Next = nil
   257  			Curfn.Func.Dcl.End = ll
   258  			break
   259  		}
   260  	}
   261  
   262  	// Reassign stack offsets of the locals that are still there.
   263  	var w int64
   264  	for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
   265  		n = ll.N
   266  		if n.Class != PAUTO || n.Op != ONAME {
   267  			continue
   268  		}
   269  
   270  		dowidth(n.Type)
   271  		w = n.Type.Width
   272  		if w >= Thearch.MAXWIDTH || w < 0 {
   273  			Fatal("bad width")
   274  		}
   275  		Stksize += w
   276  		Stksize = Rnd(Stksize, int64(n.Type.Align))
   277  		if haspointers(n.Type) {
   278  			stkptrsize = Stksize
   279  		}
   280  		if Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' {
   281  			Stksize = Rnd(Stksize, int64(Widthptr))
   282  		}
   283  		if Stksize >= 1<<31 {
   284  			setlineno(Curfn)
   285  			Yyerror("stack frame too large (>2GB)")
   286  		}
   287  
   288  		n.Stkdelta = -Stksize - n.Xoffset
   289  	}
   290  
   291  	Stksize = Rnd(Stksize, int64(Widthreg))
   292  	stkptrsize = Rnd(stkptrsize, int64(Widthreg))
   293  
   294  	fixautoused(ptxt)
   295  
   296  	// The debug information needs accurate offsets on the symbols.
   297  	for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
   298  		if ll.N.Class != PAUTO || ll.N.Op != ONAME {
   299  			continue
   300  		}
   301  		ll.N.Xoffset += ll.N.Stkdelta
   302  		ll.N.Stkdelta = 0
   303  	}
   304  }
   305  
   306  func movelarge(l *NodeList) {
   307  	for ; l != nil; l = l.Next {
   308  		if l.N.Op == ODCLFUNC {
   309  			movelargefn(l.N)
   310  		}
   311  	}
   312  }
   313  
   314  func movelargefn(fn *Node) {
   315  	var n *Node
   316  
   317  	for l := fn.Func.Dcl; l != nil; l = l.Next {
   318  		n = l.N
   319  		if n.Class == PAUTO && n.Type != nil && n.Type.Width > MaxStackVarSize {
   320  			addrescapes(n)
   321  		}
   322  	}
   323  }
   324  
   325  func Cgen_checknil(n *Node) {
   326  	if Disable_checknil != 0 {
   327  		return
   328  	}
   329  
   330  	// Ideally we wouldn't see any integer types here, but we do.
   331  	if n.Type == nil || (!Isptr[n.Type.Etype] && !Isint[n.Type.Etype] && n.Type.Etype != TUNSAFEPTR) {
   332  		Dump("checknil", n)
   333  		Fatal("bad checknil")
   334  	}
   335  
   336  	if ((Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL {
   337  		var reg Node
   338  		Regalloc(&reg, Types[Tptr], n)
   339  		Cgen(n, &reg)
   340  		Thearch.Gins(obj.ACHECKNIL, &reg, nil)
   341  		Regfree(&reg)
   342  		return
   343  	}
   344  
   345  	Thearch.Gins(obj.ACHECKNIL, n, nil)
   346  }
   347  
   348  func compile(fn *Node) {
   349  	if Newproc == nil {
   350  		Newproc = Sysfunc("newproc")
   351  		Deferproc = Sysfunc("deferproc")
   352  		Deferreturn = Sysfunc("deferreturn")
   353  		Panicindex = Sysfunc("panicindex")
   354  		panicslice = Sysfunc("panicslice")
   355  		throwreturn = Sysfunc("throwreturn")
   356  	}
   357  
   358  	lno := setlineno(fn)
   359  
   360  	Curfn = fn
   361  	dowidth(Curfn.Type)
   362  
   363  	var oldstksize int64
   364  	var nod1 Node
   365  	var ptxt *obj.Prog
   366  	var pl *obj.Plist
   367  	var p *obj.Prog
   368  	var n *Node
   369  	var nam *Node
   370  	var gcargs *Sym
   371  	var gclocals *Sym
   372  	if fn.Nbody == nil {
   373  		if pure_go != 0 || strings.HasPrefix(fn.Nname.Sym.Name, "init.") {
   374  			Yyerror("missing function body for %q", fn.Nname.Sym.Name)
   375  			goto ret
   376  		}
   377  
   378  		if Debug['A'] != 0 {
   379  			goto ret
   380  		}
   381  		emitptrargsmap()
   382  		goto ret
   383  	}
   384  
   385  	saveerrors()
   386  
   387  	// set up domain for labels
   388  	clearlabels()
   389  
   390  	if Curfn.Type.Outnamed != 0 {
   391  		// add clearing of the output parameters
   392  		var save Iter
   393  		t := Structfirst(&save, Getoutarg(Curfn.Type))
   394  
   395  		for t != nil {
   396  			if t.Nname != nil {
   397  				n = Nod(OAS, t.Nname, nil)
   398  				typecheck(&n, Etop)
   399  				Curfn.Nbody = concat(list1(n), Curfn.Nbody)
   400  			}
   401  
   402  			t = structnext(&save)
   403  		}
   404  	}
   405  
   406  	order(Curfn)
   407  	if nerrors != 0 {
   408  		goto ret
   409  	}
   410  
   411  	Hasdefer = 0
   412  	walk(Curfn)
   413  	if nerrors != 0 {
   414  		goto ret
   415  	}
   416  	if flag_race != 0 {
   417  		racewalk(Curfn)
   418  	}
   419  	if nerrors != 0 {
   420  		goto ret
   421  	}
   422  
   423  	continpc = nil
   424  	breakpc = nil
   425  
   426  	pl = newplist()
   427  	pl.Name = Linksym(Curfn.Nname.Sym)
   428  
   429  	setlineno(Curfn)
   430  
   431  	Nodconst(&nod1, Types[TINT32], 0)
   432  	nam = Curfn.Nname
   433  	if isblank(nam) {
   434  		nam = nil
   435  	}
   436  	ptxt = Thearch.Gins(obj.ATEXT, nam, &nod1)
   437  	if fn.Func.Dupok {
   438  		ptxt.From3.Offset |= obj.DUPOK
   439  	}
   440  	if fn.Func.Wrapper {
   441  		ptxt.From3.Offset |= obj.WRAPPER
   442  	}
   443  	if fn.Func.Needctxt {
   444  		ptxt.From3.Offset |= obj.NEEDCTXT
   445  	}
   446  	if fn.Func.Nosplit {
   447  		ptxt.From3.Offset |= obj.NOSPLIT
   448  	}
   449  
   450  	// Clumsy but important.
   451  	// See test/recover.go for test cases and src/reflect/value.go
   452  	// for the actual functions being considered.
   453  	if myimportpath != "" && myimportpath == "reflect" {
   454  		if Curfn.Nname.Sym.Name == "callReflect" || Curfn.Nname.Sym.Name == "callMethod" {
   455  			ptxt.From3.Offset |= obj.WRAPPER
   456  		}
   457  	}
   458  
   459  	Afunclit(&ptxt.From, Curfn.Nname)
   460  
   461  	ginit()
   462  
   463  	gcargs = makefuncdatasym("gcargs·%d", obj.FUNCDATA_ArgsPointerMaps)
   464  	gclocals = makefuncdatasym("gclocals·%d", obj.FUNCDATA_LocalsPointerMaps)
   465  
   466  	for t := Curfn.Paramfld; t != nil; t = t.Down {
   467  		gtrack(tracksym(t.Type))
   468  	}
   469  
   470  	for l := fn.Func.Dcl; l != nil; l = l.Next {
   471  		n = l.N
   472  		if n.Op != ONAME { // might be OTYPE or OLITERAL
   473  			continue
   474  		}
   475  		switch n.Class {
   476  		case PAUTO, PPARAM, PPARAMOUT:
   477  			Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width)
   478  			p = Thearch.Gins(obj.ATYPE, l.N, &nod1)
   479  			p.From.Gotype = Linksym(ngotype(l.N))
   480  		}
   481  	}
   482  
   483  	Genlist(Curfn.Func.Enter)
   484  	Genlist(Curfn.Nbody)
   485  	gclean()
   486  	checklabels()
   487  	if nerrors != 0 {
   488  		goto ret
   489  	}
   490  	if Curfn.Func.Endlineno != 0 {
   491  		lineno = Curfn.Func.Endlineno
   492  	}
   493  
   494  	if Curfn.Type.Outtuple != 0 {
   495  		Ginscall(throwreturn, 0)
   496  	}
   497  
   498  	ginit()
   499  
   500  	// TODO: Determine when the final cgen_ret can be omitted. Perhaps always?
   501  	cgen_ret(nil)
   502  
   503  	if Hasdefer != 0 {
   504  		// deferreturn pretends to have one uintptr argument.
   505  		// Reserve space for it so stack scanner is happy.
   506  		if Maxarg < int64(Widthptr) {
   507  			Maxarg = int64(Widthptr)
   508  		}
   509  	}
   510  
   511  	gclean()
   512  	if nerrors != 0 {
   513  		goto ret
   514  	}
   515  
   516  	Pc.As = obj.ARET // overwrite AEND
   517  	Pc.Lineno = lineno
   518  
   519  	fixjmp(ptxt)
   520  	if Debug['N'] == 0 || Debug['R'] != 0 || Debug['P'] != 0 {
   521  		regopt(ptxt)
   522  		nilopt(ptxt)
   523  	}
   524  
   525  	Thearch.Expandchecks(ptxt)
   526  
   527  	oldstksize = Stksize
   528  	allocauto(ptxt)
   529  
   530  	if false {
   531  		fmt.Printf("allocauto: %d to %d\n", oldstksize, int64(Stksize))
   532  	}
   533  
   534  	setlineno(Curfn)
   535  	if int64(Stksize)+Maxarg > 1<<31 {
   536  		Yyerror("stack frame too large (>2GB)")
   537  		goto ret
   538  	}
   539  
   540  	// Emit garbage collection symbols.
   541  	liveness(Curfn, ptxt, gcargs, gclocals)
   542  
   543  	gcsymdup(gcargs)
   544  	gcsymdup(gclocals)
   545  
   546  	Thearch.Defframe(ptxt)
   547  
   548  	if Debug['f'] != 0 {
   549  		frame(0)
   550  	}
   551  
   552  	// Remove leftover instrumentation from the instruction stream.
   553  	removevardef(ptxt)
   554  
   555  ret:
   556  	lineno = lno
   557  }