github.com/aloncn/graphics-go@v0.0.1/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/internal/obj" 9 "crypto/md5" 10 "fmt" 11 "strings" 12 ) 13 14 // "Portable" code generation. 15 16 var makefuncdatasym_nsym int32 17 18 func makefuncdatasym(namefmt string, funcdatakind int64) *Sym { 19 var nod Node 20 21 sym := Lookupf(namefmt, makefuncdatasym_nsym) 22 makefuncdatasym_nsym++ 23 pnod := newname(sym) 24 pnod.Class = PEXTERN 25 Nodconst(&nod, Types[TINT32], funcdatakind) 26 Thearch.Gins(obj.AFUNCDATA, &nod, pnod) 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 int) { 87 if n == nil { 88 Fatalf("gvardef nil") 89 } 90 if n.Op != ONAME { 91 Yyerror("gvardef %v; %v", Oconv(int(n.Op), obj.FmtSharp), n) 92 return 93 } 94 95 switch n.Class { 96 case PAUTO, PPARAM, PPARAMOUT: 97 if as == obj.AVARLIVE { 98 Thearch.Gins(as, n, nil) 99 } else { 100 Thearch.Gins(as, nil, n) 101 } 102 } 103 } 104 105 func Gvardef(n *Node) { 106 gvardefx(n, obj.AVARDEF) 107 } 108 109 func gvarkill(n *Node) { 110 gvardefx(n, obj.AVARKILL) 111 } 112 113 func gvarlive(n *Node) { 114 gvardefx(n, obj.AVARLIVE) 115 } 116 117 func removevardef(firstp *obj.Prog) { 118 for p := firstp; p != nil; p = p.Link { 119 for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) { 120 p.Link = p.Link.Link 121 } 122 if p.To.Type == obj.TYPE_BRANCH { 123 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) { 124 p.To.Val = p.To.Val.(*obj.Prog).Link 125 } 126 } 127 } 128 } 129 130 func gcsymdup(s *Sym) { 131 ls := Linksym(s) 132 if len(ls.R) > 0 { 133 Fatalf("cannot rosymdup %s with relocations", ls.Name) 134 } 135 ls.Name = fmt.Sprintf("gclocals·%x", md5.Sum(ls.P)) 136 ls.Dupok = 1 137 } 138 139 func emitptrargsmap() { 140 if Curfn.Func.Nname.Sym.Name == "_" { 141 return 142 } 143 sym := Lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Func.Nname.Sym.Name)) 144 145 nptr := int(Curfn.Type.Argwid / int64(Widthptr)) 146 bv := bvalloc(int32(nptr) * 2) 147 nbitmap := 1 148 if Curfn.Type.Outtuple > 0 { 149 nbitmap = 2 150 } 151 off := duint32(sym, 0, uint32(nbitmap)) 152 off = duint32(sym, off, uint32(bv.n)) 153 var xoffset int64 154 if Curfn.Type.Thistuple > 0 { 155 xoffset = 0 156 onebitwalktype1(getthisx(Curfn.Type), &xoffset, bv) 157 } 158 159 if Curfn.Type.Intuple > 0 { 160 xoffset = 0 161 onebitwalktype1(getinargx(Curfn.Type), &xoffset, bv) 162 } 163 164 for j := 0; int32(j) < bv.n; j += 32 { 165 off = duint32(sym, off, bv.b[j/32]) 166 } 167 if Curfn.Type.Outtuple > 0 { 168 xoffset = 0 169 onebitwalktype1(getoutargx(Curfn.Type), &xoffset, bv) 170 for j := 0; int32(j) < bv.n; j += 32 { 171 off = duint32(sym, off, bv.b[j/32]) 172 } 173 } 174 175 ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL) 176 } 177 178 // cmpstackvarlt reports whether the stack variable a sorts before b. 179 // 180 // Sort the list of stack variables. Autos after anything else, 181 // within autos, unused after used, within used, things with 182 // pointers first, zeroed things first, and then decreasing size. 183 // Because autos are laid out in decreasing addresses 184 // on the stack, pointers first, zeroed things first and decreasing size 185 // really means, in memory, things with pointers needing zeroing at 186 // the top of the stack and increasing in size. 187 // Non-autos sort on offset. 188 func cmpstackvarlt(a, b *Node) bool { 189 if a.Class != b.Class { 190 if a.Class == PAUTO { 191 return false 192 } 193 return true 194 } 195 196 if a.Class != PAUTO { 197 if a.Xoffset < b.Xoffset { 198 return true 199 } 200 if a.Xoffset > b.Xoffset { 201 return false 202 } 203 return false 204 } 205 206 if a.Used != b.Used { 207 return a.Used 208 } 209 210 ap := haspointers(a.Type) 211 bp := haspointers(b.Type) 212 if ap != bp { 213 return ap 214 } 215 216 ap = a.Name.Needzero 217 bp = b.Name.Needzero 218 if ap != bp { 219 return ap 220 } 221 222 if a.Type.Width < b.Type.Width { 223 return false 224 } 225 if a.Type.Width > b.Type.Width { 226 return true 227 } 228 229 return a.Sym.Name < b.Sym.Name 230 } 231 232 // stkdelta records the stack offset delta for a node 233 // during the compaction of the stack frame to remove 234 // unused stack slots. 235 var stkdelta = map[*Node]int64{} 236 237 // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. 238 func allocauto(ptxt *obj.Prog) { 239 Stksize = 0 240 stkptrsize = 0 241 242 if Curfn.Func.Dcl == nil { 243 return 244 } 245 246 // Mark the PAUTO's unused. 247 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 248 if ll.N.Class == PAUTO { 249 ll.N.Used = false 250 } 251 } 252 253 markautoused(ptxt) 254 255 listsort(&Curfn.Func.Dcl, cmpstackvarlt) 256 257 // Unused autos are at the end, chop 'em off. 258 ll := Curfn.Func.Dcl 259 260 n := ll.N 261 if n.Class == PAUTO && n.Op == ONAME && !n.Used { 262 // No locals used at all 263 Curfn.Func.Dcl = nil 264 265 fixautoused(ptxt) 266 return 267 } 268 269 for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next { 270 n = ll.Next.N 271 if n.Class == PAUTO && n.Op == ONAME && !n.Used { 272 ll.Next = nil 273 Curfn.Func.Dcl.End = ll 274 break 275 } 276 } 277 278 // Reassign stack offsets of the locals that are still there. 279 var w int64 280 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 281 n = ll.N 282 if n.Class != PAUTO || n.Op != ONAME { 283 continue 284 } 285 286 dowidth(n.Type) 287 w = n.Type.Width 288 if w >= Thearch.MAXWIDTH || w < 0 { 289 Fatalf("bad width") 290 } 291 Stksize += w 292 Stksize = Rnd(Stksize, int64(n.Type.Align)) 293 if haspointers(n.Type) { 294 stkptrsize = Stksize 295 } 296 if Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { 297 Stksize = Rnd(Stksize, int64(Widthptr)) 298 } 299 if Stksize >= 1<<31 { 300 setlineno(Curfn) 301 Yyerror("stack frame too large (>2GB)") 302 } 303 304 stkdelta[n] = -Stksize - n.Xoffset 305 } 306 307 Stksize = Rnd(Stksize, int64(Widthreg)) 308 stkptrsize = Rnd(stkptrsize, int64(Widthreg)) 309 310 fixautoused(ptxt) 311 312 // The debug information needs accurate offsets on the symbols. 313 for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { 314 if ll.N.Class != PAUTO || ll.N.Op != ONAME { 315 continue 316 } 317 ll.N.Xoffset += stkdelta[ll.N] 318 delete(stkdelta, ll.N) 319 } 320 } 321 322 func Cgen_checknil(n *Node) { 323 if Disable_checknil != 0 { 324 return 325 } 326 327 // Ideally we wouldn't see any integer types here, but we do. 328 if n.Type == nil || (!Isptr[n.Type.Etype] && !Isint[n.Type.Etype] && n.Type.Etype != TUNSAFEPTR) { 329 Dump("checknil", n) 330 Fatalf("bad checknil") 331 } 332 333 if ((Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { 334 var reg Node 335 Regalloc(®, Types[Tptr], n) 336 Cgen(n, ®) 337 Thearch.Gins(obj.ACHECKNIL, ®, nil) 338 Regfree(®) 339 return 340 } 341 342 Thearch.Gins(obj.ACHECKNIL, n, nil) 343 } 344 345 func compile(fn *Node) { 346 if Newproc == nil { 347 Newproc = Sysfunc("newproc") 348 Deferproc = Sysfunc("deferproc") 349 Deferreturn = Sysfunc("deferreturn") 350 Panicindex = Sysfunc("panicindex") 351 panicslice = Sysfunc("panicslice") 352 throwreturn = Sysfunc("throwreturn") 353 } 354 355 lno := setlineno(fn) 356 357 Curfn = fn 358 dowidth(Curfn.Type) 359 360 var oldstksize int64 361 var nod1 Node 362 var ptxt *obj.Prog 363 var pl *obj.Plist 364 var p *obj.Prog 365 var n *Node 366 var nam *Node 367 var gcargs *Sym 368 var gclocals *Sym 369 if fn.Nbody == nil { 370 if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") { 371 Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name) 372 goto ret 373 } 374 375 if Debug['A'] != 0 { 376 goto ret 377 } 378 emitptrargsmap() 379 goto ret 380 } 381 382 saveerrors() 383 384 // set up domain for labels 385 clearlabels() 386 387 if Curfn.Type.Outnamed { 388 // add clearing of the output parameters 389 var save Iter 390 t := Structfirst(&save, Getoutarg(Curfn.Type)) 391 392 for t != nil { 393 if t.Nname != nil { 394 n = Nod(OAS, t.Nname, nil) 395 typecheck(&n, Etop) 396 Curfn.Nbody = concat(list1(n), Curfn.Nbody) 397 } 398 399 t = structnext(&save) 400 } 401 } 402 403 order(Curfn) 404 if nerrors != 0 { 405 goto ret 406 } 407 408 hasdefer = false 409 walk(Curfn) 410 if nerrors != 0 { 411 goto ret 412 } 413 if instrumenting { 414 instrument(Curfn) 415 } 416 if nerrors != 0 { 417 goto ret 418 } 419 420 continpc = nil 421 breakpc = nil 422 423 pl = newplist() 424 pl.Name = Linksym(Curfn.Func.Nname.Sym) 425 426 setlineno(Curfn) 427 428 Nodconst(&nod1, Types[TINT32], 0) 429 nam = Curfn.Func.Nname 430 if isblank(nam) { 431 nam = nil 432 } 433 ptxt = Thearch.Gins(obj.ATEXT, nam, &nod1) 434 Afunclit(&ptxt.From, Curfn.Func.Nname) 435 ptxt.From3 = new(obj.Addr) 436 if fn.Func.Dupok { 437 ptxt.From3.Offset |= obj.DUPOK 438 } 439 if fn.Func.Wrapper { 440 ptxt.From3.Offset |= obj.WRAPPER 441 } 442 if fn.Func.Needctxt { 443 ptxt.From3.Offset |= obj.NEEDCTXT 444 } 445 if fn.Func.Nosplit { 446 ptxt.From3.Offset |= obj.NOSPLIT 447 } 448 if fn.Func.Systemstack { 449 ptxt.From.Sym.Cfunc = 1 450 } 451 452 // Clumsy but important. 453 // See test/recover.go for test cases and src/reflect/value.go 454 // for the actual functions being considered. 455 if myimportpath != "" && myimportpath == "reflect" { 456 if Curfn.Func.Nname.Sym.Name == "callReflect" || Curfn.Func.Nname.Sym.Name == "callMethod" { 457 ptxt.From3.Offset |= obj.WRAPPER 458 } 459 } 460 461 ginit() 462 463 gcargs = makefuncdatasym("gcargs·%d", obj.FUNCDATA_ArgsPointerMaps) 464 gclocals = makefuncdatasym("gclocals·%d", obj.FUNCDATA_LocalsPointerMaps) 465 466 for _, t := range Curfn.Func.Fieldtrack { 467 gtrack(tracksym(t)) 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 { 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 }