diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go index e9553d1185..733eeff4ac 100644 --- a/src/cmd/compile/internal/dwarfgen/dwarf.go +++ b/src/cmd/compile/internal/dwarfgen/dwarf.go @@ -218,10 +218,10 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir } 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. @@ -233,7 +233,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir 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 } } } @@ -241,7 +241,8 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir 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(), @@ -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 { - var abbrev int + var tag int var offs int64 localAutoOffset := func() int64 { @@ -367,9 +368,9 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { 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 { @@ -387,7 +388,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { if n.InlFormal() || n.InlLocal() { inlIndex = posInlIndex(n.Pos()) + 1 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, IsReturnValue: n.Class == ir.PPARAMOUT, IsInlFormal: n.InlFormal(), - Abbrev: abbrev, + Tag: tag, StackOffset: int32(offs), Type: base.Ctxt.Lookup(typename), 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) 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 } @@ -488,7 +489,7 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var 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 } } } @@ -497,7 +498,8 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var 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. diff --git a/src/cmd/compile/internal/dwarfgen/dwinl.go b/src/cmd/compile/internal/dwarfgen/dwinl.go index 655e7c66ac..bb3ef84df8 100644 --- a/src/cmd/compile/internal/dwarfgen/dwinl.go +++ b/src/cmd/compile/internal/dwarfgen/dwinl.go @@ -358,7 +358,7 @@ func dumpInlCalls(inlcalls dwarf.InlCalls) { 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 diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index d10b3731df..06cafc8886 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -45,7 +45,8 @@ type Sym interface { // 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 @@ -331,16 +332,6 @@ const ( 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 @@ -361,7 +352,7 @@ const ( DW_ABRV_STRUCTTYPE DW_ABRV_TYPEDECL DW_ABRV_DICT_INDEX - DW_NABRV + DW_ABRV_PUTVAR_START ) type dwAbbrev struct { @@ -394,22 +385,23 @@ func expandPseudoForm(form uint8) uint8 { // 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{}}, @@ -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 */ { DW_TAG_lexical_block, @@ -901,7 +781,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ 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)) @@ -1548,39 +1428,7 @@ func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev 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: @@ -1596,64 +1444,44 @@ func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) { 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 @@ -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 // 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) @@ -1698,7 +1526,7 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, 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 diff --git a/src/cmd/internal/dwarf/putvarabbrevgen.go b/src/cmd/internal/dwarf/putvarabbrevgen.go new file mode 100644 index 0000000000..418063d211 --- /dev/null +++ b/src/cmd/internal/dwarf/putvarabbrevgen.go @@ -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 + } + } + } +} diff --git a/src/cmd/internal/dwarf/putvarabbrevgen_test.go b/src/cmd/internal/dwarf/putvarabbrevgen_test.go new file mode 100644 index 0000000000..24500a3388 --- /dev/null +++ b/src/cmd/internal/dwarf/putvarabbrevgen_test.go @@ -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() +}