github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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 panicdottypeE = Sysfunc("panicdottypeE") 310 panicdottypeI = Sysfunc("panicdottypeI") 311 panicnildottype = Sysfunc("panicnildottype") 312 assertE2I = Sysfunc("assertE2I") 313 assertE2I2 = Sysfunc("assertE2I2") 314 assertI2I = Sysfunc("assertI2I") 315 assertI2I2 = Sysfunc("assertI2I2") 316 } 317 318 defer func(lno src.XPos) { 319 lineno = lno 320 }(setlineno(fn)) 321 322 Curfn = fn 323 dowidth(Curfn.Type) 324 325 if fn.Nbody.Len() == 0 { 326 if pure_go || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") { 327 yyerror("missing function body for %q", fn.Func.Nname.Sym.Name) 328 return 329 } 330 331 emitptrargsmap() 332 return 333 } 334 335 saveerrors() 336 337 if Curfn.Type.FuncType().Outnamed { 338 // add clearing of the output parameters 339 for _, t := range Curfn.Type.Results().Fields().Slice() { 340 if t.Nname != nil { 341 n := nod(OAS, t.Nname, nil) 342 n = typecheck(n, Etop) 343 Curfn.Nbody.Prepend(n) 344 } 345 } 346 } 347 348 order(Curfn) 349 if nerrors != 0 { 350 return 351 } 352 353 hasdefer = false 354 walk(Curfn) 355 if nerrors != 0 { 356 return 357 } 358 if instrumenting { 359 instrument(Curfn) 360 } 361 if nerrors != 0 { 362 return 363 } 364 365 // Build an SSA backend function. 366 ssafn := buildssa(Curfn) 367 if nerrors != 0 { 368 return 369 } 370 371 newplist() 372 373 setlineno(Curfn) 374 375 nam := Curfn.Func.Nname 376 if isblank(nam) { 377 nam = nil 378 } 379 ptxt := Gins(obj.ATEXT, nam, nil) 380 ptxt.From3 = new(obj.Addr) 381 if fn.Func.Dupok { 382 ptxt.From3.Offset |= obj.DUPOK 383 } 384 if fn.Func.Wrapper { 385 ptxt.From3.Offset |= obj.WRAPPER 386 } 387 if fn.Func.NoFramePointer { 388 ptxt.From3.Offset |= obj.NOFRAME 389 } 390 if fn.Func.Needctxt { 391 ptxt.From3.Offset |= obj.NEEDCTXT 392 } 393 if fn.Func.Pragma&Nosplit != 0 { 394 ptxt.From3.Offset |= obj.NOSPLIT 395 } 396 if fn.Func.ReflectMethod { 397 ptxt.From3.Offset |= obj.REFLECTMETHOD 398 } 399 if fn.Func.Pragma&Systemstack != 0 { 400 ptxt.From.Sym.Set(obj.AttrCFunc, true) 401 if fn.Func.Pragma&Nosplit != 0 { 402 yyerror("go:nosplit and go:systemstack cannot be combined") 403 } 404 } 405 406 // Clumsy but important. 407 // See test/recover.go for test cases and src/reflect/value.go 408 // for the actual functions being considered. 409 if myimportpath == "reflect" { 410 if Curfn.Func.Nname.Sym.Name == "callReflect" || Curfn.Func.Nname.Sym.Name == "callMethod" { 411 ptxt.From3.Offset |= obj.WRAPPER 412 } 413 } 414 415 gcargs := makefuncdatasym("gcargs·", obj.FUNCDATA_ArgsPointerMaps) 416 gclocals := makefuncdatasym("gclocals·", obj.FUNCDATA_LocalsPointerMaps) 417 418 if obj.Fieldtrack_enabled != 0 && len(Curfn.Func.FieldTrack) > 0 { 419 trackSyms := make([]*Sym, 0, len(Curfn.Func.FieldTrack)) 420 for sym := range Curfn.Func.FieldTrack { 421 trackSyms = append(trackSyms, sym) 422 } 423 sort.Sort(symByName(trackSyms)) 424 for _, sym := range trackSyms { 425 gtrack(sym) 426 } 427 } 428 429 gendebug(ptxt.From.Sym, fn.Func.Dcl) 430 431 genssa(ssafn, ptxt, gcargs, gclocals) 432 ssafn.Free() 433 } 434 435 func gendebug(fn *obj.LSym, decls []*Node) { 436 if fn == nil { 437 return 438 } 439 440 for _, n := range decls { 441 if n.Op != ONAME { // might be OTYPE or OLITERAL 442 continue 443 } 444 445 var name obj.AddrName 446 switch n.Class { 447 case PAUTO: 448 if !n.Used { 449 continue 450 } 451 name = obj.NAME_AUTO 452 case PPARAM, PPARAMOUT: 453 name = obj.NAME_PARAM 454 default: 455 continue 456 } 457 458 a := &obj.Auto{ 459 Asym: obj.Linklookup(Ctxt, n.Sym.Name, 0), 460 Aoffset: int32(n.Xoffset), 461 Name: name, 462 Gotype: Linksym(ngotype(n)), 463 } 464 465 a.Link = fn.Autom 466 fn.Autom = a 467 } 468 } 469 470 type symByName []*Sym 471 472 func (a symByName) Len() int { return len(a) } 473 func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name } 474 func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }