github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/dwarfgen/dwarf.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package dwarfgen 6 7 import ( 8 "bytes" 9 "github.com/bir3/gocompiler/src/cmd/compile/flag" 10 "fmt" 11 "github.com/bir3/gocompiler/src/internal/buildcfg" 12 "sort" 13 14 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 15 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 16 "github.com/bir3/gocompiler/src/cmd/compile/internal/reflectdata" 17 "github.com/bir3/gocompiler/src/cmd/compile/internal/ssa" 18 "github.com/bir3/gocompiler/src/cmd/compile/internal/ssagen" 19 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 20 "github.com/bir3/gocompiler/src/cmd/internal/dwarf" 21 "github.com/bir3/gocompiler/src/cmd/internal/obj" 22 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 23 "github.com/bir3/gocompiler/src/cmd/internal/src" 24 ) 25 26 func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) { 27 fn := curfn.(*ir.Func) 28 29 if fn.Nname != nil { 30 expect := fn.Linksym() 31 if fnsym.ABI() == obj.ABI0 { 32 expect = fn.LinksymABI(obj.ABI0) 33 } 34 if fnsym != expect { 35 base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect) 36 } 37 } 38 39 // Back when there were two different *Funcs for a function, this code 40 // was not consistent about whether a particular *Node being processed 41 // was an ODCLFUNC or ONAME node. Partly this is because inlined function 42 // bodies have no ODCLFUNC node, which was it's own inconsistency. 43 // In any event, the handling of the two different nodes for DWARF purposes 44 // was subtly different, likely in unintended ways. CL 272253 merged the 45 // two nodes' Func fields, so that code sees the same *Func whether it is 46 // holding the ODCLFUNC or the ONAME. This resulted in changes in the 47 // DWARF output. To preserve the existing DWARF output and leave an 48 // intentional change for a future CL, this code does the following when 49 // fn.Op == ONAME: 50 // 51 // 1. Disallow use of createComplexVars in createDwarfVars. 52 // It was not possible to reach that code for an ONAME before, 53 // because the DebugInfo was set only on the ODCLFUNC Func. 54 // Calling into it in the ONAME case causes an index out of bounds panic. 55 // 56 // 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func, 57 // not the ONAME Func. Populating apdecls for the ONAME case results 58 // in selected being populated after createSimpleVars is called in 59 // createDwarfVars, and then that causes the loop to skip all the entries 60 // in dcl, meaning that the RecordAutoType calls don't happen. 61 // 62 // These two adjustments keep toolstash -cmp working for now. 63 // Deciding the right answer is, as they say, future work. 64 // 65 // We can tell the difference between the old ODCLFUNC and ONAME 66 // cases by looking at the infosym.Name. If it's empty, DebugInfo is 67 // being called from (*obj.Link).populateDWARF, which used to use 68 // the ODCLFUNC. If it's non-empty (the name will end in $abstract), 69 // DebugInfo is being called from (*obj.Link).DwarfAbstractFunc, 70 // which used to use the ONAME form. 71 isODCLFUNC := infosym.Name == "" 72 73 var apdecls []*ir.Name 74 // Populate decls for fn. 75 if isODCLFUNC { 76 for _, n := range fn.Dcl { 77 if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL 78 continue 79 } 80 switch n.Class { 81 case ir.PAUTO: 82 if !n.Used() { 83 // Text == nil -> generating abstract function 84 if fnsym.Func().Text != nil { 85 base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") 86 } 87 continue 88 } 89 case ir.PPARAM, ir.PPARAMOUT: 90 default: 91 continue 92 } 93 apdecls = append(apdecls, n) 94 if n.Type().Kind() == types.TSSA { 95 // Can happen for TypeInt128 types. This only happens for 96 // spill locations, so not a huge deal. 97 continue 98 } 99 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 100 } 101 } 102 103 decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls) 104 105 // For each type referenced by the functions auto vars but not 106 // already referenced by a dwarf var, attach an R_USETYPE relocation to 107 // the function symbol to insure that the type included in DWARF 108 // processing during linking. 109 typesyms := []*obj.LSym{} 110 for t := range fnsym.Func().Autot { 111 typesyms = append(typesyms, t) 112 } 113 sort.Sort(obj.BySymName(typesyms)) 114 for _, sym := range typesyms { 115 r := obj.Addrel(infosym) 116 r.Sym = sym 117 r.Type = objabi.R_USETYPE 118 } 119 fnsym.Func().Autot = nil 120 121 var varScopes []ir.ScopeID 122 for _, decl := range decls { 123 pos := declPos(decl) 124 varScopes = append(varScopes, findScope(fn.Marks, pos)) 125 } 126 127 scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes) 128 var inlcalls dwarf.InlCalls 129 if base.Flag.GenDwarfInl > 0 { 130 inlcalls = assembleInlines(fnsym, dwarfVars) 131 } 132 return scopes, inlcalls 133 } 134 135 func declPos(decl *ir.Name) src.XPos { 136 return decl.Canonical().Pos() 137 } 138 139 // createDwarfVars process fn, returning a list of DWARF variables and the 140 // Nodes they represent. 141 func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) { 142 // Collect a raw list of DWARF vars. 143 var vars []*dwarf.Var 144 var decls []*ir.Name 145 var selected ir.NameSet 146 147 if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK { 148 decls, vars, selected = createComplexVars(fnsym, fn) 149 } else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK { 150 decls, vars, selected = createABIVars(fnsym, fn, apDecls) 151 } else { 152 decls, vars, selected = createSimpleVars(fnsym, apDecls) 153 } 154 if fn.DebugInfo != nil { 155 // Recover zero sized variables eliminated by the stackframe pass 156 for _, n := range fn.DebugInfo.(*ssa.FuncDebug).OptDcl { 157 if n.Class != ir.PAUTO { 158 continue 159 } 160 types.CalcSize(n.Type()) 161 if n.Type().Size() == 0 { 162 decls = append(decls, n) 163 vars = append(vars, createSimpleVar(fnsym, n)) 164 vars[len(vars)-1].StackOffset = 0 165 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 166 } 167 } 168 } 169 170 dcl := apDecls 171 if fnsym.WasInlined() { 172 dcl = preInliningDcls(fnsym) 173 } else { 174 // The backend's stackframe pass prunes away entries from the 175 // fn's Dcl list, including PARAMOUT nodes that correspond to 176 // output params passed in registers. Add back in these 177 // entries here so that we can process them properly during 178 // DWARF-gen. See issue 48573 for more details. 179 debugInfo := fn.DebugInfo.(*ssa.FuncDebug) 180 for _, n := range debugInfo.RegOutputParams { 181 if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() { 182 panic("invalid ir.Name on debugInfo.RegOutputParams list") 183 } 184 dcl = append(dcl, n) 185 } 186 } 187 188 // If optimization is enabled, the list above will typically be 189 // missing some of the original pre-optimization variables in the 190 // function (they may have been promoted to registers, folded into 191 // constants, dead-coded away, etc). Input arguments not eligible 192 // for SSA optimization are also missing. Here we add back in entries 193 // for selected missing vars. Note that the recipe below creates a 194 // conservative location. The idea here is that we want to 195 // communicate to the user that "yes, there is a variable named X 196 // in this function, but no, I don't have enough information to 197 // reliably report its contents." 198 // For non-SSA-able arguments, however, the correct information 199 // is known -- they have a single home on the stack. 200 for _, n := range dcl { 201 if selected.Has(n) { 202 continue 203 } 204 c := n.Sym().Name[0] 205 if c == '.' || n.Type().IsUntyped() { 206 continue 207 } 208 if n.Class == ir.PPARAM && !ssagen.TypeOK(n.Type()) { 209 // SSA-able args get location lists, and may move in and 210 // out of registers, so those are handled elsewhere. 211 // Autos and named output params seem to get handled 212 // with VARDEF, which creates location lists. 213 // Args not of SSA-able type are treated here; they 214 // are homed on the stack in a single place for the 215 // entire call. 216 vars = append(vars, createSimpleVar(fnsym, n)) 217 decls = append(decls, n) 218 continue 219 } 220 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) 221 decls = append(decls, n) 222 abbrev := dwarf.DW_ABRV_AUTO_LOCLIST 223 isReturnValue := (n.Class == ir.PPARAMOUT) 224 if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT { 225 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 226 } 227 if n.Esc() == ir.EscHeap { 228 // The variable in question has been promoted to the heap. 229 // Its address is in n.Heapaddr. 230 // TODO(thanm): generate a better location expression 231 } 232 inlIndex := 0 233 if base.Flag.GenDwarfInl > 1 { 234 if n.InlFormal() || n.InlLocal() { 235 inlIndex = posInlIndex(n.Pos()) + 1 236 if n.InlFormal() { 237 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 238 } 239 } 240 } 241 declpos := base.Ctxt.InnermostPos(n.Pos()) 242 vars = append(vars, &dwarf.Var{ 243 Name: n.Sym().Name, 244 IsReturnValue: isReturnValue, 245 Abbrev: abbrev, 246 StackOffset: int32(n.FrameOffset()), 247 Type: base.Ctxt.Lookup(typename), 248 DeclFile: declpos.RelFilename(), 249 DeclLine: declpos.RelLine(), 250 DeclCol: declpos.RelCol(), 251 InlIndex: int32(inlIndex), 252 ChildIndex: -1, 253 DictIndex: n.DictIndex, 254 }) 255 // Record go type of to insure that it gets emitted by the linker. 256 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 257 } 258 259 // Sort decls and vars. 260 sortDeclsAndVars(fn, decls, vars) 261 262 return decls, vars 263 } 264 265 // sortDeclsAndVars sorts the decl and dwarf var lists according to 266 // parameter declaration order, so as to insure that when a subprogram 267 // DIE is emitted, its parameter children appear in declaration order. 268 // Prior to the advent of the register ABI, sorting by frame offset 269 // would achieve this; with the register we now need to go back to the 270 // original function signature. 271 func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) { 272 paramOrder := make(map[*ir.Name]int) 273 idx := 1 274 for _, selfn := range types.RecvsParamsResults { 275 fsl := selfn(fn.Type()).FieldSlice() 276 for _, f := range fsl { 277 if n, ok := f.Nname.(*ir.Name); ok { 278 paramOrder[n] = idx 279 idx++ 280 } 281 } 282 } 283 sort.Stable(varsAndDecls{decls, vars, paramOrder}) 284 } 285 286 type varsAndDecls struct { 287 decls []*ir.Name 288 vars []*dwarf.Var 289 paramOrder map[*ir.Name]int 290 } 291 292 func (v varsAndDecls) Len() int { 293 return len(v.decls) 294 } 295 296 func (v varsAndDecls) Less(i, j int) bool { 297 nameLT := func(ni, nj *ir.Name) bool { 298 oi, foundi := v.paramOrder[ni] 299 oj, foundj := v.paramOrder[nj] 300 if foundi { 301 if foundj { 302 return oi < oj 303 } else { 304 return true 305 } 306 } 307 return false 308 } 309 return nameLT(v.decls[i], v.decls[j]) 310 } 311 312 func (v varsAndDecls) Swap(i, j int) { 313 v.vars[i], v.vars[j] = v.vars[j], v.vars[i] 314 v.decls[i], v.decls[j] = v.decls[j], v.decls[i] 315 } 316 317 // Given a function that was inlined at some point during the 318 // compilation, return a sorted list of nodes corresponding to the 319 // autos/locals in that function prior to inlining. If this is a 320 // function that is not local to the package being compiled, then the 321 // names of the variables may have been "versioned" to avoid conflicts 322 // with local vars; disregard this versioning when sorting. 323 func preInliningDcls(fnsym *obj.LSym) []*ir.Name { 324 fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func) 325 var rdcl []*ir.Name 326 for _, n := range fn.Inl.Dcl { 327 c := n.Sym().Name[0] 328 // Avoid reporting "_" parameters, since if there are more than 329 // one, it can result in a collision later on, as in #23179. 330 if unversion(n.Sym().Name) == "_" || c == '.' || n.Type().IsUntyped() { 331 continue 332 } 333 rdcl = append(rdcl, n) 334 } 335 return rdcl 336 } 337 338 // createSimpleVars creates a DWARF entry for every variable declared in the 339 // function, claiming that they are permanently on the stack. 340 func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 341 var vars []*dwarf.Var 342 var decls []*ir.Name 343 var selected ir.NameSet 344 for _, n := range apDecls { 345 if ir.IsAutoTmp(n) { 346 continue 347 } 348 349 decls = append(decls, n) 350 vars = append(vars, createSimpleVar(fnsym, n)) 351 selected.Add(n) 352 } 353 return decls, vars, selected 354 } 355 356 func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { 357 var abbrev int 358 var offs int64 359 360 localAutoOffset := func() int64 { 361 offs = n.FrameOffset() 362 if base.Ctxt.Arch.FixedFrameSize == 0 { 363 offs -= int64(types.PtrSize) 364 } 365 if buildcfg.FramePointerEnabled { 366 offs -= int64(types.PtrSize) 367 } 368 return offs 369 } 370 371 switch n.Class { 372 case ir.PAUTO: 373 offs = localAutoOffset() 374 abbrev = dwarf.DW_ABRV_AUTO 375 case ir.PPARAM, ir.PPARAMOUT: 376 abbrev = dwarf.DW_ABRV_PARAM 377 if n.IsOutputParamInRegisters() { 378 offs = localAutoOffset() 379 } else { 380 offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize 381 } 382 383 default: 384 base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n) 385 } 386 387 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) 388 delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type())) 389 inlIndex := 0 390 if base.Flag.GenDwarfInl > 1 { 391 if n.InlFormal() || n.InlLocal() { 392 inlIndex = posInlIndex(n.Pos()) + 1 393 if n.InlFormal() { 394 abbrev = dwarf.DW_ABRV_PARAM 395 } 396 } 397 } 398 declpos := base.Ctxt.InnermostPos(declPos(n)) 399 return &dwarf.Var{ 400 Name: n.Sym().Name, 401 IsReturnValue: n.Class == ir.PPARAMOUT, 402 IsInlFormal: n.InlFormal(), 403 Abbrev: abbrev, 404 StackOffset: int32(offs), 405 Type: base.Ctxt.Lookup(typename), 406 DeclFile: declpos.RelFilename(), 407 DeclLine: declpos.RelLine(), 408 DeclCol: declpos.RelCol(), 409 InlIndex: int32(inlIndex), 410 ChildIndex: -1, 411 DictIndex: n.DictIndex, 412 } 413 } 414 415 // createABIVars creates DWARF variables for functions in which the 416 // register ABI is enabled but optimization is turned off. It uses a 417 // hybrid approach in which register-resident input params are 418 // captured with location lists, and all other vars use the "simple" 419 // strategy. 420 func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 421 422 // Invoke createComplexVars to generate dwarf vars for input parameters 423 // that are register-allocated according to the ABI rules. 424 decls, vars, selected := createComplexVars(fnsym, fn) 425 426 // Now fill in the remainder of the variables: input parameters 427 // that are not register-resident, output parameters, and local 428 // variables. 429 for _, n := range apDecls { 430 if ir.IsAutoTmp(n) { 431 continue 432 } 433 if _, ok := selected[n]; ok { 434 // already handled 435 continue 436 } 437 438 decls = append(decls, n) 439 vars = append(vars, createSimpleVar(fnsym, n)) 440 selected.Add(n) 441 } 442 443 return decls, vars, selected 444 } 445 446 // createComplexVars creates recomposed DWARF vars with location lists, 447 // suitable for describing optimized code. 448 func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 449 debugInfo := fn.DebugInfo.(*ssa.FuncDebug) 450 451 // Produce a DWARF variable entry for each user variable. 452 var decls []*ir.Name 453 var vars []*dwarf.Var 454 var ssaVars ir.NameSet 455 456 for varID, dvar := range debugInfo.Vars { 457 n := dvar 458 ssaVars.Add(n) 459 for _, slot := range debugInfo.VarSlots[varID] { 460 ssaVars.Add(debugInfo.Slots[slot].N) 461 } 462 463 if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil { 464 decls = append(decls, n) 465 vars = append(vars, dvar) 466 } 467 } 468 469 return decls, vars, ssaVars 470 } 471 472 // createComplexVar builds a single DWARF variable entry and location list. 473 func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var { 474 debug := fn.DebugInfo.(*ssa.FuncDebug) 475 n := debug.Vars[varID] 476 477 var abbrev int 478 switch n.Class { 479 case ir.PAUTO: 480 abbrev = dwarf.DW_ABRV_AUTO_LOCLIST 481 case ir.PPARAM, ir.PPARAMOUT: 482 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 483 default: 484 return nil 485 } 486 487 gotype := reflectdata.TypeLinksym(n.Type()) 488 delete(fnsym.Func().Autot, gotype) 489 typename := dwarf.InfoPrefix + gotype.Name[len("type:"):] 490 inlIndex := 0 491 if base.Flag.GenDwarfInl > 1 { 492 if n.InlFormal() || n.InlLocal() { 493 inlIndex = posInlIndex(n.Pos()) + 1 494 if n.InlFormal() { 495 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 496 } 497 } 498 } 499 declpos := base.Ctxt.InnermostPos(n.Pos()) 500 dvar := &dwarf.Var{ 501 Name: n.Sym().Name, 502 IsReturnValue: n.Class == ir.PPARAMOUT, 503 IsInlFormal: n.InlFormal(), 504 Abbrev: abbrev, 505 Type: base.Ctxt.Lookup(typename), 506 // The stack offset is used as a sorting key, so for decomposed 507 // variables just give it the first one. It's not used otherwise. 508 // This won't work well if the first slot hasn't been assigned a stack 509 // location, but it's not obvious how to do better. 510 StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), 511 DeclFile: declpos.RelFilename(), 512 DeclLine: declpos.RelLine(), 513 DeclCol: declpos.RelCol(), 514 InlIndex: int32(inlIndex), 515 ChildIndex: -1, 516 DictIndex: n.DictIndex, 517 } 518 list := debug.LocationLists[varID] 519 if len(list) != 0 { 520 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { 521 debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) 522 } 523 } 524 return dvar 525 } 526 527 // RecordFlags records the specified command-line flags to be placed 528 // in the DWARF info. 529 func RecordFlags(flags ...string) { 530 if base.Ctxt.Pkgpath == "" { 531 // We can't record the flags if we don't know what the 532 // package name is. 533 return 534 } 535 536 type BoolFlag interface { 537 IsBoolFlag() bool 538 } 539 type CountFlag interface { 540 IsCountFlag() bool 541 } 542 var cmd bytes.Buffer 543 for _, name := range flags { 544 f := flag.Lookup(name) 545 if f == nil { 546 continue 547 } 548 getter := f.Value.(flag.Getter) 549 if getter.String() == f.DefValue { 550 // Flag has default value, so omit it. 551 continue 552 } 553 if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() { 554 val, ok := getter.Get().(bool) 555 if ok && val { 556 fmt.Fprintf(&cmd, " -%s", f.Name) 557 continue 558 } 559 } 560 if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() { 561 val, ok := getter.Get().(int) 562 if ok && val == 1 { 563 fmt.Fprintf(&cmd, " -%s", f.Name) 564 continue 565 } 566 } 567 fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get()) 568 } 569 570 // Adds flag to producer string signaling whether regabi is turned on or 571 // off. 572 // Once regabi is turned on across the board and the relative GOEXPERIMENT 573 // knobs no longer exist this code should be removed. 574 if buildcfg.Experiment.RegabiArgs { 575 cmd.Write([]byte(" regabi")) 576 } 577 578 if cmd.Len() == 0 { 579 return 580 } 581 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath) 582 s.Type = objabi.SDWARFCUINFO 583 // Sometimes (for example when building tests) we can link 584 // together two package main archives. So allow dups. 585 s.Set(obj.AttrDuplicateOK, true) 586 base.Ctxt.Data = append(base.Ctxt.Data, s) 587 s.P = cmd.Bytes()[1:] 588 } 589 590 // RecordPackageName records the name of the package being 591 // compiled, so that the linker can save it in the compile unit's DIE. 592 func RecordPackageName() { 593 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath) 594 s.Type = objabi.SDWARFCUINFO 595 // Sometimes (for example when building tests) we can link 596 // together two package main archives. So allow dups. 597 s.Set(obj.AttrDuplicateOK, true) 598 base.Ctxt.Data = append(base.Ctxt.Data, s) 599 s.P = []byte(types.LocalPkg.Name) 600 }