github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/compile/internal/ssa/debug.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 package ssa 5 6 import ( 7 "cmd/internal/obj" 8 "fmt" 9 "strings" 10 ) 11 12 type SlotID int32 13 14 // A FuncDebug contains all the debug information for the variables in a 15 // function. Variables are identified by their LocalSlot, which may be the 16 // result of decomposing a larger variable. 17 type FuncDebug struct { 18 // Slots is all the slots used in the debug info, indexed by their SlotID. 19 // Use this when getting a LocalSlot from a SlotID. 20 Slots []*LocalSlot 21 // VarSlots is the slots that represent part of user variables. 22 // Use this when iterating over all the slots to generate debug information. 23 VarSlots []*LocalSlot 24 // The blocks in the function, in program text order. 25 Blocks []*BlockDebug 26 // The registers of the current architecture, indexed by Register.num. 27 Registers []Register 28 } 29 30 func (f *FuncDebug) BlockString(b *BlockDebug) string { 31 var vars []string 32 33 for slot := range f.VarSlots { 34 if len(b.Variables[slot].Locations) == 0 { 35 continue 36 } 37 vars = append(vars, fmt.Sprintf("%v = %v", f.Slots[slot], b.Variables[slot])) 38 } 39 return fmt.Sprintf("{%v}", strings.Join(vars, ", ")) 40 } 41 42 func (f *FuncDebug) SlotLocsString(id SlotID) string { 43 var locs []string 44 for _, block := range f.Blocks { 45 for _, loc := range block.Variables[id].Locations { 46 locs = append(locs, block.LocString(loc)) 47 } 48 } 49 return strings.Join(locs, " ") 50 } 51 52 type BlockDebug struct { 53 // The SSA block that this tracks. For debug logging only. 54 Block *Block 55 // The variables in this block, indexed by their SlotID. 56 Variables []VarLocList 57 } 58 59 func (b *BlockDebug) LocString(loc *VarLoc) string { 60 registers := b.Block.Func.Config.registers 61 62 var storage []string 63 if loc.OnStack { 64 storage = append(storage, "stack") 65 } 66 67 for reg := 0; reg < 64; reg++ { 68 if loc.Registers&(1<<uint8(reg)) == 0 { 69 continue 70 } 71 if registers != nil { 72 storage = append(storage, registers[reg].String()) 73 } else { 74 storage = append(storage, fmt.Sprintf("reg%d", reg)) 75 } 76 } 77 if len(storage) == 0 { 78 storage = append(storage, "!!!no storage!!!") 79 } 80 pos := func(v *Value, p *obj.Prog, pc int64) string { 81 if v == nil { 82 return "?" 83 } 84 vStr := fmt.Sprintf("v%d", v.ID) 85 if v == BlockStart { 86 vStr = fmt.Sprintf("b%dStart", b.Block.ID) 87 } 88 if v == BlockEnd { 89 vStr = fmt.Sprintf("b%dEnd", b.Block.ID) 90 } 91 if p == nil { 92 return vStr 93 } 94 return fmt.Sprintf("%s/%x", vStr, pc) 95 } 96 start := pos(loc.Start, loc.StartProg, loc.StartPC) 97 end := pos(loc.End, loc.EndProg, loc.EndPC) 98 return fmt.Sprintf("%v-%v@%s", start, end, strings.Join(storage, ",")) 99 100 } 101 102 // append adds a location to the location list for slot. 103 func (b *BlockDebug) append(slot SlotID, loc *VarLoc) { 104 b.Variables[slot].append(loc) 105 } 106 107 // lastLoc returns the last VarLoc for slot, or nil if it has none. 108 func (b *BlockDebug) lastLoc(slot SlotID) *VarLoc { 109 return b.Variables[slot].last() 110 } 111 112 // A VarLocList contains the locations for a variable, in program text order. 113 // It will often have gaps. 114 type VarLocList struct { 115 Locations []*VarLoc 116 } 117 118 func (l *VarLocList) append(loc *VarLoc) { 119 l.Locations = append(l.Locations, loc) 120 } 121 122 // last returns the last location in the list. 123 func (l *VarLocList) last() *VarLoc { 124 if l == nil || len(l.Locations) == 0 { 125 return nil 126 } 127 return l.Locations[len(l.Locations)-1] 128 } 129 130 // A VarLoc describes a variable's location in a single contiguous range 131 // of program text. It is generated from the SSA representation, but it 132 // refers to the generated machine code, so the Values referenced are better 133 // understood as PCs than actual Values, and the ranges can cross blocks. 134 // The range is defined first by Values, which are then mapped to Progs 135 // during genssa and finally to function PCs after assembly. 136 // A variable can be on the stack and in any number of registers. 137 type VarLoc struct { 138 // Inclusive -- the first SSA value that the range covers. The value 139 // doesn't necessarily have anything to do with the variable; it just 140 // identifies a point in the program text. 141 // The special sentinel value BlockStart indicates that the range begins 142 // at the beginning of the containing block, even if the block doesn't 143 // actually have a Value to use to indicate that. 144 Start *Value 145 // Exclusive -- the first SSA value after start that the range doesn't 146 // cover. A location with start == end is empty. 147 // The special sentinel value BlockEnd indicates that the variable survives 148 // to the end of the of the containing block, after all its Values and any 149 // control flow instructions added later. 150 End *Value 151 152 // The prog/PCs corresponding to Start and End above. These are for the 153 // convenience of later passes, since code generation isn't done when 154 // BuildFuncDebug runs. 155 // Control flow instructions don't correspond to a Value, so EndProg 156 // may point to a Prog in the next block if SurvivedBlock is true. For 157 // the last block, where there's no later Prog, it will be nil to indicate 158 // the end of the function. 159 StartProg, EndProg *obj.Prog 160 StartPC, EndPC int64 161 162 // The registers this variable is available in. There can be more than 163 // one in various situations, e.g. it's being moved between registers. 164 Registers RegisterSet 165 // OnStack indicates that the variable is on the stack in the LocalSlot 166 // identified by StackLocation. 167 OnStack bool 168 StackLocation SlotID 169 } 170 171 var BlockStart = &Value{ 172 ID: -10000, 173 Op: OpInvalid, 174 Aux: "BlockStart", 175 } 176 177 var BlockEnd = &Value{ 178 ID: -20000, 179 Op: OpInvalid, 180 Aux: "BlockEnd", 181 } 182 183 // RegisterSet is a bitmap of registers, indexed by Register.num. 184 type RegisterSet uint64 185 186 // unexpected is used to indicate an inconsistency or bug in the debug info 187 // generation process. These are not fixable by users. At time of writing, 188 // changing this to a Fprintf(os.Stderr) and running make.bash generates 189 // thousands of warnings. 190 func (s *debugState) unexpected(v *Value, msg string, args ...interface{}) { 191 s.f.Logf("unexpected at "+fmt.Sprint(v.ID)+":"+msg, args...) 192 } 193 194 func (s *debugState) logf(msg string, args ...interface{}) { 195 s.f.Logf(msg, args...) 196 } 197 198 type debugState struct { 199 loggingEnabled bool 200 slots []*LocalSlot 201 varSlots []*LocalSlot 202 f *Func 203 cache *Cache 204 numRegisters int 205 206 // working storage for BuildFuncDebug, reused between blocks. 207 registerContents [][]SlotID 208 } 209 210 // getHomeSlot returns the SlotID of the home slot for v, adding to s.slots 211 // if necessary. 212 func (s *debugState) getHomeSlot(v *Value) SlotID { 213 home := s.f.getHome(v.ID).(LocalSlot) 214 for id, slot := range s.slots { 215 if *slot == home { 216 return SlotID(id) 217 } 218 } 219 // This slot wasn't in the NamedValue table so it needs to be added. 220 s.slots = append(s.slots, &home) 221 return SlotID(len(s.slots) - 1) 222 } 223 224 func (s *debugState) BlockString(b *BlockDebug) string { 225 f := &FuncDebug{ 226 Slots: s.slots, 227 VarSlots: s.varSlots, 228 Registers: s.f.Config.registers, 229 } 230 return f.BlockString(b) 231 } 232 233 // BuildFuncDebug returns debug information for f. 234 // f must be fully processed, so that each Value is where it will be when 235 // machine code is emitted. 236 func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug { 237 if f.RegAlloc == nil { 238 f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f) 239 } 240 state := &debugState{ 241 loggingEnabled: loggingEnabled, 242 slots: make([]*LocalSlot, len(f.Names)), 243 cache: f.Cache, 244 f: f, 245 numRegisters: len(f.Config.registers), 246 registerContents: make([][]SlotID, len(f.Config.registers)), 247 } 248 // TODO: consider storing this in Cache and reusing across functions. 249 valueNames := make([][]SlotID, f.NumValues()) 250 251 for i, slot := range f.Names { 252 slot := slot 253 state.slots[i] = &slot 254 255 if isSynthetic(&slot) { 256 continue 257 } 258 for _, value := range f.NamedValues[slot] { 259 valueNames[value.ID] = append(valueNames[value.ID], SlotID(i)) 260 } 261 } 262 // state.varSlots is never changed, and state.slots is only appended to, 263 // so aliasing is safe. 264 state.varSlots = state.slots 265 266 if state.loggingEnabled { 267 var names []string 268 for i, name := range f.Names { 269 names = append(names, fmt.Sprintf("%d = %s", i, name)) 270 } 271 state.logf("Name table: %v\n", strings.Join(names, ", ")) 272 } 273 274 // Build up block states, starting with the first block, then 275 // processing blocks once their predecessors have been processed. 276 277 // Location list entries for each block. 278 blockLocs := make([]*BlockDebug, f.NumBlocks()) 279 280 // Reverse postorder: visit a block after as many as possible of its 281 // predecessors have been visited. 282 po := f.Postorder() 283 for i := len(po) - 1; i >= 0; i-- { 284 b := po[i] 285 286 // Build the starting state for the block from the final 287 // state of its predecessors. 288 locs := state.mergePredecessors(b, blockLocs) 289 if state.loggingEnabled { 290 state.logf("Processing %v, initial locs %v, regs %v\n", b, state.BlockString(locs), state.registerContents) 291 } 292 // Update locs/registers with the effects of each Value. 293 // The location list generated here needs to be slightly adjusted for use by gdb. 294 // These adjustments are applied in genssa. 295 for _, v := range b.Values { 296 slots := valueNames[v.ID] 297 298 // Loads and stores inherit the names of their sources. 299 var source *Value 300 switch v.Op { 301 case OpStoreReg: 302 source = v.Args[0] 303 case OpLoadReg: 304 switch a := v.Args[0]; a.Op { 305 case OpArg: 306 source = a 307 case OpStoreReg: 308 source = a.Args[0] 309 default: 310 state.unexpected(v, "load with unexpected source op %v", a) 311 } 312 } 313 if source != nil { 314 slots = append(slots, valueNames[source.ID]...) 315 // As of writing, the compiler never uses a load/store as a 316 // source of another load/store, so there's no reason this should 317 // ever be consulted. Update just in case, and so that when 318 // valueNames is cached, we can reuse the memory. 319 valueNames[v.ID] = slots 320 } 321 322 if len(slots) == 0 { 323 continue 324 } 325 326 reg, _ := f.getHome(v.ID).(*Register) 327 state.processValue(locs, v, slots, reg) 328 } 329 330 // The block is done; mark any live locations as ending with the block. 331 for _, locList := range locs.Variables { 332 last := locList.last() 333 if last == nil || last.End != nil { 334 continue 335 } 336 last.End = BlockEnd 337 } 338 if state.loggingEnabled { 339 f.Logf("Block done: locs %v, regs %v\n", state.BlockString(locs), state.registerContents) 340 } 341 blockLocs[b.ID] = locs 342 } 343 344 info := &FuncDebug{ 345 Slots: state.slots, 346 VarSlots: state.varSlots, 347 Registers: f.Config.registers, 348 } 349 // Consumers want the information in textual order, not by block ID. 350 for _, b := range f.Blocks { 351 info.Blocks = append(info.Blocks, blockLocs[b.ID]) 352 } 353 354 if state.loggingEnabled { 355 f.Logf("Final result:\n") 356 for slot := range info.VarSlots { 357 f.Logf("\t%v => %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot))) 358 } 359 } 360 return info 361 } 362 363 // isSynthetic reports whether if slot represents a compiler-inserted variable, 364 // e.g. an autotmp or an anonymous return value that needed a stack slot. 365 func isSynthetic(slot *LocalSlot) bool { 366 c := slot.String()[0] 367 return c == '.' || c == '~' 368 } 369 370 // mergePredecessors takes the end state of each of b's predecessors and 371 // intersects them to form the starting state for b. 372 // The registers slice (the second return value) will be reused for each call to mergePredecessors. 373 func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *BlockDebug { 374 live := make([]VarLocList, len(state.slots)) 375 376 // Filter out back branches. 377 var preds []*Block 378 for _, pred := range b.Preds { 379 if blockLocs[pred.b.ID] != nil { 380 preds = append(preds, pred.b) 381 } 382 } 383 384 if len(preds) > 0 { 385 p := preds[0] 386 for slot, locList := range blockLocs[p.ID].Variables { 387 last := locList.last() 388 if last == nil || last.End != BlockEnd { 389 continue 390 } 391 loc := state.cache.NewVarLoc() 392 loc.Start = BlockStart 393 loc.OnStack = last.OnStack 394 loc.StackLocation = last.StackLocation 395 loc.Registers = last.Registers 396 live[slot].append(loc) 397 } 398 } 399 if state.loggingEnabled && len(b.Preds) > 1 { 400 state.logf("Starting merge with state from %v: %v\n", b.Preds[0].b, state.BlockString(blockLocs[b.Preds[0].b.ID])) 401 } 402 for i := 1; i < len(preds); i++ { 403 p := preds[i] 404 if state.loggingEnabled { 405 state.logf("Merging in state from %v: %v &= %v\n", p, live, state.BlockString(blockLocs[p.ID])) 406 } 407 408 for slot, liveVar := range live { 409 liveLoc := liveVar.last() 410 if liveLoc == nil { 411 continue 412 } 413 414 predLoc := blockLocs[p.ID].Variables[SlotID(slot)].last() 415 // Clear out slots missing/dead in p. 416 if predLoc == nil || predLoc.End != BlockEnd { 417 live[slot].Locations = nil 418 continue 419 } 420 421 // Unify storage locations. 422 if !liveLoc.OnStack || !predLoc.OnStack || liveLoc.StackLocation != predLoc.StackLocation { 423 liveLoc.OnStack = false 424 liveLoc.StackLocation = 0 425 } 426 liveLoc.Registers &= predLoc.Registers 427 } 428 } 429 430 // Create final result. 431 locs := &BlockDebug{Variables: live} 432 if state.loggingEnabled { 433 locs.Block = b 434 } 435 for reg := range state.registerContents { 436 state.registerContents[reg] = state.registerContents[reg][:0] 437 } 438 for slot, locList := range live { 439 loc := locList.last() 440 if loc == nil { 441 continue 442 } 443 for reg := 0; reg < state.numRegisters; reg++ { 444 if loc.Registers&(1<<uint8(reg)) != 0 { 445 state.registerContents[reg] = append(state.registerContents[reg], SlotID(slot)) 446 } 447 } 448 } 449 return locs 450 } 451 452 // processValue updates locs and state.registerContents to reflect v, a value with 453 // the names in vSlots and homed in vReg. "v" becomes visible after execution of 454 // the instructions evaluating it. 455 func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotID, vReg *Register) { 456 switch { 457 case v.Op == OpRegKill: 458 if state.loggingEnabled { 459 existingSlots := make([]bool, len(state.slots)) 460 for _, slot := range state.registerContents[vReg.num] { 461 existingSlots[slot] = true 462 } 463 for _, slot := range vSlots { 464 if existingSlots[slot] { 465 existingSlots[slot] = false 466 } else { 467 state.unexpected(v, "regkill of unassociated name %v\n", state.slots[slot]) 468 } 469 } 470 for slot, live := range existingSlots { 471 if live { 472 state.unexpected(v, "leftover register name: %v\n", state.slots[slot]) 473 } 474 } 475 } 476 state.registerContents[vReg.num] = nil 477 478 for _, slot := range vSlots { 479 last := locs.lastLoc(slot) 480 if last == nil { 481 state.unexpected(v, "regkill of already dead %s, %+v\n", vReg, state.slots[slot]) 482 continue 483 } 484 if state.loggingEnabled { 485 state.logf("at %v: %v regkilled out of %s\n", v.ID, state.slots[slot], vReg) 486 } 487 if last.End != nil { 488 state.unexpected(v, "regkill of dead slot, died at %v\n", last.End) 489 } 490 last.End = v 491 492 regs := last.Registers &^ (1 << uint8(vReg.num)) 493 if !last.OnStack && regs == 0 { 494 continue 495 } 496 loc := state.cache.NewVarLoc() 497 loc.Start = v 498 loc.OnStack = last.OnStack 499 loc.StackLocation = last.StackLocation 500 loc.Registers = regs 501 locs.append(slot, loc) 502 } 503 case v.Op == OpArg: 504 for _, slot := range vSlots { 505 if last := locs.lastLoc(slot); last != nil { 506 state.unexpected(v, "Arg op on already-live slot %v", state.slots[slot]) 507 last.End = v 508 } 509 loc := state.cache.NewVarLoc() 510 loc.Start = v 511 loc.OnStack = true 512 loc.StackLocation = state.getHomeSlot(v) 513 locs.append(slot, loc) 514 if state.loggingEnabled { 515 state.logf("at %v: arg %v now on stack in location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation]) 516 } 517 } 518 519 case v.Op == OpStoreReg: 520 for _, slot := range vSlots { 521 last := locs.lastLoc(slot) 522 if last == nil { 523 state.unexpected(v, "spill of unnamed register %s\n", vReg) 524 break 525 } 526 last.End = v 527 loc := state.cache.NewVarLoc() 528 loc.Start = v 529 loc.OnStack = true 530 loc.StackLocation = state.getHomeSlot(v) 531 loc.Registers = last.Registers 532 locs.append(slot, loc) 533 if state.loggingEnabled { 534 state.logf("at %v: %v spilled to stack location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation]) 535 } 536 } 537 538 case vReg != nil: 539 if state.loggingEnabled { 540 newSlots := make([]bool, len(state.slots)) 541 for _, slot := range vSlots { 542 newSlots[slot] = true 543 } 544 545 for _, slot := range state.registerContents[vReg.num] { 546 if !newSlots[slot] { 547 state.unexpected(v, "%v clobbered\n", state.slots[slot]) 548 } 549 } 550 } 551 552 for _, slot := range vSlots { 553 if state.loggingEnabled { 554 state.logf("at %v: %v now in %s\n", v.ID, state.slots[slot], vReg) 555 } 556 last := locs.lastLoc(slot) 557 if last != nil && last.End == nil { 558 last.End = v 559 } 560 state.registerContents[vReg.num] = append(state.registerContents[vReg.num], slot) 561 loc := state.cache.NewVarLoc() 562 loc.Start = v 563 if last != nil { 564 loc.OnStack = last.OnStack 565 loc.StackLocation = last.StackLocation 566 loc.Registers = last.Registers 567 } 568 loc.Registers |= 1 << uint8(vReg.num) 569 locs.append(slot, loc) 570 } 571 default: 572 state.unexpected(v, "named value with no reg\n") 573 } 574 }