]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: mark DIEs of captured variables
authorAlessandro Arzilli <alessandro.arzilli@gmail.com>
Fri, 9 Feb 2024 17:41:21 +0000 (18:41 +0100)
committerThan McIntosh <thanm@google.com>
Wed, 6 Mar 2024 15:23:18 +0000 (15:23 +0000)
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>
src/cmd/compile/internal/dwarfgen/dwarf.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/typecheck/func.go
src/cmd/internal/dwarf/dwarf.go
src/cmd/internal/dwarf/putvarabbrevgen.go

index 733eeff4ac3c244e448397a89daec9d3c2b27313..512d8d22e757b6198d496b67df1bbd949be529ed 100644 (file)
@@ -16,6 +16,7 @@ import (
        "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"
@@ -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
        // 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
 // 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.
        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)
+               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)
+               decls, vars, selected = createABIVars(fnsym, fn, apDecls, closureVars)
        } else {
-               decls, vars, selected = createSimpleVars(fnsym, apDecls)
+               decls, vars, selected = createSimpleVars(fnsym, apDecls, closureVars)
        }
        if fn.DebugInfo != nil {
                // 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())
                        if n.Type().Size() == 0 {
                                decls = append(decls, n)
-                               vars = append(vars, createSimpleVar(fnsym, n))
+                               vars = append(vars, createSimpleVar(fnsym, n, closureVars))
                                vars[len(vars)-1].StackOffset = 0
                                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
                        // are homed on the stack in a single place for the
                        // entire call.
-                       vars = append(vars, createSimpleVar(fnsym, n))
+                       vars = append(vars, createSimpleVar(fnsym, n, closureVars))
                        decls = append(decls, n)
                        continue
                }
@@ -251,6 +268,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
                        InlIndex:      int32(inlIndex),
                        ChildIndex:    -1,
                        DictIndex:     n.DictIndex,
+                       ClosureOffset: closureOffset(n, closureVars),
                })
                // Record go type of to insure that it gets emitted by the linker.
                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
 // 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 decls []*ir.Name
        var selected ir.NameSet
@@ -344,13 +362,13 @@ func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf
                }
 
                decls = append(decls, n)
-               vars = append(vars, createSimpleVar(fnsym, n))
+               vars = append(vars, createSimpleVar(fnsym, n, closureVars))
                selected.Add(n)
        }
        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 offs int64
 
@@ -406,6 +424,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
                InlIndex:      int32(inlIndex),
                ChildIndex:    -1,
                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
 // captured with location lists, and all other vars use the "simple"
 // 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
        // 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
        // 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)
-               vars = append(vars, createSimpleVar(fnsym, n))
+               vars = append(vars, createSimpleVar(fnsym, n, closureVars))
                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,
 // 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)
 
        // 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)
                }
 
-               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)
                        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.
-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)
        n := debug.Vars[varID]
 
@@ -505,13 +524,14 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var
                // variables just give it the first 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: 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,
+               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 {
@@ -594,3 +614,7 @@ func RecordPackageName() {
        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]
+}
index 05919b99245c55c303ca40fbb7aaecc67d16fcf6..6335aff8321f1990780de8bd39ab162f404a1078 100644 (file)
@@ -514,16 +514,14 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
        // Populate closure variables.
        if fn.Needctxt() {
                clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr)
