1
0
mirror of https://github.com/golang/go synced 2024-11-17 05:24:53 -07:00

cmd/compile: mark DIEs of captured variables

Adds a new custom attribute to the DIE of captured variables,
containing the offset for the variable inside the closure struct. This
can be used by debuggers to display the contents of a closure variable.

Based on a sample program (delve) this increases the executable size by 0.06%.

benchstat output:

                         │   old.txt    │                new.txt                │
                         │    sec/op    │     sec/op      vs base               │
Template                   153.0m ±  6%    152.5m ±  14%       ~ (p=0.684 n=10)
Unicode                    100.2m ± 15%    104.9m ±   7%       ~ (p=0.247 n=10)
GoTypes                    943.6m ±  8%    986.2m ±  10%       ~ (p=0.280 n=10)
Compiler                   97.79m ±  6%   101.63m ±  12%       ~ (p=0.393 n=10)
SSA                         6.872 ± 37%     9.413 ± 106%       ~ (p=0.190 n=10)
Flate                      128.0m ± 36%    125.0m ±  56%       ~ (p=0.481 n=10)
GoParser                   214.9m ± 26%    201.4m ±  68%       ~ (p=0.579 n=10)
Reflect                    452.6m ± 22%    412.2m ±  74%       ~ (p=0.739 n=10)
Tar                        166.2m ± 27%    155.9m ±  73%       ~ (p=0.393 n=10)
XML                        219.3m ± 24%    211.3m ±  76%       ~ (p=0.739 n=10)
LinkCompiler               523.2m ± 13%    513.5m ±  47%       ~ (p=0.631 n=10)
ExternalLinkCompiler        1.684 ±  2%     1.659 ±  25%       ~ (p=0.218 n=10)
LinkWithoutDebugCompiler   304.9m ± 12%    309.1m ±   7%       ~ (p=0.631 n=10)
StdCmd                      70.76 ± 14%     68.66 ±  53%       ~ (p=1.000 n=10)
geomean                    511.5m          515.4m         +0.77%

                         │   old.txt    │               new.txt                │
                         │ user-sec/op  │  user-sec/op   vs base               │
Template                   269.6m ± 13%   292.3m ±  17%       ~ (p=0.393 n=10)
Unicode                    110.2m ±  8%   101.7m ±  18%       ~ (p=0.247 n=10)
GoTypes                     2.181 ±  9%    2.356 ±  12%       ~ (p=0.280 n=10)
Compiler                   119.1m ± 11%   121.9m ±  15%       ~ (p=0.481 n=10)
SSA                         17.75 ± 52%    26.94 ± 123%       ~ (p=0.190 n=10)
Flate                      256.2m ± 43%   226.8m ±  73%       ~ (p=0.739 n=10)
GoParser                   427.0m ± 24%   422.3m ±  72%       ~ (p=0.529 n=10)
Reflect                    990.5m ± 23%   905.5m ±  75%       ~ (p=0.912 n=10)
Tar                        307.9m ± 27%   308.9m ±  64%       ~ (p=0.393 n=10)
XML                        432.8m ± 24%   427.6m ±  89%       ~ (p=0.796 n=10)
LinkCompiler               796.9m ± 14%   800.4m ±  56%       ~ (p=0.481 n=10)
ExternalLinkCompiler        1.666 ±  4%    1.671 ±  28%       ~ (p=0.971 n=10)
LinkWithoutDebugCompiler   316.7m ± 12%   325.6m ±   8%       ~ (p=0.579 n=10)
geomean                    579.5m         594.0m         +2.51%

          │   old.txt    │                new.txt                │
          │  text-bytes  │  text-bytes   vs base                 │
HelloSize   842.9Ki ± 0%   842.9Ki ± 0%       ~ (p=1.000 n=10) ¹
CmdGoSize   10.95Mi ± 0%   10.95Mi ± 0%       ~ (p=1.000 n=10) ¹
geomean     3.003Mi        3.003Mi       +0.00%
¹ all samples are equal

          │   old.txt    │                new.txt                │
          │  data-bytes  │  data-bytes   vs base                 │
