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