github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/ssa/debug.go (about) 1 // Copyright 2017 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 ssa 6 7 import ( 8 "github.com/bir3/gocompiler/src/cmd/compile/internal/abi" 9 "github.com/bir3/gocompiler/src/cmd/compile/internal/abt" 10 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 12 "github.com/bir3/gocompiler/src/cmd/internal/dwarf" 13 "github.com/bir3/gocompiler/src/cmd/internal/obj" 14 "github.com/bir3/gocompiler/src/cmd/internal/src" 15 "encoding/hex" 16 "fmt" 17 "github.com/bir3/gocompiler/src/internal/buildcfg" 18 "math/bits" 19 "sort" 20 "strings" 21 ) 22 23 type SlotID int32 24 type VarID int32 25 26 // A FuncDebug contains all the debug information for the variables in a 27 // function. Variables are identified by their LocalSlot, which may be 28 // the result of decomposing a larger variable. 29 type FuncDebug struct { 30 // Slots is all the slots used in the debug info, indexed by their SlotID. 31 Slots []LocalSlot 32 // The user variables, indexed by VarID. 33 Vars []*ir.Name 34 // The slots that make up each variable, indexed by VarID. 35 VarSlots [][]SlotID 36 // The location list data, indexed by VarID. Must be processed by PutLocationList. 37 LocationLists [][]byte 38 // Register-resident output parameters for the function. This is filled in at 39 // SSA generation time. 40 RegOutputParams []*ir.Name 41 // Variable declarations that were removed during optimization 42 OptDcl []*ir.Name 43 44 // Filled in by the user. Translates Block and Value ID to PC. 45 GetPC func(ID, ID) int64 46 } 47 48 type BlockDebug struct { 49 // State at the start and end of the block. These are initialized, 50 // and updated from new information that flows on back edges. 51 startState, endState abt.T 52 // Use these to avoid excess work in the merge. If none of the 53 // predecessors has changed since the last check, the old answer is 54 // still good. 55 lastCheckedTime, lastChangedTime int32 56 // Whether the block had any changes to user variables at all. 57 relevant bool 58 // false until the block has been processed at least once. This 59 // affects how the merge is done; the goal is to maximize sharing 60 // and avoid allocation. 61 everProcessed bool 62 } 63 64 // A liveSlot is a slot that's live in loc at entry/exit of a block. 65 type liveSlot struct { 66 VarLoc 67 } 68 69 func (ls *liveSlot) String() string { 70 return fmt.Sprintf("0x%x.%d.%d", ls.Registers, ls.stackOffsetValue(), int32(ls.StackOffset)&1) 71 } 72 73 func (loc liveSlot) absent() bool { 74 return loc.Registers == 0 && !loc.onStack() 75 } 76 77 // StackOffset encodes whether a value is on the stack and if so, where. 78 // It is a 31-bit integer followed by a presence flag at the low-order 79 // bit. 80 type StackOffset int32 81 82 func (s StackOffset) onStack() bool { 83 return s != 0 84 } 85 86 func (s StackOffset) stackOffsetValue() int32 { 87 return int32(s) >> 1 88 } 89 90 // stateAtPC is the current state of all variables at some point. 91 type stateAtPC struct { 92 // The location of each known slot, indexed by SlotID. 93 slots []VarLoc 94 // The slots present in each register, indexed by register number. 95 registers [][]SlotID 96 } 97 98 // reset fills state with the live variables from live. 99 func (state *stateAtPC) reset(live abt.T) { 100 slots, registers := state.slots, state.registers 101 for i := range slots { 102 slots[i] = VarLoc{} 103 } 104 for i := range registers { 105 registers[i] = registers[i][:0] 106 } 107 for it := live.Iterator(); !it.Done(); { 108 k, d := it.Next() 109 live := d.(*liveSlot) 110 slots[k] = live.VarLoc 111 if live.VarLoc.Registers == 0 { 112 continue 113 } 114 115 mask := uint64(live.VarLoc.Registers) 116 for { 117 if mask == 0 { 118 break 119 } 120 reg := uint8(bits.TrailingZeros64(mask)) 121 mask &^= 1 << reg 122 123 registers[reg] = append(registers[reg], SlotID(k)) 124 } 125 } 126 state.slots, state.registers = slots, registers 127 } 128 129 func (s *debugState) LocString(loc VarLoc) string { 130 if loc.absent() { 131 return "<nil>" 132 } 133 134 var storage []string 135 if loc.onStack() { 136 storage = append(storage, fmt.Sprintf("@%+d", loc.stackOffsetValue())) 137 } 138 139 mask := uint64(loc.Registers) 140 for { 141 if mask == 0 { 142 break 143 } 144 reg := uint8(bits.TrailingZeros64(mask)) 145 mask &^= 1 << reg 146 147 storage = append(storage, s.registers[reg].String()) 148 } 149 return strings.Join(storage, ",") 150 } 151 152 // A VarLoc describes the storage for part of a user variable. 153 type VarLoc struct { 154 // The registers this variable is available in. There can be more than 155 // one in various situations, e.g. it's being moved between registers. 156 Registers RegisterSet 157 158 StackOffset 159 } 160 161 func (loc VarLoc) absent() bool { 162 return loc.Registers == 0 && !loc.onStack() 163 } 164 165 func (loc VarLoc) intersect(other VarLoc) VarLoc { 166 if !loc.onStack() || !other.onStack() || loc.StackOffset != other.StackOffset { 167 loc.StackOffset = 0 168 } 169 loc.Registers &= other.Registers 170 return loc 171 } 172 173 var BlockStart = &Value{ 174 ID: -10000, 175 Op: OpInvalid, 176 Aux: StringToAux("BlockStart"), 177 } 178 179 var BlockEnd = &Value{ 180 ID: -20000, 181 Op: OpInvalid, 182 Aux: StringToAux("BlockEnd"), 183 } 184 185 var FuncEnd = &Value{ 186 ID: -30000, 187 Op: OpInvalid, 188 Aux: StringToAux("FuncEnd"), 189 } 190 191 // RegisterSet is a bitmap of registers, indexed by Register.num. 192 type RegisterSet uint64 193 194 // logf prints debug-specific logging to stdout (always stdout) if the 195 // current function is tagged by GOSSAFUNC (for ssa output directed 196 // either to stdout or html). 197 func (s *debugState) logf(msg string, args ...interface{}) { 198 if s.f.PrintOrHtmlSSA { 199 fmt.Printf(msg, args...) 200 } 201 } 202 203 type debugState struct { 204 // See FuncDebug. 205 slots []LocalSlot 206 vars []*ir.Name 207 varSlots [][]SlotID 208 lists [][]byte 209 210 // The user variable that each slot rolls up to, indexed by SlotID. 211 slotVars []VarID 212 213 f *Func 214 loggingLevel int 215 convergeCount int // testing; iterate over block debug state this many times 216 registers []Register 217 stackOffset func(LocalSlot) int32 218 ctxt *obj.Link 219 220 // The names (slots) associated with each value, indexed by Value ID. 221 valueNames [][]SlotID 222 223 // The current state of whatever analysis is running. 224 currentState stateAtPC 225 changedVars *sparseSet 226 changedSlots *sparseSet 227 228 // The pending location list entry for each user variable, indexed by VarID. 229 pendingEntries []pendingEntry 230 231 varParts map[*ir.Name][]SlotID 232 blockDebug []BlockDebug 233 pendingSlotLocs []VarLoc 234 partsByVarOffset sort.Interface 235 } 236 237 func (state *debugState) initializeCache(f *Func, numVars, numSlots int) { 238 // One blockDebug per block. Initialized in allocBlock. 239 if cap(state.blockDebug) < f.NumBlocks() { 240 state.blockDebug = make([]BlockDebug, f.NumBlocks()) 241 } else { 242 // This local variable, and the ones like it below, enable compiler 243 // optimizations. Don't inline them. 244 b := state.blockDebug[:f.NumBlocks()] 245 for i := range b { 246 b[i] = BlockDebug{} 247 } 248 } 249 250 // A list of slots per Value. Reuse the previous child slices. 251 if cap(state.valueNames) < f.NumValues() { 252 old := state.valueNames 253 state.valueNames = make([][]SlotID, f.NumValues()) 254 copy(state.valueNames, old) 255 } 256 vn := state.valueNames[:f.NumValues()] 257 for i := range vn { 258 vn[i] = vn[i][:0] 259 } 260 261 // Slot and register contents for currentState. Cleared by reset(). 262 if cap(state.currentState.slots) < numSlots { 263 state.currentState.slots = make([]VarLoc, numSlots) 264 } else { 265 state.currentState.slots = state.currentState.slots[:numSlots] 266 } 267 if cap(state.currentState.registers) < len(state.registers) { 268 state.currentState.registers = make([][]SlotID, len(state.registers)) 269 } else { 270 state.currentState.registers = state.currentState.registers[:len(state.registers)] 271 } 272 273 // A relatively small slice, but used many times as the return from processValue. 274 state.changedVars = newSparseSet(numVars) 275 state.changedSlots = newSparseSet(numSlots) 276 277 // A pending entry per user variable, with space to track each of its pieces. 278 numPieces := 0 279 for i := range state.varSlots { 280 numPieces += len(state.varSlots[i]) 281 } 282 if cap(state.pendingSlotLocs) < numPieces { 283 state.pendingSlotLocs = make([]VarLoc, numPieces) 284 } else { 285 psl := state.pendingSlotLocs[:numPieces] 286 for i := range psl { 287 psl[i] = VarLoc{} 288 } 289 } 290 if cap(state.pendingEntries) < numVars { 291 state.pendingEntries = make([]pendingEntry, numVars) 292 } 293 pe := state.pendingEntries[:numVars] 294 freePieceIdx := 0 295 for varID, slots := range state.varSlots { 296 pe[varID] = pendingEntry{ 297 pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)], 298 } 299 freePieceIdx += len(slots) 300 } 301 state.pendingEntries = pe 302 303 if cap(state.lists) < numVars { 304 state.lists = make([][]byte, numVars) 305 } else { 306 state.lists = state.lists[:numVars] 307 for i := range state.lists { 308 state.lists[i] = nil 309 } 310 } 311 } 312 313 func (state *debugState) allocBlock(b *Block) *BlockDebug { 314 return &state.blockDebug[b.ID] 315 } 316 317 func (s *debugState) blockEndStateString(b *BlockDebug) string { 318 endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.registers))} 319 endState.reset(b.endState) 320 return s.stateString(endState) 321 } 322 323 func (s *debugState) stateString(state stateAtPC) string { 324 var strs []string 325 for slotID, loc := range state.slots { 326 if !loc.absent() { 327 strs = append(strs, fmt.Sprintf("\t%v = %v\n", s.slots[slotID], s.LocString(loc))) 328 } 329 } 330 331 strs = append(strs, "\n") 332 for reg, slots := range state.registers { 333 if len(slots) != 0 { 334 var slotStrs []string 335 for _, slot := range slots { 336 slotStrs = append(slotStrs, s.slots[slot].String()) 337 } 338 strs = append(strs, fmt.Sprintf("\t%v = %v\n", &s.registers[reg], slotStrs)) 339 } 340 } 341 342 if len(strs) == 1 { 343 return "(no vars)\n" 344 } 345 return strings.Join(strs, "") 346 } 347 348 // slotCanonicalizer is a table used to lookup and canonicalize 349 // LocalSlot's in a type insensitive way (e.g. taking into account the 350 // base name, offset, and width of the slot, but ignoring the slot 351 // type). 352 type slotCanonicalizer struct { 353 slmap map[slotKey]SlKeyIdx 354 slkeys []LocalSlot 355 } 356 357 func newSlotCanonicalizer() *slotCanonicalizer { 358 return &slotCanonicalizer{ 359 slmap: make(map[slotKey]SlKeyIdx), 360 slkeys: []LocalSlot{LocalSlot{N: nil}}, 361 } 362 } 363 364 type SlKeyIdx uint32 365 366 const noSlot = SlKeyIdx(0) 367 368 // slotKey is a type-insensitive encapsulation of a LocalSlot; it 369 // is used to key a map within slotCanonicalizer. 370 type slotKey struct { 371 name *ir.Name 372 offset int64 373 width int64 374 splitOf SlKeyIdx // idx in slkeys slice in slotCanonicalizer 375 splitOffset int64 376 } 377 378 // lookup looks up a LocalSlot in the slot canonicalizer "sc", returning 379 // a canonical index for the slot, and adding it to the table if need 380 // be. Return value is the canonical slot index, and a boolean indicating 381 // whether the slot was found in the table already (TRUE => found). 382 func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) { 383 split := noSlot 384 if ls.SplitOf != nil { 385 split, _ = sc.lookup(*ls.SplitOf) 386 } 387 k := slotKey{ 388 name: ls.N, offset: ls.Off, width: ls.Type.Size(), 389 splitOf: split, splitOffset: ls.SplitOffset, 390 } 391 if idx, ok := sc.slmap[k]; ok { 392 return idx, true 393 } 394 rv := SlKeyIdx(len(sc.slkeys)) 395 sc.slkeys = append(sc.slkeys, ls) 396 sc.slmap[k] = rv 397 return rv, false 398 } 399 400 func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot { 401 return sc.slkeys[idx] 402 } 403 404 // PopulateABIInRegArgOps examines the entry block of the function 405 // and looks for incoming parameters that have missing or partial 406 // OpArg{Int,Float}Reg values, inserting additional values in 407 // cases where they are missing. Example: 408 // 409 // func foo(s string, used int, notused int) int { 410 // return len(s) + used 411 // } 412 // 413 // In the function above, the incoming parameter "used" is fully live, 414 // "notused" is not live, and "s" is partially live (only the length 415 // field of the string is used). At the point where debug value 416 // analysis runs, we might expect to see an entry block with: 417 // 418 // b1: 419 // v4 = ArgIntReg <uintptr> {s+8} [0] : BX 420 // v5 = ArgIntReg <int> {used} [0] : CX 421 // 422 // While this is an accurate picture of the live incoming params, 423 // we also want to have debug locations for non-live params (or 424 // their non-live pieces), e.g. something like 425 // 426 // b1: 427 // v9 = ArgIntReg <*uint8> {s+0} [0] : AX 428 // v4 = ArgIntReg <uintptr> {s+8} [0] : BX 429 // v5 = ArgIntReg <int> {used} [0] : CX 430 // v10 = ArgIntReg <int> {unused} [0] : DI 431 // 432 // This function examines the live OpArg{Int,Float}Reg values and 433 // synthesizes new (dead) values for the non-live params or the 434 // non-live pieces of partially live params. 435 func PopulateABIInRegArgOps(f *Func) { 436 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType()) 437 438 // When manufacturing new slots that correspond to splits of 439 // composite parameters, we want to avoid creating a new sub-slot 440 // that differs from some existing sub-slot only by type, since 441 // the debug location analysis will treat that slot as a separate 442 // entity. To achieve this, create a lookup table of existing 443 // slots that is type-insenstitive. 444 sc := newSlotCanonicalizer() 445 for _, sl := range f.Names { 446 sc.lookup(*sl) 447 } 448 449 // Add slot -> value entry to f.NamedValues if not already present. 450 addToNV := func(v *Value, sl LocalSlot) { 451 values, ok := f.NamedValues[sl] 452 if !ok { 453 // Haven't seen this slot yet. 454 sla := f.localSlotAddr(sl) 455 f.Names = append(f.Names, sla) 456 } else { 457 for _, ev := range values { 458 if v == ev { 459 return 460 } 461 } 462 } 463 values = append(values, v) 464 f.NamedValues[sl] = values 465 } 466 467 newValues := []*Value{} 468 469 abiRegIndexToRegister := func(reg abi.RegIndex) int8 { 470 i := f.ABISelf.FloatIndexFor(reg) 471 if i >= 0 { // float PR 472 return f.Config.floatParamRegs[i] 473 } else { 474 return f.Config.intParamRegs[reg] 475 } 476 } 477 478 // Helper to construct a new OpArg{Float,Int}Reg op value. 479 var pos src.XPos 480 if len(f.Entry.Values) != 0 { 481 pos = f.Entry.Values[0].Pos 482 } 483 synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl LocalSlot) *Value { 484 aux := &AuxNameOffset{n, sl.Off} 485 op, auxInt := ArgOpAndRegisterFor(reg, f.ABISelf) 486 v := f.newValueNoBlock(op, t, pos) 487 v.AuxInt = auxInt 488 v.Aux = aux 489 v.Args = nil 490 v.Block = f.Entry 491 newValues = append(newValues, v) 492 addToNV(v, sl) 493 f.setHome(v, &f.Config.registers[abiRegIndexToRegister(reg)]) 494 return v 495 } 496 497 // Make a pass through the entry block looking for 498 // OpArg{Int,Float}Reg ops. Record the slots they use in a table 499 // ("sc"). We use a type-insensitive lookup for the slot table, 500 // since the type we get from the ABI analyzer won't always match 501 // what the compiler uses when creating OpArg{Int,Float}Reg ops. 502 for _, v := range f.Entry.Values { 503 if v.Op == OpArgIntReg || v.Op == OpArgFloatReg { 504 aux := v.Aux.(*AuxNameOffset) 505 sl := LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset} 506 // install slot in lookup table 507 idx, _ := sc.lookup(sl) 508 // add to f.NamedValues if not already present 509 addToNV(v, sc.canonSlot(idx)) 510 } else if v.Op.IsCall() { 511 // if we hit a call, we've gone too far. 512 break 513 } 514 } 515 516 // Now make a pass through the ABI in-params, looking for params 517 // or pieces of params that we didn't encounter in the loop above. 518 for _, inp := range pri.InParams() { 519 if !isNamedRegParam(inp) { 520 continue 521 } 522 n := inp.Name.(*ir.Name) 523 524 // Param is spread across one or more registers. Walk through 525 // each piece to see whether we've seen an arg reg op for it. 526 types, offsets := inp.RegisterTypesAndOffsets() 527 for k, t := range types { 528 // Note: this recipe for creating a LocalSlot is designed 529 // to be compatible with the one used in expand_calls.go 530 // as opposed to decompose.go. The expand calls code just 531 // takes the base name and creates an offset into it, 532 // without using the SplitOf/SplitOffset fields. The code 533 // in decompose.go does the opposite -- it creates a 534 // LocalSlot object with "Off" set to zero, but with 535 // SplitOf pointing to a parent slot, and SplitOffset 536 // holding the offset into the parent object. 537 pieceSlot := LocalSlot{N: n, Type: t, Off: offsets[k]} 538 539 // Look up this piece to see if we've seen a reg op 540 // for it. If not, create one. 541 _, found := sc.lookup(pieceSlot) 542 if !found { 543 // This slot doesn't appear in the map, meaning it 544 // corresponds to an in-param that is not live, or 545 // a portion of an in-param that is not live/used. 546 // Add a new dummy OpArg{Int,Float}Reg for it. 547 synthesizeOpIntFloatArg(n, t, inp.Registers[k], 548 pieceSlot) 549 } 550 } 551 } 552 553 // Insert the new values into the head of the block. 554 f.Entry.Values = append(newValues, f.Entry.Values...) 555 } 556 557 // BuildFuncDebug debug information for f, placing the results 558 // in "rval". f must be fully processed, so that each Value is where it 559 // will be when machine code is emitted. 560 func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func(LocalSlot) int32, rval *FuncDebug) { 561 if f.RegAlloc == nil { 562 f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f) 563 } 564 state := &f.Cache.debugState 565 state.loggingLevel = loggingLevel % 1000 566 567 // A specific number demands exactly that many iterations. Under 568 // particular circumstances it make require more than the total of 569 // 2 passes implied by a single run through liveness and a single 570 // run through location list generation. 571 state.convergeCount = loggingLevel / 1000 572 state.f = f 573 state.registers = f.Config.registers 574 state.stackOffset = stackOffset 575 state.ctxt = ctxt 576 577 if buildcfg.Experiment.RegabiArgs { 578 PopulateABIInRegArgOps(f) 579 } 580 581 if state.loggingLevel > 0 { 582 state.logf("Generating location lists for function %q\n", f.Name) 583 } 584 585 if state.varParts == nil { 586 state.varParts = make(map[*ir.Name][]SlotID) 587 } else { 588 for n := range state.varParts { 589 delete(state.varParts, n) 590 } 591 } 592 593 // Recompose any decomposed variables, and establish the canonical 594 // IDs for each var and slot by filling out state.vars and state.slots. 595 596 state.slots = state.slots[:0] 597 state.vars = state.vars[:0] 598 for i, slot := range f.Names { 599 state.slots = append(state.slots, *slot) 600 if ir.IsSynthetic(slot.N) { 601 continue 602 } 603 604 topSlot := slot 605 for topSlot.SplitOf != nil { 606 topSlot = topSlot.SplitOf 607 } 608 if _, ok := state.varParts[topSlot.N]; !ok { 609 state.vars = append(state.vars, topSlot.N) 610 } 611 state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i)) 612 } 613 614 // Recreate the LocalSlot for each stack-only variable. 615 // This would probably be better as an output from stackframe. 616 for _, b := range f.Blocks { 617 for _, v := range b.Values { 618 if v.Op == OpVarDef { 619 n := v.Aux.(*ir.Name) 620 if ir.IsSynthetic(n) { 621 continue 622 } 623 624 if _, ok := state.varParts[n]; !ok { 625 slot := LocalSlot{N: n, Type: v.Type, Off: 0} 626 state.slots = append(state.slots, slot) 627 state.varParts[n] = []SlotID{SlotID(len(state.slots) - 1)} 628 state.vars = append(state.vars, n) 629 } 630 } 631 } 632 } 633 634 // Fill in the var<->slot mappings. 635 if cap(state.varSlots) < len(state.vars) { 636 state.varSlots = make([][]SlotID, len(state.vars)) 637 } else { 638 state.varSlots = state.varSlots[:len(state.vars)] 639 for i := range state.varSlots { 640 state.varSlots[i] = state.varSlots[i][:0] 641 } 642 } 643 if cap(state.slotVars) < len(state.slots) { 644 state.slotVars = make([]VarID, len(state.slots)) 645 } else { 646 state.slotVars = state.slotVars[:len(state.slots)] 647 } 648 649 if state.partsByVarOffset == nil { 650 state.partsByVarOffset = &partsByVarOffset{} 651 } 652 for varID, n := range state.vars { 653 parts := state.varParts[n] 654 state.varSlots[varID] = parts 655 for _, slotID := range parts { 656 state.slotVars[slotID] = VarID(varID) 657 } 658 *state.partsByVarOffset.(*partsByVarOffset) = partsByVarOffset{parts, state.slots} 659 sort.Sort(state.partsByVarOffset) 660 } 661 662 state.initializeCache(f, len(state.varParts), len(state.slots)) 663 664 for i, slot := range f.Names { 665 if ir.IsSynthetic(slot.N) { 666 continue 667 } 668 for _, value := range f.NamedValues[*slot] { 669 state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i)) 670 } 671 } 672 673 blockLocs := state.liveness() 674 state.buildLocationLists(blockLocs) 675 676 // Populate "rval" with what we've computed. 677 rval.Slots = state.slots 678 rval.VarSlots = state.varSlots 679 rval.Vars = state.vars 680 rval.LocationLists = state.lists 681 } 682 683 // liveness walks the function in control flow order, calculating the start 684 // and end state of each block. 685 func (state *debugState) liveness() []*BlockDebug { 686 blockLocs := make([]*BlockDebug, state.f.NumBlocks()) 687 counterTime := int32(1) 688 689 // Reverse postorder: visit a block after as many as possible of its 690 // predecessors have been visited. 691 po := state.f.Postorder() 692 converged := false 693 694 // The iteration rule is that by default, run until converged, but 695 // if a particular iteration count is specified, run that many 696 // iterations, no more, no less. A count is specified as the 697 // thousands digit of the location lists debug flag, 698 // e.g. -d=locationlists=4000 699 keepGoing := func(k int) bool { 700 if state.convergeCount == 0 { 701 return !converged 702 } 703 return k < state.convergeCount 704 } 705 for k := 0; keepGoing(k); k++ { 706 if state.loggingLevel > 0 { 707 state.logf("Liveness pass %d\n", k) 708 } 709 converged = true 710 for i := len(po) - 1; i >= 0; i-- { 711 b := po[i] 712 locs := blockLocs[b.ID] 713 if locs == nil { 714 locs = state.allocBlock(b) 715 blockLocs[b.ID] = locs 716 } 717 718 // Build the starting state for the block from the final 719 // state of its predecessors. 720 startState, blockChanged := state.mergePredecessors(b, blockLocs, nil, false) 721 locs.lastCheckedTime = counterTime 722 counterTime++ 723 if state.loggingLevel > 1 { 724 state.logf("Processing %v, block changed %v, initial state:\n%v", b, blockChanged, state.stateString(state.currentState)) 725 } 726 727 if blockChanged { 728 // If the start did not change, then the old endState is good 729 converged = false 730 changed := false 731 state.changedSlots.clear() 732 733 // Update locs/registers with the effects of each Value. 734 for _, v := range b.Values { 735 slots := state.valueNames[v.ID] 736 737 // Loads and stores inherit the names of their sources. 738 var source *Value 739 switch v.Op { 740 case OpStoreReg: 741 source = v.Args[0] 742 case OpLoadReg: 743 switch a := v.Args[0]; a.Op { 744 case OpArg, OpPhi: 745 source = a 746 case OpStoreReg: 747 source = a.Args[0] 748 default: 749 if state.loggingLevel > 1 { 750 state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a) 751 } 752 } 753 } 754 // Update valueNames with the source so that later steps 755 // don't need special handling. 756 if source != nil && k == 0 { 757 // limit to k == 0 otherwise there are duplicates. 758 slots = append(slots, state.valueNames[source.ID]...) 759 state.valueNames[v.ID] = slots 760 } 761 762 reg, _ := state.f.getHome(v.ID).(*Register) 763 c := state.processValue(v, slots, reg) 764 changed = changed || c 765 } 766 767 if state.loggingLevel > 1 { 768 state.logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState)) 769 } 770 771 locs.relevant = locs.relevant || changed 772 if !changed { 773 locs.endState = startState 774 } else { 775 for _, id := range state.changedSlots.contents() { 776 slotID := SlotID(id) 777 slotLoc := state.currentState.slots[slotID] 778 if slotLoc.absent() { 779 startState.Delete(int32(slotID)) 780 continue 781 } 782 old := startState.Find(int32(slotID)) // do NOT replace existing values 783 if oldLS, ok := old.(*liveSlot); !ok || oldLS.VarLoc != slotLoc { 784 startState.Insert(int32(slotID), 785 &liveSlot{VarLoc: slotLoc}) 786 } 787 } 788 locs.endState = startState 789 } 790 locs.lastChangedTime = counterTime 791 } 792 counterTime++ 793 } 794 } 795 return blockLocs 796 } 797 798 // mergePredecessors takes the end state of each of b's predecessors and 799 // intersects them to form the starting state for b. It puts that state 800 // in blockLocs[b.ID].startState, and fills state.currentState with it. 801 // It returns the start state and whether this is changed from the 802 // previously approximated value of startState for this block. After 803 // the first call, subsequent calls can only shrink startState. 804 // 805 // Passing forLocationLists=true enables additional side-effects that 806 // are necessary for building location lists but superflous while still 807 // iterating to an answer. 808 // 809 // If previousBlock is non-nil, it registers changes vs. that block's 810 // end state in state.changedVars. Note that previousBlock will often 811 // not be a predecessor. 812 // 813 // Note that mergePredecessors behaves slightly differently between 814 // first and subsequent calls for a block. For the first call, the 815 // starting state is approximated by taking the state from the 816 // predecessor whose state is smallest, and removing any elements not 817 // in all the other predecessors; this makes the smallest number of 818 // changes and shares the most state. On subsequent calls the old 819 // value of startState is adjusted with new information; this is judged 820 // to do the least amount of extra work. 821 // 822 // To improve performance, each block's state information is marked with 823 // lastChanged and lastChecked "times" so unchanged predecessors can be 824 // skipped on after-the-first iterations. Doing this allows extra 825 // iterations by the caller to be almost free. 826 // 827 // It is important to know that the set representation used for 828 // startState, endState, and merges can share data for two sets where 829 // one is a small delta from the other. Doing this does require a 830 // little care in how sets are updated, both in mergePredecessors, and 831 // using its result. 832 func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block, forLocationLists bool) (abt.T, bool) { 833 // Filter out back branches. 834 var predsBuf [10]*Block 835 836 preds := predsBuf[:0] 837 locs := blockLocs[b.ID] 838 839 blockChanged := !locs.everProcessed // the first time it always changes. 840 updating := locs.everProcessed 841 842 // For the first merge, exclude predecessors that have not been seen yet. 843 // I.e., backedges. 844 for _, pred := range b.Preds { 845 if bl := blockLocs[pred.b.ID]; bl != nil && bl.everProcessed { 846 // crucially, a self-edge has bl != nil, but bl.everProcessed is false the first time. 847 preds = append(preds, pred.b) 848 } 849 } 850 851 locs.everProcessed = true 852 853 if state.loggingLevel > 1 { 854 // The logf below would cause preds to be heap-allocated if 855 // it were passed directly. 856 preds2 := make([]*Block, len(preds)) 857 copy(preds2, preds) 858 state.logf("Merging %v into %v (changed=%d, checked=%d)\n", preds2, b, locs.lastChangedTime, locs.lastCheckedTime) 859 } 860 861 state.changedVars.clear() 862 863 markChangedVars := func(slots, merged abt.T) { 864 if !forLocationLists { 865 return 866 } 867 // Fill changedVars with those that differ between the previous 868 // block (in the emit order, not necessarily a flow predecessor) 869 // and the start state for this block. 870 for it := slots.Iterator(); !it.Done(); { 871 k, v := it.Next() 872 m := merged.Find(k) 873 if m == nil || v.(*liveSlot).VarLoc != m.(*liveSlot).VarLoc { 874 state.changedVars.add(ID(state.slotVars[k])) 875 } 876 } 877 } 878 879 reset := func(ourStartState abt.T) { 880 if !(forLocationLists || blockChanged) { 881 // there is no change and this is not for location lists, do 882 // not bother to reset currentState because it will not be 883 // examined. 884 return 885 } 886 state.currentState.reset(ourStartState) 887 } 888 889 // Zero predecessors 890 if len(preds) == 0 { 891 if previousBlock != nil { 892 state.f.Fatalf("Function %v, block %s with no predecessors is not first block, has previous %s", state.f, b.String(), previousBlock.String()) 893 } 894 // startState is empty 895 reset(abt.T{}) 896 return abt.T{}, blockChanged 897 } 898 899 // One predecessor 900 l0 := blockLocs[preds[0].ID] 901 p0 := l0.endState 902 if len(preds) == 1 { 903 if previousBlock != nil && preds[0].ID != previousBlock.ID { 904 // Change from previous block is its endState minus the predecessor's endState 905 markChangedVars(blockLocs[previousBlock.ID].endState, p0) 906 } 907 locs.startState = p0 908 blockChanged = blockChanged || l0.lastChangedTime > locs.lastCheckedTime 909 reset(p0) 910 return p0, blockChanged 911 } 912 913 // More than one predecessor 914 915 if updating { 916 // After the first approximation, i.e., when updating, results 917 // can only get smaller, because initially backedge 918 // predecessors do not participate in the intersection. This 919 // means that for the update, given the prior approximation of 920 // startState, there is no need to re-intersect with unchanged 921 // blocks. Therefore remove unchanged blocks from the 922 // predecessor list. 923 for i := len(preds) - 1; i >= 0; i-- { 924 pred := preds[i] 925 if blockLocs[pred.ID].lastChangedTime > locs.lastCheckedTime { 926 continue // keep this predecessor 927 } 928 preds[i] = preds[len(preds)-1] 929 preds = preds[:len(preds)-1] 930 if state.loggingLevel > 2 { 931 state.logf("Pruned b%d, lastChanged was %d but b%d lastChecked is %d\n", pred.ID, blockLocs[pred.ID].lastChangedTime, b.ID, locs.lastCheckedTime) 932 } 933 } 934 // Check for an early out; this should always hit for the update 935 // if there are no cycles. 936 if len(preds) == 0 { 937 blockChanged = false 938 939 reset(locs.startState) 940 if state.loggingLevel > 2 { 941 state.logf("Early out, no predecessors changed since last check\n") 942 } 943 if previousBlock != nil { 944 markChangedVars(blockLocs[previousBlock.ID].endState, locs.startState) 945 } 946 return locs.startState, blockChanged 947 } 948 } 949 950 baseID := preds[0].ID 951 baseState := p0 952 953 // Choose the predecessor with the smallest endState for intersection work 954 for _, pred := range preds[1:] { 955 if blockLocs[pred.ID].endState.Size() < baseState.Size() { 956 baseState = blockLocs[pred.ID].endState 957 baseID = pred.ID 958 } 959 } 960 961 if state.loggingLevel > 2 { 962 state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID])) 963 for _, pred := range preds { 964 if pred.ID == baseID { 965 continue 966 } 967 state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID])) 968 } 969 } 970 971 state.currentState.reset(abt.T{}) 972 // The normal logic of "reset" is incuded in the intersection loop below. 973 974 slotLocs := state.currentState.slots 975 976 // If this is the first call, do updates on the "baseState"; if this 977 // is a subsequent call, tweak the startState instead. Note that 978 // these "set" values are values; there are no side effects to 979 // other values as these are modified. 980 newState := baseState 981 if updating { 982 newState = blockLocs[b.ID].startState 983 } 984 985 for it := newState.Iterator(); !it.Done(); { 986 k, d := it.Next() 987 thisSlot := d.(*liveSlot) 988 x := thisSlot.VarLoc 989 x0 := x // initial value in newState 990 991 // Intersect this slot with the slot in all the predecessors 992 for _, other := range preds { 993 if !updating && other.ID == baseID { 994 continue 995 } 996 otherSlot := blockLocs[other.ID].endState.Find(k) 997 if otherSlot == nil { 998 x = VarLoc{} 999 break 1000 } 1001 y := otherSlot.(*liveSlot).VarLoc 1002 x = x.intersect(y) 1003 if x.absent() { 1004 x = VarLoc{} 1005 break 1006 } 1007 } 1008 1009 // Delete if necessary, but not otherwise (in order to maximize sharing). 1010 if x.absent() { 1011 if !x0.absent() { 1012 blockChanged = true 1013 newState.Delete(k) 1014 } 1015 slotLocs[k] = VarLoc{} 1016 continue 1017 } 1018 if x != x0 { 1019 blockChanged = true 1020 newState.Insert(k, &liveSlot{VarLoc: x}) 1021 } 1022 1023 slotLocs[k] = x 1024 mask := uint64(x.Registers) 1025 for { 1026 if mask == 0 { 1027 break 1028 } 1029 reg := uint8(bits.TrailingZeros64(mask)) 1030 mask &^= 1 << reg 1031 state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(k)) 1032 } 1033 } 1034 1035 if previousBlock != nil { 1036 markChangedVars(blockLocs[previousBlock.ID].endState, newState) 1037 } 1038 locs.startState = newState 1039 return newState, blockChanged 1040 } 1041 1042 // processValue updates locs and state.registerContents to reflect v, a 1043 // value with the names in vSlots and homed in vReg. "v" becomes 1044 // visible after execution of the instructions evaluating it. It 1045 // returns which VarIDs were modified by the Value's execution. 1046 func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool { 1047 locs := state.currentState 1048 changed := false 1049 setSlot := func(slot SlotID, loc VarLoc) { 1050 changed = true 1051 state.changedVars.add(ID(state.slotVars[slot])) 1052 state.changedSlots.add(ID(slot)) 1053 state.currentState.slots[slot] = loc 1054 } 1055 1056 // Handle any register clobbering. Call operations, for example, 1057 // clobber all registers even though they don't explicitly write to 1058 // them. 1059 clobbers := uint64(opcodeTable[v.Op].reg.clobbers) 1060 for { 1061 if clobbers == 0 { 1062 break 1063 } 1064 reg := uint8(bits.TrailingZeros64(clobbers)) 1065 clobbers &^= 1 << reg 1066 1067 for _, slot := range locs.registers[reg] { 1068 if state.loggingLevel > 1 { 1069 state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg]) 1070 } 1071 1072 last := locs.slots[slot] 1073 if last.absent() { 1074 state.f.Fatalf("at %v: slot %v in register %v with no location entry", v, state.slots[slot], &state.registers[reg]) 1075 continue 1076 } 1077 regs := last.Registers &^ (1 << reg) 1078 setSlot(slot, VarLoc{regs, last.StackOffset}) 1079 } 1080 1081 locs.registers[reg] = locs.registers[reg][:0] 1082 } 1083 1084 switch { 1085 case v.Op == OpVarDef: 1086 n := v.Aux.(*ir.Name) 1087 if ir.IsSynthetic(n) { 1088 break 1089 } 1090 1091 slotID := state.varParts[n][0] 1092 var stackOffset StackOffset 1093 if v.Op == OpVarDef { 1094 stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1) 1095 } 1096 setSlot(slotID, VarLoc{0, stackOffset}) 1097 if state.loggingLevel > 1 { 1098 if v.Op == OpVarDef { 1099 state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID]) 1100 } else { 1101 state.logf("at %v: stack-only var %v now dead\n", v, state.slots[slotID]) 1102 } 1103 } 1104 1105 case v.Op == OpArg: 1106 home := state.f.getHome(v.ID).(LocalSlot) 1107 stackOffset := state.stackOffset(home)<<1 | 1 1108 for _, slot := range vSlots { 1109 if state.loggingLevel > 1 { 1110 state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home) 1111 if last := locs.slots[slot]; !last.absent() { 1112 state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot]) 1113 } 1114 } 1115 1116 setSlot(slot, VarLoc{0, StackOffset(stackOffset)}) 1117 } 1118 1119 case v.Op == OpStoreReg: 1120 home := state.f.getHome(v.ID).(LocalSlot) 1121 stackOffset := state.stackOffset(home)<<1 | 1 1122 for _, slot := range vSlots { 1123 last := locs.slots[slot] 1124 if last.absent() { 1125 if state.loggingLevel > 1 { 1126 state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg) 1127 } 1128 break 1129 } 1130 1131 setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)}) 1132 if state.loggingLevel > 1 { 1133 state.logf("at %v: %v spilled to stack location %v@%d\n", v, state.slots[slot], home, state.stackOffset(home)) 1134 } 1135 } 1136 1137 case vReg != nil: 1138 if state.loggingLevel > 1 { 1139 newSlots := make([]bool, len(state.slots)) 1140 for _, slot := range vSlots { 1141 newSlots[slot] = true 1142 } 1143 1144 for _, slot := range locs.registers[vReg.num] { 1145 if !newSlots[slot] { 1146 state.logf("at %v: overwrote %v in register %v\n", v, state.slots[slot], vReg) 1147 } 1148 } 1149 } 1150 1151 for _, slot := range locs.registers[vReg.num] { 1152 last := locs.slots[slot] 1153 setSlot(slot, VarLoc{last.Registers &^ (1 << uint8(vReg.num)), last.StackOffset}) 1154 } 1155 locs.registers[vReg.num] = locs.registers[vReg.num][:0] 1156 locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...) 1157 for _, slot := range vSlots { 1158 if state.loggingLevel > 1 { 1159 state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg) 1160 } 1161 1162 last := locs.slots[slot] 1163 setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset}) 1164 } 1165 } 1166 return changed 1167 } 1168 1169 // varOffset returns the offset of slot within the user variable it was 1170 // decomposed from. This has nothing to do with its stack offset. 1171 func varOffset(slot LocalSlot) int64 { 1172 offset := slot.Off 1173 s := &slot 1174 for ; s.SplitOf != nil; s = s.SplitOf { 1175 offset += s.SplitOffset 1176 } 1177 return offset 1178 } 1179 1180 type partsByVarOffset struct { 1181 slotIDs []SlotID 1182 slots []LocalSlot 1183 } 1184 1185 func (a partsByVarOffset) Len() int { return len(a.slotIDs) } 1186 func (a partsByVarOffset) Less(i, j int) bool { 1187 return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[j]]) 1188 } 1189 func (a partsByVarOffset) Swap(i, j int) { a.slotIDs[i], a.slotIDs[j] = a.slotIDs[j], a.slotIDs[i] } 1190 1191 // A pendingEntry represents the beginning of a location list entry, missing 1192 // only its end coordinate. 1193 type pendingEntry struct { 1194 present bool 1195 startBlock, startValue ID 1196 // The location of each piece of the variable, in the same order as the 1197 // SlotIDs in varParts. 1198 pieces []VarLoc 1199 } 1200 1201 func (e *pendingEntry) clear() { 1202 e.present = false 1203 e.startBlock = 0 1204 e.startValue = 0 1205 for i := range e.pieces { 1206 e.pieces[i] = VarLoc{} 1207 } 1208 } 1209 1210 // canMerge reports whether a new location description is a superset 1211 // of the (non-empty) pending location description, if so, the two 1212 // can be merged (i.e., pending is still a valid and useful location 1213 // description). 1214 func canMerge(pending, new VarLoc) bool { 1215 if pending.absent() && new.absent() { 1216 return true 1217 } 1218 if pending.absent() || new.absent() { 1219 return false 1220 } 1221 // pending is not absent, therefore it has either a stack mapping, 1222 // or registers, or both. 1223 if pending.onStack() && pending.StackOffset != new.StackOffset { 1224 // if pending has a stack offset, then new must also, and it 1225 // must be the same (StackOffset encodes onStack). 1226 return false 1227 } 1228 if pending.Registers&new.Registers != pending.Registers { 1229 // There is at least one register in pending not mentioned in new. 1230 return false 1231 } 1232 return true 1233 } 1234 1235 // firstReg returns the first register in set that is present. 1236 func firstReg(set RegisterSet) uint8 { 1237 if set == 0 { 1238 // This is wrong, but there seem to be some situations where we 1239 // produce locations with no storage. 1240 return 0 1241 } 1242 return uint8(bits.TrailingZeros64(uint64(set))) 1243 } 1244 1245 // buildLocationLists builds location lists for all the user variables 1246 // in state.f, using the information about block state in blockLocs. 1247 // The returned location lists are not fully complete. They are in 1248 // terms of SSA values rather than PCs, and have no base address/end 1249 // entries. They will be finished by PutLocationList. 1250 func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) { 1251 // Run through the function in program text order, building up location 1252 // lists as we go. The heavy lifting has mostly already been done. 1253 1254 var prevBlock *Block 1255 for _, b := range state.f.Blocks { 1256 state.mergePredecessors(b, blockLocs, prevBlock, true) 1257 1258 // Handle any differences among predecessor blocks and previous block (perhaps not a predecessor) 1259 for _, varID := range state.changedVars.contents() { 1260 state.updateVar(VarID(varID), b, BlockStart) 1261 } 1262 state.changedVars.clear() 1263 1264 if !blockLocs[b.ID].relevant { 1265 continue 1266 } 1267 1268 mustBeFirst := func(v *Value) bool { 1269 return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() || 1270 v.Op == OpArgIntReg || v.Op == OpArgFloatReg 1271 } 1272 1273 blockPrologComplete := func(v *Value) bool { 1274 if b.ID != state.f.Entry.ID { 1275 return !opcodeTable[v.Op].zeroWidth 1276 } else { 1277 return v.Op == OpInitMem 1278 } 1279 } 1280 1281 // Examine the prolog portion of the block to process special 1282 // zero-width ops such as Arg, Phi, LoweredGetClosurePtr (etc) 1283 // whose lifetimes begin at the block starting point. In an 1284 // entry block, allow for the possibility that we may see Arg 1285 // ops that appear _after_ other non-zero-width operations. 1286 // Example: 1287 // 1288 // v33 = ArgIntReg <uintptr> {foo+0} [0] : AX (foo) 1289 // v34 = ArgIntReg <uintptr> {bar+0} [0] : BX (bar) 1290 // ... 1291 // v77 = StoreReg <unsafe.Pointer> v67 : ctx+8[unsafe.Pointer] 1292 // v78 = StoreReg <unsafe.Pointer> v68 : ctx[unsafe.Pointer] 1293 // v79 = Arg <*uint8> {args} : args[*uint8] (args[*uint8]) 1294 // v80 = Arg <int> {args} [8] : args+8[int] (args+8[int]) 1295 // ... 1296 // v1 = InitMem <mem> 1297 // 1298 // We can stop scanning the initial portion of the block when 1299 // we either see the InitMem op (for entry blocks) or the 1300 // first non-zero-width op (for other blocks). 1301 for idx := 0; idx < len(b.Values); idx++ { 1302 v := b.Values[idx] 1303 if blockPrologComplete(v) { 1304 break 1305 } 1306 // Consider only "lifetime begins at block start" ops. 1307 if !mustBeFirst(v) && v.Op != OpArg { 1308 continue 1309 } 1310 slots := state.valueNames[v.ID] 1311 reg, _ := state.f.getHome(v.ID).(*Register) 1312 changed := state.processValue(v, slots, reg) // changed == added to state.changedVars 1313 if changed { 1314 for _, varID := range state.changedVars.contents() { 1315 state.updateVar(VarID(varID), v.Block, BlockStart) 1316 } 1317 state.changedVars.clear() 1318 } 1319 } 1320 1321 // Now examine the block again, handling things other than the 1322 // "begins at block start" lifetimes. 1323 zeroWidthPending := false 1324 prologComplete := false 1325 // expect to see values in pattern (apc)* (zerowidth|real)* 1326 for _, v := range b.Values { 1327 if blockPrologComplete(v) { 1328 prologComplete = true 1329 } 1330 slots := state.valueNames[v.ID] 1331 reg, _ := state.f.getHome(v.ID).(*Register) 1332 changed := state.processValue(v, slots, reg) // changed == added to state.changedVars 1333 1334 if opcodeTable[v.Op].zeroWidth { 1335 if prologComplete && mustBeFirst(v) { 1336 panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func)) 1337 } 1338 if changed { 1339 if mustBeFirst(v) || v.Op == OpArg { 1340 // already taken care of above 1341 continue 1342 } 1343 zeroWidthPending = true 1344 } 1345 continue 1346 } 1347 if !changed && !zeroWidthPending { 1348 continue 1349 } 1350 1351 // Not zero-width; i.e., a "real" instruction. 1352 zeroWidthPending = false 1353 for _, varID := range state.changedVars.contents() { 1354 state.updateVar(VarID(varID), v.Block, v) 1355 } 1356 state.changedVars.clear() 1357 } 1358 for _, varID := range state.changedVars.contents() { 1359 state.updateVar(VarID(varID), b, BlockEnd) 1360 } 1361 1362 prevBlock = b 1363 } 1364 1365 if state.loggingLevel > 0 { 1366 state.logf("location lists:\n") 1367 } 1368 1369 // Flush any leftover entries live at the end of the last block. 1370 for varID := range state.lists { 1371 state.writePendingEntry(VarID(varID), state.f.Blocks[len(state.f.Blocks)-1].ID, FuncEnd.ID) 1372 list := state.lists[varID] 1373 if state.loggingLevel > 0 { 1374 if len(list) == 0 { 1375 state.logf("\t%v : empty list\n", state.vars[varID]) 1376 } else { 1377 state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(state.lists[varID])) 1378 } 1379 } 1380 } 1381 } 1382 1383 // updateVar updates the pending location list entry for varID to 1384 // reflect the new locations in curLoc, beginning at v in block b. 1385 // v may be one of the special values indicating block start or end. 1386 func (state *debugState) updateVar(varID VarID, b *Block, v *Value) { 1387 curLoc := state.currentState.slots 1388 // Assemble the location list entry with whatever's live. 1389 empty := true 1390 for _, slotID := range state.varSlots[varID] { 1391 if !curLoc[slotID].absent() { 1392 empty = false 1393 break 1394 } 1395 } 1396 pending := &state.pendingEntries[varID] 1397 if empty { 1398 state.writePendingEntry(varID, b.ID, v.ID) 1399 pending.clear() 1400 return 1401 } 1402 1403 // Extend the previous entry if possible. 1404 if pending.present { 1405 merge := true 1406 for i, slotID := range state.varSlots[varID] { 1407 if !canMerge(pending.pieces[i], curLoc[slotID]) { 1408 merge = false 1409 break 1410 } 1411 } 1412 if merge { 1413 return 1414 } 1415 } 1416 1417 state.writePendingEntry(varID, b.ID, v.ID) 1418 pending.present = true 1419 pending.startBlock = b.ID 1420 pending.startValue = v.ID 1421 for i, slot := range state.varSlots[varID] { 1422 pending.pieces[i] = curLoc[slot] 1423 } 1424 } 1425 1426 // writePendingEntry writes out the pending entry for varID, if any, 1427 // terminated at endBlock/Value. 1428 func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) { 1429 pending := state.pendingEntries[varID] 1430 if !pending.present { 1431 return 1432 } 1433 1434 // Pack the start/end coordinates into the start/end addresses 1435 // of the entry, for decoding by PutLocationList. 1436 start, startOK := encodeValue(state.ctxt, pending.startBlock, pending.startValue) 1437 end, endOK := encodeValue(state.ctxt, endBlock, endValue) 1438 if !startOK || !endOK { 1439 // If someone writes a function that uses >65K values, 1440 // they get incomplete debug info on 32-bit platforms. 1441 return 1442 } 1443 if start == end { 1444 if state.loggingLevel > 1 { 1445 // Printf not logf so not gated by GOSSAFUNC; this should fire very rarely. 1446 // TODO this fires a lot, need to figure out why. 1447 state.logf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name) 1448 } 1449 return 1450 } 1451 1452 list := state.lists[varID] 1453 list = appendPtr(state.ctxt, list, start) 1454 list = appendPtr(state.ctxt, list, end) 1455 // Where to write the length of the location description once 1456 // we know how big it is. 1457 sizeIdx := len(list) 1458 list = list[:len(list)+2] 1459 1460 if state.loggingLevel > 1 { 1461 var partStrs []string 1462 for i, slot := range state.varSlots[varID] { 1463 partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i]))) 1464 } 1465 state.logf("Add entry for %v: \tb%vv%v-b%vv%v = \t%v\n", state.vars[varID], pending.startBlock, pending.startValue, endBlock, endValue, strings.Join(partStrs, " ")) 1466 } 1467 1468 for i, slotID := range state.varSlots[varID] { 1469 loc := pending.pieces[i] 1470 slot := state.slots[slotID] 1471 1472 if !loc.absent() { 1473 if loc.onStack() { 1474 if loc.stackOffsetValue() == 0 { 1475 list = append(list, dwarf.DW_OP_call_frame_cfa) 1476 } else { 1477 list = append(list, dwarf.DW_OP_fbreg) 1478 list = dwarf.AppendSleb128(list, int64(loc.stackOffsetValue())) 1479 } 1480 } else { 1481 regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()] 1482 if regnum < 32 { 1483 list = append(list, dwarf.DW_OP_reg0+byte(regnum)) 1484 } else { 1485 list = append(list, dwarf.DW_OP_regx) 1486 list = dwarf.AppendUleb128(list, uint64(regnum)) 1487 } 1488 } 1489 } 1490 1491 if len(state.varSlots[varID]) > 1 { 1492 list = append(list, dwarf.DW_OP_piece) 1493 list = dwarf.AppendUleb128(list, uint64(slot.Type.Size())) 1494 } 1495 } 1496 state.ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) 1497 state.lists[varID] = list 1498 } 1499 1500 // PutLocationList adds list (a location list in its intermediate representation) to listSym. 1501 func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) { 1502 getPC := debugInfo.GetPC 1503 1504 if ctxt.UseBASEntries { 1505 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0) 1506 listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0) 1507 } 1508 1509 // Re-read list, translating its address from block/value ID to PC. 1510 for i := 0; i < len(list); { 1511 begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:]))) 1512 end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:]))) 1513 1514 // Horrible hack. If a range contains only zero-width 1515 // instructions, e.g. an Arg, and it's at the beginning of the 1516 // function, this would be indistinguishable from an 1517 // end entry. Fudge it. 1518 if begin == 0 && end == 0 { 1519 end = 1 1520 } 1521 1522 if ctxt.UseBASEntries { 1523 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(begin)) 1524 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(end)) 1525 } else { 1526 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(begin)) 1527 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(end)) 1528 } 1529 1530 i += 2 * ctxt.Arch.PtrSize 1531 datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:])) 1532 listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) // copy datalen and location encoding 1533 i += datalen 1534 } 1535 1536 // Location list contents, now with real PCs. 1537 // End entry. 1538 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) 1539 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) 1540 } 1541 1542 // Pack a value and block ID into an address-sized uint, returning 1543 // encoded value and boolean indicating whether the encoding succeeded. 1544 // For 32-bit architectures the process may fail for very large 1545 // procedures(the theory being that it's ok to have degraded debug 1546 // quality in this case). 1547 func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) { 1548 if ctxt.Arch.PtrSize == 8 { 1549 result := uint64(b)<<32 | uint64(uint32(v)) 1550 //ctxt.Logf("b %#x (%d) v %#x (%d) -> %#x\n", b, b, v, v, result) 1551 return result, true 1552 } 1553 if ctxt.Arch.PtrSize != 4 { 1554 panic("unexpected pointer size") 1555 } 1556 if ID(int16(b)) != b || ID(int16(v)) != v { 1557 return 0, false 1558 } 1559 return uint64(b)<<16 | uint64(uint16(v)), true 1560 } 1561 1562 // Unpack a value and block ID encoded by encodeValue. 1563 func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) { 1564 if ctxt.Arch.PtrSize == 8 { 1565 b, v := ID(word>>32), ID(word) 1566 //ctxt.Logf("%#x -> b %#x (%d) v %#x (%d)\n", word, b, b, v, v) 1567 return b, v 1568 } 1569 if ctxt.Arch.PtrSize != 4 { 1570 panic("unexpected pointer size") 1571 } 1572 return ID(word >> 16), ID(int16(word)) 1573 } 1574 1575 // Append a pointer-sized uint to buf. 1576 func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte { 1577 if cap(buf) < len(buf)+20 { 1578 b := make([]byte, len(buf), 20+cap(buf)*2) 1579 copy(b, buf) 1580 buf = b 1581 } 1582 writeAt := len(buf) 1583 buf = buf[0 : len(buf)+ctxt.Arch.PtrSize] 1584 writePtr(ctxt, buf[writeAt:], word) 1585 return buf 1586 } 1587 1588 // Write a pointer-sized uint to the beginning of buf. 1589 func writePtr(ctxt *obj.Link, buf []byte, word uint64) { 1590 switch ctxt.Arch.PtrSize { 1591 case 4: 1592 ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word)) 1593 case 8: 1594 ctxt.Arch.ByteOrder.PutUint64(buf, word) 1595 default: 1596 panic("unexpected pointer size") 1597 } 1598 1599 } 1600 1601 // Read a pointer-sized uint from the beginning of buf. 1602 func readPtr(ctxt *obj.Link, buf []byte) uint64 { 1603 switch ctxt.Arch.PtrSize { 1604 case 4: 1605 return uint64(ctxt.Arch.ByteOrder.Uint32(buf)) 1606 case 8: 1607 return ctxt.Arch.ByteOrder.Uint64(buf) 1608 default: 1609 panic("unexpected pointer size") 1610 } 1611 1612 } 1613 1614 // setupLocList creates the initial portion of a location list for a 1615 // user variable. It emits the encoded start/end of the range and a 1616 // placeholder for the size. Return value is the new list plus the 1617 // slot in the list holding the size (to be updated later). 1618 func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) { 1619 start, startOK := encodeValue(ctxt, f.Entry.ID, st) 1620 end, endOK := encodeValue(ctxt, f.Entry.ID, en) 1621 if !startOK || !endOK { 1622 // This could happen if someone writes a function that uses 1623 // >65K values on a 32-bit platform. Hopefully a degraded debugging 1624 // experience is ok in that case. 1625 return nil, 0 1626 } 1627 list = appendPtr(ctxt, list, start) 1628 list = appendPtr(ctxt, list, end) 1629 1630 // Where to write the length of the location description once 1631 // we know how big it is. 1632 sizeIdx := len(list) 1633 list = list[:len(list)+2] 1634 return list, sizeIdx 1635 } 1636 1637 // locatePrologEnd walks the entry block of a function with incoming 1638 // register arguments and locates the last instruction in the prolog 1639 // that spills a register arg. It returns the ID of that instruction 1640 // Example: 1641 // 1642 // b1: 1643 // v3 = ArgIntReg <int> {p1+0} [0] : AX 1644 // ... more arg regs .. 1645 // v4 = ArgFloatReg <float32> {f1+0} [0] : X0 1646 // v52 = MOVQstore <mem> {p1} v2 v3 v1 1647 // ... more stores ... 1648 // v68 = MOVSSstore <mem> {f4} v2 v67 v66 1649 // v38 = MOVQstoreconst <mem> {blob} [val=0,off=0] v2 v32 1650 // 1651 // Important: locatePrologEnd is expected to work properly only with 1652 // optimization turned off (e.g. "-N"). If optimization is enabled 1653 // we can't be assured of finding all input arguments spilled in the 1654 // entry block prolog. 1655 func locatePrologEnd(f *Func) ID { 1656 1657 // returns true if this instruction looks like it moves an ABI 1658 // register to the stack, along with the value being stored. 1659 isRegMoveLike := func(v *Value) (bool, ID) { 1660 n, ok := v.Aux.(*ir.Name) 1661 var r ID 1662 if !ok || n.Class != ir.PPARAM { 1663 return false, r 1664 } 1665 regInputs, memInputs, spInputs := 0, 0, 0 1666 for _, a := range v.Args { 1667 if a.Op == OpArgIntReg || a.Op == OpArgFloatReg { 1668 regInputs++ 1669 r = a.ID 1670 } else if a.Type.IsMemory() { 1671 memInputs++ 1672 } else if a.Op == OpSP { 1673 spInputs++ 1674 } else { 1675 return false, r 1676 } 1677 } 1678 return v.Type.IsMemory() && memInputs == 1 && 1679 regInputs == 1 && spInputs == 1, r 1680 } 1681 1682 // OpArg*Reg values we've seen so far on our forward walk, 1683 // for which we have not yet seen a corresponding spill. 1684 regArgs := make([]ID, 0, 32) 1685 1686 // removeReg tries to remove a value from regArgs, returning true 1687 // if found and removed, or false otherwise. 1688 removeReg := func(r ID) bool { 1689 for i := 0; i < len(regArgs); i++ { 1690 if regArgs[i] == r { 1691 regArgs = append(regArgs[:i], regArgs[i+1:]...) 1692 return true 1693 } 1694 } 1695 return false 1696 } 1697 1698 // Walk forwards through the block. When we see OpArg*Reg, record 1699 // the value it produces in the regArgs list. When see a store that uses 1700 // the value, remove the entry. When we hit the last store (use) 1701 // then we've arrived at the end of the prolog. 1702 for k, v := range f.Entry.Values { 1703 if v.Op == OpArgIntReg || v.Op == OpArgFloatReg { 1704 regArgs = append(regArgs, v.ID) 1705 continue 1706 } 1707 if ok, r := isRegMoveLike(v); ok { 1708 if removed := removeReg(r); removed { 1709 if len(regArgs) == 0 { 1710 // Found our last spill; return the value after 1711 // it. Note that it is possible that this spill is 1712 // the last instruction in the block. If so, then 1713 // return the "end of block" sentinel. 1714 if k < len(f.Entry.Values)-1 { 1715 return f.Entry.Values[k+1].ID 1716 } 1717 return BlockEnd.ID 1718 } 1719 } 1720 } 1721 if v.Op.IsCall() { 1722 // if we hit a call, we've gone too far. 1723 return v.ID 1724 } 1725 } 1726 // nothing found 1727 return ID(-1) 1728 } 1729 1730 // isNamedRegParam returns true if the param corresponding to "p" 1731 // is a named, non-blank input parameter assigned to one or more 1732 // registers. 1733 func isNamedRegParam(p abi.ABIParamAssignment) bool { 1734 if p.Name == nil { 1735 return false 1736 } 1737 n := p.Name.(*ir.Name) 1738 if n.Sym() == nil || n.Sym().IsBlank() { 1739 return false 1740 } 1741 if len(p.Registers) == 0 { 1742 return false 1743 } 1744 return true 1745 } 1746 1747 // BuildFuncDebugNoOptimized populates a FuncDebug object "rval" with 1748 // entries corresponding to the register-resident input parameters for 1749 // the function "f"; it is used when we are compiling without 1750 // optimization but the register ABI is enabled. For each reg param, 1751 // it constructs a 2-element location list: the first element holds 1752 // the input register, and the second element holds the stack location 1753 // of the param (the assumption being that when optimization is off, 1754 // each input param reg will be spilled in the prolog. 1755 func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) { 1756 1757 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType()) 1758 1759 // Look to see if we have any named register-promoted parameters. 1760 // If there are none, bail early and let the caller sort things 1761 // out for the remainder of the params/locals. 1762 numRegParams := 0 1763 for _, inp := range pri.InParams() { 1764 if isNamedRegParam(inp) { 1765 numRegParams++ 1766 } 1767 } 1768 if numRegParams == 0 { 1769 return 1770 } 1771 1772 state := debugState{f: f} 1773 1774 if loggingEnabled { 1775 state.logf("generating -N reg param loc lists for func %q\n", f.Name) 1776 } 1777 1778 // Allocate location lists. 1779 rval.LocationLists = make([][]byte, numRegParams) 1780 1781 // Locate the value corresponding to the last spill of 1782 // an input register. 1783 afterPrologVal := locatePrologEnd(f) 1784 1785 // Walk the input params again and process the register-resident elements. 1786 pidx := 0 1787 for _, inp := range pri.InParams() { 1788 if !isNamedRegParam(inp) { 1789 // will be sorted out elsewhere 1790 continue 1791 } 1792 1793 n := inp.Name.(*ir.Name) 1794 sl := LocalSlot{N: n, Type: inp.Type, Off: 0} 1795 rval.Vars = append(rval.Vars, n) 1796 rval.Slots = append(rval.Slots, sl) 1797 slid := len(rval.VarSlots) 1798 rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)}) 1799 1800 if afterPrologVal == ID(-1) { 1801 // This can happen for degenerate functions with infinite 1802 // loops such as that in issue 45948. In such cases, leave 1803 // the var/slot set up for the param, but don't try to 1804 // emit a location list. 1805 if loggingEnabled { 1806 state.logf("locatePrologEnd failed, skipping %v\n", n) 1807 } 1808 pidx++ 1809 continue 1810 } 1811 1812 // Param is arriving in one or more registers. We need a 2-element 1813 // location expression for it. First entry in location list 1814 // will correspond to lifetime in input registers. 1815 list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx], 1816 BlockStart.ID, afterPrologVal) 1817 if list == nil { 1818 pidx++ 1819 continue 1820 } 1821 if loggingEnabled { 1822 state.logf("param %v:\n [<entry>, %d]:\n", n, afterPrologVal) 1823 } 1824 rtypes, _ := inp.RegisterTypesAndOffsets() 1825 padding := make([]uint64, 0, 32) 1826 padding = inp.ComputePadding(padding) 1827 for k, r := range inp.Registers { 1828 reg := ObjRegForAbiReg(r, f.Config) 1829 dwreg := ctxt.Arch.DWARFRegisters[reg] 1830 if dwreg < 32 { 1831 list = append(list, dwarf.DW_OP_reg0+byte(dwreg)) 1832 } else { 1833 list = append(list, dwarf.DW_OP_regx) 1834 list = dwarf.AppendUleb128(list, uint64(dwreg)) 1835 } 1836 if loggingEnabled { 1837 state.logf(" piece %d -> dwreg %d", k, dwreg) 1838 } 1839 if len(inp.Registers) > 1 { 1840 list = append(list, dwarf.DW_OP_piece) 1841 ts := rtypes[k].Size() 1842 list = dwarf.AppendUleb128(list, uint64(ts)) 1843 if padding[k] > 0 { 1844 if loggingEnabled { 1845 state.logf(" [pad %d bytes]", padding[k]) 1846 } 1847 list = append(list, dwarf.DW_OP_piece) 1848 list = dwarf.AppendUleb128(list, padding[k]) 1849 } 1850 } 1851 if loggingEnabled { 1852 state.logf("\n") 1853 } 1854 } 1855 // fill in length of location expression element 1856 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) 1857 1858 // Second entry in the location list will be the stack home 1859 // of the param, once it has been spilled. Emit that now. 1860 list, sizeIdx = setupLocList(ctxt, f, list, 1861 afterPrologVal, FuncEnd.ID) 1862 if list == nil { 1863 pidx++ 1864 continue 1865 } 1866 soff := stackOffset(sl) 1867 if soff == 0 { 1868 list = append(list, dwarf.DW_OP_call_frame_cfa) 1869 } else { 1870 list = append(list, dwarf.DW_OP_fbreg) 1871 list = dwarf.AppendSleb128(list, int64(soff)) 1872 } 1873 if loggingEnabled { 1874 state.logf(" [%d, <end>): stackOffset=%d\n", afterPrologVal, soff) 1875 } 1876 1877 // fill in size 1878 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) 1879 1880 rval.LocationLists[pidx] = list 1881 pidx++ 1882 } 1883 }