}
typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
decls = append(decls, n)
- abbrev := dwarf.DW_ABRV_AUTO_LOCLIST
+ tag := dwarf.DW_TAG_variable
isReturnValue := (n.Class == ir.PPARAMOUT)
if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ tag = dwarf.DW_TAG_formal_parameter
}
if n.Esc() == ir.EscHeap {
// The variable in question has been promoted to the heap.
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos()) + 1
if n.InlFormal() {
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ tag = dwarf.DW_TAG_formal_parameter
}
}
}
vars = append(vars, &dwarf.Var{
Name: n.Sym().Name,
IsReturnValue: isReturnValue,
- Abbrev: abbrev,
+ Tag: tag,
+ WithLoclist: true,
StackOffset: int32(n.FrameOffset()),
Type: base.Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(),
}
func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
- var abbrev int
+ var tag int
var offs int64
localAutoOffset := func() int64 {
switch n.Class {
case ir.PAUTO:
offs = localAutoOffset()
- abbrev = dwarf.DW_ABRV_AUTO
+ tag = dwarf.DW_TAG_variable
case ir.PPARAM, ir.PPARAMOUT:
- abbrev = dwarf.DW_ABRV_PARAM
+ tag = dwarf.DW_TAG_formal_parameter
if n.IsOutputParamInRegisters() {
offs = localAutoOffset()
} else {
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos()) + 1
if n.InlFormal() {
- abbrev = dwarf.DW_ABRV_PARAM
+ tag = dwarf.DW_TAG_formal_parameter
}
}
}
Name: n.Sym().Name,
IsReturnValue: n.Class == ir.PPARAMOUT,
IsInlFormal: n.InlFormal(),
- Abbrev: abbrev,
+ Tag: tag,
StackOffset: int32(offs),
Type: base.Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(),
debug := fn.DebugInfo.(*ssa.FuncDebug)
n := debug.Vars[varID]
- var abbrev int
+ var tag int
switch n.Class {
case ir.PAUTO:
- abbrev = dwarf.DW_ABRV_AUTO_LOCLIST
+ tag = dwarf.DW_TAG_variable
case ir.PPARAM, ir.PPARAMOUT:
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ tag = dwarf.DW_TAG_formal_parameter
default:
return nil
}
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos()) + 1
if n.InlFormal() {
- abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
+ tag = dwarf.DW_TAG_formal_parameter
}
}
}
Name: n.Sym().Name,
IsReturnValue: n.Class == ir.PPARAMOUT,
IsInlFormal: n.InlFormal(),
- Abbrev: abbrev,
+ Tag: tag,
+ WithLoclist: true,
Type: base.Ctxt.Lookup(typename),
// The stack offset is used as a sorting key, so for decomposed
// variables just give it the first one. It's not used otherwise.
func dumpInlVars(dwvars []*dwarf.Var) {
for i, dwv := range dwvars {
typ := "local"
- if dwv.Abbrev == dwarf.DW_ABRV_PARAM_LOCLIST || dwv.Abbrev == dwarf.DW_ABRV_PARAM {
+ if dwv.Tag == dwarf.DW_TAG_formal_parameter {
typ = "param"
}
ia := 0
// A Var represents a local variable or a function parameter.
type Var struct {
Name string
- Abbrev int // Either DW_ABRV_AUTO[_LOCLIST] or DW_ABRV_PARAM[_LOCLIST]
+ Tag int // Either DW_TAG_variable or DW_TAG_formal_parameter
+ WithLoclist bool
IsReturnValue bool
IsInlFormal bool
DictIndex uint16 // index of the dictionary entry describing the type of this variable
DW_ABRV_INLINED_SUBROUTINE_RANGES
DW_ABRV_VARIABLE
DW_ABRV_INT_CONSTANT
- DW_ABRV_AUTO
- DW_ABRV_AUTO_LOCLIST
- DW_ABRV_AUTO_ABSTRACT
- DW_ABRV_AUTO_CONCRETE
- DW_ABRV_AUTO_CONCRETE_LOCLIST
- DW_ABRV_PARAM
- DW_ABRV_PARAM_LOCLIST
- DW_ABRV_PARAM_ABSTRACT
- DW_ABRV_PARAM_CONCRETE
- DW_ABRV_PARAM_CONCRETE_LOCLIST
DW_ABRV_LEXICAL_BLOCK_RANGES
DW_ABRV_LEXICAL_BLOCK_SIMPLE
DW_ABRV_STRUCTFIELD
DW_ABRV_STRUCTTYPE
DW_ABRV_TYPEDECL
DW_ABRV_DICT_INDEX
- DW_NABRV
+ DW_ABRV_PUTVAR_START
)
type dwAbbrev struct {
// expanding any DW_FORM pseudo-ops to real values.
func Abbrevs() []dwAbbrev {
if abbrevsFinalized {
- return abbrevs[:]
+ return abbrevs
}
- for i := 1; i < DW_NABRV; i++ {
+ abbrevs = append(abbrevs, putvarAbbrevs...)
+ for i := 1; i < len(abbrevs); i++ {
for j := 0; j < len(abbrevs[i].attr); j++ {
abbrevs[i].attr[j].form = expandPseudoForm(abbrevs[i].attr[j].form)
}
}
abbrevsFinalized = true
- return abbrevs[:]
+ return abbrevs
}
// abbrevs is a raw table of abbrev entries; it needs to be post-processed
// by the Abbrevs() function above prior to being consumed, to expand
// the 'pseudo-form' entries below to real DWARF form values.
-var abbrevs = [DW_NABRV]dwAbbrev{
+var abbrevs = []dwAbbrev{
/* The mandatory DW_ABRV_NULL entry. */
{0, 0, []dwAttrForm{}},
},
},
- /* AUTO */
- {
- 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_location, DW_FORM_block1},
- },
- },
-
- /* AUTO_LOCLIST */
- {
- 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_location, DW_FORM_sec_offset},
- },
- },
-
- /* AUTO_ABSTRACT */
- {
- 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},
- },
- },
-
- /* AUTO_CONCRETE */
- {
- DW_TAG_variable,
- DW_CHILDREN_no,
- []dwAttrForm{
- {DW_AT_abstract_origin, DW_FORM_ref_addr},
- {DW_AT_location, DW_FORM_block1},
- },
- },
-
- /* AUTO_CONCRETE_LOCLIST */
- {
- DW_TAG_variable,
- DW_CHILDREN_no,
- []dwAttrForm{
- {DW_AT_abstract_origin, DW_FORM_ref_addr},
- {DW_AT_location, DW_FORM_sec_offset},
- },
- },
-
- /* PARAM */
- {
- 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_location, DW_FORM_block1},
- },
- },
-
- /* PARAM_LOCLIST */
- {
- 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_location, DW_FORM_sec_offset},
- },
- },
-
- /* PARAM_ABSTRACT */
- {
- DW_TAG_formal_parameter,
- DW_CHILDREN_no,
- []dwAttrForm{
- {DW_AT_name, DW_FORM_string},
- {DW_AT_variable_parameter, DW_FORM_flag},
- {DW_AT_type, DW_FORM_ref_addr},
- },
- },
-
- /* PARAM_CONCRETE */
- {
- DW_TAG_formal_parameter,
- DW_CHILDREN_no,
- []dwAttrForm{
- {DW_AT_abstract_origin, DW_FORM_ref_addr},
- {DW_AT_location, DW_FORM_block1},
- },
- },
-
- /* PARAM_CONCRETE_LOCLIST */
- {
- DW_TAG_formal_parameter,
- DW_CHILDREN_no,
- []dwAttrForm{
- {DW_AT_abstract_origin, DW_FORM_ref_addr},
- {DW_AT_location, DW_FORM_sec_offset},
- },
- },
-
/* LEXICAL_BLOCK_RANGES */
{
DW_TAG_lexical_block,
func GetAbbrev() []byte {
abbrevs := Abbrevs()
var buf []byte
- for i := 1; i < DW_NABRV; i++ {
+ for i := 1; i < len(abbrevs); i++ {
// See section 7.5.3
buf = AppendUleb128(buf, uint64(i))
buf = AppendUleb128(buf, uint64(abbrevs[i].tag))
return curscope
}
-// Given a default var abbrev code, select corresponding concrete code.
-func concreteVarAbbrev(varAbbrev int) int {
- switch varAbbrev {
- case DW_ABRV_AUTO:
- return DW_ABRV_AUTO_CONCRETE
- case DW_ABRV_PARAM:
- return DW_ABRV_PARAM_CONCRETE
- case DW_ABRV_AUTO_LOCLIST:
- return DW_ABRV_AUTO_CONCRETE_LOCLIST
- case DW_ABRV_PARAM_LOCLIST:
- return DW_ABRV_PARAM_CONCRETE_LOCLIST
- default:
- panic("should never happen")
- }
-}
-
-// Pick the correct abbrev code for variable or parameter DIE.
-func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) {
- abbrev := v.Abbrev
-
- // If the variable was entirely optimized out, don't emit a location list;
- // convert to an inline abbreviation and emit an empty location.
- missing := false
- switch {
- case abbrev == DW_ABRV_AUTO_LOCLIST && v.PutLocationList == nil:
- missing = true
- abbrev = DW_ABRV_AUTO
- case abbrev == DW_ABRV_PARAM_LOCLIST && v.PutLocationList == nil:
- missing = true
- abbrev = DW_ABRV_PARAM
- }
-
- // Determine whether to use a concrete variable or regular variable DIE.
+func concreteVar(fnabbrev int, v *Var) bool {
concrete := true
switch fnabbrev {
case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER:
default:
panic("should never happen")
}
-
- // Select proper abbrev based on concrete/non-concrete
- if concrete {
- abbrev = concreteVarAbbrev(abbrev)
- }
-
- return abbrev, missing, concrete
-}
-
-func abbrevUsesLoclist(abbrev int) bool {
- switch abbrev {
- case DW_ABRV_AUTO_LOCLIST, DW_ABRV_AUTO_CONCRETE_LOCLIST,
- DW_ABRV_PARAM_LOCLIST, DW_ABRV_PARAM_CONCRETE_LOCLIST:
- return true
- default:
- return false
- }
+ return concrete
}
// Emit DWARF attributes for a variable belonging to an 'abstract' subprogram.
func putAbstractVar(ctxt Context, info Sym, v *Var) {
- // Remap abbrev
- abbrev := v.Abbrev
- switch abbrev {
- case DW_ABRV_AUTO, DW_ABRV_AUTO_LOCLIST:
- abbrev = DW_ABRV_AUTO_ABSTRACT
- case DW_ABRV_PARAM, DW_ABRV_PARAM_LOCLIST:
- abbrev = DW_ABRV_PARAM_ABSTRACT
- }
-
+ // The contents of this functions are used to generate putAbstractVarAbbrev automatically, see TestPutVarAbbrevGenerator.
+ abbrev := putAbstractVarAbbrev(v)
Uleb128put(ctxt, info, int64(abbrev))
- putattr(ctxt, info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(v.Name)), v.Name)
+ putattr(ctxt, info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(v.Name)), v.Name) // DW_AT_name
// Isreturn attribute if this is a param
- if abbrev == DW_ABRV_PARAM_ABSTRACT {
+ if v.Tag == DW_TAG_formal_parameter {
var isReturn int64
if v.IsReturnValue {
isReturn = 1
}
- putattr(ctxt, info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil)
+ putattr(ctxt, info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil) // DW_AT_variable_parameter
}
// Line
- if abbrev != DW_ABRV_PARAM_ABSTRACT {
+ if v.Tag == DW_TAG_variable {
// See issue 23374 for more on why decl line is skipped for abs params.
- putattr(ctxt, info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil)
+ putattr(ctxt, info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil) // DW_AT_decl_line
}
// Type
- putattr(ctxt, info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+ putattr(ctxt, info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) // DW_AT_type
// Var has no children => no terminator
}
func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, encbuf []byte) {
- // Remap abbrev according to parent DIE abbrev
- abbrev, missing, concrete := determineVarAbbrev(v, fnabbrev)
+ // The contents of this functions are used to generate putvarAbbrev automatically, see TestPutVarAbbrevGenerator.
+ concrete := concreteVar(fnabbrev, v)
+ hasParametricType := !concrete && (v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0)
+ withLoclist := v.WithLoclist && v.PutLocationList != nil
+ abbrev := putvarAbbrev(v, concrete, withLoclist)
Uleb128put(ctxt, s.Info, int64(abbrev))
// Abstract origin for concrete / inlined case
// function subprogram DIE. The child DIE has no LSym, so instead
// after the call to 'putattr' below we make a call to register
// the child DIE reference.
- putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, absfn)
+ putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, absfn) // DW_AT_abstract_origin
ctxt.RecordDclReference(s.Info, absfn, int(v.ChildIndex), inlIndex)
} else {
// Var name, line for abstract and default cases
n := v.Name
- putattr(ctxt, s.Info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
- if abbrev == DW_ABRV_PARAM || abbrev == DW_ABRV_PARAM_LOCLIST || abbrev == DW_ABRV_PARAM_ABSTRACT {
+ putattr(ctxt, s.Info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n) // DW_AT_name
+ if v.Tag == DW_TAG_formal_parameter {
var isReturn int64
if v.IsReturnValue {
isReturn = 1
}
- putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil)
+ putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil) // DW_AT_variable_parameter
}
- putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil)
- if v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0 {
+ putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil) // DW_AT_decl_line
+ if hasParametricType {
// If the type of this variable is parametric use the entry emitted by putparamtypes
- putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, s.dictIndexToOffset[v.DictIndex-1], s.Info)
+ putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, s.dictIndexToOffset[v.DictIndex-1], s.Info) // DW_AT_type
} else {
- putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+ putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) // DW_AT_type
}
}
- if abbrevUsesLoclist(abbrev) {
- putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, ctxt.Size(s.Loc), s.Loc)
+ if withLoclist {
+ putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, ctxt.Size(s.Loc), s.Loc) // DW_AT_location
v.PutLocationList(s.Loc, s.StartPC)
} else {
loc := encbuf[:0]
switch {
- case missing:
+ case v.WithLoclist:
break // no location
case v.StackOffset == 0:
loc = append(loc, DW_OP_call_frame_cfa)
loc = append(loc, DW_OP_fbreg)
loc = AppendSleb128(loc, int64(v.StackOffset))
}
- putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
+ putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc) // DW_AT_location
}
// Var has no children => no terminator
--- /dev/null
+// Code generated by TestPutVarAbbrevGenerator. DO NOT EDIT.
+// Regenerate using go test -run TestPutVarAbbrevGenerator -generate instead.
+
+package dwarf
+
+var putvarAbbrevs = []dwAbbrev{
+ {
+ 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_TAG_formal_parameter,
+ DW_CHILDREN_no,
+ []dwAttrForm{
+ {DW_AT_name, DW_FORM_string},
+ {DW_AT_variable_parameter, DW_FORM_flag},
+ {DW_AT_type, DW_FORM_ref_addr},
+ },
+ },
+ {
+ DW_TAG_variable,
+ DW_CHILDREN_no,
+ []dwAttrForm{
+ {DW_AT_abstract_origin, DW_FORM_ref_addr},
+ {DW_AT_location, DW_FORM_sec_offset},
+ },
+ },
+ {
+ DW_TAG_variable,
+ DW_CHILDREN_no,
+ []dwAttrForm{
+ {DW_AT_abstract_origin, DW_FORM_ref_addr},
+ {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_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_location, DW_FORM_block1},
+ },
+ },
+ {
+ DW_TAG_formal_parameter,
+ DW_CHILDREN_no,
+ []dwAttrForm{
+ {DW_AT_abstract_origin, DW_FORM_ref_addr},
+ {DW_AT_location, DW_FORM_sec_offset},
+ },
+ },
+ {
+ DW_TAG_formal_parameter,
+ DW_CHILDREN_no,
+ []dwAttrForm{
+ {DW_AT_abstract_origin, DW_FORM_ref_addr},
+ {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_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_location, DW_FORM_block1},
+ },
+ },
+}
+
+func putAbstractVarAbbrev(v *Var) int {
+ if v.Tag == DW_TAG_variable {
+ return DW_ABRV_PUTVAR_START + 0
+ } else {
+ return DW_ABRV_PUTVAR_START + 1
+ }
+}
+
+func putvarAbbrev(v *Var, concrete, withLoclist bool) int {
+ if v.Tag == DW_TAG_variable {
+ if concrete {
+ if withLoclist {
+ return DW_ABRV_PUTVAR_START + 2
+ } else {
+ return DW_ABRV_PUTVAR_START + 3
+ }
+ } else {
+ if withLoclist {
+ return DW_ABRV_PUTVAR_START + 4
+ } else {
+ return DW_ABRV_PUTVAR_START + 5
+ }
+ }
+ } else {
+ if concrete {
+ if withLoclist {
+ return DW_ABRV_PUTVAR_START + 6
+ } else {
+ return DW_ABRV_PUTVAR_START + 7
+ }
+ } else {
+ if withLoclist {
+ return DW_ABRV_PUTVAR_START + 8
+ } else {
+ return DW_ABRV_PUTVAR_START + 9
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright 2024 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 dwarf
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/printer"
+ "go/token"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+const pvagenfile = "./putvarabbrevgen.go"
+
+var pvaDoGenerate bool
+
+func TestMain(m *testing.M) {
+ flag.BoolVar(&pvaDoGenerate, "generate", false, "regenerates "+pvagenfile)
+ flag.Parse()
+ os.Exit(m.Run())
+
+}
+
+// TestPutVarAbbrevGenerator checks that putvarabbrevgen.go is kept in sync
+// with the contents of functions putvar and putAbstractVar. If test flag -generate
+// is specified the file is regenerated instead.
+//
+// The block of code in putvar and putAbstractVar that picks the correct
+// abbrev is also generated automatically by this function by looking at all
+// the possible paths in their CFG and the order in which putattr is called.
+//
+// There are some restrictions on how putattr can be used in putvar and
+// putAbstractVar:
+//
+// 1. it shouldn't appear inside a for or switch statements
+// 2. it can appear within any number of nested if/else statements but the
+// conditionals must not change after putvarAbbrev/putAbstractVarAbbrev
+// are called
+// 3. the form argument of putattr must be a compile time constant
+// 4. each putattr call must be followed by a comment containing the name of
+// the attribute it is setting
+//
+// TestPutVarAbbrevGenerator will fail if (1) or (4) are not respected and
+// the generated code will not compile if (3) is violated. Violating (2)
+// will result in code silently wrong code (which will usually be detected
+// by one of the tests that parse debug_info).
+func TestPutVarAbbrevGenerator(t *testing.T) {
+ spvagenfile := pvagenerate(t)
+
+ if pvaDoGenerate {
+ err := os.WriteFile(pvagenfile, []byte(spvagenfile), 0660)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return
+ }
+
+ slurp := func(name string) string {
+ out, err := os.ReadFile(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return string(out)
+ }
+
+ if spvagenfile != slurp(pvagenfile) {
+ t.Error(pvagenfile + " is out of date")
+ }
+
+}
+
+func pvagenerate(t *testing.T) string {
+ var fset token.FileSet
+ f, err := parser.ParseFile(&fset, "./dwarf.go", nil, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cm := ast.NewCommentMap(&fset, f, f.Comments)
+ abbrevs := make(map[string]int)
+ funcs := map[string]ast.Stmt{}
+ for _, decl := range f.Decls {
+ decl, ok := decl.(*ast.FuncDecl)
+ if !ok || decl.Body == nil {
+ continue
+ }
+ if decl.Name.Name == "putvar" || decl.Name.Name == "putAbstractVar" {
+ // construct the simplified CFG
+ pvagraph, _ := pvacfgbody(t, &fset, cm, decl.Body.List)
+ funcs[decl.Name.Name+"Abbrev"] = pvacfgvisit(pvagraph, abbrevs)
+ }
+ }
+ abbrevslice := make([]string, len(abbrevs))
+ for abbrev, n := range abbrevs {
+ abbrevslice[n] = abbrev
+ }
+
+ buf := new(bytes.Buffer)
+ fmt.Fprint(buf, `// Code generated by TestPutVarAbbrevGenerator. DO NOT EDIT.
+// Regenerate using go test -run TestPutVarAbbrevGenerator -generate instead.
+
+package dwarf
+
+var putvarAbbrevs = []dwAbbrev{
+`)
+
+ for _, abbrev := range abbrevslice {
+ fmt.Fprint(buf, abbrev+",\n")
+ }
+
+ fmt.Fprint(buf, "\n}\n\n")
+
+ fmt.Fprint(buf, "func putAbstractVarAbbrev(v *Var) int {\n")
+ format.Node(buf, &token.FileSet{}, funcs["putAbstractVarAbbrev"])
+ fmt.Fprint(buf, "}\n\n")
+
+ fmt.Fprint(buf, "func putvarAbbrev(v *Var, concrete, withLoclist bool) int {\n")
+ format.Node(buf, &token.FileSet{}, funcs["putvarAbbrev"])
+ fmt.Fprint(buf, "}\n")
+
+ out, err := format.Source(buf.Bytes())
+ if err != nil {
+ t.Log(string(buf.Bytes()))
+ t.Fatal(err)
+ }
+
+ return string(out)
+}
+
+type pvacfgnode struct {
+ attr, form string
+
+ cond ast.Expr
+ then, els *pvacfgnode
+}
+
+// pvacfgbody generates a simplified CFG for a slice of statements,
+// containing only calls to putattr and the if statements affecting them.
+func pvacfgbody(t *testing.T, fset *token.FileSet, cm ast.CommentMap, body []ast.Stmt) (start, end *pvacfgnode) {
+ add := func(n *pvacfgnode) {
+ if start == nil || end == nil {
+ start = n
+ end = n
+ } else {
+ end.then = n
+ end = n
+ }
+ }
+ for _, stmt := range body {
+ switch stmt := stmt.(type) {
+ case *ast.ExprStmt:
+ if x, _ := stmt.X.(*ast.CallExpr); x != nil {
+ funstr := exprToString(x.Fun)
+ if funstr == "putattr" {
+ form, _ := x.Args[3].(*ast.Ident)
+ if form == nil {
+ t.Fatalf("%s invalid use of putattr", fset.Position(x.Pos()))
+ }
+ cmt := findLineComment(cm, stmt)
+ if cmt == nil {
+ t.Fatalf("%s invalid use of putattr (no comment containing the attribute name)", fset.Position(x.Pos()))
+ }
+ add(&pvacfgnode{attr: strings.TrimSpace(cmt.Text[2:]), form: form.Name})
+ }
+ }
+ case *ast.IfStmt:
+ ifStart, ifEnd := pvacfgif(t, fset, cm, stmt)
+ if ifStart != nil {
+ add(ifStart)
+ end = ifEnd
+ }
+ default:
+ // check that nothing under this contains a putattr call
+ ast.Inspect(stmt, func(n ast.Node) bool {
+ if call, _ := n.(*ast.CallExpr); call != nil {
+ if exprToString(call.Fun) == "putattr" {
+ t.Fatalf("%s use of putattr in unsupported block", fset.Position(call.Pos()))
+ }
+ }
+ return true
+ })
+ }
+ }
+ return start, end
+}
+
+func pvacfgif(t *testing.T, fset *token.FileSet, cm ast.CommentMap, ifstmt *ast.IfStmt) (start, end *pvacfgnode) {
+ thenStart, thenEnd := pvacfgbody(t, fset, cm, ifstmt.Body.List)
+ var elseStart, elseEnd *pvacfgnode
+ if ifstmt.Else != nil {
+ switch els := ifstmt.Else.(type) {
+ case *ast.IfStmt:
+ elseStart, elseEnd = pvacfgif(t, fset, cm, els)
+ case *ast.BlockStmt:
+ elseStart, elseEnd = pvacfgbody(t, fset, cm, els.List)
+ default:
+ t.Fatalf("%s: unexpected statement %T", fset.Position(els.Pos()), els)
+ }
+ }
+
+ if thenStart != nil && elseStart != nil && thenStart == thenEnd && elseStart == elseEnd && thenStart.form == elseStart.form && thenStart.attr == elseStart.attr {
+ return thenStart, thenEnd
+ }
+
+ if thenStart != nil || elseStart != nil {
+ start = &pvacfgnode{cond: ifstmt.Cond}
+ end = &pvacfgnode{}
+ if thenStart != nil {
+ start.then = thenStart
+ thenEnd.then = end
+ } else {
+ start.then = end
+ }
+ if elseStart != nil {
+ start.els = elseStart
+ elseEnd.then = end
+ } else {
+ start.els = end
+ }
+ }
+ return start, end
+}
+
+func exprToString(t ast.Expr) string {
+ var buf bytes.Buffer
+ printer.Fprint(&buf, token.NewFileSet(), t)
+ return buf.String()
+}
+
+// findLineComment finds the line comment for statement stmt.
+func findLineComment(cm ast.CommentMap, stmt *ast.ExprStmt) *ast.Comment {
+ var r *ast.Comment
+ for _, cmtg := range cm[stmt] {
+ for _, cmt := range cmtg.List {
+ if cmt.Slash > stmt.Pos() {
+ if r != nil {
+ return nil
+ }
+ r = cmt
+ }
+ }
+ }
+ return r
+}
+
+// pvacfgvisit visits the CFG depth first, populates abbrevs with all
+// possible dwAbbrev definitions and returns a tree of if/else statements
+// that picks the correct abbrev.
+func pvacfgvisit(pvacfg *pvacfgnode, abbrevs map[string]int) ast.Stmt {
+ r := &ast.IfStmt{Cond: &ast.BinaryExpr{
+ Op: token.EQL,
+ X: &ast.SelectorExpr{X: &ast.Ident{Name: "v"}, Sel: &ast.Ident{Name: "Tag"}},
+ Y: &ast.Ident{Name: "DW_TAG_variable"}}}
+ r.Body = &ast.BlockStmt{List: []ast.Stmt{
+ pvacfgvisitnode(pvacfg, "DW_TAG_variable", []*pvacfgnode{}, abbrevs),
+ }}
+ r.Else = &ast.BlockStmt{List: []ast.Stmt{
+ pvacfgvisitnode(pvacfg, "DW_TAG_formal_parameter", []*pvacfgnode{}, abbrevs),
+ }}
+ return r
+}
+
+func pvacfgvisitnode(pvacfg *pvacfgnode, tag string, path []*pvacfgnode, abbrevs map[string]int) ast.Stmt {
+ if pvacfg == nil {
+ abbrev := toabbrev(tag, path)
+ if _, ok := abbrevs[abbrev]; !ok {
+ abbrevs[abbrev] = len(abbrevs)
+ }
+ return &ast.ReturnStmt{
+ Results: []ast.Expr{&ast.BinaryExpr{
+ Op: token.ADD,
+ X: &ast.Ident{Name: "DW_ABRV_PUTVAR_START"},
+ Y: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(abbrevs[abbrev])}}}}
+ }
+ if pvacfg.attr != "" {
+ return pvacfgvisitnode(pvacfg.then, tag, append(path, pvacfg), abbrevs)
+ } else if pvacfg.cond != nil {
+ if bx, _ := pvacfg.cond.(*ast.BinaryExpr); bx != nil && bx.Op == token.EQL && exprToString(bx.X) == "v.Tag" {
+ // this condition is "v.Tag == Xxx", check the value of 'tag'
+ y := exprToString(bx.Y)
+ if y == tag {
+ return pvacfgvisitnode(pvacfg.then, tag, path, abbrevs)
+ } else {
+ return pvacfgvisitnode(pvacfg.els, tag, path, abbrevs)
+ }
+ } else {
+ r := &ast.IfStmt{Cond: pvacfg.cond}
+ r.Body = &ast.BlockStmt{List: []ast.Stmt{pvacfgvisitnode(pvacfg.then, tag, path, abbrevs)}}
+ r.Else = &ast.BlockStmt{List: []ast.Stmt{pvacfgvisitnode(pvacfg.els, tag, path, abbrevs)}}
+ return r
+ }
+ } else {
+ return pvacfgvisitnode(pvacfg.then, tag, path, abbrevs)
+ }
+}
+
+func toabbrev(tag string, path []*pvacfgnode) string {
+ buf := new(bytes.Buffer)
+ fmt.Fprintf(buf, "{\n%s,\nDW_CHILDREN_no,\n[]dwAttrForm{\n", tag)
+ for _, node := range path {
+ if node.cond == nil {
+ fmt.Fprintf(buf, "{%s, %s},\n", node.attr, node.form)
+
+ }
+ }
+ fmt.Fprint(buf, "},\n}")
+ return buf.String()
+}