github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/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  	// TODO: use a reverse post-order traversal instead of the work queue.
   278  
   279  	// Location list entries for each block.
   280  	blockLocs := make([]*BlockDebug, f.NumBlocks())
   281  
   282  	// Work queue of blocks to visit. Some of them may already be processed.
   283  	work := []*Block{f.Entry}
   284  
   285  	for len(work) > 0 {
   286  		b := work[0]
   287  		work = work[1:]
   288  		if blockLocs[b.ID] != nil {
   289  			continue // already processed
   290  		}
   291  		if !state.predecessorsDone(b, blockLocs) {
   292  			continue // not ready yet
   293  		}
   294  
   295  		for _, edge := range b.Succs {
   296  			if blockLocs[edge.Block().ID] != nil {
   297  				continue
   298  			}
   299  			work = append(work, edge.Block())
   300  		}
   301  
   302  		// Build the starting state for the block from the final
   303  		// state of its predecessors.
   304  		locs := state.mergePredecessors(b, blockLocs)
   305  		if state.loggingEnabled {
   306  			state.logf("Processing %v, initial locs %v, regs %v\n", b, state.BlockString(locs), state.registerContents)
   307  		}
   308  		// Update locs/registers with the effects of each Value.
   309  		for _, v := range b.Values {
   310  			slots := valueNames[v.ID]
   311  
   312  			// Loads and stores inherit the names of their sources.
   313  			var source *Value
   314  			switch v.Op {
   315  			case OpStoreReg:
   316  				source = v.Args[0]
   317  			case OpLoadReg:
   318  				switch a := v.Args[0]; a.Op {
   319  				case OpArg:
   320  					source = a
   321  				case OpStoreReg:
   322  					source = a.Args[0]
   323  				default:
   324  					state.unexpected(v, "load with unexpected source op %v", a)
   325  				}
   326  			}
   327  			if source != nil {
   328  				slots = append(slots, valueNames[source.ID]...)
   329  				// As of writing, the compiler never uses a load/store as a
   330  				// source of another load/store, so there's no reason this should
   331  				// ever be consulted. Update just in case, and so that when
   332  				// valueNames is cached, we can reuse the memory.
   333  				valueNames[v.ID] = slots
   334  			}
   335  
   336  			if len(slots) == 0 {
   337  				continue
   338  			}
   339  
   340  			reg, _ := f.getHome(v.ID).(*Register)
   341  			state.processValue(locs, v, slots, reg)
   342  
   343  		}
   344  
   345  		// The block is done; mark any live locations as ending with the block.
   346  		for _, locList := range locs.Variables {
   347  			last := locList.last()
   348  			if last == nil || last.End != nil {
   349  				continue
   350  			}
   351  			last.End = BlockEnd
   352  		}
   353  		if state.loggingEnabled {
   354  			f.Logf("Block done: locs %v, regs %v. work = %+v\n", state.BlockString(locs), state.registerContents, work)
   355  		}
   356  		blockLocs[b.ID] = locs
   357  	}
   358  
   359  	info := &FuncDebug{
   360  		Slots:     state.slots,
   361  		VarSlots:  state.varSlots,
   362  		Registers: f.Config.registers,
   363  	}
   364  	// Consumers want the information in textual order, not by block ID.
   365  	for _, b := range f.Blocks {
   366  		info.Blocks = append(info.Blocks, blockLocs[b.ID])
   367  	}
   368  
   369  	if state.loggingEnabled {
   370  		f.Logf("Final result:\n")
   371  		for slot := range info.VarSlots {
   372  			f.Logf("\t%v => %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot)))
   373  		}
   374  	}
   375  	return info
   376  }
   377  
   378  // isSynthetic reports whether if slot represents a compiler-inserted variable,
   379  // e.g. an autotmp or an anonymous return value that needed a stack slot.
   380  func isSynthetic(slot *LocalSlot) bool {
   381  	c := slot.String()[0]
   382  	return c == '.' || c == '~'
   383  }
   384  
   385  // predecessorsDone reports whether block is ready to be processed.
   386  func (state *debugState) predecessorsDone(b *Block, blockLocs []*BlockDebug) bool {
   387  	f := b.Func
   388  	for _, edge := range b.Preds {
   389  		// Ignore back branches, e.g. the continuation of a for loop.
   390  		// This may not work for functions with mutual gotos, which are not
   391  		// reducible, in which case debug information will be missing for any
   392  		// code after that point in the control flow.
   393  		if f.sdom().isAncestorEq(b, edge.b) {
   394  			if state.loggingEnabled {
   395  				f.Logf("ignoring back branch from %v to %v\n", edge.b, b)
   396  			}
   397  			continue // back branch
   398  		}
   399  		if blockLocs[edge.b.ID] == nil {
   400  			if state.loggingEnabled {
   401  				f.Logf("%v is not ready because %v isn't done\n", b, edge.b)
   402  			}
   403  			return false
   404  		}
   405  	}
   406  	return true
   407  }
   408  
   409  // mergePredecessors takes the end state of each of b's predecessors and
   410  // intersects them to form the starting state for b.
   411  // The registers slice (the second return value) will be reused for each call to mergePredecessors.
   412  func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *BlockDebug {
   413  	live := make([]VarLocList, len(state.slots))
   414  
   415  	// Filter out back branches.
   416  	var preds []*Block
   417  	for _, pred := range b.Preds {
   418  		if blockLocs[pred.b.ID] != nil {
   419  			preds = append(preds, pred.b)
   420  		}
   421  	}
   422  
   423  	if len(preds) > 0 {
   424  		p := preds[0]
   425  		for slot, locList := range blockLocs[p.ID].Variables {
   426  			last := locList.last()
   427  			if last == nil || last.End != BlockEnd {
   428  				continue
   429  			}
   430  			loc := state.cache.NewVarLoc()
   431  			loc.Start = BlockStart
   432  			loc.OnStack = last.OnStack
   433  			loc.StackLocation = last.StackLocation
   434  			loc.Registers = last.Registers
   435  			live[slot].append(loc)
   436  		}
   437  	}
   438  	if state.loggingEnabled && len(b.Preds) > 1 {
   439  		state.logf("Starting merge with state from %v: %v\n", b.Preds[0].b, state.BlockString(blockLocs[b.Preds[0].b.ID]))
   440  	}
   441  	for i := 1; i < len(preds); i++ {
   442  		p := preds[i]
   443  		if state.loggingEnabled {
   444  			state.logf("Merging in state from %v: %v &= %v\n", p, live, state.BlockString(blockLocs[p.ID]))
   445  		}
   446  
   447  		for slot, liveVar := range live {
   448  			liveLoc := liveVar.last()
   449  			if liveLoc == nil {
   450  				continue
   451  			}
   452  
   453  			predLoc := blockLocs[p.ID].Variables[SlotID(slot)].last()
   454  			// Clear out slots missing/dead in p.
   455  			if predLoc == nil || predLoc.End != BlockEnd {
   456  				live[slot].Locations = nil
   457  				continue
   458  			}
   459  
   460  			// Unify storage locations.
   461  			if !liveLoc.OnStack || !predLoc.OnStack || liveLoc.StackLocation != predLoc.StackLocation {
   462  				liveLoc.OnStack = false
   463  				liveLoc.StackLocation = 0
   464  			}
   465  			liveLoc.Registers &= predLoc.Registers
   466  		}
   467  	}
   468  
   469  	// Create final result.
   470  	locs := &BlockDebug{Variables: live}
   471  	if state.loggingEnabled {
   472  		locs.Block = b
   473  	}
   474  	for reg := range state.registerContents {
   475  		state.registerContents[reg] = state.registerContents[reg][:0]
   476  	}
   477  	for slot, locList := range live {
   478  		loc := locList.last()
   479  		if loc == nil {
   480  			continue
   481  		}
   482  		for reg := 0; reg < state.numRegisters; reg++ {
   483  			if loc.Registers&(1<<uint8(reg)) != 0 {
   484  				state.registerContents[reg] = append(state.registerContents[reg], SlotID(slot))
   485  			}
   486  		}
   487  	}
   488  	return locs
   489  }
   490  
   491  // processValue updates locs and state.registerContents to reflect v, a value with
   492  // the names in vSlots and homed in vReg.
   493  func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotID, vReg *Register) {
   494  	switch {
   495  	case v.Op == OpRegKill:
   496  		if state.loggingEnabled {
   497  			existingSlots := make([]bool, len(state.slots))
   498  			for _, slot := range state.registerContents[vReg.num] {
   499  				existingSlots[slot] = true
   500  			}
   501  			for _, slot := range vSlots {
   502  				if existingSlots[slot] {
   503  					existingSlots[slot] = false
   504  				} else {
   505  					state.unexpected(v, "regkill of unassociated name %v\n", state.slots[slot])
   506  				}
   507  			}
   508  			for slot, live := range existingSlots {
   509  				if live {
   510  					state.unexpected(v, "leftover register name: %v\n", state.slots[slot])
   511  				}
   512  			}
   513  		}
   514  		state.registerContents[vReg.num] = nil
   515  
   516  		for _, slot := range vSlots {
   517  			last := locs.lastLoc(slot)
   518  			if last == nil {
   519  				state.unexpected(v, "regkill of already dead %s, %+v\n", vReg, state.slots[slot])
   520  				continue
   521  			}
   522  			if state.loggingEnabled {
   523  				state.logf("at %v: %v regkilled out of %s\n", v.ID, state.slots[slot], vReg)
   524  			}
   525  			if last.End != nil {
   526  				state.unexpected(v, "regkill of dead slot, died at %v\n", last.End)
   527  			}
   528  			last.End = v
   529  
   530  			regs := last.Registers &^ (1 << uint8(vReg.num))
   531  			if !last.OnStack && regs == 0 {
   532  				continue
   533  			}
   534  			loc := state.cache.NewVarLoc()
   535  			loc.Start = v
   536  			loc.OnStack = last.OnStack
   537  			loc.StackLocation = last.StackLocation
   538  			loc.Registers = regs
   539  			locs.append(slot, loc)
   540  		}
   541  	case v.Op == OpArg:
   542  		for _, slot := range vSlots {
   543  			if last := locs.lastLoc(slot); last != nil {
   544  				state.unexpected(v, "Arg op on already-live slot %v", state.slots[slot])
   545  				last.End = v
   546  			}
   547  			loc := state.cache.NewVarLoc()
   548  			loc.Start = v
   549  			loc.OnStack = true
   550  			loc.StackLocation = state.getHomeSlot(v)
   551  			locs.append(slot, loc)
   552  			if state.loggingEnabled {
   553  				state.logf("at %v: arg %v now on stack in location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation])
   554  			}
   555  		}
   556  
   557  	case v.Op == OpStoreReg:
   558  		for _, slot := range vSlots {
   559  			last := locs.lastLoc(slot)
   560  			if last == nil {
   561  				state.unexpected(v, "spill of unnamed register %s\n", vReg)
   562  				break
   563  			}
   564  			last.End = v
   565  			loc := state.cache.NewVarLoc()
   566  			loc.Start = v
   567  			loc.OnStack = true
   568  			loc.StackLocation = state.getHomeSlot(v)
   569  			loc.Registers = last.Registers
   570  			locs.append(slot, loc)
   571  			if state.loggingEnabled {
   572  				state.logf("at %v: %v spilled to stack location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation])
   573  			}
   574  
   575  		}
   576  
   577  	case vReg != nil:
   578  		if state.loggingEnabled {
   579  			newSlots := make([]bool, len(state.slots))
   580  			for _, slot := range vSlots {
   581  				newSlots[slot] = true
   582  			}
   583  
   584  			for _, slot := range state.registerContents[vReg.num] {
   585  				if !newSlots[slot] {
   586  					state.unexpected(v, "%v clobbered\n", state.slots[slot])
   587  				}
   588  			}
   589  		}
   590  
   591  		for _, slot := range vSlots {
   592  			if state.loggingEnabled {
   593  				state.logf("at %v: %v now in %s\n", v.ID, state.slots[slot], vReg)
   594  			}
   595  			last := locs.lastLoc(slot)
   596  			if last != nil && last.End == nil {
   597  				last.End = v
   598  			}
   599  			state.registerContents[vReg.num] = append(state.registerContents[vReg.num], slot)
   600  			loc := state.cache.NewVarLoc()
   601  			loc.Start = v
   602  			if last != nil {
   603  				loc.OnStack = last.OnStack
   604  				loc.StackLocation = last.StackLocation
   605  				loc.Registers = last.Registers
   606  			}
   607  			loc.Registers |= 1 << uint8(vReg.num)
   608  			locs.append(slot, loc)
   609  		}
   610  	default:
   611  		state.unexpected(v, "named value with no reg\n")
   612  	}
   613  }