mirror of
https://github.com/golang/go.git
synced 2025-05-18 13:54:40 +00:00
cmd/compile/internal/dwarfgen: refactor putvar and putAbstractVar
Currently, changing putvar or putAbstractVar involves: 1. changing the abbrevs array to add new abbrevs or modify existing ones 2. changing the DW_ABRV_XXX const block to add the new abbrevs, this const block must match the changes to the abbrevs array 3. change the code at the start of putvar and putAbstractVar that selects the abbrev to use 4. change the body of putvar/putAbstractVar to emit the right attributes in the right sequence Each change must agree with all other, this is error prone and if an mistake is made there is no compile time or runtime check detecting it. Erroneous code will simply produce unreadable debug sections. This commit adds a mechanism to automatically generate code for abbrev selection as well as the abbrev definitions based on static examination of the body of putvar and putAbstractVar. TestPutVarAbbrevGenerator is responsible for checking that the generated code is kept updated and will regenerated it by passing the '-generate' option to it. benchstat output: | old.txt | new.txt | | sec/op | sec/op vs base | Template 153.8m ± 6% 153.0m ± 6% ~ (p=0.853 n=10) Unicode 98.98m ± 19% 100.22m ± 15% ~ (p=0.796 n=10) GoTypes 1013.7m ± 13% 943.6m ± 8% ~ (p=0.353 n=10) Compiler 98.48m ± 10% 97.79m ± 6% ~ (p=0.353 n=10) SSA 8.921 ± 31% 6.872 ± 37% ~ (p=0.912 n=10) Flate 114.3m ± 21% 128.0m ± 36% ~ (p=0.436 n=10) GoParser 219.0m ± 27% 214.9m ± 26% ~ (p=0.631 n=10) Reflect 447.5m ± 20% 452.6m ± 22% ~ (p=0.684 n=10) Tar 166.9m ± 27% 166.2m ± 27% ~ (p=0.529 n=10) XML 218.6m ± 25% 219.3m ± 24% ~ (p=0.631 n=10) LinkCompiler 492.7m ± 12% 523.2m ± 13% ~ (p=0.315 n=10) ExternalLinkCompiler 1.684 ± 3% 1.684 ± 2% ~ (p=0.684 n=10) LinkWithoutDebugCompiler 296.0m ± 8% 304.9m ± 12% ~ (p=0.579 n=10) StdCmd 69.59 ± 15% 70.76 ± 14% ~ (p=0.436 n=10) geomean 516.0m 511.5m -0.87% | old.txt | new.txt | | user-sec/op | user-sec/op vs base | Template 281.5m ± 10% 269.6m ± 13% ~ (p=0.315 n=10) Unicode 107.3m ± 8% 110.2m ± 8% ~ (p=0.165 n=10) GoTypes 2.414 ± 16% 2.181 ± 9% ~ (p=0.315 n=10) Compiler 116.0m ± 16% 119.1m ± 11% ~ (p=0.971 n=10) SSA 25.47 ± 39% 17.75 ± 52% ~ (p=0.739 n=10) Flate 205.2m ± 25% 256.2m ± 43% ~ (p=0.393 n=10) GoParser 456.8m ± 28% 427.0m ± 24% ~ (p=0.912 n=10) Reflect 960.3m ± 22% 990.5m ± 23% ~ (p=0.280 n=10) Tar 299.8m ± 27% 307.9m ± 27% ~ (p=0.631 n=10) XML 425.0m ± 21% 432.8m ± 24% ~ (p=0.353 n=10) LinkCompiler 768.1m ± 11% 796.9m ± 14% ~ (p=0.631 n=10) ExternalLinkCompiler 1.713 ± 5% 1.666 ± 4% ~ (p=0.190 n=10) LinkWithoutDebugCompiler 313.0m ± 9% 316.7m ± 12% ~ (p=0.481 n=10) geomean 588.6m 579.5m -1.55% | 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.01% (p=0.000 n=10) CmdGoSize 16.37Mi ± 0% 16.37Mi ± 0% -0.00% (p=0.000 n=10) geomean 4.631Mi 4.631Mi -0.00% Change-Id: I7edf37b5a47fd9aceef931ddf2c701e66a7b38b2 Reviewed-on: https://go-review.googlesource.com/c/go/+/563815 Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Than McIntosh <thanm@google.com> Auto-Submit: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
5bce5362da
commit
08029be9fc
@ -218,10 +218,10 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
|
|||||||
}
|
}
|
||||||
typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
|
typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
|
||||||
decls = append(decls, n)
|
decls = append(decls, n)
|
||||||
abbrev := dwarf.DW_ABRV_AUTO_LOCLIST
|
tag := dwarf.DW_TAG_variable
|
||||||
isReturnValue := (n.Class == ir.PPARAMOUT)
|
isReturnValue := (n.Class == ir.PPARAMOUT)
|
||||||
if n.Class == ir.PPARAM || 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 {
|
if n.Esc() == ir.EscHeap {
|
||||||
// The variable in question has been promoted to the heap.
|
// The variable in question has been promoted to the heap.
|
||||||
@ -233,7 +233,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
|
|||||||
if n.InlFormal() || n.InlLocal() {
|
if n.InlFormal() || n.InlLocal() {
|
||||||
inlIndex = posInlIndex(n.Pos()) + 1
|
inlIndex = posInlIndex(n.Pos()) + 1
|
||||||
if n.InlFormal() {
|
if n.InlFormal() {
|
||||||
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
|
tag = dwarf.DW_TAG_formal_parameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,7 +241,8 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
|
|||||||
vars = append(vars, &dwarf.Var{
|
vars = append(vars, &dwarf.Var{
|
||||||
Name: n.Sym().Name,
|
Name: n.Sym().Name,
|
||||||
IsReturnValue: isReturnValue,
|
IsReturnValue: isReturnValue,
|
||||||
Abbrev: abbrev,
|
Tag: tag,
|
||||||
|
WithLoclist: true,
|
||||||
StackOffset: int32(n.FrameOffset()),
|
StackOffset: int32(n.FrameOffset()),
|
||||||
Type: base.Ctxt.Lookup(typename),
|
Type: base.Ctxt.Lookup(typename),
|
||||||
DeclFile: declpos.RelFilename(),
|
DeclFile: declpos.RelFilename(),
|
||||||
@ -350,7 +351,7 @@ func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
|
func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
|
||||||
var abbrev int
|
var tag int
|
||||||
var offs int64
|
var offs int64
|
||||||
|
|
||||||
localAutoOffset := func() int64 {
|
localAutoOffset := func() int64 {
|
||||||
@ -367,9 +368,9 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
|
|||||||
switch n.Class {
|
switch n.Class {
|
||||||
case ir.PAUTO:
|
case ir.PAUTO:
|
||||||
offs = localAutoOffset()
|
offs = localAutoOffset()
|
||||||
abbrev = dwarf.DW_ABRV_AUTO
|
tag = dwarf.DW_TAG_variable
|
||||||
case ir.PPARAM, ir.PPARAMOUT:
|
case ir.PPARAM, ir.PPARAMOUT:
|
||||||
abbrev = dwarf.DW_ABRV_PARAM
|
tag = dwarf.DW_TAG_formal_parameter
|
||||||
if n.IsOutputParamInRegisters() {
|
if n.IsOutputParamInRegisters() {
|
||||||
offs = localAutoOffset()
|
offs = localAutoOffset()
|
||||||
} else {
|
} else {
|
||||||
@ -387,7 +388,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
|
|||||||
if n.InlFormal() || n.InlLocal() {
|
if n.InlFormal() || n.InlLocal() {
|
||||||
inlIndex = posInlIndex(n.Pos()) + 1
|
inlIndex = posInlIndex(n.Pos()) + 1
|
||||||
if n.InlFormal() {
|
if n.InlFormal() {
|
||||||
abbrev = dwarf.DW_ABRV_PARAM
|
tag = dwarf.DW_TAG_formal_parameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,7 +397,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
|
|||||||
Name: n.Sym().Name,
|
Name: n.Sym().Name,
|
||||||
IsReturnValue: n.Class == ir.PPARAMOUT,
|
IsReturnValue: n.Class == ir.PPARAMOUT,
|
||||||
IsInlFormal: n.InlFormal(),
|
IsInlFormal: n.InlFormal(),
|
||||||
Abbrev: abbrev,
|
Tag: tag,
|
||||||
StackOffset: int32(offs),
|
StackOffset: int32(offs),
|
||||||
Type: base.Ctxt.Lookup(typename),
|
Type: base.Ctxt.Lookup(typename),
|
||||||
DeclFile: declpos.RelFilename(),
|
DeclFile: declpos.RelFilename(),
|
||||||
@ -470,12 +471,12 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var
|
|||||||
debug := fn.DebugInfo.(*ssa.FuncDebug)
|
debug := fn.DebugInfo.(*ssa.FuncDebug)
|
||||||
n := debug.Vars[varID]
|
n := debug.Vars[varID]
|
||||||
|
|
||||||
var abbrev int
|
var tag int
|
||||||
switch n.Class {
|
switch n.Class {
|
||||||
case ir.PAUTO:
|
case ir.PAUTO:
|
||||||
abbrev = dwarf.DW_ABRV_AUTO_LOCLIST
|
tag = dwarf.DW_TAG_variable
|
||||||
case ir.PPARAM, ir.PPARAMOUT:
|
case ir.PPARAM, ir.PPARAMOUT:
|
||||||
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
|
tag = dwarf.DW_TAG_formal_parameter
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -488,7 +489,7 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var
|
|||||||
if n.InlFormal() || n.InlLocal() {
|
if n.InlFormal() || n.InlLocal() {
|
||||||
inlIndex = posInlIndex(n.Pos()) + 1
|
inlIndex = posInlIndex(n.Pos()) + 1
|
||||||
if n.InlFormal() {
|
if n.InlFormal() {
|
||||||
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
|
tag = dwarf.DW_TAG_formal_parameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -497,7 +498,8 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var
|
|||||||
Name: n.Sym().Name,
|
Name: n.Sym().Name,
|
||||||
IsReturnValue: n.Class == ir.PPARAMOUT,
|
IsReturnValue: n.Class == ir.PPARAMOUT,
|
||||||
IsInlFormal: n.InlFormal(),
|
IsInlFormal: n.InlFormal(),
|
||||||
Abbrev: abbrev,
|
Tag: tag,
|
||||||
|
WithLoclist: true,
|
||||||
Type: base.Ctxt.Lookup(typename),
|
Type: base.Ctxt.Lookup(typename),
|
||||||
// The stack offset is used as a sorting key, so for decomposed
|
// The stack offset is used as a sorting key, so for decomposed
|
||||||
// variables just give it the first one. It's not used otherwise.
|
// variables just give it the first one. It's not used otherwise.
|
||||||
|
@ -358,7 +358,7 @@ func dumpInlCalls(inlcalls dwarf.InlCalls) {
|
|||||||
func dumpInlVars(dwvars []*dwarf.Var) {
|
func dumpInlVars(dwvars []*dwarf.Var) {
|
||||||
for i, dwv := range dwvars {
|
for i, dwv := range dwvars {
|
||||||
typ := "local"
|
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"
|
typ = "param"
|
||||||
}
|
}
|
||||||
ia := 0
|
ia := 0
|
||||||
|
@ -45,7 +45,8 @@ type Sym interface {
|
|||||||
// A Var represents a local variable or a function parameter.
|
// A Var represents a local variable or a function parameter.
|
||||||
type Var struct {
|
type Var struct {
|
||||||
Name string
|
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
|
IsReturnValue bool
|
||||||
IsInlFormal bool
|
IsInlFormal bool
|
||||||
DictIndex uint16 // index of the dictionary entry describing the type of this variable
|
DictIndex uint16 // index of the dictionary entry describing the type of this variable
|
||||||
@ -331,16 +332,6 @@ const (
|
|||||||
DW_ABRV_INLINED_SUBROUTINE_RANGES
|
DW_ABRV_INLINED_SUBROUTINE_RANGES
|
||||||
DW_ABRV_VARIABLE
|
DW_ABRV_VARIABLE
|
||||||
DW_ABRV_INT_CONSTANT
|
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_RANGES
|
||||||
DW_ABRV_LEXICAL_BLOCK_SIMPLE
|
DW_ABRV_LEXICAL_BLOCK_SIMPLE
|
||||||
DW_ABRV_STRUCTFIELD
|
DW_ABRV_STRUCTFIELD
|
||||||
@ -361,7 +352,7 @@ const (
|
|||||||
DW_ABRV_STRUCTTYPE
|
DW_ABRV_STRUCTTYPE
|
||||||
DW_ABRV_TYPEDECL
|
DW_ABRV_TYPEDECL
|
||||||
DW_ABRV_DICT_INDEX
|
DW_ABRV_DICT_INDEX
|
||||||
DW_NABRV
|
DW_ABRV_PUTVAR_START
|
||||||
)
|
)
|
||||||
|
|
||||||
type dwAbbrev struct {
|
type dwAbbrev struct {
|
||||||
@ -394,22 +385,23 @@ func expandPseudoForm(form uint8) uint8 {
|
|||||||
// expanding any DW_FORM pseudo-ops to real values.
|
// expanding any DW_FORM pseudo-ops to real values.
|
||||||
func Abbrevs() []dwAbbrev {
|
func Abbrevs() []dwAbbrev {
|
||||||
if abbrevsFinalized {
|
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++ {
|
for j := 0; j < len(abbrevs[i].attr); j++ {
|
||||||
abbrevs[i].attr[j].form = expandPseudoForm(abbrevs[i].attr[j].form)
|
abbrevs[i].attr[j].form = expandPseudoForm(abbrevs[i].attr[j].form)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
abbrevsFinalized = true
|
abbrevsFinalized = true
|
||||||
return abbrevs[:]
|
return abbrevs
|
||||||
}
|
}
|
||||||
|
|
||||||
// abbrevs is a raw table of abbrev entries; it needs to be post-processed
|
// 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
|
// by the Abbrevs() function above prior to being consumed, to expand
|
||||||
// the 'pseudo-form' entries below to real DWARF form values.
|
// the 'pseudo-form' entries below to real DWARF form values.
|
||||||
|
|
||||||
var abbrevs = [DW_NABRV]dwAbbrev{
|
var abbrevs = []dwAbbrev{
|
||||||
/* The mandatory DW_ABRV_NULL entry. */
|
/* The mandatory DW_ABRV_NULL entry. */
|
||||||
{0, 0, []dwAttrForm{}},
|
{0, 0, []dwAttrForm{}},
|
||||||
|
|
||||||
@ -555,118 +547,6 @@ var abbrevs = [DW_NABRV]dwAbbrev{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/* 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 */
|
/* LEXICAL_BLOCK_RANGES */
|
||||||
{
|
{
|
||||||
DW_TAG_lexical_block,
|
DW_TAG_lexical_block,
|
||||||
@ -901,7 +781,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
|
|||||||
func GetAbbrev() []byte {
|
func GetAbbrev() []byte {
|
||||||
abbrevs := Abbrevs()
|
abbrevs := Abbrevs()
|
||||||
var buf []byte
|
var buf []byte
|
||||||
for i := 1; i < DW_NABRV; i++ {
|
for i := 1; i < len(abbrevs); i++ {
|
||||||
// See section 7.5.3
|
// See section 7.5.3
|
||||||
buf = AppendUleb128(buf, uint64(i))
|
buf = AppendUleb128(buf, uint64(i))
|
||||||
buf = AppendUleb128(buf, uint64(abbrevs[i].tag))
|
buf = AppendUleb128(buf, uint64(abbrevs[i].tag))
|
||||||
@ -1548,39 +1428,7 @@ func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev
|
|||||||
return curscope
|
return curscope
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a default var abbrev code, select corresponding concrete code.
|
func concreteVar(fnabbrev int, v *Var) bool {
|
||||||
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.
|
|
||||||
concrete := true
|
concrete := true
|
||||||
switch fnabbrev {
|
switch fnabbrev {
|
||||||
case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER:
|
case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER:
|
||||||
@ -1596,64 +1444,44 @@ func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) {
|
|||||||
default:
|
default:
|
||||||
panic("should never happen")
|
panic("should never happen")
|
||||||
}
|
}
|
||||||
|
return concrete
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit DWARF attributes for a variable belonging to an 'abstract' subprogram.
|
// Emit DWARF attributes for a variable belonging to an 'abstract' subprogram.
|
||||||
func putAbstractVar(ctxt Context, info Sym, v *Var) {
|
func putAbstractVar(ctxt Context, info Sym, v *Var) {
|
||||||
// Remap abbrev
|
// The contents of this functions are used to generate putAbstractVarAbbrev automatically, see TestPutVarAbbrevGenerator.
|
||||||
abbrev := v.Abbrev
|
abbrev := putAbstractVarAbbrev(v)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
Uleb128put(ctxt, info, int64(abbrev))
|
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
|
// Isreturn attribute if this is a param
|
||||||
if abbrev == DW_ABRV_PARAM_ABSTRACT {
|
if v.Tag == DW_TAG_formal_parameter {
|
||||||
var isReturn int64
|
var isReturn int64
|
||||||
if v.IsReturnValue {
|
if v.IsReturnValue {
|
||||||
isReturn = 1
|
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
|
// 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.
|
// 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
|
// 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
|
// Var has no children => no terminator
|
||||||
}
|
}
|
||||||
|
|
||||||
func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, encbuf []byte) {
|
func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, encbuf []byte) {
|
||||||
// Remap abbrev according to parent DIE abbrev
|
// The contents of this functions are used to generate putvarAbbrev automatically, see TestPutVarAbbrevGenerator.
|
||||||
abbrev, missing, concrete := determineVarAbbrev(v, fnabbrev)
|
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))
|
Uleb128put(ctxt, s.Info, int64(abbrev))
|
||||||
|
|
||||||
// Abstract origin for concrete / inlined case
|
// Abstract origin for concrete / inlined case
|
||||||
@ -1662,35 +1490,35 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int,
|
|||||||
// function subprogram DIE. The child DIE has no LSym, so instead
|
// function subprogram DIE. The child DIE has no LSym, so instead
|
||||||
// after the call to 'putattr' below we make a call to register
|
// after the call to 'putattr' below we make a call to register
|
||||||
// the child DIE reference.
|
// 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)
|
ctxt.RecordDclReference(s.Info, absfn, int(v.ChildIndex), inlIndex)
|
||||||
} else {
|
} else {
|
||||||
// Var name, line for abstract and default cases
|
// Var name, line for abstract and default cases
|
||||||
n := v.Name
|
n := v.Name
|
||||||
putattr(ctxt, s.Info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
|
putattr(ctxt, s.Info, abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n) // DW_AT_name
|
||||||
if abbrev == DW_ABRV_PARAM || abbrev == DW_ABRV_PARAM_LOCLIST || abbrev == DW_ABRV_PARAM_ABSTRACT {
|
if v.Tag == DW_TAG_formal_parameter {
|
||||||
var isReturn int64
|
var isReturn int64
|
||||||
if v.IsReturnValue {
|
if v.IsReturnValue {
|
||||||
isReturn = 1
|
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)
|
putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil) // DW_AT_decl_line
|
||||||
if v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0 {
|
if hasParametricType {
|
||||||
// If the type of this variable is parametric use the entry emitted by putparamtypes
|
// 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 {
|
} 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) {
|
if withLoclist {
|
||||||
putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, ctxt.Size(s.Loc), s.Loc)
|
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)
|
v.PutLocationList(s.Loc, s.StartPC)
|
||||||
} else {
|
} else {
|
||||||
loc := encbuf[:0]
|
loc := encbuf[:0]
|
||||||
switch {
|
switch {
|
||||||
case missing:
|
case v.WithLoclist:
|
||||||
break // no location
|
break // no location
|
||||||
case v.StackOffset == 0:
|
case v.StackOffset == 0:
|
||||||
loc = append(loc, DW_OP_call_frame_cfa)
|
loc = append(loc, DW_OP_call_frame_cfa)
|
||||||
@ -1698,7 +1526,7 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int,
|
|||||||
loc = append(loc, DW_OP_fbreg)
|
loc = append(loc, DW_OP_fbreg)
|
||||||
loc = AppendSleb128(loc, int64(v.StackOffset))
|
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
|
// Var has no children => no terminator
|
||||||
|
139
src/cmd/internal/dwarf/putvarabbrevgen.go
Normal file
139
src/cmd/internal/dwarf/putvarabbrevgen.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
316
src/cmd/internal/dwarf/putvarabbrevgen_test.go
Normal file
316
src/cmd/internal/dwarf/putvarabbrevgen_test.go
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user