HelloSize   15.08Ki ± 0%   15.08Ki ± 0%       ~ (p=1.000 n=10) ¹
CmdGoSize   314.7Ki ± 0%   314.7Ki ± 0%       ~ (p=1.000 n=10) ¹
geomean     68.88Ki        68.88Ki       +0.00%
¹ all samples are equal

          │   old.txt    │                new.txt                │
          │  bss-bytes   │  bss-bytes    vs base                 │
HelloSize   396.8Ki ± 0%   396.8Ki ± 0%       ~ (p=1.000 n=10) ¹
CmdGoSize   428.8Ki ± 0%   428.8Ki ± 0%       ~ (p=1.000 n=10) ¹
geomean     412.5Ki        412.5Ki       +0.00%
¹ all samples are equal

          │   old.txt    │               new.txt               │
          │  exe-bytes   │  exe-bytes    vs base               │
HelloSize   1.310Mi ± 0%   1.310Mi ± 0%  +0.02% (p=0.000 n=10)
CmdGoSize   16.37Mi ± 0%   16.38Mi ± 0%  +0.01% (p=0.000 n=10)
geomean     4.631Mi        4.632Mi       +0.02%

Change-Id: Ib416ee2d916ec61ad4a5c26bab09597595f57e04
Reviewed-on: https://go-review.googlesource.com/c/go/+/563816
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Alessandro Arzilli 2024-02-09 18:41:21 +01:00 committed by Than McIntosh
parent e8b5bc63be
commit f048829d70
5 changed files with 172 additions and 45 deletions

View File

