Home | History | Annotate | Download | only in gc
      1 // Copyright 2011 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 package gc
      6 
      7 import (
      8 	"cmd/compile/internal/ssa"
      9 	"cmd/internal/obj"
     10 	"cmd/internal/sys"
     11 	"fmt"
     12 	"sort"
     13 	"strings"
     14 )
     15 
     16 // "Portable" code generation.
     17 
     18 var makefuncdatasym_nsym int
     19 
     20 func makefuncdatasym(nameprefix string, funcdatakind int64) *Sym {
     21 	sym := lookupN(nameprefix, makefuncdatasym_nsym)
     22 	makefuncdatasym_nsym++
     23 	pnod := newname(sym)
     24 	pnod.Class = PEXTERN
     25 	p := Gins(obj.AFUNCDATA, nil, pnod)
     26 	Addrconst(&p.From, funcdatakind)
     27 	return sym
     28 }
     29 
     30 // gvardef inserts a VARDEF for n into the instruction stream.
     31 // VARDEF is an annotation for the liveness analysis, marking a place
     32 // where a complete initialization (definition) of a variable begins.
     33 // Since the liveness analysis can see initialization of single-word
     34 // variables quite easy, gvardef is usually only called for multi-word
     35 // or 'fat' variables, those satisfying isfat(n->type).
     36 // However, gvardef is also called when a non-fat variable is initialized
     37 // via a block move; the only time this happens is when you have
     38 //	return f()
     39 // for a function with multiple return values exactly matching the return
     40 // types of the current function.
     41 //
     42 // A 'VARDEF x' annotation in the instruction stream tells the liveness
     43 // analysis to behave as though the variable x is being initialized at that
     44 // point in the instruction stream. The VARDEF must appear before the
     45 // actual (multi-instruction) initialization, and it must also appear after
     46 // any uses of the previous value, if any. For example, if compiling:
     47 //
     48 //	x = x[1:]
     49 //
     50 // it is important to generate code like:
     51 //
     52 //	base, len, cap = pieces of x[1:]
     53 //	VARDEF x
     54 //	x = {base, len, cap}
     55 //
     56 // If instead the generated code looked like:
     57 //
     58 //	VARDEF x
     59 //	base, len, cap = pieces of x[1:]
     60 //	x = {base, len, cap}
     61 //
     62 // then the liveness analysis would decide the previous value of x was
     63 // unnecessary even though it is about to be used by the x[1:] computation.
     64 // Similarly, if the generated code looked like:
     65 //
     66 //	base, len, cap = pieces of x[1:]
     67 //	x = {base, len, cap}
     68 //	VARDEF x
     69 //
     70 // then the liveness analysis will not preserve the new value of x, because
     71 // the VARDEF appears to have "overwritten" it.
     72 //
     73 // VARDEF is a bit of a kludge to work around the fact that the instruction
     74 // stream is working on single-word values but the liveness analysis
     75 // wants to work on individual variables, which might be multi-word
     76 // aggregates. It might make sense at some point to look into letting
     77 // the liveness analysis work on single-word values as well, although
     78 // there are complications around interface values, slices, and strings,
     79 // all of which cannot be treated as individual words.
     80 //
     81 // VARKILL is the opposite of VARDEF: it marks a value as no longer needed,
     82 // even if its address has been taken. That is, a VARKILL annotation asserts
     83 // that its argument is certainly dead, for use when the liveness analysis
     84 // would not otherwise be able to deduce that fact.
     85 
     86 func gvardefx(n *Node, as obj.As) {
     87 	if n == nil {
     88 		Fatalf("gvardef nil")
     89 	}
     90 	if n.Op != ONAME {
     91 		yyerror("gvardef %#v; %v", n.Op, n)
     92 		return
     93 	}
     94 
     95 	switch n.Class {
     96 	case PAUTO, PPARAM, PPARAMOUT:
     97 		if !n.Used {
     98 			Prog(obj.ANOP)
     99 			return
    100 		}
    101 
    102 		if as == obj.AVARLIVE {
    103 			Gins(as, n, nil)
    104 		} else {
    105 			Gins(as, nil, n)
    106 		}
    107 	}
    108 }
    109 
    110 func Gvardef(n *Node) {
    111 	gvardefx(n, obj.AVARDEF)
    112 }
    113 
    114 func Gvarkill(n *Node) {
    115 	gvardefx(n, obj.AVARKILL)
    116 }
    117 
    118 func Gvarlive(n *Node) {
    119 	gvardefx(n, obj.AVARLIVE)
    120 }
    121 
    122 func removevardef(firstp *obj.Prog) {
    123 	for p := firstp; p != nil; p = p.Link {
    124 		for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
    125 			p.Link = p.Link.Link
    126 		}
    127 		if p.To.Type == obj.TYPE_BRANCH {
    128 			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.(*obj.Prog).As == obj.AVARLIVE) {
    129 				p.To.Val = p.To.Val.(*obj.Prog).Link
    130 			}
    131 		}
    132 	}
    133 }
    134 
    135 func emitptrargsmap() {
    136 	if Curfn.Func.Nname.Sym.Name == "_" {
    137 		return
    138 	}
    139 	sym := lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Func.Nname.Sym.Name))
    140 
    141 	nptr := int(Curfn.Type.ArgWidth() / int64(Widthptr))
    142 	bv := bvalloc(int32(nptr) * 2)
    143 	nbitmap := 1
    144 	if Curfn.Type.Results().NumFields() > 0 {
    145 		nbitmap = 2
    146 	}
    147 	off := duint32(sym, 0, uint32(nbitmap))
    148 	off = duint32(sym, off, uint32(bv.n))
    149 	var xoffset int64
    150 	if Curfn.IsMethod() {
    151 		xoffset = 0
    152 		onebitwalktype1(Curfn.Type.Recvs(), &xoffset, bv)
    153 	}
    154 
    155 	if Curfn.Type.Params().NumFields() > 0 {
    156 		xoffset = 0
    157 		onebitwalktype1(Curfn.Type.Params(), &xoffset, bv)
    158 	}
    159 
    160 	off = dbvec(sym, off, bv)
    161 	if Curfn.Type.Results().NumFields() > 0 {
    162 		xoffset = 0
    163 		onebitwalktype1(Curfn.Type.Results(), &xoffset, bv)
    164 		off = dbvec(sym, off, bv)
    165 	}
    166 
    167 	ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL)
    168 }
    169 
    170 // cmpstackvarlt reports whether the stack variable a sorts before b.
    171 //
    172 // Sort the list of stack variables. Autos after anything else,
    173 // within autos, unused after used, within used, things with
    174 // pointers first, zeroed things first, and then decreasing size.
    175 // Because autos are laid out in decreasing addresses
    176 // on the stack, pointers first, zeroed things first and decreasing size
    177 // really means, in memory, things with pointers needing zeroing at
    178 // the top of the stack and increasing in size.
    179 // Non-autos sort on offset.
    180 func cmpstackvarlt(a, b *Node) bool {
    181 	if (a.Class == PAUTO) != (b.Class == PAUTO) {
    182 		return b.Class == PAUTO
    183 	}
    184 
    185 	if a.Class != PAUTO {
    186 		return a.Xoffset < b.Xoffset
    187 	}
    188 
    189 	if a.Used != b.Used {
    190 		return a.Used
    191 	}
    192 
    193 	ap := haspointers(a.Type)
    194 	bp := haspointers(b.Type)
    195 	if ap != bp {
    196 		return ap
    197 	}
    198 
    199 	ap = a.Name.Needzero
    200 	bp = b.Name.Needzero
    201 	if ap != bp {
    202 		return ap
    203 	}
    204 
    205 	if a.Type.Width != b.Type.Width {
    206 		return a.Type.Width > b.Type.Width
    207 	}
    208 
    209 	return a.Sym.Name < b.Sym.Name
    210 }
    211 
    212 // byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
    213 type byStackVar []*Node
    214 
    215 func (s byStackVar) Len() int           { return len(s) }
    216 func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
    217 func (s byStackVar) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    218 
    219 var scratchFpMem *Node
    220 
    221 func (s *ssaExport) AllocFrame(f *ssa.Func) {
    222 	Stksize = 0
    223 	stkptrsize = 0
    224 
    225 	// Mark the PAUTO's unused.
    226 	for _, ln := range Curfn.Func.Dcl {
    227 		if ln.Class == PAUTO {
    228 			ln.Used = false
    229 		}
    230 	}
    231 
    232 	for _, l := range f.RegAlloc {
    233 		if ls, ok := l.(ssa.LocalSlot); ok {
    234 			ls.N.(*Node).Used = true
    235 		}
    236 
    237 	}
    238 
    239 	scratchUsed := false
    240 	for _, b := range f.Blocks {
    241 		for _, v := range b.Values {
    242 			switch a := v.Aux.(type) {
    243 			case *ssa.ArgSymbol:
    244 				a.Node.(*Node).Used = true
    245 			case *ssa.AutoSymbol:
    246 				a.Node.(*Node).Used = true
    247 			}
    248 
    249 			if !scratchUsed {
    250 				scratchUsed = v.Op.UsesScratch()
    251 			}
    252 		}
    253 	}
    254 
    255 	if f.Config.NeedsFpScratch {
    256 		scratchFpMem = temp(Types[TUINT64])
    257 		scratchFpMem.Used = scratchUsed
    258 	}
    259 
    260 	sort.Sort(byStackVar(Curfn.Func.Dcl))
    261 
    262 	// Reassign stack offsets of the locals that are used.
    263 	for i, n := range Curfn.Func.Dcl {
    264 		if n.Op != ONAME || n.Class != PAUTO {
    265 			continue
    266 		}
    267 		if !n.Used {
    268 			Curfn.Func.Dcl = Curfn.Func.Dcl[:i]
    269 			break
    270 		}
    271 
    272 		dowidth(n.Type)
    273 		w := n.Type.Width
    274 		if w >= Thearch.MAXWIDTH || w < 0 {
    275 			Fatalf("bad width")
    276 		}
    277 		Stksize += w
    278 		Stksize = Rnd(Stksize, int64(n.Type.Align))
    279 		if haspointers(n.Type) {
    280 			stkptrsize = Stksize
    281 		}
    282 		if Thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
    283 			Stksize = Rnd(Stksize, int64(Widthptr))
    284 		}
    285 		if Stksize >= 1<<31 {
    286 			setlineno(Curfn)
    287 			yyerror("stack frame too large (>2GB)")
    288 		}
    289 
    290 		n.Xoffset = -Stksize
    291 	}
    292 
    293 	Stksize = Rnd(Stksize, int64(Widthreg))
    294 	stkptrsize = Rnd(stkptrsize, int64(Widthreg))
    295 }
    296 
    297 func compile(fn *Node) {
    298 	if Newproc == nil {
    299 		Newproc = Sysfunc("newproc")
    300 		Deferproc = Sysfunc("deferproc")
    301 		Deferreturn = Sysfunc("deferreturn")
    302 		panicindex = Sysfunc("panicindex")
    303 		panicslice = Sysfunc("panicslice")
    304 		panicdivide = Sysfunc("panicdivide")
    305 		growslice = Sysfunc("growslice")
    306 		panicdottype = Sysfunc("panicdottype")
    307 		panicnildottype = Sysfunc("panicnildottype")
    308 		assertE2I = Sysfunc("assertE2I")
    309 		assertE2I2 = Sysfunc("assertE2I2")
    310 		assertI2I = Sysfunc("assertI2I")
    311 		assertI2I2 = Sysfunc("assertI2I2")
    312 	}
    313 
    314 	defer func(lno int32) {
    315 		lineno = lno
    316 	}(setlineno(fn))
    317 
    318 	Curfn = fn
    319 	dowidth(Curfn.Type)
    320 
    321 	if fn.Nbody.Len() == 0 {
    322 		if pure_go || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
    323 			yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
    324 			return
    325 		}
    326 
    327 		emitptrargsmap()
    328 		return
    329 	}
    330 
    331 	saveerrors()
    332 
    333 	if Curfn.Type.FuncType().Outnamed {
    334 		// add clearing of the output parameters
    335 		for _, t := range Curfn.Type.Results().Fields().Slice() {
    336 			if t.Nname != nil {
    337 				n := nod(OAS, t.Nname, nil)
    338 				n = typecheck(n, Etop)
    339 				Curfn.Nbody.Prepend(n)
    340 			}
    341 		}
    342 	}
    343 
    344 	order(Curfn)
    345 	if nerrors != 0 {
    346 		return
    347 	}
    348 
    349 	hasdefer = false
    350 	walk(Curfn)
    351 	if nerrors != 0 {
    352 		return
    353 	}
    354 	if instrumenting {
    355 		instrument(Curfn)
    356 	}
    357 	if nerrors != 0 {
    358 		return
    359 	}
    360 
    361 	// Build an SSA backend function.
    362 	ssafn := buildssa(Curfn)
    363 	if nerrors != 0 {
    364 		return
    365 	}
    366 
    367 	newplist()
    368 
    369 	setlineno(Curfn)
    370 
    371 	nam := Curfn.Func.Nname
    372 	if isblank(nam) {
    373 		nam = nil
    374 	}
    375 	ptxt := Gins(obj.ATEXT, nam, nil)
    376 	ptxt.From3 = new(obj.Addr)
    377 	if fn.Func.Dupok {
    378 		ptxt.From3.Offset |= obj.DUPOK
    379 	}
    380 	if fn.Func.Wrapper {
    381 		ptxt.From3.Offset |= obj.WRAPPER
    382 	}
    383 	if fn.Func.NoFramePointer {
    384 		ptxt.From3.Offset |= obj.NOFRAME
    385 	}
    386 	if fn.Func.Needctxt {
    387 		ptxt.From3.Offset |= obj.NEEDCTXT
    388 	}
    389 	if fn.Func.Pragma&Nosplit != 0 {
    390 		ptxt.From3.Offset |= obj.NOSPLIT
    391 	}
    392 	if fn.Func.ReflectMethod {
    393 		ptxt.From3.Offset |= obj.REFLECTMETHOD
    394 	}
    395 	if fn.Func.Pragma&Systemstack != 0 {
    396 		ptxt.From.Sym.Set(obj.AttrCFunc, true)
    397 	}
    398 
    399 	// Clumsy but important.
    400 	// See test/recover.go for test cases and src/reflect/value.go
    401 	// for the actual functions being considered.
    402 	if myimportpath == "reflect" {
    403 		if Curfn.Func.Nname.Sym.Name == "callReflect" || Curfn.Func.Nname.Sym.Name == "callMethod" {
    404 			ptxt.From3.Offset |= obj.WRAPPER
    405 		}
    406 	}
    407 
    408 	gcargs := makefuncdatasym("gcargs", obj.FUNCDATA_ArgsPointerMaps)
    409 	gclocals := makefuncdatasym("gclocals", obj.FUNCDATA_LocalsPointerMaps)
    410 
    411 	if obj.Fieldtrack_enabled != 0 && len(Curfn.Func.FieldTrack) > 0 {
    412 		trackSyms := make([]*Sym, 0, len(Curfn.Func.FieldTrack))
    413 		for sym := range Curfn.Func.FieldTrack {
    414 			trackSyms = append(trackSyms, sym)
    415 		}
    416 		sort.Sort(symByName(trackSyms))
    417 		for _, sym := range trackSyms {
    418 			gtrack(sym)
    419 		}
    420 	}
    421 
    422 	for _, n := range fn.Func.Dcl {
    423 		if n.Op != ONAME { // might be OTYPE or OLITERAL
    424 			continue
    425 		}
    426 		switch n.Class {
    427 		case PAUTO:
    428 			if !n.Used {
    429 				continue
    430 			}
    431 			fallthrough
    432 		case PPARAM, PPARAMOUT:
    433 			// The symbol is excluded later from debugging info if its name begins ".autotmp_", but the type is still necessary.
    434 			// See bugs #17644 and #17830 and cmd/internal/dwarf/dwarf.go
    435 			p := Gins(obj.ATYPE, n, nil)
    436 			p.From.Sym = obj.Linklookup(Ctxt, n.Sym.Name, 0)
    437 			p.To.Type = obj.TYPE_MEM
    438 			p.To.Name = obj.NAME_EXTERN
    439 			p.To.Sym = Linksym(ngotype(n))
    440 		}
    441 	}
    442 
    443 	genssa(ssafn, ptxt, gcargs, gclocals)
    444 	ssafn.Free()
    445 }
    446 
    447 type symByName []*Sym
    448 
    449 func (a symByName) Len() int           { return len(a) }
    450 func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
    451 func (a symByName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    452