github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/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  }