@ -16,6 +16,7 @@ import (
"cmd/compile/internal/reflectdata" "cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssa" "cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen" "cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/dwarf" "cmd/internal/dwarf"
"cmd/internal/obj" "cmd/internal/obj"
@ -100,7 +101,23 @@ func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Sc
} }
} }
decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls) 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)
// For each type referenced by the functions auto vars but not // For each type referenced by the functions auto vars but not
// already referenced by a dwarf var, attach an R_USETYPE relocation to // already referenced by a dwarf var, attach an R_USETYPE relocation to
@ -137,18 +154,18 @@ func declPos(decl *ir.Name) src.XPos {
// createDwarfVars process fn, returning a list of DWARF variables and the // createDwarfVars process fn, returning a list of DWARF variables and the
// Nodes they represent. // Nodes they represent.
func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) { func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var) {
// Collect a raw list of DWARF vars. // Collect a raw list of DWARF vars.
var vars []*dwarf.Var var vars []*dwarf.Var
var decls []*ir.Name var decls []*ir.Name
var selected ir.NameSet var selected ir.NameSet
if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK { if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK {
decls, vars, selected = createComplexVars(fnsym, fn) decls, vars, selected = createComplexVars(fnsym, fn, closureVars)
} else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK { } else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK {
decls, vars, selected = createABIVars(fnsym, fn, apDecls) decls, vars, selected = createABIVars(fnsym, fn, apDecls, closureVars)
} else { } else {
decls, vars, selected = createSimpleVars(fnsym, apDecls) decls, vars, selected = createSimpleVars(fnsym, apDecls, closureVars)
} }
if fn.DebugInfo != nil { if fn.DebugInfo != nil {
// Recover zero sized variables eliminated by the stackframe pass // Recover zero sized variables eliminated by the stackframe pass
@ -159,7 +176,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
types.CalcSize(n.Type()) types.CalcSize(n.Type())
if n.Type().Size() == 0 { if n.Type().Size() == 0 {
decls = append(decls, n) decls = append(decls, n)
vars = append(vars, createSimpleVar(fnsym, n)) vars = append(vars, createSimpleVar(fnsym, n, closureVars))
vars[len(vars)-1].StackOffset = 0 vars[len(vars)-1].StackOffset = 0
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
} }
@ -212,7 +229,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
// Args not of SSA-able type are treated here; they // Args not of SSA-able type are treated here; they
// are homed on the stack in a single place for the // are homed on the stack in a single place for the
// entire call. // entire call.
vars = append(vars, createSimpleVar(fnsym, n)) vars = append(vars, createSimpleVar(fnsym, n, closureVars))
decls = append(decls, n) decls = append(decls, n)
continue continue
} }
@ -251,6 +268,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
InlIndex: int32(inlIndex), InlIndex: int32(inlIndex),
ChildIndex: -1, ChildIndex: -1,
DictIndex: n.DictIndex, DictIndex: n.DictIndex,
ClosureOffset: closureOffset(n, closureVars),
}) })
// Record go type of to insure that it gets emitted by the linker. // Record go type of to insure that it gets emitted by the linker.
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
@ -334,7 +352,7 @@ func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
// createSimpleVars creates a DWARF entry for every variable declared in the // createSimpleVars creates a DWARF entry for every variable declared in the
// function, claiming that they are permanently on the stack. // function, claiming that they are permanently on the stack.
func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
var vars []*dwarf.Var var vars []*dwarf.Var
var decls []*ir.Name var decls []*ir.Name
var selected ir.NameSet var selected ir.NameSet
@ -344,13 +362,13 @@ func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf
} }
decls = append(decls, n) decls = append(decls, n)
vars = append(vars, createSimpleVar(fnsym, n)) vars = append(vars, createSimpleVar(fnsym, n, closureVars))
selected.Add(n) selected.Add(n)
} }
return decls, vars, selected return decls, vars, selected
} }
func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
var tag int var tag int
var offs int64 var offs int64
@ -406,6 +424,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
InlIndex: int32(inlIndex), InlIndex: int32(inlIndex),
ChildIndex: -1, ChildIndex: -1,
DictIndex: n.DictIndex, DictIndex: n.DictIndex,
ClosureOffset: closureOffset(n, closureVars),
} }
} }
@ -414,11 +433,11 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
// hybrid approach in which register-resident input params are // hybrid approach in which register-resident input params are
// captured with location lists, and all other vars use the "simple" // captured with location lists, and all other vars use the "simple"
// strategy. // strategy.
func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
// Invoke createComplexVars to generate dwarf vars for input parameters // Invoke createComplexVars to generate dwarf vars for input parameters
// that are register-allocated according to the ABI rules. // that are register-allocated according to the ABI rules.
decls, vars, selected := createComplexVars(fnsym, fn) decls, vars, selected := createComplexVars(fnsym, fn, closureVars)
// Now fill in the remainder of the variables: input parameters // Now fill in the remainder of the variables: input parameters
// that are not register-resident, output parameters, and local // that are not register-resident, output parameters, and local
@ -433,7 +452,7 @@ func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name
} }
decls = append(decls, n) decls = append(decls, n)
vars = append(vars, createSimpleVar(fnsym, n)) vars = append(vars, createSimpleVar(fnsym, n, closureVars))
selected.Add(n) selected.Add(n)
} }
@ -442,7 +461,7 @@ func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name
// createComplexVars creates recomposed DWARF vars with location lists, // createComplexVars creates recomposed DWARF vars with location lists,
// suitable for describing optimized code. // suitable for describing optimized code.
func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { func createComplexVars(fnsym *obj.LSym, fn *ir.Func, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
debugInfo := fn.DebugInfo.(*ssa.FuncDebug) debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
// Produce a DWARF variable entry for each user variable. // Produce a DWARF variable entry for each user variable.
@ -457,7 +476,7 @@ func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var,
ssaVars.Add(debugInfo.Slots[slot].N) ssaVars.Add(debugInfo.Slots[slot].N)
} }
if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil { if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID), closureVars); dvar != nil {
decls = append(decls, n) decls = append(decls, n)
vars = append(vars, dvar) vars = append(vars, dvar)
} }
@ -467,7 +486,7 @@ func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var,
} }
// createComplexVar builds a single DWARF variable entry and location list. // createComplexVar builds a single DWARF variable entry and location list.
func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var { func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars map[*ir.Name]int64) *dwarf.Var {
debug := fn.DebugInfo.(*ssa.FuncDebug) debug := fn.DebugInfo.(*ssa.FuncDebug)
n := debug.Vars[varID] n := debug.Vars[varID]
@ -512,6 +531,7 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var
InlIndex: int32(inlIndex), InlIndex: int32(inlIndex),
ChildIndex: -1, ChildIndex: -1,
DictIndex: n.DictIndex, DictIndex: n.DictIndex,
ClosureOffset: closureOffset(n, closureVars),
} }
list := debug.LocationLists[varID] list := debug.LocationLists[varID]
if len(list) != 0 { if len(list) != 0 {
@ -594,3 +614,7 @@ func RecordPackageName() {
base.Ctxt.Data = append(base.Ctxt.Data, s) base.Ctxt.Data = append(base.Ctxt.Data, s)
s.P = []byte(types.LocalPkg.Name) s.P = []byte(types.LocalPkg.Name)
} }
func closureOffset(n *ir.Name, closureVars map[*ir.Name]int64) int64 {
return closureVars[n]
}

View File

@ -514,16 +514,14 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
// Populate closure variables. // Populate closure variables.
if fn.Needctxt() { if fn.Needctxt() {
clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr) clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr)
offset := int64(types.PtrSize) // PtrSize to skip past function entry PC field csiter := typecheck.NewClosureStructIter(fn.ClosureVars)
for _, n := range fn.ClosureVars { for {
typ := n.Type() n, typ, offset := csiter.Next()
if !n.Byval() { if n == nil {
typ = types.NewPtr(typ) break
} }
offset = types.RoundUp(offset, typ.Alignment())
ptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(typ), offset, clo) ptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(typ), offset, clo)
offset += typ.Size()
// If n is a small variable captured by value, promote // If n is a small variable captured by value, promote
// it to PAUTO so it can be converted to SSA. // it to PAUTO so it can be converted to SSA.

View File

@ -103,12 +103,15 @@ func ClosureType(clo *ir.ClosureExpr) *types.Type {
fields := make([]*types.Field, 1+len(clo.Func.ClosureVars)) fields := make([]*types.Field, 1+len(clo.Func.ClosureVars))
fields[0] = types.NewField(base.AutogeneratedPos, types.LocalPkg.Lookup("F"), types.Types[types.TUINTPTR]) fields[0] = types.NewField(base.AutogeneratedPos, types.LocalPkg.Lookup("F"), types.Types[types.TUINTPTR])
for i, v := range clo.Func.ClosureVars { it := NewClosureStructIter(clo.Func.ClosureVars)
typ := v.Type() i := 0
if !v.Byval() { for {
typ = types.NewPtr(typ) n, typ, _ := it.Next()
if n == nil {
break
} }
fields[1+i] = types.NewField(base.AutogeneratedPos, types.LocalPkg.LookupNum("X", i), typ) fields[1+i] = types.NewField(base.AutogeneratedPos, types.LocalPkg.LookupNum("X", i), typ)
i++
} }
typ := types.NewStruct(fields) typ := types.NewStruct(fields)
typ.SetNoalg(true) typ.SetNoalg(true)
@ -832,3 +835,37 @@ func tcUnsafeString(n *ir.BinaryExpr) *ir.BinaryExpr {
n.SetType(types.Types[types.TSTRING]) n.SetType(types.Types[types.TSTRING])
return n return n
} }
// ClosureStructIter iterates through a slice of closure variables returning
// their type and offset in the closure struct.
type ClosureStructIter struct {
closureVars []*ir.Name
offset int64
next int
}
// NewClosureStructIter creates a new ClosureStructIter for closureVars.
func NewClosureStructIter(closureVars []*ir.Name) *ClosureStructIter {
return &ClosureStructIter{
closureVars: closureVars,
offset: int64(types.PtrSize), // PtrSize to skip past function entry PC field
next: 0,
}
}
// Next returns the next name, type and offset of the next closure variable.
// A nil name is returned after the last closure variable.
func (iter *ClosureStructIter) Next() (n *ir.Name, typ *types.Type, offset int64) {
if iter.next >= len(iter.closureVars) {
return nil, nil, 0
}
n = iter.closureVars[iter.next]
typ = n.Type()
if !n.Byval() {
typ = types.NewPtr(typ)
}
iter.next++
offset = types.RoundUp(iter.offset, typ.Alignment())
iter.offset = offset + typ.Size()
return n, typ, offset
}

View File

@ -62,6 +62,7 @@ type Var struct {
InlIndex int32 // subtract 1 to form real index into InlTree InlIndex int32 // subtract 1 to form real index into InlTree
ChildIndex int32 // child DIE index in abstract function ChildIndex int32 // child DIE index in abstract function
IsInAbstract bool // variable exists in abstract function IsInAbstract bool // variable exists in abstract function
ClosureOffset int64 // if non-zero this is the offset of this variable in the closure struct
} }
// A Scope represents a lexical scope. All variables declared within a // A Scope represents a lexical scope. All variables declared within a
@ -314,6 +315,7 @@ const (
DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit
DW_AT_go_dict_index = 0x2906 // Attribute for DW_TAG_typedef_type, index of the dictionary entry describing the real type of this type shape DW_AT_go_dict_index = 0x2906 // Attribute for DW_TAG_typedef_type, index of the dictionary entry describing the real type of this type shape
DW_AT_go_closure_offset = 0x2907 // Attribute for DW_TAG_variable, offset in the closure struct where this captured variable resides
DW_AT_internal_location = 253 // params and locals; not emitted DW_AT_internal_location = 253 // params and locals; not emitted
) )
@ -1510,6 +1512,10 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int,
} else { } else {
putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) // DW_AT_type putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) // DW_AT_type
} }
if v.ClosureOffset > 0 {
putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, v.ClosureOffset, nil) // DW_AT_go_closure_offset
}
} }
if withLoclist { if withLoclist {

View File

@ -38,6 +38,28 @@ var putvarAbbrevs = []dwAbbrev{
{DW_AT_location, DW_FORM_block1}, {DW_AT_location, DW_FORM_block1},
}, },
}, },
{
DW_TAG_variable,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_closure_offset, DW_FORM_udata},
{DW_AT_location, DW_FORM_sec_offset},
},
},
{
DW_TAG_variable,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_closure_offset, DW_FORM_udata},
{DW_AT_location, DW_FORM_block1},
},
},
{ {
DW_TAG_variable, DW_TAG_variable,
DW_CHILDREN_no, DW_CHILDREN_no,
@ -74,6 +96,30 @@ var putvarAbbrevs = []dwAbbrev{
{DW_AT_location, DW_FORM_block1}, {DW_AT_location, DW_FORM_block1},
}, },
}, },
{
DW_TAG_formal_parameter,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_variable_parameter, DW_FORM_flag},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_closure_offset, DW_FORM_udata},
{DW_AT_location, DW_FORM_sec_offset},
},
},
{
DW_TAG_formal_parameter,
DW_CHILDREN_no,
[]dwAttrForm{
{DW_AT_name, DW_FORM_string},
{DW_AT_variable_parameter, DW_FORM_flag},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_type, DW_FORM_ref_addr},
{DW_AT_go_closure_offset, DW_FORM_udata},
{DW_AT_location, DW_FORM_block1},
},
},
{ {
DW_TAG_formal_parameter, DW_TAG_formal_parameter,
DW_CHILDREN_no, DW_CHILDREN_no,
@ -115,25 +161,41 @@ func putvarAbbrev(v *Var, concrete, withLoclist bool) int {
return DW_ABRV_PUTVAR_START + 3 return DW_ABRV_PUTVAR_START + 3
} }
} else { } else {
if v.ClosureOffset > 0 {
if withLoclist { if withLoclist {
return DW_ABRV_PUTVAR_START + 4 return DW_ABRV_PUTVAR_START + 4
} else { } else {
return DW_ABRV_PUTVAR_START + 5 return DW_ABRV_PUTVAR_START + 5
} }
}
} else { } else {
if concrete {
if withLoclist { if withLoclist {
return DW_ABRV_PUTVAR_START + 6 return DW_ABRV_PUTVAR_START + 6
} else { } else {
return DW_ABRV_PUTVAR_START + 7 return DW_ABRV_PUTVAR_START + 7
} }
}
}
} else { } else {
if concrete {
if withLoclist { if withLoclist {
return DW_ABRV_PUTVAR_START + 8 return DW_ABRV_PUTVAR_START + 8
} else { } else {
return DW_ABRV_PUTVAR_START + 9 return DW_ABRV_PUTVAR_START + 9
} }
} else {
if v.ClosureOffset > 0 {
if withLoclist {
return DW_ABRV_PUTVAR_START + 10
} else {
return DW_ABRV_PUTVAR_START + 11
}
} else {
if withLoclist {
return DW_ABRV_PUTVAR_START + 12
} else {
return DW_ABRV_PUTVAR_START + 13
}
}
} }
} }
} }