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