| |
| |
| |
|
|
| package dwarfgen |
|
|
| import ( |
| "bytes" |
| "flag" |
| "fmt" |
| "internal/buildcfg" |
| "slices" |
| "sort" |
| "strings" |
|
|
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| "cmd/compile/internal/reflectdata" |
| "cmd/compile/internal/ssa" |
| "cmd/compile/internal/ssagen" |
| "cmd/compile/internal/typecheck" |
| "cmd/compile/internal/types" |
| "cmd/internal/dwarf" |
| "cmd/internal/obj" |
| "cmd/internal/objabi" |
| "cmd/internal/src" |
| ) |
|
|
| func Info(ctxt *obj.Link, fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Scope, inlcalls dwarf.InlCalls) { |
| fn := curfn.(*ir.Func) |
|
|
| if fn.Nname != nil { |
| expect := fn.Linksym() |
| if fnsym.ABI() == obj.ABI0 { |
| expect = fn.LinksymABI(obj.ABI0) |
| } |
| if fnsym != expect { |
| base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| isODCLFUNC := infosym.Name == "" |
|
|
| var apdecls []*ir.Name |
| |
| if isODCLFUNC { |
| for _, n := range fn.Dcl { |
| if n.Op() != ir.ONAME { |
| continue |
| } |
| switch n.Class { |
| case ir.PAUTO: |
| if !n.Used() { |
| |
| if fnsym.Func().Text != nil { |
| base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") |
| } |
| continue |
| } |
| case ir.PPARAM, ir.PPARAMOUT: |
| default: |
| continue |
| } |
| if !ssa.IsVarWantedForDebug(n) { |
| continue |
| } |
| apdecls = append(apdecls, n) |
| if n.Type().Kind() == types.TSSA { |
| |
| |
| continue |
| } |
| fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) |
| } |
| } |
|
|
| var closureVars map[*ir.Name]int64 |
| if fn.Needctxt() { |
| closureVars = make(map[*ir.Name]int64) |
| csiter := typecheck.NewClosureStructIter(fn.ClosureVars) |
| for { |
| n, _, offset := csiter.Next() |
| if n == nil { |
| break |
| } |
| closureVars[n] = offset |
| if n.Heapaddr != nil { |
| closureVars[n.Heapaddr] = offset |
| } |
| } |
| } |
|
|
| decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls, closureVars) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| typesyms := []*obj.LSym{} |
| for t := range fnsym.Func().Autot { |
| typesyms = append(typesyms, t) |
| } |
| for i := range fnsym.R { |
| if fnsym.R[i].Type == objabi.R_USEIFACE && !strings.HasPrefix(fnsym.R[i].Sym.Name, "go:itab.") { |
| |
| typesyms = append(typesyms, fnsym.R[i].Sym) |
| } |
| } |
| slices.SortFunc(typesyms, func(a, b *obj.LSym) int { |
| return strings.Compare(a.Name, b.Name) |
| }) |
| var lastsym *obj.LSym |
| for _, sym := range typesyms { |
| if sym == lastsym { |
| continue |
| } |
| lastsym = sym |
| infosym.AddRel(ctxt, obj.Reloc{Type: objabi.R_USETYPE, Sym: sym}) |
| } |
| fnsym.Func().Autot = nil |
|
|
| var varScopes []ir.ScopeID |
| for _, decl := range decls { |
| pos := declPos(decl) |
| varScopes = append(varScopes, findScope(fn.Marks, pos)) |
| } |
|
|
| scopes = assembleScopes(fnsym, fn, dwarfVars, varScopes) |
| if base.Flag.GenDwarfInl > 0 { |
| inlcalls = assembleInlines(fnsym, dwarfVars) |
| } |
| return scopes, inlcalls |
| } |
|
|
| func declPos(decl *ir.Name) src.XPos { |
| return decl.Canonical().Pos() |
| } |
|
|
| |
| |
| func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var) { |
| |
| var vars []*dwarf.Var |
| var decls []*ir.Name |
| var selected ir.NameSet |
|
|
| if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK { |
| decls, vars, selected = createComplexVars(fnsym, fn, closureVars) |
| } else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK { |
| decls, vars, selected = createABIVars(fnsym, fn, apDecls, closureVars) |
| } else { |
| decls, vars, selected = createSimpleVars(fnsym, apDecls, closureVars) |
| } |
| if fn.DebugInfo != nil { |
| |
| for _, n := range fn.DebugInfo.(*ssa.FuncDebug).OptDcl { |
| if n.Class != ir.PAUTO { |
| continue |
| } |
| types.CalcSize(n.Type()) |
| if n.Type().Size() == 0 { |
| decls = append(decls, n) |
| vars = append(vars, createSimpleVar(fnsym, n, closureVars)) |
| vars[len(vars)-1].StackOffset = 0 |
| fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) |
| } |
| } |
| } |
|
|
| dcl := apDecls |
| if fnsym.WasInlined() { |
| dcl = preInliningDcls(fnsym) |
| } else { |
| |
| |
| |
| |
| |
| debugInfo := fn.DebugInfo.(*ssa.FuncDebug) |
| for _, n := range debugInfo.RegOutputParams { |
| if !ssa.IsVarWantedForDebug(n) { |
| continue |
| } |
| if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() { |
| base.Fatalf("invalid ir.Name on debugInfo.RegOutputParams list") |
| } |
| dcl = append(dcl, n) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| for _, n := range dcl { |
| if selected.Has(n) { |
| continue |
| } |
| c := n.Sym().Name[0] |
| if c == '.' || n.Type().IsUntyped() { |
| continue |
| } |
| if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) { |
| |
| |
| |
| |
| |
| |
| |
| vars = append(vars, createSimpleVar(fnsym, n, closureVars)) |
| decls = append(decls, n) |
| continue |
| } |
| typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) |
| decls = append(decls, n) |
| tag := dwarf.DW_TAG_variable |
| isReturnValue := (n.Class == ir.PPARAMOUT) |
| if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT { |
| tag = dwarf.DW_TAG_formal_parameter |
| } |
| inlIndex := 0 |
| if base.Flag.GenDwarfInl > 1 { |
| if n.InlFormal() || n.InlLocal() { |
| inlIndex = posInlIndex(n.Pos()) + 1 |
| if n.InlFormal() { |
| tag = dwarf.DW_TAG_formal_parameter |
| } |
| } |
| } |
| declpos := base.Ctxt.InnermostPos(n.Pos()) |
| dvar := &dwarf.Var{ |
| Name: n.Sym().Name, |
| IsReturnValue: isReturnValue, |
| Tag: tag, |
| WithLoclist: true, |
| StackOffset: int32(n.FrameOffset()), |
| Type: base.Ctxt.Lookup(typename), |
| DeclFile: declpos.RelFilename(), |
| DeclLine: declpos.RelLine(), |
| DeclCol: declpos.RelCol(), |
| InlIndex: int32(inlIndex), |
| ChildIndex: -1, |
| DictIndex: n.DictIndex, |
| ClosureOffset: closureOffset(n, closureVars), |
| } |
| if n.Esc() == ir.EscHeap { |
| if n.Heapaddr == nil { |
| base.Fatalf("invalid heap allocated var without Heapaddr") |
| } |
| debug := fn.DebugInfo.(*ssa.FuncDebug) |
| list := createHeapDerefLocationList(n, debug.EntryID) |
| dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { |
| debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) |
| } |
| } |
| vars = append(vars, dvar) |
| |
| fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) |
| } |
|
|
| |
| sortDeclsAndVars(fn, decls, vars) |
|
|
| return decls, vars |
| } |
|
|
| |
| |
| |
| |
| |
| |
| func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) { |
| paramOrder := make(map[*ir.Name]int) |
| idx := 1 |
| for _, f := range fn.Type().RecvParamsResults() { |
| if n, ok := f.Nname.(*ir.Name); ok { |
| paramOrder[n] = idx |
| idx++ |
| } |
| } |
| sort.Stable(varsAndDecls{decls, vars, paramOrder}) |
| } |
|
|
| type varsAndDecls struct { |
| decls []*ir.Name |
| vars []*dwarf.Var |
| paramOrder map[*ir.Name]int |
| } |
|
|
| func (v varsAndDecls) Len() int { |
| return len(v.decls) |
| } |
|
|
| func (v varsAndDecls) Less(i, j int) bool { |
| nameLT := func(ni, nj *ir.Name) bool { |
| oi, foundi := v.paramOrder[ni] |
| oj, foundj := v.paramOrder[nj] |
| if foundi { |
| if foundj { |
| return oi < oj |
| } else { |
| return true |
| } |
| } |
| return false |
| } |
| return nameLT(v.decls[i], v.decls[j]) |
| } |
|
|
| func (v varsAndDecls) Swap(i, j int) { |
| v.vars[i], v.vars[j] = v.vars[j], v.vars[i] |
| v.decls[i], v.decls[j] = v.decls[j], v.decls[i] |
| } |
|
|
| |
| |
| |
| |
| |
| |
| func preInliningDcls(fnsym *obj.LSym) []*ir.Name { |
| fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func) |
| var rdcl []*ir.Name |
| for _, n := range fn.Inl.Dcl { |
| c := n.Sym().Name[0] |
| |
| |
| if n.Sym().Name == "_" || c == '.' || n.Type().IsUntyped() { |
| continue |
| } |
| rdcl = append(rdcl, n) |
| } |
| return rdcl |
| } |
|
|
| |
| |
| func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { |
| var vars []*dwarf.Var |
| var decls []*ir.Name |
| var selected ir.NameSet |
| for _, n := range apDecls { |
| if ir.IsAutoTmp(n) { |
| continue |
| } |
|
|
| decls = append(decls, n) |
| vars = append(vars, createSimpleVar(fnsym, n, closureVars)) |
| selected.Add(n) |
| } |
| return decls, vars, selected |
| } |
|
|
| func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var { |
| var tag int |
| var offs int64 |
|
|
| localAutoOffset := func() int64 { |
| offs = n.FrameOffset() |
| if base.Ctxt.Arch.FixedFrameSize == 0 { |
| offs -= int64(types.PtrSize) |
| } |
| if buildcfg.FramePointerEnabled { |
| offs -= int64(types.PtrSize) |
| } |
| return offs |
| } |
|
|
| switch n.Class { |
| case ir.PAUTO: |
| offs = localAutoOffset() |
| tag = dwarf.DW_TAG_variable |
| case ir.PPARAM, ir.PPARAMOUT: |
| tag = dwarf.DW_TAG_formal_parameter |
| if n.IsOutputParamInRegisters() { |
| offs = localAutoOffset() |
| } else { |
| offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize |
| } |
|
|
| default: |
| base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n) |
| } |
|
|
| typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) |
| delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type())) |
| inlIndex := 0 |
| if base.Flag.GenDwarfInl > 1 { |
| if n.InlFormal() || n.InlLocal() { |
| inlIndex = posInlIndex(n.Pos()) + 1 |
| if n.InlFormal() { |
| tag = dwarf.DW_TAG_formal_parameter |
| } |
| } |
| } |
| declpos := base.Ctxt.InnermostPos(declPos(n)) |
| return &dwarf.Var{ |
| Name: n.Sym().Name, |
| IsReturnValue: n.Class == ir.PPARAMOUT, |
| IsInlFormal: n.InlFormal(), |
| Tag: tag, |
| StackOffset: int32(offs), |
| Type: base.Ctxt.Lookup(typename), |
| DeclFile: declpos.RelFilename(), |
| DeclLine: declpos.RelLine(), |
| DeclCol: declpos.RelCol(), |
| InlIndex: int32(inlIndex), |
| ChildIndex: -1, |
| DictIndex: n.DictIndex, |
| ClosureOffset: closureOffset(n, closureVars), |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { |
|
|
| |
| |
| decls, vars, selected := createComplexVars(fnsym, fn, closureVars) |
|
|
| |
| |
| |
| for _, n := range apDecls { |
| if ir.IsAutoTmp(n) { |
| continue |
| } |
| if _, ok := selected[n]; ok { |
| |
| continue |
| } |
|
|
| decls = append(decls, n) |
| vars = append(vars, createSimpleVar(fnsym, n, closureVars)) |
| selected.Add(n) |
| } |
|
|
| return decls, vars, selected |
| } |
|
|
| |
| |
| func createComplexVars(fnsym *obj.LSym, fn *ir.Func, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { |
| debugInfo := fn.DebugInfo.(*ssa.FuncDebug) |
|
|
| |
| var decls []*ir.Name |
| var vars []*dwarf.Var |
| var ssaVars ir.NameSet |
|
|
| for varID, dvar := range debugInfo.Vars { |
| n := dvar |
| ssaVars.Add(n) |
| for _, slot := range debugInfo.VarSlots[varID] { |
| ssaVars.Add(debugInfo.Slots[slot].N) |
| } |
|
|
| if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID), closureVars); dvar != nil { |
| decls = append(decls, n) |
| vars = append(vars, dvar) |
| } |
| } |
|
|
| return decls, vars, ssaVars |
| } |
|
|
| |
| func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars map[*ir.Name]int64) *dwarf.Var { |
| debug := fn.DebugInfo.(*ssa.FuncDebug) |
| n := debug.Vars[varID] |
|
|
| var tag int |
| switch n.Class { |
| case ir.PAUTO: |
| tag = dwarf.DW_TAG_variable |
| case ir.PPARAM, ir.PPARAMOUT: |
| tag = dwarf.DW_TAG_formal_parameter |
| default: |
| return nil |
| } |
|
|
| gotype := reflectdata.TypeLinksym(n.Type()) |
| delete(fnsym.Func().Autot, gotype) |
| typename := dwarf.InfoPrefix + gotype.Name[len("type:"):] |
| inlIndex := 0 |
| if base.Flag.GenDwarfInl > 1 { |
| if n.InlFormal() || n.InlLocal() { |
| inlIndex = posInlIndex(n.Pos()) + 1 |
| if n.InlFormal() { |
| tag = dwarf.DW_TAG_formal_parameter |
| } |
| } |
| } |
| declpos := base.Ctxt.InnermostPos(n.Pos()) |
| dvar := &dwarf.Var{ |
| Name: n.Sym().Name, |
| IsReturnValue: n.Class == ir.PPARAMOUT, |
| IsInlFormal: n.InlFormal(), |
| Tag: tag, |
| WithLoclist: true, |
| Type: base.Ctxt.Lookup(typename), |
| |
| |
| |
| |
| StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), |
| DeclFile: declpos.RelFilename(), |
| DeclLine: declpos.RelLine(), |
| DeclCol: declpos.RelCol(), |
| InlIndex: int32(inlIndex), |
| ChildIndex: -1, |
| DictIndex: n.DictIndex, |
| ClosureOffset: closureOffset(n, closureVars), |
| } |
| list := debug.LocationLists[varID] |
| if len(list) != 0 { |
| dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { |
| debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) |
| } |
| } |
| return dvar |
| } |
|
|
| |
| |
| func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []byte { |
| |
| heapPtrOffset := n.Heapaddr.FrameOffset() |
| if base.Ctxt.Arch.FixedFrameSize == 0 { |
| heapPtrOffset -= int64(types.PtrSize) |
| } |
| if buildcfg.FramePointerEnabled { |
| heapPtrOffset -= int64(types.PtrSize) |
| } |
|
|
| |
| var locExpr []byte |
| var sizeIdx int |
| locExpr, sizeIdx = ssa.SetupLocList(base.Ctxt, entryID, locExpr, ssa.BlockStart.ID, ssa.FuncEnd.ID) |
| locExpr = append(locExpr, dwarf.DW_OP_fbreg) |
| locExpr = dwarf.AppendSleb128(locExpr, heapPtrOffset) |
| locExpr = append(locExpr, dwarf.DW_OP_deref) |
| base.Ctxt.Arch.ByteOrder.PutUint16(locExpr[sizeIdx:], uint16(len(locExpr)-sizeIdx-2)) |
| return locExpr |
| } |
|
|
| |
| |
| func RecordFlags(flags ...string) { |
| if base.Ctxt.Pkgpath == "" { |
| base.Fatalf("missing pkgpath") |
| } |
|
|
| type BoolFlag interface { |
| IsBoolFlag() bool |
| } |
| type CountFlag interface { |
| IsCountFlag() bool |
| } |
| var cmd bytes.Buffer |
| for _, name := range flags { |
| f := flag.Lookup(name) |
| if f == nil { |
| continue |
| } |
| getter := f.Value.(flag.Getter) |
| if getter.String() == f.DefValue { |
| |
| continue |
| } |
| if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() { |
| val, ok := getter.Get().(bool) |
| if ok && val { |
| fmt.Fprintf(&cmd, " -%s", f.Name) |
| continue |
| } |
| } |
| if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() { |
| val, ok := getter.Get().(int) |
| if ok && val == 1 { |
| fmt.Fprintf(&cmd, " -%s", f.Name) |
| continue |
| } |
| } |
| fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get()) |
| } |
|
|
| |
| |
| |
| |
| if buildcfg.Experiment.RegabiArgs { |
| cmd.Write([]byte(" regabi")) |
| } |
|
|
| if cmd.Len() == 0 { |
| return |
| } |
| s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath) |
| s.Type = objabi.SDWARFCUINFO |
| |
| |
| s.Set(obj.AttrDuplicateOK, true) |
| base.Ctxt.Data = append(base.Ctxt.Data, s) |
| s.P = cmd.Bytes()[1:] |
| } |
|
|
| |
| |
| func RecordPackageName() { |
| s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath) |
| s.Type = objabi.SDWARFCUINFO |
| |
| |
| s.Set(obj.AttrDuplicateOK, true) |
| base.Ctxt.Data = append(base.Ctxt.Data, s) |
| s.P = []byte(types.LocalPkg.Name) |
| } |
|
|
| func closureOffset(n *ir.Name, closureVars map[*ir.Name]int64) int64 { |
| return closureVars[n] |
| } |
|
|