github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/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/compile/internal/types" 10 "cmd/internal/dwarf" 11 "cmd/internal/obj" 12 "cmd/internal/objabi" 13 "cmd/internal/src" 14 "cmd/internal/sys" 15 "fmt" 16 "math/rand" 17 "sort" 18 "sync" 19 "time" 20 ) 21 22 // "Portable" code generation. 23 24 var ( 25 nBackendWorkers int // number of concurrent backend workers, set by a compiler flag 26 compilequeue []*Node // functions waiting to be compiled 27 ) 28 29 func emitptrargsmap(fn *Node) { 30 if fn.funcname() == "_" { 31 return 32 } 33 sym := lookup(fmt.Sprintf("%s.args_stackmap", fn.funcname())) 34 lsym := sym.Linksym() 35 36 nptr := int(fn.Type.ArgWidth() / int64(Widthptr)) 37 bv := bvalloc(int32(nptr) * 2) 38 nbitmap := 1 39 if fn.Type.NumResults() > 0 { 40 nbitmap = 2 41 } 42 off := duint32(lsym, 0, uint32(nbitmap)) 43 off = duint32(lsym, off, uint32(bv.n)) 44 45 if fn.IsMethod() { 46 onebitwalktype1(fn.Type.Recvs(), 0, bv) 47 } 48 if fn.Type.NumParams() > 0 { 49 onebitwalktype1(fn.Type.Params(), 0, bv) 50 } 51 off = dbvec(lsym, off, bv) 52 53 if fn.Type.NumResults() > 0 { 54 onebitwalktype1(fn.Type.Results(), 0, bv) 55 off = dbvec(lsym, off, bv) 56 } 57 58 ggloblsym(lsym, int32(off), obj.RODATA|obj.LOCAL) 59 } 60 61 // cmpstackvarlt reports whether the stack variable a sorts before b. 62 // 63 // Sort the list of stack variables. Autos after anything else, 64 // within autos, unused after used, within used, things with 65 // pointers first, zeroed things first, and then decreasing size. 66 // Because autos are laid out in decreasing addresses 67 // on the stack, pointers first, zeroed things first and decreasing size 68 // really means, in memory, things with pointers needing zeroing at 69 // the top of the stack and increasing in size. 70 // Non-autos sort on offset. 71 func cmpstackvarlt(a, b *Node) bool { 72 if (a.Class() == PAUTO) != (b.Class() == PAUTO) { 73 return b.Class() == PAUTO 74 } 75 76 if a.Class() != PAUTO { 77 return a.Xoffset < b.Xoffset 78 } 79 80 if a.Name.Used() != b.Name.Used() { 81 return a.Name.Used() 82 } 83 84 ap := types.Haspointers(a.Type) 85 bp := types.Haspointers(b.Type) 86 if ap != bp { 87 return ap 88 } 89 90 ap = a.Name.Needzero() 91 bp = b.Name.Needzero() 92 if ap != bp { 93 return ap 94 } 95 96 if a.Type.Width != b.Type.Width { 97 return a.Type.Width > b.Type.Width 98 } 99 100 return a.Sym.Name < b.Sym.Name 101 } 102 103 // byStackvar implements sort.Interface for []*Node using cmpstackvarlt. 104 type byStackVar []*Node 105 106 func (s byStackVar) Len() int { return len(s) } 107 func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) } 108 func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 109 110 func (s *ssafn) AllocFrame(f *ssa.Func) { 111 s.stksize = 0 112 s.stkptrsize = 0 113 fn := s.curfn.Func 114 115 // Mark the PAUTO's unused. 116 for _, ln := range fn.Dcl { 117 if ln.Class() == PAUTO { 118 ln.Name.SetUsed(false) 119 } 120 } 121 122 for _, l := range f.RegAlloc { 123 if ls, ok := l.(ssa.LocalSlot); ok { 124 ls.N.(*Node).Name.SetUsed(true) 125 } 126 } 127 128 scratchUsed := false 129 for _, b := range f.Blocks { 130 for _, v := range b.Values { 131 if n, ok := v.Aux.(*Node); ok { 132 switch n.Class() { 133 case PPARAM, PPARAMOUT: 134 // Don't modify nodfp; it is a global. 135 if n != nodfp { 136 n.Name.SetUsed(true) 137 } 138 case PAUTO: 139 n.Name.SetUsed(true) 140 } 141 } 142 if !scratchUsed { 143 scratchUsed = v.Op.UsesScratch() 144 } 145 146 } 147 } 148 149 if f.Config.NeedsFpScratch && scratchUsed { 150 s.scratchFpMem = tempAt(src.NoXPos, s.curfn, types.Types[TUINT64]) 151 } 152 153 sort.Sort(byStackVar(fn.Dcl)) 154 155 // Reassign stack offsets of the locals that are used. 156 for i, n := range fn.Dcl { 157 if n.Op != ONAME || n.Class() != PAUTO { 158 continue 159 } 160 if !n.Name.Used() { 161 fn.Dcl = fn.Dcl[:i] 162 break 163 } 164 165 dowidth(n.Type) 166 w := n.Type.Width 167 if w >= thearch.MAXWIDTH || w < 0 { 168 Fatalf("bad width") 169 } 170 s.stksize += w 171 s.stksize = Rnd(s.stksize, int64(n.Type.Align)) 172 if types.Haspointers(n.Type) { 173 s.stkptrsize = s.stksize 174 } 175 if thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { 176 s.stksize = Rnd(s.stksize, int64(Widthptr)) 177 } 178 n.Xoffset = -s.stksize 179 } 180 181 s.stksize = Rnd(s.stksize, int64(Widthreg)) 182 s.stkptrsize = Rnd(s.stkptrsize, int64(Widthreg)) 183 } 184 185 func funccompile(fn *Node) { 186 if Curfn != nil { 187 Fatalf("funccompile %v inside %v", fn.Func.Nname.Sym, Curfn.Func.Nname.Sym) 188 } 189 190 if fn.Type == nil { 191 if nerrors == 0 { 192 Fatalf("funccompile missing type") 193 } 194 return 195 } 196 197 // assign parameter offsets 198 dowidth(fn.Type) 199 200 if fn.Nbody.Len() == 0 { 201 emitptrargsmap(fn) 202 return 203 } 204 205 dclcontext = PAUTO 206 Curfn = fn 207 208 compile(fn) 209 210 Curfn = nil 211 dclcontext = PEXTERN 212 } 213 214 func compile(fn *Node) { 215 saveerrors() 216 217 order(fn) 218 if nerrors != 0 { 219 return 220 } 221 222 walk(fn) 223 if nerrors != 0 { 224 return 225 } 226 if instrumenting { 227 instrument(fn) 228 } 229 230 // From this point, there should be no uses of Curfn. Enforce that. 231 Curfn = nil 232 233 // Set up the function's LSym early to avoid data races with the assemblers. 234 fn.Func.initLSym() 235 236 // Make sure type syms are declared for all types that might 237 // be types of stack objects. We need to do this here 238 // because symbols must be allocated before the parallel 239 // phase of the compiler. 240 if fn.Func.lsym != nil { // not func _(){} 241 for _, n := range fn.Func.Dcl { 242 switch n.Class() { 243 case PPARAM, PPARAMOUT, PAUTO: 244 if livenessShouldTrack(n) && n.Addrtaken() { 245 dtypesym(n.Type) 246 // Also make sure we allocate a linker symbol 247 // for the stack object data, for the same reason. 248 if fn.Func.lsym.Func.StackObjects == nil { 249 fn.Func.lsym.Func.StackObjects = lookup(fmt.Sprintf("%s.stkobj", fn.funcname())).Linksym() 250 } 251 } 252 } 253 } 254 } 255 256 if compilenow() { 257 compileSSA(fn, 0) 258 } else { 259 compilequeue = append(compilequeue, fn) 260 } 261 } 262 263 // compilenow reports whether to compile immediately. 264 // If functions are not compiled immediately, 265 // they are enqueued in compilequeue, 266 // which is drained by compileFunctions. 267 func compilenow() bool { 268 return nBackendWorkers == 1 && Debug_compilelater == 0 269 } 270 271 const maxStackSize = 1 << 30 272 273 // compileSSA builds an SSA backend function, 274 // uses it to generate a plist, 275 // and flushes that plist to machine code. 276 // worker indicates which of the backend workers is doing the processing. 277 func compileSSA(fn *Node, worker int) { 278 f := buildssa(fn, worker) 279 // Note: check arg size to fix issue 25507. 280 if f.Frontend().(*ssafn).stksize >= maxStackSize || fn.Type.ArgWidth() >= maxStackSize { 281 largeStackFramesMu.Lock() 282 largeStackFrames = append(largeStackFrames, fn.Pos) 283 largeStackFramesMu.Unlock() 284 return 285 } 286 pp := newProgs(fn, worker) 287 defer pp.Free() 288 genssa(f, pp) 289 // Check frame size again. 290 // The check above included only the space needed for local variables. 291 // After genssa, the space needed includes local variables and the callee arg region. 292 // We must do this check prior to calling pp.Flush. 293 // If there are any oversized stack frames, 294 // the assembler may emit inscrutable complaints about invalid instructions. 295 if pp.Text.To.Offset >= maxStackSize { 296 largeStackFramesMu.Lock() 297 largeStackFrames = append(largeStackFrames, fn.Pos) 298 largeStackFramesMu.Unlock() 299 return 300 } 301 302 pp.Flush() // assemble, fill in boilerplate, etc. 303 // fieldtrack must be called after pp.Flush. See issue 20014. 304 fieldtrack(pp.Text.From.Sym, fn.Func.FieldTrack) 305 } 306 307 func init() { 308 if raceEnabled { 309 rand.Seed(time.Now().UnixNano()) 310 } 311 } 312 313 // compileFunctions compiles all functions in compilequeue. 314 // It fans out nBackendWorkers to do the work 315 // and waits for them to complete. 316 func compileFunctions() { 317 if len(compilequeue) != 0 { 318 sizeCalculationDisabled = true // not safe to calculate sizes concurrently 319 if raceEnabled { 320 // Randomize compilation order to try to shake out races. 321 tmp := make([]*Node, len(compilequeue)) 322 perm := rand.Perm(len(compilequeue)) 323 for i, v := range perm { 324 tmp[v] = compilequeue[i] 325 } 326 copy(compilequeue, tmp) 327 } else { 328 // Compile the longest functions first, 329 // since they're most likely to be the slowest. 330 // This helps avoid stragglers. 331 obj.SortSlice(compilequeue, func(i, j int) bool { 332 return compilequeue[i].Nbody.Len() > compilequeue[j].Nbody.Len() 333 }) 334 } 335 var wg sync.WaitGroup 336 Ctxt.InParallel = true 337 c := make(chan *Node, nBackendWorkers) 338 for i := 0; i < nBackendWorkers; i++ { 339 wg.Add(1) 340 go func(worker int) { 341 for fn := range c { 342 compileSSA(fn, worker) 343 } 344 wg.Done() 345 }(i) 346 } 347 for _, fn := range compilequeue { 348 c <- fn 349 } 350 close(c) 351 compilequeue = nil 352 wg.Wait() 353 Ctxt.InParallel = false 354 sizeCalculationDisabled = false 355 } 356 } 357 358 func debuginfo(fnsym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) { 359 fn := curfn.(*Node) 360 if fn.Func.Nname != nil { 361 if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect { 362 Fatalf("unexpected fnsym: %v != %v", fnsym, expect) 363 } 364 } 365 366 var automDecls []*Node 367 // Populate Automs for fn. 368 for _, n := range fn.Func.Dcl { 369 if n.Op != ONAME { // might be OTYPE or OLITERAL 370 continue 371 } 372 var name obj.AddrName 373 switch n.Class() { 374 case PAUTO: 375 if !n.Name.Used() { 376 // Text == nil -> generating abstract function 377 if fnsym.Func.Text != nil { 378 Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") 379 } 380 continue 381 } 382 name = obj.NAME_AUTO 383 case PPARAM, PPARAMOUT: 384 name = obj.NAME_PARAM 385 default: 386 continue 387 } 388 automDecls = append(automDecls, n) 389 gotype := ngotype(n).Linksym() 390 fnsym.Func.Autom = append(fnsym.Func.Autom, &obj.Auto{ 391 Asym: Ctxt.Lookup(n.Sym.Name), 392 Aoffset: int32(n.Xoffset), 393 Name: name, 394 Gotype: gotype, 395 }) 396 } 397 398 decls, dwarfVars := createDwarfVars(fnsym, fn.Func, automDecls) 399 400 var varScopes []ScopeID 401 for _, decl := range decls { 402 pos := decl.Pos 403 if decl.Name.Defn != nil && (decl.Name.Captured() || decl.Name.Byval()) { 404 // It's not clear which position is correct for captured variables here: 405 // * decl.Pos is the wrong position for captured variables, in the inner 406 // function, but it is the right position in the outer function. 407 // * decl.Name.Defn is nil for captured variables that were arguments 408 // on the outer function, however the decl.Pos for those seems to be 409 // correct. 410 // * decl.Name.Defn is the "wrong" thing for variables declared in the 411 // header of a type switch, it's their position in the header, rather 412 // than the position of the case statement. In principle this is the 413 // right thing, but here we prefer the latter because it makes each 414 // instance of the header variable local to the lexical block of its 415 // case statement. 416 // This code is probably wrong for type switch variables that are also 417 // captured. 418 pos = decl.Name.Defn.Pos 419 } 420 varScopes = append(varScopes, findScope(fn.Func.Marks, pos)) 421 } 422 423 scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes) 424 var inlcalls dwarf.InlCalls 425 if genDwarfInline > 0 { 426 inlcalls = assembleInlines(fnsym, dwarfVars) 427 } 428 return scopes, inlcalls 429 } 430 431 // createSimpleVars creates a DWARF entry for every variable declared in the 432 // function, claiming that they are permanently on the stack. 433 func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) { 434 var vars []*dwarf.Var 435 var decls []*Node 436 selected := make(map[*Node]bool) 437 for _, n := range automDecls { 438 if n.IsAutoTmp() { 439 continue 440 } 441 var abbrev int 442 offs := n.Xoffset 443 444 switch n.Class() { 445 case PAUTO: 446 abbrev = dwarf.DW_ABRV_AUTO 447 if Ctxt.FixedFrameSize() == 0 { 448 offs -= int64(Widthptr) 449 } 450 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" { 451 // There is a word space for FP on ARM64 even if the frame pointer is disabled 452 offs -= int64(Widthptr) 453 } 454 455 case PPARAM, PPARAMOUT: 456 abbrev = dwarf.DW_ABRV_PARAM 457 offs += Ctxt.FixedFrameSize() 458 default: 459 Fatalf("createSimpleVars unexpected type %v for node %v", n.Class(), n) 460 } 461 462 selected[n] = true 463 typename := dwarf.InfoPrefix + typesymname(n.Type) 464 decls = append(decls, n) 465 inlIndex := 0 466 if genDwarfInline > 1 { 467 if n.InlFormal() || n.InlLocal() { 468 inlIndex = posInlIndex(n.Pos) + 1 469 if n.InlFormal() { 470 abbrev = dwarf.DW_ABRV_PARAM 471 } 472 } 473 } 474 declpos := Ctxt.InnermostPos(n.Pos) 475 vars = append(vars, &dwarf.Var{ 476 Name: n.Sym.Name, 477 IsReturnValue: n.Class() == PPARAMOUT, 478 IsInlFormal: n.InlFormal(), 479 Abbrev: abbrev, 480 StackOffset: int32(offs), 481 Type: Ctxt.Lookup(typename), 482 DeclFile: declpos.RelFilename(), 483 DeclLine: declpos.RelLine(), 484 DeclCol: declpos.Col(), 485 InlIndex: int32(inlIndex), 486 ChildIndex: -1, 487 }) 488 } 489 return decls, vars, selected 490 } 491 492 // createComplexVars creates recomposed DWARF vars with location lists, 493 // suitable for describing optimized code. 494 func createComplexVars(fn *Func) ([]*Node, []*dwarf.Var, map[*Node]bool) { 495 debugInfo := fn.DebugInfo 496 497 // Produce a DWARF variable entry for each user variable. 498 var decls []*Node 499 var vars []*dwarf.Var 500 ssaVars := make(map[*Node]bool) 501 502 for varID, dvar := range debugInfo.Vars { 503 n := dvar.(*Node) 504 ssaVars[n] = true 505 for _, slot := range debugInfo.VarSlots[varID] { 506 ssaVars[debugInfo.Slots[slot].N.(*Node)] = true 507 } 508 509 if dvar := createComplexVar(fn, ssa.VarID(varID)); dvar != nil { 510 decls = append(decls, n) 511 vars = append(vars, dvar) 512 } 513 } 514 515 return decls, vars, ssaVars 516 } 517 518 // createDwarfVars process fn, returning a list of DWARF variables and the 519 // Nodes they represent. 520 func createDwarfVars(fnsym *obj.LSym, fn *Func, automDecls []*Node) ([]*Node, []*dwarf.Var) { 521 // Collect a raw list of DWARF vars. 522 var vars []*dwarf.Var 523 var decls []*Node 524 var selected map[*Node]bool 525 if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil { 526 decls, vars, selected = createComplexVars(fn) 527 } else { 528 decls, vars, selected = createSimpleVars(automDecls) 529 } 530 531 var dcl []*Node 532 if fnsym.WasInlined() { 533 dcl = preInliningDcls(fnsym) 534 } else { 535 dcl = automDecls 536 } 537 538 // If optimization is enabled, the list above will typically be 539 // missing some of the original pre-optimization variables in the 540 // function (they may have been promoted to registers, folded into 541 // constants, dead-coded away, etc). Here we add back in entries 542 // for selected missing vars. Note that the recipe below creates a 543 // conservative location. The idea here is that we want to 544 // communicate to the user that "yes, there is a variable named X 545 // in this function, but no, I don't have enough information to 546 // reliably report its contents." 547 for _, n := range dcl { 548 if _, found := selected[n]; found { 549 continue 550 } 551 c := n.Sym.Name[0] 552 if c == '.' || n.Type.IsUntyped() { 553 continue 554 } 555 typename := dwarf.InfoPrefix + typesymname(n.Type) 556 decls = append(decls, n) 557 abbrev := dwarf.DW_ABRV_AUTO_LOCLIST 558 if n.Class() == PPARAM || n.Class() == PPARAMOUT { 559 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 560 } 561 inlIndex := 0 562 if genDwarfInline > 1 { 563 if n.InlFormal() || n.InlLocal() { 564 inlIndex = posInlIndex(n.Pos) + 1 565 if n.InlFormal() { 566 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 567 } 568 } 569 } 570 declpos := Ctxt.InnermostPos(n.Pos) 571 vars = append(vars, &dwarf.Var{ 572 Name: n.Sym.Name, 573 IsReturnValue: n.Class() == PPARAMOUT, 574 Abbrev: abbrev, 575 StackOffset: int32(n.Xoffset), 576 Type: Ctxt.Lookup(typename), 577 DeclFile: declpos.RelFilename(), 578 DeclLine: declpos.RelLine(), 579 DeclCol: declpos.Col(), 580 InlIndex: int32(inlIndex), 581 ChildIndex: -1, 582 }) 583 // Append a "deleted auto" entry to the autom list so as to 584 // insure that the type in question is picked up by the linker. 585 // See issue 22941. 586 gotype := ngotype(n).Linksym() 587 fnsym.Func.Autom = append(fnsym.Func.Autom, &obj.Auto{ 588 Asym: Ctxt.Lookup(n.Sym.Name), 589 Aoffset: int32(-1), 590 Name: obj.NAME_DELETED_AUTO, 591 Gotype: gotype, 592 }) 593 594 } 595 596 return decls, vars 597 } 598 599 // Given a function that was inlined at some point during the 600 // compilation, return a sorted list of nodes corresponding to the 601 // autos/locals in that function prior to inlining. If this is a 602 // function that is not local to the package being compiled, then the 603 // names of the variables may have been "versioned" to avoid conflicts 604 // with local vars; disregard this versioning when sorting. 605 func preInliningDcls(fnsym *obj.LSym) []*Node { 606 fn := Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*Node) 607 var rdcl []*Node 608 for _, n := range fn.Func.Inl.Dcl { 609 c := n.Sym.Name[0] 610 // Avoid reporting "_" parameters, since if there are more than 611 // one, it can result in a collision later on, as in #23179. 612 if unversion(n.Sym.Name) == "_" || c == '.' || n.Type.IsUntyped() { 613 continue 614 } 615 rdcl = append(rdcl, n) 616 } 617 return rdcl 618 } 619 620 // stackOffset returns the stack location of a LocalSlot relative to the 621 // stack pointer, suitable for use in a DWARF location entry. This has nothing 622 // to do with its offset in the user variable. 623 func stackOffset(slot ssa.LocalSlot) int32 { 624 n := slot.N.(*Node) 625 var base int64 626 switch n.Class() { 627 case PAUTO: 628 if Ctxt.FixedFrameSize() == 0 { 629 base -= int64(Widthptr) 630 } 631 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" { 632 // There is a word space for FP on ARM64 even if the frame pointer is disabled 633 base -= int64(Widthptr) 634 } 635 case PPARAM, PPARAMOUT: 636 base += Ctxt.FixedFrameSize() 637 } 638 return int32(base + n.Xoffset + slot.Off) 639 } 640 641 // createComplexVar builds a single DWARF variable entry and location list. 642 func createComplexVar(fn *Func, varID ssa.VarID) *dwarf.Var { 643 debug := fn.DebugInfo 644 n := debug.Vars[varID].(*Node) 645 646 var abbrev int 647 switch n.Class() { 648 case PAUTO: 649 abbrev = dwarf.DW_ABRV_AUTO_LOCLIST 650 case PPARAM, PPARAMOUT: 651 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 652 default: 653 return nil 654 } 655 656 gotype := ngotype(n).Linksym() 657 typename := dwarf.InfoPrefix + gotype.Name[len("type."):] 658 inlIndex := 0 659 if genDwarfInline > 1 { 660 if n.InlFormal() || n.InlLocal() { 661 inlIndex = posInlIndex(n.Pos) + 1 662 if n.InlFormal() { 663 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 664 } 665 } 666 } 667 declpos := Ctxt.InnermostPos(n.Pos) 668 dvar := &dwarf.Var{ 669 Name: n.Sym.Name, 670 IsReturnValue: n.Class() == PPARAMOUT, 671 IsInlFormal: n.InlFormal(), 672 Abbrev: abbrev, 673 Type: Ctxt.Lookup(typename), 674 // The stack offset is used as a sorting key, so for decomposed 675 // variables just give it the first one. It's not used otherwise. 676 // This won't work well if the first slot hasn't been assigned a stack 677 // location, but it's not obvious how to do better. 678 StackOffset: stackOffset(debug.Slots[debug.VarSlots[varID][0]]), 679 DeclFile: declpos.RelFilename(), 680 DeclLine: declpos.RelLine(), 681 DeclCol: declpos.Col(), 682 InlIndex: int32(inlIndex), 683 ChildIndex: -1, 684 } 685 list := debug.LocationLists[varID] 686 if len(list) != 0 { 687 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { 688 debug.PutLocationList(list, Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) 689 } 690 } 691 return dvar 692 } 693 694 // fieldtrack adds R_USEFIELD relocations to fnsym to record any 695 // struct fields that it used. 696 func fieldtrack(fnsym *obj.LSym, tracked map[*types.Sym]struct{}) { 697 if fnsym == nil { 698 return 699 } 700 if objabi.Fieldtrack_enabled == 0 || len(tracked) == 0 { 701 return 702 } 703 704 trackSyms := make([]*types.Sym, 0, len(tracked)) 705 for sym := range tracked { 706 trackSyms = append(trackSyms, sym) 707 } 708 sort.Sort(symByName(trackSyms)) 709 for _, sym := range trackSyms { 710 r := obj.Addrel(fnsym) 711 r.Sym = sym.Linksym() 712 r.Type = objabi.R_USEFIELD 713 } 714 } 715 716 type symByName []*types.Sym 717 718 func (a symByName) Len() int { return len(a) } 719 func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name } 720 func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }