github.com/bir3/gocompiler@v0.9.2202/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 obj.Func) (scopes []dwarf.Scope, inlcalls 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 if base.Flag.GenDwarfInl > 0 { 129 inlcalls = assembleInlines(fnsym, dwarfVars) 130 } 131 return scopes, inlcalls 132 } 133 134 func declPos(decl *ir.Name) src.XPos { 135 return decl.Canonical().Pos() 136 } 137 138 // createDwarfVars process fn, returning a list of DWARF variables and the 139 // Nodes they represent. 140 func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) { 141 // Collect a raw list of DWARF vars. 142 var vars []*dwarf.Var 143 var decls []*ir.Name 144 var selected ir.NameSet 145 146 if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK { 147 decls, vars, selected = createComplexVars(fnsym, fn) 148 } else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK { 149 decls, vars, selected = createABIVars(fnsym, fn, apDecls) 150 } else { 151 decls, vars, selected = createSimpleVars(fnsym, apDecls) 152 } 153 if fn.DebugInfo != nil { 154 // Recover zero sized variables eliminated by the stackframe pass 155 for _, n := range fn.DebugInfo.(*ssa.FuncDebug).OptDcl { 156 if n.Class != ir.PAUTO { 157 continue 158 } 159 types.CalcSize(n.Type()) 160 if n.Type().Size() == 0 { 161 decls = append(decls, n) 162 vars = append(vars, createSimpleVar(fnsym, n)) 163 vars[len(vars)-1].StackOffset = 0 164 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 165 } 166 } 167 } 168 169 dcl := apDecls 170 if fnsym.WasInlined() { 171 dcl = preInliningDcls(fnsym) 172 } else { 173 // The backend's stackframe pass prunes away entries from the 174 // fn's Dcl list, including PARAMOUT nodes that correspond to 175 // output params passed in registers. Add back in these 176 // entries here so that we can process them properly during 177 // DWARF-gen. See issue 48573 for more details. 178 debugInfo := fn.DebugInfo.(*ssa.FuncDebug) 179 for _, n := range debugInfo.RegOutputParams { 180 if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() { 181 panic("invalid ir.Name on debugInfo.RegOutputParams list") 182 } 183 dcl = append(dcl, n) 184 } 185 } 186 187 // If optimization is enabled, the list above will typically be 188 // missing some of the original pre-optimization variables in the 189 // function (they may have been promoted to registers, folded into 190 // constants, dead-coded away, etc). Input arguments not eligible 191 // for SSA optimization are also missing. Here we add back in entries 192 // for selected missing vars. Note that the recipe below creates a 193 // conservative location. The idea here is that we want to 194 // communicate to the user that "yes, there is a variable named X 195 // in this function, but no, I don't have enough information to 196 // reliably report its contents." 197 // For non-SSA-able arguments, however, the correct information 198 // is known -- they have a single home on the stack. 199 for _, n := range dcl { 200 if selected.Has(n) { 201 continue 202 } 203 c := n.Sym().Name[0] 204 if c == '.' || n.Type().IsUntyped() { 205 continue 206 } 207 if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) { 208 // SSA-able args get location lists, and may move in and 209 // out of registers, so those are handled elsewhere. 210 // Autos and named output params seem to get handled 211 // with VARDEF, which creates location lists. 212 // Args not of SSA-able type are treated here; they 213 // are homed on the stack in a single place for the 214 // entire call. 215 vars = append(vars, createSimpleVar(fnsym, n)) 216 decls = append(decls, n) 217 continue 218 } 219 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) 220 decls = append(decls, n) 221 abbrev := dwarf.DW_ABRV_AUTO_LOCLIST 222 isReturnValue := (n.Class == ir.PPARAMOUT) 223 if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT { 224 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 225 } 226 if n.Esc() == ir.EscHeap { 227 // The variable in question has been promoted to the heap. 228 // Its address is in n.Heapaddr. 229 // TODO(thanm): generate a better location expression 230 } 231 inlIndex := 0 232 if base.Flag.GenDwarfInl > 1 { 233 if n.InlFormal() || n.InlLocal() { 234 inlIndex = posInlIndex(n.Pos()) + 1 235 if n.InlFormal() { 236 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 237 } 238 } 239 } 240 declpos := base.Ctxt.InnermostPos(n.Pos()) 241 vars = append(vars, &dwarf.Var{ 242 Name: n.Sym().Name, 243 IsReturnValue: isReturnValue, 244 Abbrev: abbrev, 245 StackOffset: int32(n.FrameOffset()), 246 Type: base.Ctxt.Lookup(typename), 247 DeclFile: declpos.RelFilename(), 248 DeclLine: declpos.RelLine(), 249 DeclCol: declpos.RelCol(), 250 InlIndex: int32(inlIndex), 251 ChildIndex: -1, 252 DictIndex: n.DictIndex, 253 }) 254 // Record go type of to insure that it gets emitted by the linker. 255 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) 256 } 257 258 // Sort decls and vars. 259 sortDeclsAndVars(fn, decls, vars) 260 261 return decls, vars 262 } 263 264 // sortDeclsAndVars sorts the decl and dwarf var lists according to 265 // parameter declaration order, so as to insure that when a subprogram 266 // DIE is emitted, its parameter children appear in declaration order. 267 // Prior to the advent of the register ABI, sorting by frame offset 268 // would achieve this; with the register we now need to go back to the 269 // original function signature. 270 func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) { 271 paramOrder := make(map[*ir.Name]int) 272 idx := 1 273 for _, f := range fn.Type().RecvParamsResults() { 274 if n, ok := f.Nname.(*ir.Name); ok { 275 paramOrder[n] = idx 276 idx++ 277 } 278 } 279 sort.Stable(varsAndDecls{decls, vars, paramOrder}) 280 } 281 282 type varsAndDecls struct { 283 decls []*ir.Name 284 vars []*dwarf.Var 285 paramOrder map[*ir.Name]int 286 } 287 288 func (v varsAndDecls) Len() int { 289 return len(v.decls) 290 } 291 292 func (v varsAndDecls) Less(i, j int) bool { 293 nameLT := func(ni, nj *ir.Name) bool { 294 oi, foundi := v.paramOrder[ni] 295 oj, foundj := v.paramOrder[nj] 296 if foundi { 297 if foundj { 298 return oi < oj 299 } else { 300 return true 301 } 302 } 303 return false 304 } 305 return nameLT(v.decls[i], v.decls[j]) 306 } 307 308 func (v varsAndDecls) Swap(i, j int) { 309 v.vars[i], v.vars[j] = v.vars[j], v.vars[i] 310 v.decls[i], v.decls[j] = v.decls[j], v.decls[i] 311 } 312 313 // Given a function that was inlined at some point during the 314 // compilation, return a sorted list of nodes corresponding to the 315 // autos/locals in that function prior to inlining. If this is a 316 // function that is not local to the package being compiled, then the 317 // names of the variables may have been "versioned" to avoid conflicts 318 // with local vars; disregard this versioning when sorting. 319 func preInliningDcls(fnsym *obj.LSym) []*ir.Name { 320 fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func) 321 var rdcl []*ir.Name 322 for _, n := range fn.Inl.Dcl { 323 c := n.Sym().Name[0] 324 // Avoid reporting "_" parameters, since if there are more than 325 // one, it can result in a collision later on, as in #23179. 326 if n.Sym().Name == "_" || c == '.' || n.Type().IsUntyped() { 327 continue 328 } 329 rdcl = append(rdcl, n) 330 } 331 return rdcl 332 } 333 334 // createSimpleVars creates a DWARF entry for every variable declared in the 335 // function, claiming that they are permanently on the stack. 336 func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 337 var vars []*dwarf.Var 338 var decls []*ir.Name 339 var selected ir.NameSet 340 for _, n := range apDecls { 341 if ir.IsAutoTmp(n) { 342 continue 343 } 344 345 decls = append(decls, n) 346 vars = append(vars, createSimpleVar(fnsym, n)) 347 selected.Add(n) 348 } 349 return decls, vars, selected 350 } 351 352 func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { 353 var abbrev int 354 var offs int64 355 356 localAutoOffset := func() int64 { 357 offs = n.FrameOffset() 358 if base.Ctxt.Arch.FixedFrameSize == 0 { 359 offs -= int64(types.PtrSize) 360 } 361 if buildcfg.FramePointerEnabled { 362 offs -= int64(types.PtrSize) 363 } 364 return offs 365 } 366 367 switch n.Class { 368 case ir.PAUTO: 369 offs = localAutoOffset() 370 abbrev = dwarf.DW_ABRV_AUTO 371 case ir.PPARAM, ir.PPARAMOUT: 372 abbrev = dwarf.DW_ABRV_PARAM 373 if n.IsOutputParamInRegisters() { 374 offs = localAutoOffset() 375 } else { 376 offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize 377 } 378 379 default: 380 base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n) 381 } 382 383 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) 384 delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type())) 385 inlIndex := 0 386 if base.Flag.GenDwarfInl > 1 { 387 if n.InlFormal() || n.InlLocal() { 388 inlIndex = posInlIndex(n.Pos()) + 1 389 if n.InlFormal() { 390 abbrev = dwarf.DW_ABRV_PARAM 391 } 392 } 393 } 394 declpos := base.Ctxt.InnermostPos(declPos(n)) 395 return &dwarf.Var{ 396 Name: n.Sym().Name, 397 IsReturnValue: n.Class == ir.PPARAMOUT, 398 IsInlFormal: n.InlFormal(), 399 Abbrev: abbrev, 400 StackOffset: int32(offs), 401 Type: base.Ctxt.Lookup(typename), 402 DeclFile: declpos.RelFilename(), 403 DeclLine: declpos.RelLine(), 404 DeclCol: declpos.RelCol(), 405 InlIndex: int32(inlIndex), 406 ChildIndex: -1, 407 DictIndex: n.DictIndex, 408 } 409 } 410 411 // createABIVars creates DWARF variables for functions in which the 412 // register ABI is enabled but optimization is turned off. It uses a 413 // hybrid approach in which register-resident input params are 414 // captured with location lists, and all other vars use the "simple" 415 // strategy. 416 func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 417 418 // Invoke createComplexVars to generate dwarf vars for input parameters 419 // that are register-allocated according to the ABI rules. 420 decls, vars, selected := createComplexVars(fnsym, fn) 421 422 // Now fill in the remainder of the variables: input parameters 423 // that are not register-resident, output parameters, and local 424 // variables. 425 for _, n := range apDecls { 426 if ir.IsAutoTmp(n) { 427 continue 428 } 429 if _, ok := selected[n]; ok { 430 // already handled 431 continue 432 } 433 434 decls = append(decls, n) 435 vars = append(vars, createSimpleVar(fnsym, n)) 436 selected.Add(n) 437 } 438 439 return decls, vars, selected 440 } 441 442 // createComplexVars creates recomposed DWARF vars with location lists, 443 // suitable for describing optimized code. 444 func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { 445 debugInfo := fn.DebugInfo.(*ssa.FuncDebug) 446 447 // Produce a DWARF variable entry for each user variable. 448 var decls []*ir.Name 449 var vars []*dwarf.Var 450 var ssaVars ir.NameSet 451 452 for varID, dvar := range debugInfo.Vars { 453 n := dvar 454 ssaVars.Add(n) 455 for _, slot := range debugInfo.VarSlots[varID] { 456 ssaVars.Add(debugInfo.Slots[slot].N) 457 } 458 459 if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil { 460 decls = append(decls, n) 461 vars = append(vars, dvar) 462 } 463 } 464 465 return decls, vars, ssaVars 466 } 467 468 // createComplexVar builds a single DWARF variable entry and location list. 469 func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var { 470 debug := fn.DebugInfo.(*ssa.FuncDebug) 471 n := debug.Vars[varID] 472 473 var abbrev int 474 switch n.Class { 475 case ir.PAUTO: 476 abbrev = dwarf.DW_ABRV_AUTO_LOCLIST 477 case ir.PPARAM, ir.PPARAMOUT: 478 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 479 default: 480 return nil 481 } 482 483 gotype := reflectdata.TypeLinksym(n.Type()) 484 delete(fnsym.Func().Autot, gotype) 485 typename := dwarf.InfoPrefix + gotype.Name[len("type:"):] 486 inlIndex := 0 487 if base.Flag.GenDwarfInl > 1 { 488 if n.InlFormal() || n.InlLocal() { 489 inlIndex = posInlIndex(n.Pos()) + 1 490 if n.InlFormal() { 491 abbrev = dwarf.DW_ABRV_PARAM_LOCLIST 492 } 493 } 494 } 495 declpos := base.Ctxt.InnermostPos(n.Pos()) 496 dvar := &dwarf.Var{ 497 Name: n.Sym().Name, 498 IsReturnValue: n.Class == ir.PPARAMOUT, 499 IsInlFormal: n.InlFormal(), 500 Abbrev: abbrev, 501 Type: base.Ctxt.Lookup(typename), 502 // The stack offset is used as a sorting key, so for decomposed 503 // variables just give it the first one. It's not used otherwise. 504 // This won't work well if the first slot hasn't been assigned a stack 505 // location, but it's not obvious how to do better. 506 StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), 507 DeclFile: declpos.RelFilename(), 508 DeclLine: declpos.RelLine(), 509 DeclCol: declpos.RelCol(), 510 InlIndex: int32(inlIndex), 511 ChildIndex: -1, 512 DictIndex: n.DictIndex, 513 } 514 list := debug.LocationLists[varID] 515 if len(list) != 0 { 516 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { 517 debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) 518 } 519 } 520 return dvar 521 } 522 523 // RecordFlags records the specified command-line flags to be placed 524 // in the DWARF info. 525 func RecordFlags(flags ...string) { 526 if base.Ctxt.Pkgpath == "" { 527 panic("missing pkgpath") 528 } 529 530 type BoolFlag interface { 531 IsBoolFlag() bool 532 } 533 type CountFlag interface { 534 IsCountFlag() bool 535 } 536 var cmd bytes.Buffer 537 for _, name := range flags { 538 f := flag.Lookup(name) 539 if f == nil { 540 continue 541 } 542 getter := f.Value.(flag.Getter) 543 if getter.String() == f.DefValue { 544 // Flag has default value, so omit it. 545 continue 546 } 547 if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() { 548 val, ok := getter.Get().(bool) 549 if ok && val { 550 fmt.Fprintf(&cmd, " -%s", f.Name) 551 continue 552 } 553 } 554 if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() { 555 val, ok := getter.Get().(int) 556 if ok && val == 1 { 557 fmt.Fprintf(&cmd, " -%s", f.Name) 558 continue 559 } 560 } 561 fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get()) 562 } 563 564 // Adds flag to producer string signaling whether regabi is turned on or 565 // off. 566 // Once regabi is turned on across the board and the relative GOEXPERIMENT 567 // knobs no longer exist this code should be removed. 568 if buildcfg.Experiment.RegabiArgs { 569 cmd.Write([]byte(" regabi")) 570 } 571 572 if cmd.Len() == 0 { 573 return 574 } 575 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath) 576 s.Type = objabi.SDWARFCUINFO 577 // Sometimes (for example when building tests) we can link 578 // together two package main archives. So allow dups. 579 s.Set(obj.AttrDuplicateOK, true) 580 base.Ctxt.Data = append(base.Ctxt.Data, s) 581 s.P = cmd.Bytes()[1:] 582 } 583 584 // RecordPackageName records the name of the package being 585 // compiled, so that the linker can save it in the compile unit's DIE. 586 func RecordPackageName() { 587 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath) 588 s.Type = objabi.SDWARFCUINFO 589 // Sometimes (for example when building tests) we can link 590 // together two package main archives. So allow dups. 591 s.Set(obj.AttrDuplicateOK, true) 592 base.Ctxt.Data = append(base.Ctxt.Data, s) 593 s.P = []byte(types.LocalPkg.Name) 594 }