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