github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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 package ssa 5 6 import ( 7 "github.com/gagliardetto/golang-go/cmd/internal/dwarf" 8 "github.com/gagliardetto/golang-go/cmd/internal/obj" 9 "encoding/hex" 10 "fmt" 11 "math/bits" 12 "sort" 13 "strings" 14 ) 15 16 type SlotID int32 17 type VarID int32 18 19 // A FuncDebug contains all the debug information for the variables in a 20 // function. Variables are identified by their LocalSlot, which may be the 21 // result of decomposing a larger variable. 22 type FuncDebug struct { 23 // Slots is all the slots used in the debug info, indexed by their SlotID. 24 Slots []LocalSlot 25 // The user variables, indexed by VarID. 26 Vars []GCNode 27 // The slots that make up each variable, indexed by VarID. 28 VarSlots [][]SlotID 29 // The location list data, indexed by VarID. Must be processed by PutLocationList. 30 LocationLists [][]byte 31 32 // Filled in by the user. Translates Block and Value ID to PC. 33 GetPC func(ID, ID) int64 34 } 35 36 type BlockDebug struct { 37 // Whether the block had any changes to user variables at all. 38 relevant bool 39 // State at the end of the block if it's fully processed. Immutable once initialized. 40 endState []liveSlot 41 } 42 43 // A liveSlot is a slot that's live in loc at entry/exit of a block. 44 type liveSlot struct { 45 // An inlined VarLoc, so it packs into 16 bytes instead of 20. 46 Registers RegisterSet 47 StackOffset 48 49 slot SlotID 50 } 51 52 func (loc liveSlot) absent() bool { 53 return loc.Registers == 0 && !loc.onStack() 54 } 55 56 // StackOffset encodes whether a value is on the stack and if so, where. It is 57 // a 31-bit integer followed by a presence flag at the low-order bit. 58 type StackOffset int32 59 60 func (s StackOffset) onStack() bool { 61 return s != 0 62 } 63 64 func (s StackOffset) stackOffsetValue() int32 { 65 return int32(s) >> 1 66 } 67 68 // stateAtPC is the current state of all variables at some point. 69 type stateAtPC struct { 70 // The location of each known slot, indexed by SlotID. 71 slots []VarLoc 72 // The slots present in each register, indexed by register number. 73 registers [][]SlotID 74 } 75 76 // reset fills state with the live variables from live. 77 func (state *stateAtPC) reset(live []liveSlot) { 78 slots, registers := state.slots, state.registers 79 for i := range slots { 80 slots[i] = VarLoc{} 81 } 82 for i := range registers { 83 registers[i] = registers[i][:0] 84 } 85 for _, live := range live { 86 slots[live.slot] = VarLoc{live.Registers, live.StackOffset} 87 if live.Registers == 0 { 88 continue 89 } 90 91 mask := uint64(live.Registers) 92 for { 93 if mask == 0 { 94 break 95 } 96 reg := uint8(bits.TrailingZeros64(mask)) 97 mask &^= 1 << reg 98 99 registers[reg] = append(registers[reg], live.slot) 100 } 101 } 102 state.slots, state.registers = slots, registers 103 } 104 105 func (s *debugState) LocString(loc VarLoc) string { 106 if loc.absent() { 107 return "<nil>" 108 } 109 110 var storage []string 111 if loc.onStack() { 112 storage = append(storage, "stack") 113 } 114 115 mask := uint64(loc.Registers) 116 for { 117 if mask == 0 { 118 break 119 } 120 reg := uint8(bits.TrailingZeros64(mask)) 121 mask &^= 1 << reg 122 123 storage = append(storage, s.registers[reg].String()) 124 } 125 return strings.Join(storage, ",") 126 } 127 128 // A VarLoc describes the storage for part of a user variable. 129 type VarLoc struct { 130 // The registers this variable is available in. There can be more than 131 // one in various situations, e.g. it's being moved between registers. 132 Registers RegisterSet 133 134 StackOffset 135 } 136 137 func (loc VarLoc) absent() bool { 138 return loc.Registers == 0 && !loc.onStack() 139 } 140 141 var BlockStart = &Value{ 142 ID: -10000, 143 Op: OpInvalid, 144 Aux: "BlockStart", 145 } 146 147 var BlockEnd = &Value{ 148 ID: -20000, 149 Op: OpInvalid, 150 Aux: "BlockEnd", 151 } 152 153 // RegisterSet is a bitmap of registers, indexed by Register.num. 154 type RegisterSet uint64 155 156 // logf prints debug-specific logging to stdout (always stdout) if the current 157 // function is tagged by GOSSAFUNC (for ssa output directed either to stdout or html). 158 func (s *debugState) logf(msg string, args ...interface{}) { 159 if s.f.PrintOrHtmlSSA { 160 fmt.Printf(msg, args...) 161 } 162 } 163 164 type debugState struct { 165 // See FuncDebug. 166 slots []LocalSlot 167 vars []GCNode 168 varSlots [][]SlotID 169 lists [][]byte 170 171 // The user variable that each slot rolls up to, indexed by SlotID. 172 slotVars []VarID 173 174 f *Func 175 loggingEnabled bool 176 registers []Register 177 stackOffset func(LocalSlot) int32 178 ctxt *obj.Link 179 180 // The names (slots) associated with each value, indexed by Value ID. 181 valueNames [][]SlotID 182 183 // The current state of whatever analysis is running. 184 currentState stateAtPC 185 liveCount []int 186 changedVars *sparseSet 187 188 // The pending location list entry for each user variable, indexed by VarID. 189 pendingEntries []pendingEntry 190 191 varParts map[GCNode][]SlotID 192 blockDebug []BlockDebug 193 pendingSlotLocs []VarLoc 194 liveSlots []liveSlot 195 liveSlotSliceBegin int 196 partsByVarOffset sort.Interface 197 } 198 199 func (state *debugState) initializeCache(f *Func, numVars, numSlots int) { 200 // One blockDebug per block. Initialized in allocBlock. 201 if cap(state.blockDebug) < f.NumBlocks() { 202 state.blockDebug = make([]BlockDebug, f.NumBlocks()) 203 } else { 204 // This local variable, and the ones like it below, enable compiler 205 // optimizations. Don't inline them. 206 b := state.blockDebug[:f.NumBlocks()] 207 for i := range b { 208 b[i] = BlockDebug{} 209 } 210 } 211 212 // A list of slots per Value. Reuse the previous child slices. 213 if cap(state.valueNames) < f.NumValues() { 214 old := state.valueNames 215 state.valueNames = make([][]SlotID, f.NumValues()) 216 copy(state.valueNames, old) 217 } 218 vn := state.valueNames[:f.NumValues()] 219 for i := range vn { 220 vn[i] = vn[i][:0] 221 } 222 223 // Slot and register contents for currentState. Cleared by reset(). 224 if cap(state.currentState.slots) < numSlots { 225 state.currentState.slots = make([]VarLoc, numSlots) 226 } else { 227 state.currentState.slots = state.currentState.slots[:numSlots] 228 } 229 if cap(state.currentState.registers) < len(state.registers) { 230 state.currentState.registers = make([][]SlotID, len(state.registers)) 231 } else { 232 state.currentState.registers = state.currentState.registers[:len(state.registers)] 233 } 234 235 // Used many times by mergePredecessors. 236 if cap(state.liveCount) < numSlots { 237 state.liveCount = make([]int, numSlots) 238 } else { 239 state.liveCount = state.liveCount[:numSlots] 240 } 241 242 // A relatively small slice, but used many times as the return from processValue. 243 state.changedVars = newSparseSet(numVars) 244 245 // A pending entry per user variable, with space to track each of its pieces. 246 numPieces := 0 247 for i := range state.varSlots { 248 numPieces += len(state.varSlots[i]) 249 } 250 if cap(state.pendingSlotLocs) < numPieces { 251 state.pendingSlotLocs = make([]VarLoc, numPieces) 252 } else { 253 psl := state.pendingSlotLocs[:numPieces] 254 for i := range psl { 255 psl[i] = VarLoc{} 256 } 257 } 258 if cap(state.pendingEntries) < numVars { 259 state.pendingEntries = make([]pendingEntry, numVars) 260 } 261 pe := state.pendingEntries[:numVars] 262 freePieceIdx := 0 263 for varID, slots := range state.varSlots { 264 pe[varID] = pendingEntry{ 265 pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)], 266 } 267 freePieceIdx += len(slots) 268 } 269 state.pendingEntries = pe 270 271 if cap(state.lists) < numVars { 272 state.lists = make([][]byte, numVars) 273 } else { 274 state.lists = state.lists[:numVars] 275 for i := range state.lists { 276 state.lists[i] = nil 277 } 278 } 279 280 state.liveSlots = state.liveSlots[:0] 281 state.liveSlotSliceBegin = 0 282 } 283 284 func (state *debugState) allocBlock(b *Block) *BlockDebug { 285 return &state.blockDebug[b.ID] 286 } 287 288 func (state *debugState) appendLiveSlot(ls liveSlot) { 289 state.liveSlots = append(state.liveSlots, ls) 290 } 291 292 func (state *debugState) getLiveSlotSlice() []liveSlot { 293 s := state.liveSlots[state.liveSlotSliceBegin:] 294 state.liveSlotSliceBegin = len(state.liveSlots) 295 return s 296 } 297 298 func (s *debugState) blockEndStateString(b *BlockDebug) string { 299 endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.registers))} 300 endState.reset(b.endState) 301 return s.stateString(endState) 302 } 303 304 func (s *debugState) stateString(state stateAtPC) string { 305 var strs []string 306 for slotID, loc := range state.slots { 307 if !loc.absent() { 308 strs = append(strs, fmt.Sprintf("\t%v = %v\n", s.slots[slotID], s.LocString(loc))) 309 } 310 } 311 312 strs = append(strs, "\n") 313 for reg, slots := range state.registers { 314 if len(slots) != 0 { 315 var slotStrs []string 316 for _, slot := range slots { 317 slotStrs = append(slotStrs, s.slots[slot].String()) 318 } 319 strs = append(strs, fmt.Sprintf("\t%v = %v\n", &s.registers[reg], slotStrs)) 320 } 321 } 322 323 if len(strs) == 1 { 324 return "(no vars)\n" 325 } 326 return strings.Join(strs, "") 327 } 328 329 // BuildFuncDebug returns debug information for f. 330 // f must be fully processed, so that each Value is where it will be when 331 // machine code is emitted. 332 func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug { 333 if f.RegAlloc == nil { 334 f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f) 335 } 336 state := &f.Cache.debugState 337 state.loggingEnabled = loggingEnabled 338 state.f = f 339 state.registers = f.Config.registers 340 state.stackOffset = stackOffset 341 state.ctxt = ctxt 342 343 if state.loggingEnabled { 344 state.logf("Generating location lists for function %q\n", f.Name) 345 } 346 347 if state.varParts == nil { 348 state.varParts = make(map[GCNode][]SlotID) 349 } else { 350 for n := range state.varParts { 351 delete(state.varParts, n) 352 } 353 } 354 355 // Recompose any decomposed variables, and establish the canonical 356 // IDs for each var and slot by filling out state.vars and state.slots. 357 358 state.slots = state.slots[:0] 359 state.vars = state.vars[:0] 360 for i, slot := range f.Names { 361 state.slots = append(state.slots, slot) 362 if slot.N.IsSynthetic() { 363 continue 364 } 365 366 topSlot := &slot 367 for topSlot.SplitOf != nil { 368 topSlot = topSlot.SplitOf 369 } 370 if _, ok := state.varParts[topSlot.N]; !ok { 371 state.vars = append(state.vars, topSlot.N) 372 } 373 state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i)) 374 } 375 376 // Recreate the LocalSlot for each stack-only variable. 377 // This would probably be better as an output from stackframe. 378 for _, b := range f.Blocks { 379 for _, v := range b.Values { 380 if v.Op == OpVarDef || v.Op == OpVarKill { 381 n := v.Aux.(GCNode) 382 if n.IsSynthetic() { 383 continue 384 } 385 386 if _, ok := state.varParts[n]; !ok { 387 slot := LocalSlot{N: n, Type: v.Type, Off: 0} 388 state.slots = append(state.slots, slot) 389 state.varParts[n] = []SlotID{SlotID(len(state.slots) - 1)} 390 state.vars = append(state.vars, n) 391 } 392 } 393 } 394 } 395 396 // Fill in the var<->slot mappings. 397 if cap(state.varSlots) < len(state.vars) { 398 state.varSlots = make([][]SlotID, len(state.vars)) 399 } else { 400 state.varSlots = state.varSlots[:len(state.vars)] 401 for i := range state.varSlots { 402 state.varSlots[i] = state.varSlots[i][:0] 403 } 404 } 405 if cap(state.slotVars) < len(state.slots) { 406 state.slotVars = make([]VarID, len(state.slots)) 407 } else { 408 state.slotVars = state.slotVars[:len(state.slots)] 409 } 410 411 if state.partsByVarOffset == nil { 412 state.partsByVarOffset = &partsByVarOffset{} 413 } 414 for varID, n := range state.vars { 415 parts := state.varParts[n] 416 state.varSlots[varID] = parts 417 for _, slotID := range parts { 418 state.slotVars[slotID] = VarID(varID) 419 } 420 *state.partsByVarOffset.(*partsByVarOffset) = partsByVarOffset{parts, state.slots} 421 sort.Sort(state.partsByVarOffset) 422 } 423 424 state.initializeCache(f, len(state.varParts), len(state.slots)) 425 426 for i, slot := range f.Names { 427 if slot.N.IsSynthetic() { 428 continue 429 } 430 for _, value := range f.NamedValues[slot] { 431 state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i)) 432 } 433 } 434 435 blockLocs := state.liveness() 436 state.buildLocationLists(blockLocs) 437 438 return &FuncDebug{ 439 Slots: state.slots, 440 VarSlots: state.varSlots, 441 Vars: state.vars, 442 LocationLists: state.lists, 443 } 444 } 445 446 // liveness walks the function in control flow order, calculating the start 447 // and end state of each block. 448 func (state *debugState) liveness() []*BlockDebug { 449 blockLocs := make([]*BlockDebug, state.f.NumBlocks()) 450 451 // Reverse postorder: visit a block after as many as possible of its 452 // predecessors have been visited. 453 po := state.f.Postorder() 454 for i := len(po) - 1; i >= 0; i-- { 455 b := po[i] 456 457 // Build the starting state for the block from the final 458 // state of its predecessors. 459 startState, startValid := state.mergePredecessors(b, blockLocs, nil) 460 changed := false 461 if state.loggingEnabled { 462 state.logf("Processing %v, initial state:\n%v", b, state.stateString(state.currentState)) 463 } 464 465 // Update locs/registers with the effects of each Value. 466 for _, v := range b.Values { 467 slots := state.valueNames[v.ID] 468 469 // Loads and stores inherit the names of their sources. 470 var source *Value 471 switch v.Op { 472 case OpStoreReg: 473 source = v.Args[0] 474 case OpLoadReg: 475 switch a := v.Args[0]; a.Op { 476 case OpArg, OpPhi: 477 source = a 478 case OpStoreReg: 479 source = a.Args[0] 480 default: 481 if state.loggingEnabled { 482 state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a) 483 } 484 } 485 } 486 // Update valueNames with the source so that later steps 487 // don't need special handling. 488 if source != nil { 489 slots = append(slots, state.valueNames[source.ID]...) 490 state.valueNames[v.ID] = slots 491 } 492 493 reg, _ := state.f.getHome(v.ID).(*Register) 494 c := state.processValue(v, slots, reg) 495 changed = changed || c 496 } 497 498 if state.loggingEnabled { 499 state.f.Logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState)) 500 } 501 502 locs := state.allocBlock(b) 503 locs.relevant = changed 504 if !changed && startValid { 505 locs.endState = startState 506 } else { 507 for slotID, slotLoc := range state.currentState.slots { 508 if slotLoc.absent() { 509 continue 510 } 511 state.appendLiveSlot(liveSlot{slot: SlotID(slotID), Registers: slotLoc.Registers, StackOffset: slotLoc.StackOffset}) 512 } 513 locs.endState = state.getLiveSlotSlice() 514 } 515 blockLocs[b.ID] = locs 516 } 517 return blockLocs 518 } 519 520 // mergePredecessors takes the end state of each of b's predecessors and 521 // intersects them to form the starting state for b. It puts that state in 522 // blockLocs, and fills state.currentState with it. If convenient, it returns 523 // a reused []liveSlot, true that represents the starting state. 524 // If previousBlock is non-nil, it registers changes vs. that block's end 525 // state in state.changedVars. Note that previousBlock will often not be a 526 // predecessor. 527 func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block) ([]liveSlot, bool) { 528 // Filter out back branches. 529 var predsBuf [10]*Block 530 preds := predsBuf[:0] 531 for _, pred := range b.Preds { 532 if blockLocs[pred.b.ID] != nil { 533 preds = append(preds, pred.b) 534 } 535 } 536 537 if state.loggingEnabled { 538 // The logf below would cause preds to be heap-allocated if 539 // it were passed directly. 540 preds2 := make([]*Block, len(preds)) 541 copy(preds2, preds) 542 state.logf("Merging %v into %v\n", preds2, b) 543 } 544 545 // TODO all the calls to this are overkill; only need to do this for slots that are not present in the merge. 546 markChangedVars := func(slots []liveSlot) { 547 for _, live := range slots { 548 state.changedVars.add(ID(state.slotVars[live.slot])) 549 } 550 } 551 552 if len(preds) == 0 { 553 if previousBlock != nil { 554 // Mark everything in previous block as changed because it is not a predecessor. 555 markChangedVars(blockLocs[previousBlock.ID].endState) 556 } 557 state.currentState.reset(nil) 558 return nil, true 559 } 560 561 p0 := blockLocs[preds[0].ID].endState 562 if len(preds) == 1 { 563 if previousBlock != nil && preds[0].ID != previousBlock.ID { 564 // Mark everything in previous block as changed because it is not a predecessor. 565 markChangedVars(blockLocs[previousBlock.ID].endState) 566 } 567 state.currentState.reset(p0) 568 return p0, true 569 } 570 571 baseID := preds[0].ID 572 baseState := p0 573 574 // If previous block is not a predecessor, its location information changes at boundary with this block. 575 previousBlockIsNotPredecessor := previousBlock != nil // If it's nil, no info to change. 576 577 if previousBlock != nil { 578 // Try to use previousBlock as the base state 579 // if possible. 580 for _, pred := range preds[1:] { 581 if pred.ID == previousBlock.ID { 582 baseID = pred.ID 583 baseState = blockLocs[pred.ID].endState 584 previousBlockIsNotPredecessor = false 585 break 586 } 587 } 588 } 589 590 if state.loggingEnabled { 591 state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID])) 592 } 593 594 slotLocs := state.currentState.slots 595 for _, predSlot := range baseState { 596 slotLocs[predSlot.slot] = VarLoc{predSlot.Registers, predSlot.StackOffset} 597 state.liveCount[predSlot.slot] = 1 598 } 599 for _, pred := range preds { 600 if pred.ID == baseID { 601 continue 602 } 603 if state.loggingEnabled { 604 state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID])) 605 } 606 for _, predSlot := range blockLocs[pred.ID].endState { 607 state.liveCount[predSlot.slot]++ 608 liveLoc := slotLocs[predSlot.slot] 609 if !liveLoc.onStack() || !predSlot.onStack() || liveLoc.StackOffset != predSlot.StackOffset { 610 liveLoc.StackOffset = 0 611 } 612 liveLoc.Registers &= predSlot.Registers 613 slotLocs[predSlot.slot] = liveLoc 614 } 615 } 616 617 // Check if the final state is the same as the first predecessor's 618 // final state, and reuse it if so. In principle it could match any, 619 // but it's probably not worth checking more than the first. 620 unchanged := true 621 for _, predSlot := range baseState { 622 if state.liveCount[predSlot.slot] != len(preds) || 623 slotLocs[predSlot.slot].Registers != predSlot.Registers || 624 slotLocs[predSlot.slot].StackOffset != predSlot.StackOffset { 625 unchanged = false 626 break 627 } 628 } 629 if unchanged { 630 if state.loggingEnabled { 631 state.logf("After merge, %v matches b%v exactly.\n", b, baseID) 632 } 633 if previousBlockIsNotPredecessor { 634 // Mark everything in previous block as changed because it is not a predecessor. 635 markChangedVars(blockLocs[previousBlock.ID].endState) 636 } 637 state.currentState.reset(baseState) 638 return baseState, true 639 } 640 641 for reg := range state.currentState.registers { 642 state.currentState.registers[reg] = state.currentState.registers[reg][:0] 643 } 644 645 // A slot is live if it was seen in all predecessors, and they all had 646 // some storage in common. 647 for _, predSlot := range baseState { 648 slotLoc := slotLocs[predSlot.slot] 649 650 if state.liveCount[predSlot.slot] != len(preds) { 651 // Seen in only some predecessors. Clear it out. 652 slotLocs[predSlot.slot] = VarLoc{} 653 continue 654 } 655 656 // Present in all predecessors. 657 mask := uint64(slotLoc.Registers) 658 for { 659 if mask == 0 { 660 break 661 } 662 reg := uint8(bits.TrailingZeros64(mask)) 663 mask &^= 1 << reg 664 state.currentState.registers[reg] = append(state.currentState.registers[reg], predSlot.slot) 665 } 666 } 667 668 if previousBlockIsNotPredecessor { 669 // Mark everything in previous block as changed because it is not a predecessor. 670 markChangedVars(blockLocs[previousBlock.ID].endState) 671 672 } 673 return nil, false 674 } 675 676 // processValue updates locs and state.registerContents to reflect v, a value with 677 // the names in vSlots and homed in vReg. "v" becomes visible after execution of 678 // the instructions evaluating it. It returns which VarIDs were modified by the 679 // Value's execution. 680 func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool { 681 locs := state.currentState 682 changed := false 683 setSlot := func(slot SlotID, loc VarLoc) { 684 changed = true 685 state.changedVars.add(ID(state.slotVars[slot])) 686 state.currentState.slots[slot] = loc 687 } 688 689 // Handle any register clobbering. Call operations, for example, 690 // clobber all registers even though they don't explicitly write to 691 // them. 692 clobbers := uint64(opcodeTable[v.Op].reg.clobbers) 693 for { 694 if clobbers == 0 { 695 break 696 } 697 reg := uint8(bits.TrailingZeros64(clobbers)) 698 clobbers &^= 1 << reg 699 700 for _, slot := range locs.registers[reg] { 701 if state.loggingEnabled { 702 state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg]) 703 } 704 705 last := locs.slots[slot] 706 if last.absent() { 707 state.f.Fatalf("at %v: slot %v in register %v with no location entry", v, state.slots[slot], &state.registers[reg]) 708 continue 709 } 710 regs := last.Registers &^ (1 << reg) 711 setSlot(slot, VarLoc{regs, last.StackOffset}) 712 } 713 714 locs.registers[reg] = locs.registers[reg][:0] 715 } 716 717 switch { 718 case v.Op == OpVarDef, v.Op == OpVarKill: 719 n := v.Aux.(GCNode) 720 if n.IsSynthetic() { 721 break 722 } 723 724 slotID := state.varParts[n][0] 725 var stackOffset StackOffset 726 if v.Op == OpVarDef { 727 stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1) 728 } 729 setSlot(slotID, VarLoc{0, stackOffset}) 730 if state.loggingEnabled { 731 if v.Op == OpVarDef { 732 state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID]) 733 } else { 734 state.logf("at %v: stack-only var %v now dead\n", v, state.slots[slotID]) 735 } 736 } 737 738 case v.Op == OpArg: 739 home := state.f.getHome(v.ID).(LocalSlot) 740 stackOffset := state.stackOffset(home)<<1 | 1 741 for _, slot := range vSlots { 742 if state.loggingEnabled { 743 state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home) 744 if last := locs.slots[slot]; !last.absent() { 745 state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot]) 746 } 747 } 748 749 setSlot(slot, VarLoc{0, StackOffset(stackOffset)}) 750 } 751 752 case v.Op == OpStoreReg: 753 home := state.f.getHome(v.ID).(LocalSlot) 754 stackOffset := state.stackOffset(home)<<1 | 1 755 for _, slot := range vSlots { 756 last := locs.slots[slot] 757 if last.absent() { 758 if state.loggingEnabled { 759 state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg) 760 } 761 break 762 } 763 764 setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)}) 765 if state.loggingEnabled { 766 state.logf("at %v: %v spilled to stack location %v\n", v, state.slots[slot], home) 767 } 768 } 769 770 case vReg != nil: 771 if state.loggingEnabled { 772 newSlots := make([]bool, len(state.slots)) 773 for _, slot := range vSlots { 774 newSlots[slot] = true 775 } 776 777 for _, slot := range locs.registers[vReg.num] { 778 if !newSlots[slot] { 779 state.logf("at %v: overwrote %v in register %v\n", v, state.slots[slot], vReg) 780 } 781 } 782 } 783 784 for _, slot := range locs.registers[vReg.num] { 785 last := locs.slots[slot] 786 setSlot(slot, VarLoc{last.Registers &^ (1 << uint8(vReg.num)), last.StackOffset}) 787 } 788 locs.registers[vReg.num] = locs.registers[vReg.num][:0] 789 locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...) 790 for _, slot := range vSlots { 791 if state.loggingEnabled { 792 state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg) 793 } 794 795 last := locs.slots[slot] 796 setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset}) 797 } 798 } 799 return changed 800 } 801 802 // varOffset returns the offset of slot within the user variable it was 803 // decomposed from. This has nothing to do with its stack offset. 804 func varOffset(slot LocalSlot) int64 { 805 offset := slot.Off 806 s := &slot 807 for ; s.SplitOf != nil; s = s.SplitOf { 808 offset += s.SplitOffset 809 } 810 return offset 811 } 812 813 type partsByVarOffset struct { 814 slotIDs []SlotID 815 slots []LocalSlot 816 } 817 818 func (a partsByVarOffset) Len() int { return len(a.slotIDs) } 819 func (a partsByVarOffset) Less(i, j int) bool { 820 return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[j]]) 821 } 822 func (a partsByVarOffset) Swap(i, j int) { a.slotIDs[i], a.slotIDs[j] = a.slotIDs[j], a.slotIDs[i] } 823 824 // A pendingEntry represents the beginning of a location list entry, missing 825 // only its end coordinate. 826 type pendingEntry struct { 827 present bool 828 startBlock, startValue ID 829 // The location of each piece of the variable, in the same order as the 830 // SlotIDs in varParts. 831 pieces []VarLoc 832 } 833 834 func (e *pendingEntry) clear() { 835 e.present = false 836 e.startBlock = 0 837 e.startValue = 0 838 for i := range e.pieces { 839 e.pieces[i] = VarLoc{} 840 } 841 } 842 843 // canMerge reports whether the location description for new is the same as 844 // pending. 845 func canMerge(pending, new VarLoc) bool { 846 if pending.absent() && new.absent() { 847 return true 848 } 849 if pending.absent() || new.absent() { 850 return false 851 } 852 if pending.onStack() { 853 return pending.StackOffset == new.StackOffset 854 } 855 if pending.Registers != 0 && new.Registers != 0 { 856 return firstReg(pending.Registers) == firstReg(new.Registers) 857 } 858 return false 859 } 860 861 // firstReg returns the first register in set that is present. 862 func firstReg(set RegisterSet) uint8 { 863 if set == 0 { 864 // This is wrong, but there seem to be some situations where we 865 // produce locations with no storage. 866 return 0 867 } 868 return uint8(bits.TrailingZeros64(uint64(set))) 869 } 870 871 // buildLocationLists builds location lists for all the user variables in 872 // state.f, using the information about block state in blockLocs. 873 // The returned location lists are not fully complete. They are in terms of 874 // SSA values rather than PCs, and have no base address/end entries. They will 875 // be finished by PutLocationList. 876 func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) { 877 // Run through the function in program text order, building up location 878 // lists as we go. The heavy lifting has mostly already been done. 879 880 var prevBlock *Block 881 for _, b := range state.f.Blocks { 882 state.mergePredecessors(b, blockLocs, prevBlock) 883 884 if !blockLocs[b.ID].relevant { 885 // Handle any differences among predecessor blocks and previous block (perhaps not a predecessor) 886 for _, varID := range state.changedVars.contents() { 887 state.updateVar(VarID(varID), b, BlockStart) 888 } 889 continue 890 } 891 892 zeroWidthPending := false 893 apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr 894 // expect to see values in pattern (apc)* (zerowidth|real)* 895 for _, v := range b.Values { 896 slots := state.valueNames[v.ID] 897 reg, _ := state.f.getHome(v.ID).(*Register) 898 changed := state.processValue(v, slots, reg) // changed == added to state.changedVars 899 900 if opcodeTable[v.Op].zeroWidth { 901 if changed { 902 if v.Op == OpArg || v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() { 903 // These ranges begin at true beginning of block, not after first instruction 904 if zeroWidthPending { 905 b.Func.Fatalf("Unexpected op mixed with OpArg/OpPhi/OpLoweredGetClosurePtr at beginning of block %s in %s\n%s", b, b.Func.Name, b.Func) 906 } 907 apcChangedSize = len(state.changedVars.contents()) 908 continue 909 } 910 // Other zero-width ops must wait on a "real" op. 911 zeroWidthPending = true 912 } 913 continue 914 } 915 916 if !changed && !zeroWidthPending { 917 continue 918 } 919 // Not zero-width; i.e., a "real" instruction. 920 921 zeroWidthPending = false 922 for i, varID := range state.changedVars.contents() { 923 if i < apcChangedSize { // buffered true start-of-block changes 924 state.updateVar(VarID(varID), v.Block, BlockStart) 925 } else { 926 state.updateVar(VarID(varID), v.Block, v) 927 } 928 } 929 state.changedVars.clear() 930 apcChangedSize = 0 931 } 932 for i, varID := range state.changedVars.contents() { 933 if i < apcChangedSize { // buffered true start-of-block changes 934 state.updateVar(VarID(varID), b, BlockStart) 935 } else { 936 state.updateVar(VarID(varID), b, BlockEnd) 937 } 938 } 939 940 prevBlock = b 941 } 942 943 if state.loggingEnabled { 944 state.logf("location lists:\n") 945 } 946 947 // Flush any leftover entries live at the end of the last block. 948 for varID := range state.lists { 949 state.writePendingEntry(VarID(varID), state.f.Blocks[len(state.f.Blocks)-1].ID, BlockEnd.ID) 950 list := state.lists[varID] 951 if state.loggingEnabled { 952 if len(list) == 0 { 953 state.logf("\t%v : empty list\n", state.vars[varID]) 954 } else { 955 state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(state.lists[varID])) 956 } 957 } 958 } 959 } 960 961 // updateVar updates the pending location list entry for varID to 962 // reflect the new locations in curLoc, beginning at v in block b. 963 // v may be one of the special values indicating block start or end. 964 func (state *debugState) updateVar(varID VarID, b *Block, v *Value) { 965 curLoc := state.currentState.slots 966 // Assemble the location list entry with whatever's live. 967 empty := true 968 for _, slotID := range state.varSlots[varID] { 969 if !curLoc[slotID].absent() { 970 empty = false 971 break 972 } 973 } 974 pending := &state.pendingEntries[varID] 975 if empty { 976 state.writePendingEntry(varID, b.ID, v.ID) 977 pending.clear() 978 return 979 } 980 981 // Extend the previous entry if possible. 982 if pending.present { 983 merge := true 984 for i, slotID := range state.varSlots[varID] { 985 if !canMerge(pending.pieces[i], curLoc[slotID]) { 986 merge = false 987 break 988 } 989 } 990 if merge { 991 return 992 } 993 } 994 995 state.writePendingEntry(varID, b.ID, v.ID) 996 pending.present = true 997 pending.startBlock = b.ID 998 pending.startValue = v.ID 999 for i, slot := range state.varSlots[varID] { 1000 pending.pieces[i] = curLoc[slot] 1001 } 1002 } 1003 1004 // writePendingEntry writes out the pending entry for varID, if any, 1005 // terminated at endBlock/Value. 1006 func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) { 1007 pending := state.pendingEntries[varID] 1008 if !pending.present { 1009 return 1010 } 1011 1012 // Pack the start/end coordinates into the start/end addresses 1013 // of the entry, for decoding by PutLocationList. 1014 start, startOK := encodeValue(state.ctxt, pending.startBlock, pending.startValue) 1015 end, endOK := encodeValue(state.ctxt, endBlock, endValue) 1016 if !startOK || !endOK { 1017 // If someone writes a function that uses >65K values, 1018 // they get incomplete debug info on 32-bit platforms. 1019 return 1020 } 1021 if start == end { 1022 if state.loggingEnabled { 1023 // Printf not logf so not gated by GOSSAFUNC; this should fire very rarely. 1024 fmt.Printf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name) 1025 } 1026 return 1027 } 1028 1029 list := state.lists[varID] 1030 list = appendPtr(state.ctxt, list, start) 1031 list = appendPtr(state.ctxt, list, end) 1032 // Where to write the length of the location description once 1033 // we know how big it is. 1034 sizeIdx := len(list) 1035 list = list[:len(list)+2] 1036 1037 if state.loggingEnabled { 1038 var partStrs []string 1039 for i, slot := range state.varSlots[varID] { 1040 partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i]))) 1041 } 1042 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, " ")) 1043 } 1044 1045 for i, slotID := range state.varSlots[varID] { 1046 loc := pending.pieces[i] 1047 slot := state.slots[slotID] 1048 1049 if !loc.absent() { 1050 if loc.onStack() { 1051 if loc.stackOffsetValue() == 0 { 1052 list = append(list, dwarf.DW_OP_call_frame_cfa) 1053 } else { 1054 list = append(list, dwarf.DW_OP_fbreg) 1055 list = dwarf.AppendSleb128(list, int64(loc.stackOffsetValue())) 1056 } 1057 } else { 1058 regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()] 1059 if regnum < 32 { 1060 list = append(list, dwarf.DW_OP_reg0+byte(regnum)) 1061 } else { 1062 list = append(list, dwarf.DW_OP_regx) 1063 list = dwarf.AppendUleb128(list, uint64(regnum)) 1064 } 1065 } 1066 } 1067 1068 if len(state.varSlots[varID]) > 1 { 1069 list = append(list, dwarf.DW_OP_piece) 1070 list = dwarf.AppendUleb128(list, uint64(slot.Type.Size())) 1071 } 1072 } 1073 state.ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) 1074 state.lists[varID] = list 1075 } 1076 1077 // PutLocationList adds list (a location list in its intermediate representation) to listSym. 1078 func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) { 1079 getPC := debugInfo.GetPC 1080 1081 if ctxt.UseBASEntries { 1082 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0) 1083 listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0) 1084 } 1085 1086 // Re-read list, translating its address from block/value ID to PC. 1087 for i := 0; i < len(list); { 1088 begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:]))) 1089 end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:]))) 1090 1091 // Horrible hack. If a range contains only zero-width 1092 // instructions, e.g. an Arg, and it's at the beginning of the 1093 // function, this would be indistinguishable from an 1094 // end entry. Fudge it. 1095 if begin == 0 && end == 0 { 1096 end = 1 1097 } 1098 1099 if ctxt.UseBASEntries { 1100 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(begin)) 1101 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(end)) 1102 } else { 1103 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(begin)) 1104 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(end)) 1105 } 1106 1107 i += 2 * ctxt.Arch.PtrSize 1108 datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:])) 1109 listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) // copy datalen and location encoding 1110 i += datalen 1111 } 1112 1113 // Location list contents, now with real PCs. 1114 // End entry. 1115 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) 1116 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) 1117 } 1118 1119 // Pack a value and block ID into an address-sized uint, returning ~0 if they 1120 // don't fit. 1121 func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) { 1122 if ctxt.Arch.PtrSize == 8 { 1123 result := uint64(b)<<32 | uint64(uint32(v)) 1124 //ctxt.Logf("b %#x (%d) v %#x (%d) -> %#x\n", b, b, v, v, result) 1125 return result, true 1126 } 1127 if ctxt.Arch.PtrSize != 4 { 1128 panic("unexpected pointer size") 1129 } 1130 if ID(int16(b)) != b || ID(int16(v)) != v { 1131 return 0, false 1132 } 1133 return uint64(b)<<16 | uint64(uint16(v)), true 1134 } 1135 1136 // Unpack a value and block ID encoded by encodeValue. 1137 func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) { 1138 if ctxt.Arch.PtrSize == 8 { 1139 b, v := ID(word>>32), ID(word) 1140 //ctxt.Logf("%#x -> b %#x (%d) v %#x (%d)\n", word, b, b, v, v) 1141 return b, v 1142 } 1143 if ctxt.Arch.PtrSize != 4 { 1144 panic("unexpected pointer size") 1145 } 1146 return ID(word >> 16), ID(int16(word)) 1147 } 1148 1149 // Append a pointer-sized uint to buf. 1150 func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte { 1151 if cap(buf) < len(buf)+20 { 1152 b := make([]byte, len(buf), 20+cap(buf)*2) 1153 copy(b, buf) 1154 buf = b 1155 } 1156 writeAt := len(buf) 1157 buf = buf[0 : len(buf)+ctxt.Arch.PtrSize] 1158 writePtr(ctxt, buf[writeAt:], word) 1159 return buf 1160 } 1161 1162 // Write a pointer-sized uint to the beginning of buf. 1163 func writePtr(ctxt *obj.Link, buf []byte, word uint64) { 1164 switch ctxt.Arch.PtrSize { 1165 case 4: 1166 ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word)) 1167 case 8: 1168 ctxt.Arch.ByteOrder.PutUint64(buf, word) 1169 default: 1170 panic("unexpected pointer size") 1171 } 1172 1173 } 1174 1175 // Read a pointer-sized uint from the beginning of buf. 1176 func readPtr(ctxt *obj.Link, buf []byte) uint64 { 1177 switch ctxt.Arch.PtrSize { 1178 case 4: 1179 return uint64(ctxt.Arch.ByteOrder.Uint32(buf)) 1180 case 8: 1181 return ctxt.Arch.ByteOrder.Uint64(buf) 1182 default: 1183 panic("unexpected pointer size") 1184 } 1185 1186 }