// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gc import ( "cmd/compile/internal/ssa" "cmd/compile/internal/types" "cmd/internal/dwarf" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" "cmd/internal/sys" "fmt" "math" "math/rand" "sort" "strings" "sync" "time" ) // "Portable" code generation. var ( nBackendWorkers int // number of concurrent backend workers, set by a compiler flag compilequeue []*Node // functions waiting to be compiled ) func emitptrargsmap() { if Curfn.funcname() == "_" { return } sym := lookup(fmt.Sprintf("%s.args_stackmap", Curfn.funcname())) lsym := sym.Linksym() nptr := int(Curfn.Type.ArgWidth() / int64(Widthptr)) bv := bvalloc(int32(nptr) * 2) nbitmap := 1 if Curfn.Type.NumResults() > 0 { nbitmap = 2 } off := duint32(lsym, 0, uint32(nbitmap)) off = duint32(lsym, off, uint32(bv.n)) if Curfn.IsMethod() { onebitwalktype1(Curfn.Type.Recvs(), 0, bv) } if Curfn.Type.NumParams() > 0 { onebitwalktype1(Curfn.Type.Params(), 0, bv) } off = dbvec(lsym, off, bv) if Curfn.Type.NumResults() > 0 { onebitwalktype1(Curfn.Type.Results(), 0, bv) off = dbvec(lsym, off, bv) } ggloblsym(lsym, int32(off), obj.RODATA|obj.LOCAL) } // cmpstackvarlt reports whether the stack variable a sorts before b. // // Sort the list of stack variables. Autos after anything else, // within autos, unused after used, within used, things with // pointers first, zeroed things first, and then decreasing size. // Because autos are laid out in decreasing addresses // on the stack, pointers first, zeroed things first and decreasing size // really means, in memory, things with pointers needing zeroing at // the top of the stack and increasing in size. // Non-autos sort on offset. func cmpstackvarlt(a, b *Node) bool { if (a.Class() == PAUTO) != (b.Class() == PAUTO) { return b.Class() == PAUTO } if a.Class() != PAUTO { return a.Xoffset < b.Xoffset } if a.Name.Used() != b.Name.Used() { return a.Name.Used() } ap := types.Haspointers(a.Type) bp := types.Haspointers(b.Type) if ap != bp { return ap } ap = a.Name.Needzero() bp = b.Name.Needzero() if ap != bp { return ap } if a.Type.Width != b.Type.Width { return a.Type.Width > b.Type.Width } return a.Sym.Name < b.Sym.Name } // byStackvar implements sort.Interface for []*Node using cmpstackvarlt. type byStackVar []*Node func (s byStackVar) Len() int { return len(s) } func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) } func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s *ssafn) AllocFrame(f *ssa.Func) { s.stksize = 0 s.stkptrsize = 0 fn := s.curfn.Func // Mark the PAUTO's unused. for _, ln := range fn.Dcl { if ln.Class() == PAUTO { ln.Name.SetUsed(false) } } for _, l := range f.RegAlloc { if ls, ok := l.(ssa.LocalSlot); ok { ls.N.(*Node).Name.SetUsed(true) } } scratchUsed := false for _, b := range f.Blocks { for _, v := range b.Values { if n, ok := v.Aux.(*Node); ok { switch n.Class() { case PPARAM, PPARAMOUT: // Don't modify nodfp; it is a global. if n != nodfp { n.Name.SetUsed(true) } case PAUTO: n.Name.SetUsed(true) } } if !scratchUsed { scratchUsed = v.Op.UsesScratch() } } } if f.Config.NeedsFpScratch && scratchUsed { s.scratchFpMem = tempAt(src.NoXPos, s.curfn, types.Types[TUINT64]) } sort.Sort(byStackVar(fn.Dcl)) // Reassign stack offsets of the locals that are used. for i, n := range fn.Dcl { if n.Op != ONAME || n.Class() != PAUTO { continue } if !n.Name.Used() { fn.Dcl = fn.Dcl[:i] break } dowidth(n.Type) w := n.Type.Width if w >= thearch.MAXWIDTH || w < 0 { Fatalf("bad width") } s.stksize += w s.stksize = Rnd(s.stksize, int64(n.Type.Align)) if types.Haspointers(n.Type) { s.stkptrsize = s.stksize } if thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { s.stksize = Rnd(s.stksize, int64(Widthptr)) } n.Xoffset = -s.stksize } s.stksize = Rnd(s.stksize, int64(Widthreg)) s.stkptrsize = Rnd(s.stkptrsize, int64(Widthreg)) } func compile(fn *Node) { Curfn = fn dowidth(fn.Type) if fn.Nbody.Len() == 0 { emitptrargsmap() return } saveerrors() order(fn) if nerrors != 0 { return } walk(fn) if nerrors != 0 { return } if instrumenting { instrument(fn) } // From this point, there should be no uses of Curfn. Enforce that. Curfn = nil // Set up the function's LSym early to avoid data races with the assemblers. fn.Func.initLSym() if compilenow() { compileSSA(fn, 0) } else { compilequeue = append(compilequeue, fn) } } // compilenow reports whether to compile immediately. // If functions are not compiled immediately, // they are enqueued in compilequeue, // which is drained by compileFunctions. func compilenow() bool { return nBackendWorkers == 1 && Debug_compilelater == 0 } const maxStackSize = 1 << 30 // compileSSA builds an SSA backend function, // uses it to generate a plist, // and flushes that plist to machine code. // worker indicates which of the backend workers is doing the processing. func compileSSA(fn *Node, worker int) { f := buildssa(fn, worker) if f.Frontend().(*ssafn).stksize >= maxStackSize { largeStackFramesMu.Lock() largeStackFrames = append(largeStackFrames, fn.Pos) largeStackFramesMu.Unlock() return } pp := newProgs(fn, worker) genssa(f, pp) pp.Flush() // fieldtrack must be called after pp.Flush. See issue 20014. fieldtrack(pp.Text.From.Sym, fn.Func.FieldTrack) pp.Free() } func init() { if raceEnabled { rand.Seed(time.Now().UnixNano()) } } // compileFunctions compiles all functions in compilequeue. // It fans out nBackendWorkers to do the work // and waits for them to complete. func compileFunctions() { if len(compilequeue) != 0 { sizeCalculationDisabled = true // not safe to calculate sizes concurrently if raceEnabled { // Randomize compilation order to try to shake out races. tmp := make([]*Node, len(compilequeue)) perm := rand.Perm(len(compilequeue)) for i, v := range perm { tmp[v] = compilequeue[i] } copy(compilequeue, tmp) } else { // Compile the longest functions first, // since they're most likely to be the slowest. // This helps avoid stragglers. obj.SortSlice(compilequeue, func(i, j int) bool { return compilequeue[i].Nbody.Len() > compilequeue[j].Nbody.Len() }) } var wg sync.WaitGroup Ctxt.InParallel = true c := make(chan *Node, nBackendWorkers) for i := 0; i < nBackendWorkers; i++ { wg.Add(1) go func(worker int) { for fn := range c { compileSSA(fn, worker) } wg.Done() }(i) } for _, fn := range compilequeue { c <- fn } close(c) compilequeue = nil wg.Wait() Ctxt.InParallel = false sizeCalculationDisabled = false } } func debuginfo(fnsym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) { fn := curfn.(*Node) debugInfo := fn.Func.DebugInfo fn.Func.DebugInfo = nil if fn.Func.Nname != nil { if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect { Fatalf("unexpected fnsym: %v != %v", fnsym, expect) } } var automDecls []*Node // Populate Automs for fn. for _, n := range fn.Func.Dcl { if n.Op != ONAME { // might be OTYPE or OLITERAL continue } var name obj.AddrName switch n.Class() { case PAUTO: if !n.Name.Used() { // Text == nil -> generating abstract function if fnsym.Func.Text != nil { Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") } continue } name = obj.NAME_AUTO case PPARAM, PPARAMOUT: name = obj.NAME_PARAM default: continue } automDecls = append(automDecls, n) gotype := ngotype(n).Linksym() fnsym.Func.Autom = append(fnsym.Func.Autom, &obj.Auto{ Asym: Ctxt.Lookup(n.Sym.Name), Aoffset: int32(n.Xoffset), Name: name, Gotype: gotype, }) } decls, dwarfVars := createDwarfVars(fnsym, debugInfo, automDecls) var varScopes []ScopeID for _, decl := range decls { pos := decl.Pos if decl.Name.Defn != nil && (decl.Name.Captured() || decl.Name.Byval()) { // It's not clear which position is correct for captured variables here: // * decl.Pos is the wrong position for captured variables, in the inner // function, but it is the right position in the outer function. // * decl.Name.Defn is nil for captured variables that were arguments // on the outer function, however the decl.Pos for those seems to be // correct. // * decl.Name.Defn is the "wrong" thing for variables declared in the // header of a type switch, it's their position in the header, rather // than the position of the case statement. In principle this is the // right thing, but here we prefer the latter because it makes each // instance of the header variable local to the lexical block of its // case statement. // This code is probably wrong for type switch variables that are also // captured. pos = decl.Name.Defn.Pos } varScopes = append(varScopes, findScope(fn.Func.Marks, pos)) } scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes) var inlcalls dwarf.InlCalls if genDwarfInline > 0 { inlcalls = assembleInlines(fnsym, fn, dwarfVars) } return scopes, inlcalls } // createSimpleVars creates a DWARF entry for every variable declared in the // function, claiming that they are permanently on the stack. func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) { var vars []*dwarf.Var var decls []*Node selected := make(map[*Node]bool) for _, n := range automDecls { if n.IsAutoTmp() { continue } var abbrev int offs := n.Xoffset switch n.Class() { case PAUTO: abbrev = dwarf.DW_ABRV_AUTO if Ctxt.FixedFrameSize() == 0 { offs -= int64(Widthptr) } if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { offs -= int64(Widthptr) } case PPARAM, PPARAMOUT: abbrev = dwarf.DW_ABRV_PARAM offs += Ctxt.FixedFrameSize() default: Fatalf("createSimpleVars unexpected type %v for node %v", n.Class(), n) } selected[n] = true typename := dwarf.InfoPrefix + typesymname(n.Type) decls = append(decls, n) inlIndex := 0 if genDwarfInline > 1 { if n.InlFormal() || n.InlLocal() { inlIndex = posInlIndex(n.Pos) + 1 if n.InlFormal() { abbrev = dwarf.DW_ABRV_PARAM } } } declpos := Ctxt.InnermostPos(n.Pos) vars = append(vars, &dwarf.Var{ Name: n.Sym.Name, IsReturnValue: n.Class() == PPARAMOUT, IsInlFormal: n.InlFormal(), Abbrev: abbrev, StackOffset: int32(offs), Type: Ctxt.Lookup(typename), DeclFile: declpos.Base().SymFilename(), DeclLine: declpos.Line(), DeclCol: declpos.Col(), InlIndex: int32(inlIndex), ChildIndex: -1, }) } return decls, vars, selected } type varPart struct { varOffset int64 slot ssa.SlotID } func createComplexVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) { for _, blockDebug := range debugInfo.Blocks { for _, locList := range blockDebug.Variables { for _, loc := range locList.Locations { if loc.StartProg != nil { loc.StartPC = loc.StartProg.Pc } if loc.EndProg != nil { loc.EndPC = loc.EndProg.Pc } else { loc.EndPC = fnsym.Size } if Debug_locationlist == 0 { loc.EndProg = nil loc.StartProg = nil } } } } // Group SSA variables by the user variable they were decomposed from. varParts := map[*Node][]varPart{} ssaVars := make(map[*Node]bool) for slotID, slot := range debugInfo.VarSlots { for slot.SplitOf != nil { slot = slot.SplitOf } n := slot.N.(*Node) ssaVars[n] = true varParts[n] = append(varParts[n], varPart{varOffset(slot), ssa.SlotID(slotID)}) } // Produce a DWARF variable entry for each user variable. // Don't iterate over the map -- that's nondeterministic, and // createComplexVar has side effects. Instead, go by slot. var decls []*Node var vars []*dwarf.Var for _, slot := range debugInfo.VarSlots { for slot.SplitOf != nil { slot = slot.SplitOf } n := slot.N.(*Node) parts := varParts[n] if parts == nil { continue } // Don't work on this variable again, no matter how many slots it has. delete(varParts, n) // Get the order the parts need to be in to represent the memory // of the decomposed user variable. sort.Sort(partsByVarOffset(parts)) if dvar := createComplexVar(debugInfo, n, parts); dvar != nil { decls = append(decls, n) vars = append(vars, dvar) } } return decls, vars, ssaVars } func createDwarfVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*Node) ([]*Node, []*dwarf.Var) { // Collect a raw list of DWARF vars. var vars []*dwarf.Var var decls []*Node var selected map[*Node]bool if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && debugInfo != nil { decls, vars, selected = createComplexVars(fnsym, debugInfo, automDecls) } else { decls, vars, selected = createSimpleVars(automDecls) } var dcl []*Node if fnsym.WasInlined() { dcl = preInliningDcls(fnsym) } else { dcl = automDecls } // If optimization is enabled, the list above will typically be // missing some of the original pre-optimization variables in the // function (they may have been promoted to registers, folded into // constants, dead-coded away, etc). Here we add back in entries // for selected missing vars. Note that the recipe below creates a // conservative location. The idea here is that we want to // communicate to the user that "yes, there is a variable named X // in this function, but no, I don't have enough information to // reliably report its contents." for _, n := range dcl { if _, found := selected[n]; found { continue } c := n.Sym.Name[0] if c == '.' || n.Type.IsUntyped() { continue } typename := dwarf.InfoPrefix + typesymname(n.Type) decls = append(decls, n) abbrev := dwarf.DW_ABRV_AUTO_LOCLIST if n.Class() == PPARAM || n.Class() == PPARAMOUT { abbrev = dwarf.DW_ABRV_PARAM_LOCLIST } inlIndex := 0 if genDwarfInline > 1 { if n.InlFormal() || n.InlLocal() { inlIndex = posInlIndex(n.Pos) + 1 if n.InlFormal() { abbrev = dwarf.DW_ABRV_PARAM_LOCLIST } } } declpos := Ctxt.InnermostPos(n.Pos) vars = append(vars, &dwarf.Var{ Name: n.Sym.Name, IsReturnValue: n.Class() == PPARAMOUT, Abbrev: abbrev, StackOffset: int32(n.Xoffset), Type: Ctxt.Lookup(typename), DeclFile: declpos.Base().SymFilename(), DeclLine: declpos.Line(), DeclCol: declpos.Col(), InlIndex: int32(inlIndex), ChildIndex: -1, }) // Append a "deleted auto" entry to the autom list so as to // insure that the type in question is picked up by the linker. // See issue 22941. gotype := ngotype(n).Linksym() fnsym.Func.Autom = append(fnsym.Func.Autom, &obj.Auto{ Asym: Ctxt.Lookup(n.Sym.Name), Aoffset: int32(-1), Name: obj.NAME_DELETED_AUTO, Gotype: gotype, }) } return decls, vars } // Given a function that was inlined at some point during the // compilation, return a sorted list of nodes corresponding to the // autos/locals in that function prior to inlining. If this is a // function that is not local to the package being compiled, then the // names of the variables may have been "versioned" to avoid conflicts // with local vars; disregard this versioning when sorting. func preInliningDcls(fnsym *obj.LSym) []*Node { fn := Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*Node) var dcl, rdcl []*Node if fn.Name.Defn != nil { dcl = fn.Func.Inldcl.Slice() // local function } else { dcl = fn.Func.Dcl // imported function } for _, n := range dcl { c := n.Sym.Name[0] // Avoid reporting "_" parameters, since if there are more than // one, it can result in a collision later on, as in #23179. if unversion(n.Sym.Name) == "_" || c == '.' || n.Type.IsUntyped() { continue } rdcl = append(rdcl, n) } sort.Sort(byNodeName(rdcl)) return rdcl } func cmpNodeName(a, b *Node) bool { aart := 0 if strings.HasPrefix(a.Sym.Name, "~") { aart = 1 } bart := 0 if strings.HasPrefix(b.Sym.Name, "~") { bart = 1 } if aart != bart { return aart < bart } aname := unversion(a.Sym.Name) bname := unversion(b.Sym.Name) return aname < bname } // byNodeName implements sort.Interface for []*Node using cmpNodeName. type byNodeName []*Node func (s byNodeName) Len() int { return len(s) } func (s byNodeName) Less(i, j int) bool { return cmpNodeName(s[i], s[j]) } func (s byNodeName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // varOffset returns the offset of slot within the user variable it was // decomposed from. This has nothing to do with its stack offset. func varOffset(slot *ssa.LocalSlot) int64 { offset := slot.Off for ; slot.SplitOf != nil; slot = slot.SplitOf { offset += slot.SplitOffset } return offset } type partsByVarOffset []varPart func (a partsByVarOffset) Len() int { return len(a) } func (a partsByVarOffset) Less(i, j int) bool { return a[i].varOffset < a[j].varOffset } func (a partsByVarOffset) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // stackOffset returns the stack location of a LocalSlot relative to the // stack pointer, suitable for use in a DWARF location entry. This has nothing // to do with its offset in the user variable. func stackOffset(slot *ssa.LocalSlot) int32 { n := slot.N.(*Node) var base int64 switch n.Class() { case PAUTO: if Ctxt.FixedFrameSize() == 0 { base -= int64(Widthptr) } if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { base -= int64(Widthptr) } case PPARAM, PPARAMOUT: base += Ctxt.FixedFrameSize() } return int32(base + n.Xoffset + slot.Off) } // createComplexVar builds a DWARF variable entry and location list representing n. func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf.Var { slots := debugInfo.Slots var offs int64 // base stack offset for this kind of variable var abbrev int switch n.Class() { case PAUTO: abbrev = dwarf.DW_ABRV_AUTO_LOCLIST if Ctxt.FixedFrameSize() == 0 { offs -= int64(Widthptr) } if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { offs -= int64(Widthptr) } case PPARAM, PPARAMOUT: abbrev = dwarf.DW_ABRV_PARAM_LOCLIST offs += Ctxt.FixedFrameSize() default: return nil } gotype := ngotype(n).Linksym() typename := dwarf.InfoPrefix + gotype.Name[len("type."):] inlIndex := 0 if genDwarfInline > 1 { if n.InlFormal() || n.InlLocal() { inlIndex = posInlIndex(n.Pos) + 1 if n.InlFormal() { abbrev = dwarf.DW_ABRV_PARAM_LOCLIST } } } declpos := Ctxt.InnermostPos(n.Pos) dvar := &dwarf.Var{ Name: n.Sym.Name, IsReturnValue: n.Class() == PPARAMOUT, IsInlFormal: n.InlFormal(), Abbrev: abbrev, Type: Ctxt.Lookup(typename), // The stack offset is used as a sorting key, so for decomposed // variables just give it the lowest one. It's not used otherwise. // This won't work well if the first slot hasn't been assigned a stack // location, but it's not obvious how to do better. StackOffset: int32(stackOffset(slots[parts[0].slot])), DeclFile: declpos.Base().SymFilename(), DeclLine: declpos.Line(), DeclCol: declpos.Col(), InlIndex: int32(inlIndex), ChildIndex: -1, } if Debug_locationlist != 0 { Ctxt.Logf("Building location list for %+v. Parts:\n", n) for _, part := range parts { Ctxt.Logf("\t%v => %v\n", debugInfo.Slots[part.slot], debugInfo.SlotLocsString(part.slot)) } } // Given a variable that's been decomposed into multiple parts, // its location list may need a new entry after the beginning or // end of every location entry for each of its parts. For example: // // [variable] [pc range] // string.ptr |----|-----| |----| // string.len |------------| |--| // ... needs a location list like: // string |----|-----|-| |--|-| // // Note that location entries may or may not line up with each other, // and some of the result will only have one or the other part. // // To build the resulting list: // - keep a "current" pointer for each part // - find the next transition point // - advance the current pointer for each part up to that transition point // - build the piece for the range between that transition point and the next // - repeat type locID struct { block int loc int } findLoc := func(part varPart, id locID) *ssa.VarLoc { if id.block >= len(debugInfo.Blocks) { return nil } return debugInfo.Blocks[id.block].Variables[part.slot].Locations[id.loc] } nextLoc := func(part varPart, id locID) (locID, *ssa.VarLoc) { // Check if there's another loc in this block id.loc++ if b := debugInfo.Blocks[id.block]; b != nil && id.loc < len(b.Variables[part.slot].Locations) { return id, findLoc(part, id) } // Find the next block that has a loc for this part. id.loc = 0 id.block++ for ; id.block < len(debugInfo.Blocks); id.block++ { if b := debugInfo.Blocks[id.block]; b != nil && len(b.Variables[part.slot].Locations) != 0 { return id, findLoc(part, id) } } return id, nil } curLoc := make([]locID, len(slots)) // Position each pointer at the first entry for its slot. for _, part := range parts { if b := debugInfo.Blocks[0]; b != nil && len(b.Variables[part.slot].Locations) != 0 { // Block 0 has an entry; no need to advance. continue } curLoc[part.slot], _ = nextLoc(part, curLoc[part.slot]) } // findBoundaryAfter finds the next beginning or end of a piece after currentPC. findBoundaryAfter := func(currentPC int64) int64 { min := int64(math.MaxInt64) for _, part := range parts { // For each part, find the first PC greater than current. Doesn't // matter if it's a start or an end, since we're looking for any boundary. // If it's the new winner, save it. onePart: for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) { for _, pc := range [2]int64{loc.StartPC, loc.EndPC} { if pc > currentPC { if pc < min { min = pc } break onePart } } } } return min } var start int64 end := findBoundaryAfter(0) for { // Advance to the next chunk. start = end end = findBoundaryAfter(start) if end == math.MaxInt64 { break } dloc := dwarf.Location{StartPC: start, EndPC: end} if Debug_locationlist != 0 { Ctxt.Logf("Processing range %x -> %x\n", start, end) } // Advance curLoc to the last location that starts before/at start. // After this loop, if there's a location that covers [start, end), it will be current. // Otherwise the current piece will be too early. for _, part := range parts { choice := locID{-1, -1} for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) { if loc.StartPC > start { break //overshot } choice = i // best yet } if choice.block != -1 { curLoc[part.slot] = choice } if Debug_locationlist != 0 { Ctxt.Logf("\t %v => %v", slots[part.slot], curLoc[part.slot]) } } if Debug_locationlist != 0 { Ctxt.Logf("\n") } // Assemble the location list entry for this chunk. present := 0 for _, part := range parts { dpiece := dwarf.Piece{ Length: slots[part.slot].Type.Size(), } loc := findLoc(part, curLoc[part.slot]) if loc == nil || start >= loc.EndPC || end <= loc.StartPC { if Debug_locationlist != 0 { Ctxt.Logf("\t%v: missing", slots[part.slot]) } dpiece.Missing = true dloc.Pieces = append(dloc.Pieces, dpiece) continue } present++ if Debug_locationlist != 0 { Ctxt.Logf("\t%v: %v", slots[part.slot], debugInfo.Blocks[curLoc[part.slot].block].LocString(loc)) } if loc.OnStack { dpiece.OnStack = true dpiece.StackOffset = stackOffset(slots[loc.StackLocation]) } else { for reg := 0; reg < len(debugInfo.Registers); reg++ { if loc.Registers&(1<<uint8(reg)) != 0 { dpiece.RegNum = Ctxt.Arch.DWARFRegisters[debugInfo.Registers[reg].ObjNum()] } } } dloc.Pieces = append(dloc.Pieces, dpiece) } if present == 0 { if Debug_locationlist != 0 { Ctxt.Logf(" -> totally missing\n") } continue } // Extend the previous entry if possible. if len(dvar.LocationList) > 0 { prev := &dvar.LocationList[len(dvar.LocationList)-1] if prev.EndPC == dloc.StartPC && len(prev.Pieces) == len(dloc.Pieces) { equal := true for i := range prev.Pieces { if prev.Pieces[i] != dloc.Pieces[i] { equal = false } } if equal { prev.EndPC = end if Debug_locationlist != 0 { Ctxt.Logf("-> merged with previous, now %#v\n", prev) } continue } } } dvar.LocationList = append(dvar.LocationList, dloc) if Debug_locationlist != 0 { Ctxt.Logf("-> added: %#v\n", dloc) } } return dvar } // fieldtrack adds R_USEFIELD relocations to fnsym to record any // struct fields that it used. func fieldtrack(fnsym *obj.LSym, tracked map[*types.Sym]struct{}) { if fnsym == nil { return } if objabi.Fieldtrack_enabled == 0 || len(tracked) == 0 { return } trackSyms := make([]*types.Sym, 0, len(tracked)) for sym := range tracked { trackSyms = append(trackSyms, sym) } sort.Sort(symByName(trackSyms)) for _, sym := range trackSyms { r := obj.Addrel(fnsym) r.Sym = sym.Linksym() r.Type = objabi.R_USEFIELD } } type symByName []*types.Sym func (a symByName) Len() int { return len(a) } func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name } func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }