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

     1  // Derived from Inferno utils/6c/txt.c
     2  // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/txt.c
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  //	Portions Copyright © 2004,2006 Bruce Ellis
     9  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  package gc
    32  
    33  import (
    34  	"cmd/compile/internal/types"
    35  	"cmd/internal/obj"
    36  	"cmd/internal/src"
    37  )
    38  
    39  var sharedProgArray *[10000]obj.Prog // *T instead of T to work around issue 19839
    40  
    41  func init() {
    42  	sharedProgArray = new([10000]obj.Prog)
    43  }
    44  
    45  // Progs accumulates Progs for a function and converts them into machine code.
    46  type Progs struct {
    47  	Text      *obj.Prog  // ATEXT Prog for this function
    48  	next      *obj.Prog  // next Prog
    49  	pc        int64      // virtual PC; count of Progs
    50  	pos       src.XPos   // position to use for new Progs
    51  	curfn     *Node      // fn these Progs are for
    52  	progcache []obj.Prog // local progcache
    53  	cacheidx  int        // first free element of progcache
    54  }
    55  
    56  // newProgs returns a new Progs for fn.
    57  func newProgs(fn *Node) *Progs {
    58  	pp := new(Progs)
    59  	if Ctxt.CanReuseProgs() {
    60  		pp.progcache = sharedProgArray[:]
    61  	}
    62  	pp.curfn = fn
    63  
    64  	// prime the pump
    65  	pp.next = pp.NewProg()
    66  	pp.clearp(pp.next)
    67  
    68  	pp.pos = fn.Pos
    69  	pp.settext(fn)
    70  	return pp
    71  }
    72  
    73  func (pp *Progs) NewProg() *obj.Prog {
    74  	if pp.cacheidx < len(pp.progcache) {
    75  		p := &pp.progcache[pp.cacheidx]
    76  		p.Ctxt = Ctxt
    77  		pp.cacheidx++
    78  		return p
    79  	}
    80  	p := new(obj.Prog)
    81  	p.Ctxt = Ctxt
    82  	return p
    83  }
    84  
    85  // Flush converts from pp to machine code.
    86  func (pp *Progs) Flush() {
    87  	plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.curfn}
    88  	obj.Flushplist(Ctxt, plist, pp.NewProg)
    89  }
    90  
    91  // Free clears pp and any associated resources.
    92  func (pp *Progs) Free() {
    93  	if Ctxt.CanReuseProgs() {
    94  		// Clear progs to enable GC and avoid abuse.
    95  		s := pp.progcache[:pp.cacheidx]
    96  		for i := range s {
    97  			s[i] = obj.Prog{}
    98  		}
    99  	}
   100  	// Clear pp to avoid abuse.
   101  	*pp = Progs{}
   102  }
   103  
   104  // Prog adds a Prog with instruction As to pp.
   105  func (pp *Progs) Prog(as obj.As) *obj.Prog {
   106  	p := pp.next
   107  	pp.next = pp.NewProg()
   108  	pp.clearp(pp.next)
   109  	p.Link = pp.next
   110  
   111  	if !pp.pos.IsKnown() && Debug['K'] != 0 {
   112  		Warn("prog: unknown position (line 0)")
   113  	}
   114  
   115  	p.As = as
   116  	p.Pos = pp.pos
   117  	return p
   118  }
   119  
   120  func (pp *Progs) clearp(p *obj.Prog) {
   121  	obj.Nopout(p)
   122  	p.As = obj.AEND
   123  	p.Pc = pp.pc
   124  	pp.pc++
   125  }
   126  
   127  func (pp *Progs) Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
   128  	q := pp.NewProg()
   129  	pp.clearp(q)
   130  	q.As = as
   131  	q.Pos = p.Pos
   132  	q.From.Type = ftype
   133  	q.From.Reg = freg
   134  	q.From.Offset = foffset
   135  	q.To.Type = ttype
   136  	q.To.Reg = treg
   137  	q.To.Offset = toffset
   138  	q.Link = p.Link
   139  	p.Link = q
   140  	return q
   141  }
   142  
   143  func (pp *Progs) settext(fn *Node) {
   144  	if pp.Text != nil {
   145  		Fatalf("Progs.settext called twice")
   146  	}
   147  	ptxt := pp.Prog(obj.ATEXT)
   148  	if fn.Func.lsym != nil {
   149  		fn.Func.lsym.Text = ptxt
   150  		ptxt.From.Type = obj.TYPE_MEM
   151  		ptxt.From.Name = obj.NAME_EXTERN
   152  		ptxt.From.Sym = fn.Func.lsym
   153  	}
   154  	pp.Text = ptxt
   155  }
   156  
   157  func (f *Func) initLSym() {
   158  	if f.lsym != nil {
   159  		Fatalf("Func.initLSym called twice")
   160  	}
   161  
   162  	if nam := f.Nname; !isblank(nam) {
   163  		f.lsym = Linksym(nam.Sym)
   164  		if f.Pragma&Systemstack != 0 {
   165  			f.lsym.Set(obj.AttrCFunc, true)
   166  		}
   167  	}
   168  
   169  	var flag int
   170  	if f.Dupok() {
   171  		flag |= obj.DUPOK
   172  	}
   173  	if f.Wrapper() {
   174  		flag |= obj.WRAPPER
   175  	}
   176  	if f.NoFramePointer() {
   177  		flag |= obj.NOFRAME
   178  	}
   179  	if f.Needctxt() {
   180  		flag |= obj.NEEDCTXT
   181  	}
   182  	if f.Pragma&Nosplit != 0 {
   183  		flag |= obj.NOSPLIT
   184  	}
   185  	if f.ReflectMethod() {
   186  		flag |= obj.REFLECTMETHOD
   187  	}
   188  
   189  	// Clumsy but important.
   190  	// See test/recover.go for test cases and src/reflect/value.go
   191  	// for the actual functions being considered.
   192  	if myimportpath == "reflect" {
   193  		switch f.Nname.Sym.Name {
   194  		case "callReflect", "callMethod":
   195  			flag |= obj.WRAPPER
   196  		}
   197  	}
   198  
   199  	Ctxt.InitTextSym(f.lsym, flag)
   200  }
   201  
   202  func ggloblnod(nam *Node) {
   203  	s := Linksym(nam.Sym)
   204  	s.Gotype = Linksym(ngotype(nam))
   205  	flags := 0
   206  	if nam.Name.Readonly() {
   207  		flags = obj.RODATA
   208  	}
   209  	if nam.Type != nil && !types.Haspointers(nam.Type) {
   210  		flags |= obj.NOPTR
   211  	}
   212  	Ctxt.Globl(s, nam.Type.Width, flags)
   213  }
   214  
   215  func ggloblsym(s *types.Sym, width int32, flags int16) {
   216  	ggloblLSym(Linksym(s), width, flags)
   217  }
   218  
   219  func ggloblLSym(s *obj.LSym, width int32, flags int16) {
   220  	if flags&obj.LOCAL != 0 {
   221  		s.Set(obj.AttrLocal, true)
   222  		flags &^= obj.LOCAL
   223  	}
   224  	Ctxt.Globl(s, int64(width), int(flags))
   225  }
   226  
   227  func isfat(t *types.Type) bool {
   228  	if t != nil {
   229  		switch t.Etype {
   230  		case TSTRUCT, TARRAY, TSLICE, TSTRING,
   231  			TINTER: // maybe remove later
   232  			return true
   233  		}
   234  	}
   235  
   236  	return false
   237  }
   238  
   239  func Addrconst(a *obj.Addr, v int64) {
   240  	a.Sym = nil
   241  	a.Type = obj.TYPE_CONST
   242  	a.Offset = v
   243  }
   244  
   245  // nodarg returns a Node for the function argument denoted by t,
   246  // which is either the entire function argument or result struct (t is a  struct *types.Type)
   247  // or a specific argument (t is a *types.Field within a struct *types.Type).
   248  //
   249  // If fp is 0, the node is for use by a caller invoking the given
   250  // function, preparing the arguments before the call
   251  // or retrieving the results after the call.
   252  // In this case, the node will correspond to an outgoing argument
   253  // slot like 8(SP).
   254  //
   255  // If fp is 1, the node is for use by the function itself
   256  // (the callee), to retrieve its arguments or write its results.
   257  // In this case the node will be an ONAME with an appropriate
   258  // type and offset.
   259  func nodarg(t interface{}, fp int) *Node {
   260  	var n *Node
   261  
   262  	var funarg types.Funarg
   263  	switch t := t.(type) {
   264  	default:
   265  		Fatalf("bad nodarg %T(%v)", t, t)
   266  
   267  	case *types.Type:
   268  		// Entire argument struct, not just one arg
   269  		if !t.IsFuncArgStruct() {
   270  			Fatalf("nodarg: bad type %v", t)
   271  		}
   272  		funarg = t.StructType().Funarg
   273  
   274  		// Build fake variable name for whole arg struct.
   275  		n = newname(lookup(".args"))
   276  		n.Type = t
   277  		first := t.Field(0)
   278  		if first == nil {
   279  			Fatalf("nodarg: bad struct")
   280  		}
   281  		if first.Offset == BADWIDTH {
   282  			Fatalf("nodarg: offset not computed for %v", t)
   283  		}
   284  		n.Xoffset = first.Offset
   285  
   286  	case *types.Field:
   287  		funarg = t.Funarg
   288  		if fp == 1 {
   289  			// NOTE(rsc): This should be using t.Nname directly,
   290  			// except in the case where t.Nname.Sym is the blank symbol and
   291  			// so the assignment would be discarded during code generation.
   292  			// In that case we need to make a new node, and there is no harm
   293  			// in optimization passes to doing so. But otherwise we should
   294  			// definitely be using the actual declaration and not a newly built node.
   295  			// The extra Fatalf checks here are verifying that this is the case,
   296  			// without changing the actual logic (at time of writing, it's getting
   297  			// toward time for the Go 1.7 beta).
   298  			// At some quieter time (assuming we've never seen these Fatalfs happen)
   299  			// we could change this code to use "expect" directly.
   300  			expect := asNode(t.Nname)
   301  			if expect.isParamHeapCopy() {
   302  				expect = expect.Name.Param.Stackcopy
   303  			}
   304  
   305  			for _, n := range Curfn.Func.Dcl {
   306  				if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym {
   307  					if n != expect {
   308  						Fatalf("nodarg: unexpected node: %v (%p %v) vs %v (%p %v)", n, n, n.Op, asNode(t.Nname), asNode(t.Nname), asNode(t.Nname).Op)
   309  					}
   310  					return n
   311  				}
   312  			}
   313  
   314  			if !isblanksym(expect.Sym) {
   315  				Fatalf("nodarg: did not find node in dcl list: %v", expect)
   316  			}
   317  		}
   318  
   319  		// Build fake name for individual variable.
   320  		// This is safe because if there was a real declared name
   321  		// we'd have used it above.
   322  		n = newname(lookup("__"))
   323  		n.Type = t.Type
   324  		if t.Offset == BADWIDTH {
   325  			Fatalf("nodarg: offset not computed for %v", t)
   326  		}
   327  		n.Xoffset = t.Offset
   328  		n.Orig = asNode(t.Nname)
   329  	}
   330  
   331  	// Rewrite argument named _ to __,
   332  	// or else the assignment to _ will be
   333  	// discarded during code generation.
   334  	if isblank(n) {
   335  		n.Sym = lookup("__")
   336  	}
   337  
   338  	switch fp {
   339  	default:
   340  		Fatalf("bad fp")
   341  
   342  	case 0: // preparing arguments for call
   343  		n.Op = OINDREGSP
   344  		n.Xoffset += Ctxt.FixedFrameSize()
   345  
   346  	case 1: // reading arguments inside call
   347  		n.Class = PPARAM
   348  		if funarg == types.FunargResults {
   349  			n.Class = PPARAMOUT
   350  		}
   351  	}
   352  
   353  	n.Typecheck = 1
   354  	n.SetAddrtaken(true) // keep optimizers at bay
   355  	return n
   356  }
   357  
   358  func Patch(p *obj.Prog, to *obj.Prog) {
   359  	if p.To.Type != obj.TYPE_BRANCH {
   360  		Fatalf("patch: not a branch")
   361  	}
   362  	p.To.Val = to
   363  	p.To.Offset = to.Pc
   364  }