-               offset := int64(types.PtrSize) // PtrSize to skip past function entry PC field
-               for _, n := range fn.ClosureVars {
-                       typ := n.Type()
-                       if !n.Byval() {
-                               typ = types.NewPtr(typ)
+               csiter := typecheck.NewClosureStructIter(fn.ClosureVars)
+               for {
+                       n, typ, offset := csiter.Next()
+                       if n == nil {
+                               break
                        }
 
-                       offset = types.RoundUp(offset, typ.Alignment())
                        ptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(typ), offset, clo)
-                       offset += typ.Size()
 
                        // If n is a small variable captured by value, promote
                        // it to PAUTO so it can be converted to SSA.
index 5c54a5bd49e2484865924db36ddf08afc11afed8..02e59fa360451d06731f932533b3b49bfc9c9adc 100644 (file)
@@ -103,12 +103,15 @@ func ClosureType(clo *ir.ClosureExpr) *types.Type {
 
        fields := make([]*types.Field, 1+len(clo.Func.ClosureVars))
        fields[0] = types.NewField(base.AutogeneratedPos, types.LocalPkg.Lookup("F"), types.Types[types.TUINTPTR])
-       for i, v := range clo.Func.ClosureVars {
-               typ := v.Type()
-               if !v.Byval() {
-                       typ = types.NewPtr(typ)
+       it := NewClosureStructIter(clo.Func.ClosureVars)
+       i := 0
+       for {
+               n, typ, _ := it.Next()
+               if n == nil {
+                       break
                }
                fields[1+i] = types.NewField(base.AutogeneratedPos, types.LocalPkg.LookupNum("X", i), typ)
+               i++
        }
        typ := types.NewStruct(fields)
        typ.SetNoalg(true)
@@ -832,3 +835,37 @@ func tcUnsafeString(n *ir.BinaryExpr) *ir.BinaryExpr {
        n.SetType(types.Types[types.TSTRING])
        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
+}
index 06cafc8886c83eeb31bd8bfb51d51dee5d1f8f45..40ec8a6ec29d852460a6eea650046c8a277a2038 100644 (file)
@@ -62,6 +62,7 @@ type Var struct {
        InlIndex        int32 // subtract 1 to form real index into InlTree
        ChildIndex      int32 // child DIE index 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
@@ -312,8 +313,9 @@ const (
        DW_AT_go_embedded_field = 0x2903
        DW_AT_go_runtime_type   = 0x2904
 
-       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_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_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
 )
@@ -1510,6 +1512,10 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int,
                } else {
                        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 {
index 418063d211c6b41f4a2e32896f43c36a6381b4a9..f930fdbb9bf3dcf7e68ad83630615ed022a5948b 100644 (file)
@@ -38,6 +38,28 @@ var putvarAbbrevs = []dwAbbrev{
                        {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_CHILDREN_no,
@@ -74,6 +96,30 @@ var putvarAbbrevs = []dwAbbrev{
                        {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_CHILDREN_no,
@@ -115,24 +161,40 @@ func putvarAbbrev(v *Var, concrete, withLoclist bool) int {
                                return DW_ABRV_PUTVAR_START + 3
                        }
                } else {
-                       if withLoclist {
-                               return DW_ABRV_PUTVAR_START + 4
+                       if v.ClosureOffset > 0 {
+                               if withLoclist {
+                                       return DW_ABRV_PUTVAR_START + 4
+                               } else {
+                                       return DW_ABRV_PUTVAR_START + 5
+                               }
                        } else {
-                               return DW_ABRV_PUTVAR_START + 5
+                               if withLoclist {
+                                       return DW_ABRV_PUTVAR_START + 6
+                               } else {
+                                       return DW_ABRV_PUTVAR_START + 7
+                               }
                        }
                }
        } else {
                if concrete {
                        if withLoclist {
-                               return DW_ABRV_PUTVAR_START + 6
+                               return DW_ABRV_PUTVAR_START + 8
                        } else {
-                               return DW_ABRV_PUTVAR_START + 7
+                               return DW_ABRV_PUTVAR_START + 9
                        }
                } else {
-                       if withLoclist {
-                               return DW_ABRV_PUTVAR_START + 8
+                       if v.ClosureOffset > 0 {
+                               if withLoclist {
+                                       return DW_ABRV_PUTVAR_START + 10
+                               } else {
+                                       return DW_ABRV_PUTVAR_START + 11
+                               }
                        } else {
-                               return DW_ABRV_PUTVAR_START + 9
+                               if withLoclist {
+                                       return DW_ABRV_PUTVAR_START + 12
+                               } else {
+                                       return DW_ABRV_PUTVAR_START + 13
+                               }
                        }
                }
        }