Source file src/cmd/compile/internal/ssa/debug.go

     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  
     5  package ssa
     6  
     7  import (
     8  	"cmd/compile/internal/abi"
     9  	"cmd/compile/internal/abt"
    10  	"cmd/compile/internal/ir"
    11  	"cmd/compile/internal/types"
    12  	"cmd/internal/dwarf"
    13  	"cmd/internal/obj"
    14  	"cmd/internal/src"
    15  	"cmp"
    16  	"fmt"
    17  	"internal/buildcfg"
    18  	"math/bits"
    19  	"slices"
    20  	"strings"
    21  )
    22  
    23  type SlotID int32
    24  type VarID int32
    25  
    26  // A FuncDebug contains all the debug information for the variables in a
    27  // function. Variables are identified by their LocalSlot, which may be
    28  // the result of decomposing a larger variable.
    29  type FuncDebug struct {
    30  	// Slots is all the slots used in the debug info, indexed by their SlotID.
    31  	Slots []LocalSlot
    32  	// The user variables, indexed by VarID.
    33  	Vars []*ir.Name
    34  	// The slots that make up each variable, indexed by VarID.
    35  	VarSlots [][]SlotID
    36  	// The location list data, indexed by VarID. Must be processed by PutLocationList.
    37  	LocationLists [][]LocListEntry
    38  	// Register-resident output parameters for the function. This is filled in at
    39  	// SSA generation time.
    40  	RegOutputParams []*ir.Name
    41  	// Variable declarations that were removed during optimization
    42  	OptDcl []*ir.Name
    43  	// The ssa.Func.EntryID value, used to build location lists for
    44  	// return values promoted to heap in later DWARF generation.
    45  	EntryID ID
    46  
    47  	// Filled in by the user. Translates Block and Value ID to PC.
    48  	//
    49  	// NOTE: block is only used if value is BlockStart.ID or BlockEnd.ID.
    50  	// Otherwise, it is ignored.
    51  	GetPC func(block, value ID) int64
    52  }
    53  
    54  // LocListEntry represents a single entry in a location list.
    55  // StartBlock/StartValue and EndBlock/EndValue are SSA coordinates
    56  // that get resolved to PCs during final encoding.
    57  type LocListEntry struct {
    58  	StartBlock, StartValue ID
    59  	EndBlock, EndValue     ID
    60  	Expr                   []byte // DWARF location expression (DW_OP_*)
    61  }
    62  
    63  type BlockDebug struct {
    64  	// State at the start and end of the block. These are initialized,
    65  	// and updated from new information that flows on back edges.
    66  	startState, endState abt.T
    67  	// Use these to avoid excess work in the merge. If none of the
    68  	// predecessors has changed since the last check, the old answer is
    69  	// still good.
    70  	lastCheckedTime, lastChangedTime int32
    71  	// Whether the block had any changes to user variables at all.
    72  	relevant bool
    73  	// false until the block has been processed at least once. This
    74  	// affects how the merge is done; the goal is to maximize sharing
    75  	// and avoid allocation.
    76  	everProcessed bool
    77  }
    78  
    79  // A liveSlot is a slot that's live in loc at entry/exit of a block.
    80  type liveSlot struct {
    81  	VarLoc
    82  }
    83  
    84  func (ls *liveSlot) String() string {
    85  	return fmt.Sprintf("0x%x.%d.%d", ls.Registers, ls.stackOffsetValue(), int32(ls.StackOffset)&1)
    86  }
    87  
    88  // StackOffset encodes whether a value is on the stack and if so, where.
    89  // It is a 31-bit integer followed by a presence flag at the low-order
    90  // bit.
    91  type StackOffset int32
    92  
    93  func (s StackOffset) onStack() bool {
    94  	return s != 0
    95  }
    96  
    97  func (s StackOffset) stackOffsetValue() int32 {
    98  	return int32(s) >> 1
    99  }
   100  
   101  // stateAtPC is the current state of all variables at some point.
   102  type stateAtPC struct {
   103  	// The location of each known slot, indexed by SlotID.
   104  	slots []VarLoc
   105  	// The slots present in each register, indexed by register number.
   106  	registers [][]SlotID
   107  }
   108  
   109  // reset fills state with the live variables from live.
   110  func (state *stateAtPC) reset(live abt.T) {
   111  	slots, registers := state.slots, state.registers
   112  	clear(slots)
   113  	for i := range registers {
   114  		registers[i] = registers[i][:0]
   115  	}
   116  	for it := live.Iterator(); !it.Done(); {
   117  		k, d := it.Next()
   118  		live := d.(*liveSlot)
   119  		slots[k] = live.VarLoc
   120  		if live.VarLoc.Registers == 0 {
   121  			continue
   122  		}
   123  
   124  		mask := uint64(live.VarLoc.Registers)
   125  		for {
   126  			if mask == 0 {
   127  				break
   128  			}
   129  			reg := uint8(bits.TrailingZeros64(mask))
   130  			mask &^= 1 << reg
   131  
   132  			registers[reg] = append(registers[reg], SlotID(k))
   133  		}
   134  	}
   135  	state.slots, state.registers = slots, registers
   136  }
   137  
   138  func (s *debugState) LocString(loc VarLoc) string {
   139  	if loc.absent() {
   140  		return "<nil>"
   141  	}
   142  
   143  	var storage []string
   144  	if loc.onStack() {
   145  		storage = append(storage, fmt.Sprintf("@%+d", loc.stackOffsetValue()))
   146  	}
   147  
   148  	mask := uint64(loc.Registers)
   149  	for {
   150  		if mask == 0 {
   151  			break
   152  		}
   153  		reg := uint8(bits.TrailingZeros64(mask))
   154  		mask &^= 1 << reg
   155  
   156  		storage = append(storage, s.registers[reg].String())
   157  	}
   158  	return strings.Join(storage, ",")
   159  }
   160  
   161  // A VarLoc describes the storage for part of a user variable.
   162  type VarLoc struct {
   163  	// The registers this variable is available in. There can be more than
   164  	// one in various situations, e.g. it's being moved between registers.
   165  	Registers RegisterSet
   166  
   167  	StackOffset
   168  }
   169  
   170  func (loc VarLoc) absent() bool {
   171  	return loc.Registers == 0 && !loc.onStack()
   172  }
   173  
   174  func (loc VarLoc) intersect(other VarLoc) VarLoc {
   175  	if !loc.onStack() || !other.onStack() || loc.StackOffset != other.StackOffset {
   176  		loc.StackOffset = 0
   177  	}
   178  	loc.Registers &= other.Registers
   179  	return loc
   180  }
   181  
   182  var BlockStart = &Value{
   183  	ID:  -10000,
   184  	Op:  OpInvalid,
   185  	Aux: StringToAux("BlockStart"),
   186  }
   187  
   188  var BlockEnd = &Value{
   189  	ID:  -20000,
   190  	Op:  OpInvalid,
   191  	Aux: StringToAux("BlockEnd"),
   192  }
   193  
   194  var FuncEnd = &Value{
   195  	ID:  -30000,
   196  	Op:  OpInvalid,
   197  	Aux: StringToAux("FuncEnd"),
   198  }
   199  
   200  // RegisterSet is a bitmap of registers, indexed by Register.num.
   201  type RegisterSet uint64
   202  
   203  // logf prints debug-specific logging to stdout (always stdout) if the
   204  // current function is tagged by GOSSAFUNC (for ssa output directed
   205  // either to stdout or html).
   206  func (s *debugState) logf(msg string, args ...any) {
   207  	if s.f.PrintOrHtmlSSA {
   208  		fmt.Printf(msg, args...)
   209  	}
   210  }
   211  
   212  type debugState struct {
   213  	// See FuncDebug.
   214  	slots    []LocalSlot
   215  	vars     []*ir.Name
   216  	varSlots [][]SlotID
   217  	lists    [][]LocListEntry
   218  
   219  	// The user variable that each slot rolls up to, indexed by SlotID.
   220  	slotVars []VarID
   221  
   222  	f             *Func
   223  	loggingLevel  int
   224  	convergeCount int // testing; iterate over block debug state this many times
   225  	registers     []Register
   226  	stackOffset   func(LocalSlot) int32
   227  	ctxt          *obj.Link
   228  
   229  	// The names (slots) associated with each value, indexed by Value ID.
   230  	valueNames [][]SlotID
   231  
   232  	// The current state of whatever analysis is running.
   233  	currentState stateAtPC
   234  	changedVars  *sparseSet
   235  	changedSlots *sparseSet
   236  
   237  	// The pending location list entry for each user variable, indexed by VarID.
   238  	pendingEntries []pendingEntry
   239  
   240  	varParts        map[*ir.Name][]SlotID
   241  	blockDebug      []BlockDebug
   242  	pendingSlotLocs []VarLoc
   243  }
   244  
   245  func (state *debugState) initializeCache(f *Func, numVars, numSlots int) {
   246  	// One blockDebug per block. Initialized in allocBlock.
   247  	if cap(state.blockDebug) < f.NumBlocks() {
   248  		state.blockDebug = make([]BlockDebug, f.NumBlocks())
   249  	} else {
   250  		clear(state.blockDebug[:f.NumBlocks()])
   251  	}
   252  
   253  	// A list of slots per Value. Reuse the previous child slices.
   254  	if cap(state.valueNames) < f.NumValues() {
   255  		old := state.valueNames
   256  		state.valueNames = make([][]SlotID, f.NumValues())
   257  		copy(state.valueNames, old)
   258  	}
   259  	vn := state.valueNames[:f.NumValues()]
   260  	for i := range vn {
   261  		vn[i] = vn[i][:0]
   262  	}
   263  
   264  	// Slot and register contents for currentState. Cleared by reset().
   265  	if cap(state.currentState.slots) < numSlots {
   266  		state.currentState.slots = make([]VarLoc, numSlots)
   267  	} else {
   268  		state.currentState.slots = state.currentState.slots[:numSlots]
   269  	}
   270  	if cap(state.currentState.registers) < len(state.registers) {
   271  		state.currentState.registers = make([][]SlotID, len(state.registers))
   272  	} else {
   273  		state.currentState.registers = state.currentState.registers[:len(state.registers)]
   274  	}
   275  
   276  	// A relatively small slice, but used many times as the return from processValue.
   277  	state.changedVars = newSparseSet(numVars)
   278  	state.changedSlots = newSparseSet(numSlots)
   279  
   280  	// A pending entry per user variable, with space to track each of its pieces.
   281  	numPieces := 0
   282  	for i := range state.varSlots {
   283  		numPieces += len(state.varSlots[i])
   284  	}
   285  	if cap(state.pendingSlotLocs) < numPieces {
   286  		state.pendingSlotLocs = make([]VarLoc, numPieces)
   287  	} else {
   288  		clear(state.pendingSlotLocs[:numPieces])
   289  	}
   290  	if cap(state.pendingEntries) < numVars {
   291  		state.pendingEntries = make([]pendingEntry, numVars)
   292  	}
   293  	pe := state.pendingEntries[:numVars]
   294  	freePieceIdx := 0
   295  	for varID, slots := range state.varSlots {
   296  		pe[varID] = pendingEntry{
   297  			pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)],
   298  		}
   299  		freePieceIdx += len(slots)
   300  	}
   301  	state.pendingEntries = pe
   302  
   303  	if cap(state.lists) < numVars {
   304  		state.lists = make([][]LocListEntry, numVars)
   305  	} else {
   306  		state.lists = state.lists[:numVars]
   307  		clear(state.lists)
   308  	}
   309  }
   310  
   311  func (state *debugState) allocBlock(b *Block) *BlockDebug {
   312  	return &state.blockDebug[b.ID]
   313  }
   314  
   315  func (s *debugState) blockEndStateString(b *BlockDebug) string {
   316  	endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.registers))}
   317  	endState.reset(b.endState)
   318  	return s.stateString(endState)
   319  }
   320  
   321  func (s *debugState) stateString(state stateAtPC) string {
   322  	var strs []string
   323  	for slotID, loc := range state.slots {
   324  		if !loc.absent() {
   325  			strs = append(strs, fmt.Sprintf("\t%v = %v\n", s.slots[slotID], s.LocString(loc)))
   326  		}
   327  	}
   328  
   329  	strs = append(strs, "\n")
   330  	for reg, slots := range state.registers {
   331  		if len(slots) != 0 {
   332  			var slotStrs []string
   333  			for _, slot := range slots {
   334  				slotStrs = append(slotStrs, s.slots[slot].String())
   335  			}
   336  			strs = append(strs, fmt.Sprintf("\t%v = %v\n", &s.registers[reg], slotStrs))
   337  		}
   338  	}
   339  
   340  	if len(strs) == 1 {
   341  		return "(no vars)\n"
   342  	}
   343  	return strings.Join(strs, "")
   344  }
   345  
   346  // slotCanonicalizer is a table used to lookup and canonicalize
   347  // LocalSlot's in a type insensitive way (e.g. taking into account the
   348  // base name, offset, and width of the slot, but ignoring the slot
   349  // type).
   350  type slotCanonicalizer struct {
   351  	slmap  map[slotKey]SlKeyIdx
   352  	slkeys []LocalSlot
   353  }
   354  
   355  func newSlotCanonicalizer() *slotCanonicalizer {
   356  	return &slotCanonicalizer{
   357  		slmap:  make(map[slotKey]SlKeyIdx),
   358  		slkeys: []LocalSlot{LocalSlot{N: nil}},
   359  	}
   360  }
   361  
   362  type SlKeyIdx uint32
   363  
   364  const noSlot = SlKeyIdx(0)
   365  
   366  // slotKey is a type-insensitive encapsulation of a LocalSlot; it
   367  // is used to key a map within slotCanonicalizer.
   368  type slotKey struct {
   369  	name        *ir.Name
   370  	offset      int64
   371  	width       int64
   372  	splitOf     SlKeyIdx // idx in slkeys slice in slotCanonicalizer
   373  	splitOffset int64
   374  }
   375  
   376  // lookup looks up a LocalSlot in the slot canonicalizer "sc", returning
   377  // a canonical index for the slot, and adding it to the table if need
   378  // be. Return value is the canonical slot index, and a boolean indicating
   379  // whether the slot was found in the table already (TRUE => found).
   380  func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) {
   381  	split := noSlot
   382  	if ls.SplitOf != nil {
   383  		split, _ = sc.lookup(*ls.SplitOf)
   384  	}
   385  	k := slotKey{
   386  		name: ls.N, offset: ls.Off, width: ls.Type.Size(),
   387  		splitOf: split, splitOffset: ls.SplitOffset,
   388  	}
   389  	if idx, ok := sc.slmap[k]; ok {
   390  		return idx, true
   391  	}
   392  	rv := SlKeyIdx(len(sc.slkeys))
   393  	sc.slkeys = append(sc.slkeys, ls)
   394  	sc.slmap[k] = rv
   395  	return rv, false
   396  }
   397  
   398  func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot {
   399  	return sc.slkeys[idx]
   400  }
   401  
   402  // PopulateABIInRegArgOps examines the entry block of the function
   403  // and looks for incoming parameters that have missing or partial
   404  // OpArg{Int,Float}Reg values, inserting additional values in
   405  // cases where they are missing. Example:
   406  //
   407  //	func foo(s string, used int, notused int) int {
   408  //	  return len(s) + used
   409  //	}
   410  //
   411  // In the function above, the incoming parameter "used" is fully live,
   412  // "notused" is not live, and "s" is partially live (only the length
   413  // field of the string is used). At the point where debug value
   414  // analysis runs, we might expect to see an entry block with:
   415  //
   416  //	b1:
   417  //	  v4 = ArgIntReg <uintptr> {s+8} [0] : BX
   418  //	  v5 = ArgIntReg <int> {used} [0] : CX
   419  //
   420  // While this is an accurate picture of the live incoming params,
   421  // we also want to have debug locations for non-live params (or
   422  // their non-live pieces), e.g. something like
   423  //
   424  //	b1:
   425  //	  v9 = ArgIntReg <*uint8> {s+0} [0] : AX
   426  //	  v4 = ArgIntReg <uintptr> {s+8} [0] : BX
   427  //	  v5 = ArgIntReg <int> {used} [0] : CX
   428  //	  v10 = ArgIntReg <int> {unused} [0] : DI
   429  //
   430  // This function examines the live OpArg{Int,Float}Reg values and
   431  // synthesizes new (dead) values for the non-live params or the
   432  // non-live pieces of partially live params.
   433  func PopulateABIInRegArgOps(f *Func) {
   434  	pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
   435  
   436  	// When manufacturing new slots that correspond to splits of
   437  	// composite parameters, we want to avoid creating a new sub-slot
   438  	// that differs from some existing sub-slot only by type, since
   439  	// the debug location analysis will treat that slot as a separate
   440  	// entity. To achieve this, create a lookup table of existing
   441  	// slots that is type-insenstitive.
   442  	sc := newSlotCanonicalizer()
   443  	for _, sl := range f.Names {
   444  		sc.lookup(*sl)
   445  	}
   446  
   447  	// Add slot -> value entry to f.NamedValues if not already present.
   448  	addToNV := func(v *Value, sl LocalSlot) {
   449  		values, ok := f.NamedValues[sl]
   450  		if !ok {
   451  			// Haven't seen this slot yet.
   452  			sla := f.localSlotAddr(sl)
   453  			f.Names = append(f.Names, sla)
   454  		} else {
   455  			for _, ev := range values {
   456  				if v == ev {
   457  					return
   458  				}
   459  			}
   460  		}
   461  		values = append(values, v)
   462  		f.NamedValues[sl] = values
   463  	}
   464  
   465  	newValues := []*Value{}
   466  
   467  	abiRegIndexToRegister := func(reg abi.RegIndex) int8 {
   468  		i := f.ABISelf.FloatIndexFor(reg)
   469  		if i >= 0 { // float PR
   470  			return f.Config.floatParamRegs[i]
   471  		} else {
   472  			return f.Config.intParamRegs[reg]
   473  		}
   474  	}
   475  
   476  	// Helper to construct a new OpArg{Float,Int}Reg op value.
   477  	var pos src.XPos
   478  	if len(f.Entry.Values) != 0 {
   479  		pos = f.Entry.Values[0].Pos
   480  	}
   481  	synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl LocalSlot) *Value {
   482  		aux := &AuxNameOffset{n, sl.Off}
   483  		op, auxInt := ArgOpAndRegisterFor(reg, f.ABISelf)
   484  		v := f.newValueNoBlock(op, t, pos)
   485  		v.AuxInt = auxInt
   486  		v.Aux = aux
   487  		v.Args = nil
   488  		v.Block = f.Entry
   489  		newValues = append(newValues, v)
   490  		addToNV(v, sl)
   491  		f.setHome(v, &f.Config.registers[abiRegIndexToRegister(reg)])
   492  		return v
   493  	}
   494  
   495  	// Make a pass through the entry block looking for
   496  	// OpArg{Int,Float}Reg ops. Record the slots they use in a table
   497  	// ("sc"). We use a type-insensitive lookup for the slot table,
   498  	// since the type we get from the ABI analyzer won't always match
   499  	// what the compiler uses when creating OpArg{Int,Float}Reg ops.
   500  	for _, v := range f.Entry.Values {
   501  		if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
   502  			aux := v.Aux.(*AuxNameOffset)
   503  			sl := LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset}
   504  			// install slot in lookup table
   505  			idx, _ := sc.lookup(sl)
   506  			// add to f.NamedValues if not already present
   507  			addToNV(v, sc.canonSlot(idx))
   508  		} else if v.Op.IsCall() {
   509  			// if we hit a call, we've gone too far.
   510  			break
   511  		}
   512  	}
   513  
   514  	// Now make a pass through the ABI in-params, looking for params
   515  	// or pieces of params that we didn't encounter in the loop above.
   516  	for _, inp := range pri.InParams() {
   517  		if !isNamedRegParam(inp) {
   518  			continue
   519  		}
   520  		n := inp.Name
   521  
   522  		// Param is spread across one or more registers. Walk through
   523  		// each piece to see whether we've seen an arg reg op for it.
   524  		types, offsets := inp.RegisterTypesAndOffsets()
   525  		for k, t := range types {
   526  			// Note: this recipe for creating a LocalSlot is designed
   527  			// to be compatible with the one used in expand_calls.go
   528  			// as opposed to decompose.go. The expand calls code just
   529  			// takes the base name and creates an offset into it,
   530  			// without using the SplitOf/SplitOffset fields. The code
   531  			// in decompose.go does the opposite -- it creates a
   532  			// LocalSlot object with "Off" set to zero, but with
   533  			// SplitOf pointing to a parent slot, and SplitOffset
   534  			// holding the offset into the parent object.
   535  			pieceSlot := LocalSlot{N: n, Type: t, Off: offsets[k]}
   536  
   537  			// Look up this piece to see if we've seen a reg op
   538  			// for it. If not, create one.
   539  			_, found := sc.lookup(pieceSlot)
   540  			if !found {
   541  				// This slot doesn't appear in the map, meaning it
   542  				// corresponds to an in-param that is not live, or
   543  				// a portion of an in-param that is not live/used.
   544  				// Add a new dummy OpArg{Int,Float}Reg for it.
   545  				synthesizeOpIntFloatArg(n, t, inp.Registers[k],
   546  					pieceSlot)
   547  			}
   548  		}
   549  	}
   550  
   551  	// Insert the new values into the head of the block.
   552  	f.Entry.Values = append(newValues, f.Entry.Values...)
   553  }
   554  
   555  // BuildFuncDebug builds debug information for f, placing the results
   556  // in "rval". f must be fully processed, so that each Value is where it
   557  // will be when machine code is emitted.
   558  func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
   559  	if f.RegAlloc == nil {
   560  		f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
   561  	}
   562  	state := &f.Cache.debugState
   563  	state.loggingLevel = loggingLevel % 1000
   564  
   565  	// A specific number demands exactly that many iterations. Under
   566  	// particular circumstances it make require more than the total of
   567  	// 2 passes implied by a single run through liveness and a single
   568  	// run through location list generation.
   569  	state.convergeCount = loggingLevel / 1000
   570  	state.f = f
   571  	state.registers = f.Config.registers
   572  	state.stackOffset = stackOffset
   573  	state.ctxt = ctxt
   574  
   575  	if buildcfg.Experiment.RegabiArgs {
   576  		PopulateABIInRegArgOps(f)
   577  	}
   578  
   579  	if state.loggingLevel > 0 {
   580  		state.logf("Generating location lists for function %q\n", f.Name)
   581  	}
   582  
   583  	if state.varParts == nil {
   584  		state.varParts = make(map[*ir.Name][]SlotID)
   585  	} else {
   586  		clear(state.varParts)
   587  	}
   588  
   589  	// Recompose any decomposed variables, and establish the canonical
   590  	// IDs for each var and slot by filling out state.vars and state.slots.
   591  
   592  	state.slots = state.slots[:0]
   593  	state.vars = state.vars[:0]
   594  	for i, slot := range f.Names {
   595  		state.slots = append(state.slots, *slot)
   596  		if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) {
   597  			continue
   598  		}
   599  
   600  		topSlot := slot
   601  		for topSlot.SplitOf != nil {
   602  			topSlot = topSlot.SplitOf
   603  		}
   604  		if _, ok := state.varParts[topSlot.N]; !ok {
   605  			state.vars = append(state.vars, topSlot.N)
   606  		}
   607  		state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i))
   608  	}
   609  
   610  	// Recreate the LocalSlot for each stack-only variable.
   611  	// This would probably be better as an output from stackframe.
   612  	for _, b := range f.Blocks {
   613  		for _, v := range b.Values {
   614  			if v.Op == OpVarDef {
   615  				n := v.Aux.(*ir.Name)
   616  				if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) {
   617  					continue
   618  				}
   619  
   620  				if _, ok := state.varParts[n]; !ok {
   621  					slot := LocalSlot{N: n, Type: v.Type, Off: 0}
   622  					state.slots = append(state.slots, slot)
   623  					state.varParts[n] = []SlotID{SlotID(len(state.slots) - 1)}
   624  					state.vars = append(state.vars, n)
   625  				}
   626  			}
   627  		}
   628  	}
   629  
   630  	// Fill in the var<->slot mappings.
   631  	if cap(state.varSlots) < len(state.vars) {
   632  		state.varSlots = make([][]SlotID, len(state.vars))
   633  	} else {
   634  		state.varSlots = state.varSlots[:len(state.vars)]
   635  		for i := range state.varSlots {
   636  			state.varSlots[i] = state.varSlots[i][:0]
   637  		}
   638  	}
   639  	if cap(state.slotVars) < len(state.slots) {
   640  		state.slotVars = make([]VarID, len(state.slots))
   641  	} else {
   642  		state.slotVars = state.slotVars[:len(state.slots)]
   643  	}
   644  
   645  	for varID, n := range state.vars {
   646  		parts := state.varParts[n]
   647  		slices.SortFunc(parts, func(a, b SlotID) int {
   648  			return cmp.Compare(varOffset(state.slots[a]), varOffset(state.slots[b]))
   649  		})
   650  
   651  		state.varSlots[varID] = parts
   652  		for _, slotID := range parts {
   653  			state.slotVars[slotID] = VarID(varID)
   654  		}
   655  	}
   656  
   657  	state.initializeCache(f, len(state.varParts), len(state.slots))
   658  
   659  	for i, slot := range f.Names {
   660  		if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) {
   661  			continue
   662  		}
   663  		for _, value := range f.NamedValues[*slot] {
   664  			state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i))
   665  		}
   666  	}
   667  
   668  	blockLocs := state.liveness()
   669  	state.buildLocationLists(blockLocs)
   670  
   671  	// Populate "rval" with what we've computed.
   672  	rval.Slots = state.slots
   673  	rval.VarSlots = state.varSlots
   674  	rval.Vars = state.vars
   675  	rval.LocationLists = state.lists
   676  }
   677  
   678  // liveness walks the function in control flow order, calculating the start
   679  // and end state of each block.
   680  func (state *debugState) liveness() []*BlockDebug {
   681  	blockLocs := make([]*BlockDebug, state.f.NumBlocks())
   682  	counterTime := int32(1)
   683  
   684  	// Reverse postorder: visit a block after as many as possible of its
   685  	// predecessors have been visited.
   686  	po := state.f.Postorder()
   687  	converged := false
   688  
   689  	// The iteration rule is that by default, run until converged, but
   690  	// if a particular iteration count is specified, run that many
   691  	// iterations, no more, no less.  A count is specified as the
   692  	// thousands digit of the location lists debug flag,
   693  	// e.g. -d=locationlists=4000
   694  	keepGoing := func(k int) bool {
   695  		if state.convergeCount == 0 {
   696  			return !converged
   697  		}
   698  		return k < state.convergeCount
   699  	}
   700  	for k := 0; keepGoing(k); k++ {
   701  		if state.loggingLevel > 0 {
   702  			state.logf("Liveness pass %d\n", k)
   703  		}
   704  		converged = true
   705  		for i := len(po) - 1; i >= 0; i-- {
   706  			b := po[i]
   707  			locs := blockLocs[b.ID]
   708  			if locs == nil {
   709  				locs = state.allocBlock(b)
   710  				blockLocs[b.ID] = locs
   711  			}
   712  
   713  			// Build the starting state for the block from the final
   714  			// state of its predecessors.
   715  			startState, blockChanged := state.mergePredecessors(b, blockLocs, nil, false)
   716  			locs.lastCheckedTime = counterTime
   717  			counterTime++
   718  			if state.loggingLevel > 1 {
   719  				state.logf("Processing %v, block changed %v, initial state:\n%v", b, blockChanged, state.stateString(state.currentState))
   720  			}
   721  
   722  			if blockChanged {
   723  				// If the start did not change, then the old endState is good
   724  				converged = false
   725  				changed := false
   726  				state.changedSlots.clear()
   727  
   728  				// Update locs/registers with the effects of each Value.
   729  				for _, v := range b.Values {
   730  					slots := state.valueNames[v.ID]
   731  
   732  					// Loads and stores inherit the names of their sources.
   733  					var source *Value
   734  					switch v.Op {
   735  					case OpStoreReg:
   736  						source = v.Args[0]
   737  					case OpLoadReg:
   738  						switch a := v.Args[0]; a.Op {
   739  						case OpArg, OpPhi:
   740  							source = a
   741  						case OpStoreReg:
   742  							source = a.Args[0]
   743  						default:
   744  							if state.loggingLevel > 1 {
   745  								state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a)
   746  							}
   747  						}
   748  					}
   749  					// Update valueNames with the source so that later steps
   750  					// don't need special handling.
   751  					if source != nil && k == 0 {
   752  						// limit to k == 0 otherwise there are duplicates.
   753  						slots = append(slots, state.valueNames[source.ID]...)
   754  						state.valueNames[v.ID] = slots
   755  					}
   756  
   757  					reg, _ := state.f.getHome(v.ID).(*Register)
   758  					c := state.processValue(v, slots, reg)
   759  					changed = changed || c
   760  				}
   761  
   762  				if state.loggingLevel > 1 {
   763  					state.logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState))
   764  				}
   765  
   766  				locs.relevant = locs.relevant || changed
   767  				if !changed {
   768  					locs.endState = startState
   769  				} else {
   770  					for _, id := range state.changedSlots.contents() {
   771  						slotID := SlotID(id)
   772  						slotLoc := state.currentState.slots[slotID]
   773  						if slotLoc.absent() {
   774  							startState.Delete(int32(slotID))
   775  							continue
   776  						}
   777  						old := startState.Find(int32(slotID)) // do NOT replace existing values
   778  						if oldLS, ok := old.(*liveSlot); !ok || oldLS.VarLoc != slotLoc {
   779  							startState.Insert(int32(slotID),
   780  								&liveSlot{VarLoc: slotLoc})
   781  						}
   782  					}
   783  					locs.endState = startState
   784  				}
   785  				locs.lastChangedTime = counterTime
   786  			}
   787  			counterTime++
   788  		}
   789  	}
   790  	return blockLocs
   791  }
   792  
   793  // mergePredecessors takes the end state of each of b's predecessors and
   794  // intersects them to form the starting state for b. It puts that state
   795  // in blockLocs[b.ID].startState, and fills state.currentState with it.
   796  // It returns the start state and whether this is changed from the
   797  // previously approximated value of startState for this block.  After
   798  // the first call, subsequent calls can only shrink startState.
   799  //
   800  // Passing forLocationLists=true enables additional side-effects that
   801  // are necessary for building location lists but superfluous while still
   802  // iterating to an answer.
   803  //
   804  // If previousBlock is non-nil, it registers changes vs. that block's
   805  // end state in state.changedVars. Note that previousBlock will often
   806  // not be a predecessor.
   807  //
   808  // Note that mergePredecessors behaves slightly differently between
   809  // first and subsequent calls for a block.  For the first call, the
   810  // starting state is approximated by taking the state from the
   811  // predecessor whose state is smallest, and removing any elements not
   812  // in all the other predecessors; this makes the smallest number of
   813  // changes and shares the most state.  On subsequent calls the old
   814  // value of startState is adjusted with new information; this is judged
   815  // to do the least amount of extra work.
   816  //
   817  // To improve performance, each block's state information is marked with
   818  // lastChanged and lastChecked "times" so unchanged predecessors can be
   819  // skipped on after-the-first iterations.  Doing this allows extra
   820  // iterations by the caller to be almost free.
   821  //
   822  // It is important to know that the set representation used for
   823  // startState, endState, and merges can share data for two sets where
   824  // one is a small delta from the other.  Doing this does require a
   825  // little care in how sets are updated, both in mergePredecessors, and
   826  // using its result.
   827  func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block, forLocationLists bool) (abt.T, bool) {
   828  	// Filter out back branches.
   829  	var predsBuf [10]*Block
   830  
   831  	preds := predsBuf[:0]
   832  	locs := blockLocs[b.ID]
   833  
   834  	blockChanged := !locs.everProcessed // the first time it always changes.
   835  	updating := locs.everProcessed
   836  
   837  	// For the first merge, exclude predecessors that have not been seen yet.
   838  	// I.e., backedges.
   839  	for _, pred := range b.Preds {
   840  		if bl := blockLocs[pred.b.ID]; bl != nil && bl.everProcessed {
   841  			// crucially, a self-edge has bl != nil, but bl.everProcessed is false the first time.
   842  			preds = append(preds, pred.b)
   843  		}
   844  	}
   845  
   846  	locs.everProcessed = true
   847  
   848  	if state.loggingLevel > 1 {
   849  		// The logf below would cause preds to be heap-allocated if
   850  		// it were passed directly.
   851  		preds2 := make([]*Block, len(preds))
   852  		copy(preds2, preds)
   853  		state.logf("Merging %v into %v (changed=%d, checked=%d)\n", preds2, b, locs.lastChangedTime, locs.lastCheckedTime)
   854  	}
   855  
   856  	state.changedVars.clear()
   857  
   858  	markChangedVars := func(slots, merged abt.T) {
   859  		if !forLocationLists {
   860  			return
   861  		}
   862  		// Fill changedVars with those that differ between the previous
   863  		// block (in the emit order, not necessarily a flow predecessor)
   864  		// and the start state for this block.
   865  		for it := slots.Iterator(); !it.Done(); {
   866  			k, v := it.Next()
   867  			m := merged.Find(k)
   868  			if m == nil || v.(*liveSlot).VarLoc != m.(*liveSlot).VarLoc {
   869  				state.changedVars.add(ID(state.slotVars[k]))
   870  			}
   871  		}
   872  	}
   873  
   874  	reset := func(ourStartState abt.T) {
   875  		if !(forLocationLists || blockChanged) {
   876  			// there is no change and this is not for location lists, do
   877  			// not bother to reset currentState because it will not be
   878  			// examined.
   879  			return
   880  		}
   881  		state.currentState.reset(ourStartState)
   882  	}
   883  
   884  	// Zero predecessors
   885  	if len(preds) == 0 {
   886  		if previousBlock != nil {
   887  			state.f.Fatalf("Function %v, block %s with no predecessors is not first block, has previous %s", state.f, b.String(), previousBlock.String())
   888  		}
   889  		// startState is empty
   890  		reset(abt.T{})
   891  		return abt.T{}, blockChanged
   892  	}
   893  
   894  	// One predecessor
   895  	l0 := blockLocs[preds[0].ID]
   896  	p0 := l0.endState
   897  	if len(preds) == 1 {
   898  		if previousBlock != nil && preds[0].ID != previousBlock.ID {
   899  			// Change from previous block is its endState minus the predecessor's endState
   900  			markChangedVars(blockLocs[previousBlock.ID].endState, p0)
   901  		}
   902  		locs.startState = p0
   903  		blockChanged = blockChanged || l0.lastChangedTime > locs.lastCheckedTime
   904  		reset(p0)
   905  		return p0, blockChanged
   906  	}
   907  
   908  	// More than one predecessor
   909  
   910  	if updating {
   911  		// After the first approximation, i.e., when updating, results
   912  		// can only get smaller, because initially backedge
   913  		// predecessors do not participate in the intersection.  This
   914  		// means that for the update, given the prior approximation of
   915  		// startState, there is no need to re-intersect with unchanged
   916  		// blocks.  Therefore remove unchanged blocks from the
   917  		// predecessor list.
   918  		for i := len(preds) - 1; i >= 0; i-- {
   919  			pred := preds[i]
   920  			if blockLocs[pred.ID].lastChangedTime > locs.lastCheckedTime {
   921  				continue // keep this predecessor
   922  			}
   923  			preds[i] = preds[len(preds)-1]
   924  			preds = preds[:len(preds)-1]
   925  			if state.loggingLevel > 2 {
   926  				state.logf("Pruned b%d, lastChanged was %d but b%d lastChecked is %d\n", pred.ID, blockLocs[pred.ID].lastChangedTime, b.ID, locs.lastCheckedTime)
   927  			}
   928  		}
   929  		// Check for an early out; this should always hit for the update
   930  		// if there are no cycles.
   931  		if len(preds) == 0 {
   932  			blockChanged = false
   933  
   934  			reset(locs.startState)
   935  			if state.loggingLevel > 2 {
   936  				state.logf("Early out, no predecessors changed since last check\n")
   937  			}
   938  			if previousBlock != nil {
   939  				markChangedVars(blockLocs[previousBlock.ID].endState, locs.startState)
   940  			}
   941  			return locs.startState, blockChanged
   942  		}
   943  	}
   944  
   945  	baseID := preds[0].ID
   946  	baseState := p0
   947  
   948  	// Choose the predecessor with the smallest endState for intersection work
   949  	for _, pred := range preds[1:] {
   950  		if blockLocs[pred.ID].endState.Size() < baseState.Size() {
   951  			baseState = blockLocs[pred.ID].endState
   952  			baseID = pred.ID
   953  		}
   954  	}
   955  
   956  	if state.loggingLevel > 2 {
   957  		state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID]))
   958  		for _, pred := range preds {
   959  			if pred.ID == baseID {
   960  				continue
   961  			}
   962  			state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID]))
   963  		}
   964  	}
   965  
   966  	state.currentState.reset(abt.T{})
   967  	// The normal logic of "reset" is included in the intersection loop below.
   968  
   969  	slotLocs := state.currentState.slots
   970  
   971  	// If this is the first call, do updates on the "baseState"; if this
   972  	// is a subsequent call, tweak the startState instead. Note that
   973  	// these "set" values are values; there are no side effects to
   974  	// other values as these are modified.
   975  	newState := baseState
   976  	if updating {
   977  		newState = blockLocs[b.ID].startState
   978  	}
   979  
   980  	for it := newState.Iterator(); !it.Done(); {
   981  		k, d := it.Next()
   982  		thisSlot := d.(*liveSlot)
   983  		x := thisSlot.VarLoc
   984  		x0 := x // initial value in newState
   985  
   986  		// Intersect this slot with the slot in all the predecessors
   987  		for _, other := range preds {
   988  			if !updating && other.ID == baseID {
   989  				continue
   990  			}
   991  			otherSlot := blockLocs[other.ID].endState.Find(k)
   992  			if otherSlot == nil {
   993  				x = VarLoc{}
   994  				break
   995  			}
   996  			y := otherSlot.(*liveSlot).VarLoc
   997  			x = x.intersect(y)
   998  			if x.absent() {
   999  				x = VarLoc{}
  1000  				break
  1001  			}
  1002  		}
  1003  
  1004  		// Delete if necessary, but not otherwise (in order to maximize sharing).
  1005  		if x.absent() {
  1006  			if !x0.absent() {
  1007  				blockChanged = true
  1008  				newState.Delete(k)
  1009  			}
  1010  			slotLocs[k] = VarLoc{}
  1011  			continue
  1012  		}
  1013  		if x != x0 {
  1014  			blockChanged = true
  1015  			newState.Insert(k, &liveSlot{VarLoc: x})
  1016  		}
  1017  
  1018  		slotLocs[k] = x
  1019  		mask := uint64(x.Registers)
  1020  		for {
  1021  			if mask == 0 {
  1022  				break
  1023  			}
  1024  			reg := uint8(bits.TrailingZeros64(mask))
  1025  			mask &^= 1 << reg
  1026  			state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(k))
  1027  		}
  1028  	}
  1029  
  1030  	if previousBlock != nil {
  1031  		markChangedVars(blockLocs[previousBlock.ID].endState, newState)
  1032  	}
  1033  	locs.startState = newState
  1034  	return newState, blockChanged
  1035  }
  1036  
  1037  // processValue updates locs and state.registerContents to reflect v, a
  1038  // value with the names in vSlots and homed in vReg.  "v" becomes
  1039  // visible after execution of the instructions evaluating it. It
  1040  // returns which VarIDs were modified by the Value's execution.
  1041  func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool {
  1042  	locs := state.currentState
  1043  	changed := false
  1044  	setSlot := func(slot SlotID, loc VarLoc) {
  1045  		changed = true
  1046  		state.changedVars.add(ID(state.slotVars[slot]))
  1047  		state.changedSlots.add(ID(slot))
  1048  		state.currentState.slots[slot] = loc
  1049  	}
  1050  
  1051  	// Handle any register clobbering. Call operations, for example,
  1052  	// clobber all registers even though they don't explicitly write to
  1053  	// them.
  1054  	clobbers := opcodeTable[v.Op].reg.clobbers
  1055  	for {
  1056  		if clobbers.empty() {
  1057  			break
  1058  		}
  1059  		reg := pickReg(clobbers)
  1060  		clobbers = clobbers.removeReg(reg)
  1061  
  1062  		for _, slot := range locs.registers[reg] {
  1063  			if state.loggingLevel > 1 {
  1064  				state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg])
  1065  			}
  1066  
  1067  			last := locs.slots[slot]
  1068  			if last.absent() {
  1069  				state.f.Fatalf("at %v: slot %v in register %v with no location entry", v, state.slots[slot], &state.registers[reg])
  1070  				continue
  1071  			}
  1072  			regs := last.Registers &^ (1 << reg)
  1073  			setSlot(slot, VarLoc{regs, last.StackOffset})
  1074  		}
  1075  
  1076  		locs.registers[reg] = locs.registers[reg][:0]
  1077  	}
  1078  
  1079  	switch {
  1080  	case v.Op == OpVarDef:
  1081  		n := v.Aux.(*ir.Name)
  1082  		if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) {
  1083  			break
  1084  		}
  1085  
  1086  		slotID := state.varParts[n][0]
  1087  		var stackOffset StackOffset
  1088  		if v.Op == OpVarDef {
  1089  			stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1)
  1090  		}
  1091  		setSlot(slotID, VarLoc{0, stackOffset})
  1092  		if state.loggingLevel > 1 {
  1093  			if v.Op == OpVarDef {
  1094  				state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID])
  1095  			} else {
  1096  				state.logf("at %v: stack-only var %v now dead\n", v, state.slots[slotID])
  1097  			}
  1098  		}
  1099  
  1100  	case v.Op == OpArg:
  1101  		home := state.f.getHome(v.ID).(LocalSlot)
  1102  		stackOffset := state.stackOffset(home)<<1 | 1
  1103  		for _, slot := range vSlots {
  1104  			if state.loggingLevel > 1 {
  1105  				state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home)
  1106  				if last := locs.slots[slot]; !last.absent() {
  1107  					state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot])
  1108  				}
  1109  			}
  1110  
  1111  			setSlot(slot, VarLoc{0, StackOffset(stackOffset)})
  1112  		}
  1113  
  1114  	case v.Op == OpStoreReg:
  1115  		home := state.f.getHome(v.ID).(LocalSlot)
  1116  		stackOffset := state.stackOffset(home)<<1 | 1
  1117  		for _, slot := range vSlots {
  1118  			last := locs.slots[slot]
  1119  			if last.absent() {
  1120  				if state.loggingLevel > 1 {
  1121  					state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg)
  1122  				}
  1123  				break
  1124  			}
  1125  
  1126  			setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)})
  1127  			if state.loggingLevel > 1 {
  1128  				state.logf("at %v: %v spilled to stack location %v@%d\n", v, state.slots[slot], home, state.stackOffset(home))
  1129  			}
  1130  		}
  1131  
  1132  	case vReg != nil:
  1133  		if state.loggingLevel > 1 {
  1134  			newSlots := make([]bool, len(state.slots))
  1135  			for _, slot := range vSlots {
  1136  				newSlots[slot] = true
  1137  			}
  1138  
  1139  			for _, slot := range locs.registers[vReg.num] {
  1140  				if !newSlots[slot] {
  1141  					state.logf("at %v: overwrote %v in register %v\n", v, state.slots[slot], vReg)
  1142  				}
  1143  			}
  1144  		}
  1145  
  1146  		for _, slot := range locs.registers[vReg.num] {
  1147  			last := locs.slots[slot]
  1148  			setSlot(slot, VarLoc{last.Registers &^ (1 << uint8(vReg.num)), last.StackOffset})
  1149  		}
  1150  		locs.registers[vReg.num] = locs.registers[vReg.num][:0]
  1151  		locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...)
  1152  		for _, slot := range vSlots {
  1153  			if state.loggingLevel > 1 {
  1154  				state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg)
  1155  			}
  1156  
  1157  			last := locs.slots[slot]
  1158  			setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset})
  1159  		}
  1160  	}
  1161  	return changed
  1162  }
  1163  
  1164  // varOffset returns the offset of slot within the user variable it was
  1165  // decomposed from. This has nothing to do with its stack offset.
  1166  func varOffset(slot LocalSlot) int64 {
  1167  	offset := slot.Off
  1168  	s := &slot
  1169  	for ; s.SplitOf != nil; s = s.SplitOf {
  1170  		offset += s.SplitOffset
  1171  	}
  1172  	return offset
  1173  }
  1174  
  1175  // A pendingEntry represents the beginning of a location list entry, missing
  1176  // only its end coordinate.
  1177  type pendingEntry struct {
  1178  	present                bool
  1179  	startBlock, startValue ID
  1180  	// The location of each piece of the variable, in the same order as the
  1181  	// SlotIDs in varParts.
  1182  	pieces []VarLoc
  1183  }
  1184  
  1185  func (e *pendingEntry) clear() {
  1186  	e.present = false
  1187  	e.startBlock = 0
  1188  	e.startValue = 0
  1189  	clear(e.pieces)
  1190  }
  1191  
  1192  // canMerge reports whether a new location description is a superset
  1193  // of the (non-empty) pending location description, if so, the two
  1194  // can be merged (i.e., pending is still a valid and useful location
  1195  // description).
  1196  func canMerge(pending, new VarLoc) bool {
  1197  	if pending.absent() && new.absent() {
  1198  		return true
  1199  	}
  1200  	if pending.absent() || new.absent() {
  1201  		return false
  1202  	}
  1203  	// pending is not absent, therefore it has either a stack mapping,
  1204  	// or registers, or both.
  1205  	if pending.onStack() && pending.StackOffset != new.StackOffset {
  1206  		// if pending has a stack offset, then new must also, and it
  1207  		// must be the same (StackOffset encodes onStack).
  1208  		return false
  1209  	}
  1210  	if pending.Registers&new.Registers != pending.Registers {
  1211  		// There is at least one register in pending not mentioned in new.
  1212  		return false
  1213  	}
  1214  	return true
  1215  }
  1216  
  1217  // firstReg returns the first register in set that is present.
  1218  func firstReg(set RegisterSet) uint8 {
  1219  	if set == 0 {
  1220  		// This is wrong, but there seem to be some situations where we
  1221  		// produce locations with no storage.
  1222  		return 0
  1223  	}
  1224  	return uint8(bits.TrailingZeros64(uint64(set)))
  1225  }
  1226  
  1227  // buildLocationLists builds location lists for all the user variables
  1228  // in state.f, using the information about block state in blockLocs.
  1229  // The returned location lists are not fully complete. They are in
  1230  // terms of SSA values rather than PCs, and have no base address/end
  1231  // entries. They will be finished by PutLocationList.
  1232  func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
  1233  	// Run through the function in program text order, building up location
  1234  	// lists as we go. The heavy lifting has mostly already been done.
  1235  
  1236  	var prevBlock *Block
  1237  	for _, b := range state.f.Blocks {
  1238  		state.mergePredecessors(b, blockLocs, prevBlock, true)
  1239  
  1240  		// Handle any differences among predecessor blocks and previous block (perhaps not a predecessor)
  1241  		for _, varID := range state.changedVars.contents() {
  1242  			state.updateVar(VarID(varID), b, BlockStart)
  1243  		}
  1244  		state.changedVars.clear()
  1245  
  1246  		if !blockLocs[b.ID].relevant {
  1247  			continue
  1248  		}
  1249  
  1250  		mustBeFirst := func(v *Value) bool {
  1251  			return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() ||
  1252  				v.Op == OpArgIntReg || v.Op == OpArgFloatReg
  1253  		}
  1254  
  1255  		blockPrologComplete := func(v *Value) bool {
  1256  			if b.ID != state.f.Entry.ID {
  1257  				return !opcodeTable[v.Op].zeroWidth
  1258  			} else {
  1259  				return v.Op == OpInitMem
  1260  			}
  1261  		}
  1262  
  1263  		// Examine the prolog portion of the block to process special
  1264  		// zero-width ops such as Arg, Phi, LoweredGetClosurePtr (etc)
  1265  		// whose lifetimes begin at the block starting point. In an
  1266  		// entry block, allow for the possibility that we may see Arg
  1267  		// ops that appear _after_ other non-zero-width operations.
  1268  		// Example:
  1269  		//
  1270  		//   v33 = ArgIntReg <uintptr> {foo+0} [0] : AX (foo)
  1271  		//   v34 = ArgIntReg <uintptr> {bar+0} [0] : BX (bar)
  1272  		//   ...
  1273  		//   v77 = StoreReg <unsafe.Pointer> v67 : ctx+8[unsafe.Pointer]
  1274  		//   v78 = StoreReg <unsafe.Pointer> v68 : ctx[unsafe.Pointer]
  1275  		//   v79 = Arg <*uint8> {args} : args[*uint8] (args[*uint8])
  1276  		//   v80 = Arg <int> {args} [8] : args+8[int] (args+8[int])
  1277  		//   ...
  1278  		//   v1 = InitMem <mem>
  1279  		//
  1280  		// We can stop scanning the initial portion of the block when
  1281  		// we either see the InitMem op (for entry blocks) or the
  1282  		// first non-zero-width op (for other blocks).
  1283  		for idx := 0; idx < len(b.Values); idx++ {
  1284  			v := b.Values[idx]
  1285  			if blockPrologComplete(v) {
  1286  				break
  1287  			}
  1288  			// Consider only "lifetime begins at block start" ops.
  1289  			if !mustBeFirst(v) && v.Op != OpArg {
  1290  				continue
  1291  			}
  1292  			slots := state.valueNames[v.ID]
  1293  			reg, _ := state.f.getHome(v.ID).(*Register)
  1294  			changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
  1295  			if changed {
  1296  				for _, varID := range state.changedVars.contents() {
  1297  					state.updateVar(VarID(varID), v.Block, BlockStart)
  1298  				}
  1299  				state.changedVars.clear()
  1300  			}
  1301  		}
  1302  
  1303  		// Now examine the block again, handling things other than the
  1304  		// "begins at block start" lifetimes.
  1305  		zeroWidthPending := false
  1306  		prologComplete := false
  1307  		// expect to see values in pattern (apc)* (zerowidth|real)*
  1308  		for _, v := range b.Values {
  1309  			if blockPrologComplete(v) {
  1310  				prologComplete = true
  1311  			}
  1312  			slots := state.valueNames[v.ID]
  1313  			reg, _ := state.f.getHome(v.ID).(*Register)
  1314  			changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
  1315  
  1316  			if opcodeTable[v.Op].zeroWidth {
  1317  				if prologComplete && mustBeFirst(v) {
  1318  					panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
  1319  				}
  1320  				if changed {
  1321  					if mustBeFirst(v) || v.Op == OpArg {
  1322  						// already taken care of above
  1323  						continue
  1324  					}
  1325  					zeroWidthPending = true
  1326  				}
  1327  				continue
  1328  			}
  1329  			if !changed && !zeroWidthPending {
  1330  				continue
  1331  			}
  1332  
  1333  			// Not zero-width; i.e., a "real" instruction.
  1334  			zeroWidthPending = false
  1335  			for _, varID := range state.changedVars.contents() {
  1336  				state.updateVar(VarID(varID), v.Block, v)
  1337  			}
  1338  			state.changedVars.clear()
  1339  		}
  1340  		for _, varID := range state.changedVars.contents() {
  1341  			state.updateVar(VarID(varID), b, BlockEnd)
  1342  		}
  1343  
  1344  		prevBlock = b
  1345  	}
  1346  
  1347  	if state.loggingLevel > 0 {
  1348  		state.logf("location lists:\n")
  1349  	}
  1350  
  1351  	// Flush any leftover entries live at the end of the last block.
  1352  	for varID := range state.lists {
  1353  		state.writePendingEntry(VarID(varID), -1, FuncEnd.ID)
  1354  		list := state.lists[varID]
  1355  		if state.loggingLevel > 0 {
  1356  			if len(list) == 0 {
  1357  				state.logf("\t%v : empty list\n", state.vars[varID])
  1358  			} else {
  1359  				state.logf("\t%v : %d entries\n", state.vars[varID], len(list))
  1360  			}
  1361  		}
  1362  	}
  1363  }
  1364  
  1365  // updateVar updates the pending location list entry for varID to
  1366  // reflect the new locations in curLoc, beginning at v in block b.
  1367  // v may be one of the special values indicating block start or end.
  1368  func (state *debugState) updateVar(varID VarID, b *Block, v *Value) {
  1369  	curLoc := state.currentState.slots
  1370  	// Assemble the location list entry with whatever's live.
  1371  	empty := true
  1372  	for _, slotID := range state.varSlots[varID] {
  1373  		if !curLoc[slotID].absent() {
  1374  			empty = false
  1375  			break
  1376  		}
  1377  	}
  1378  	pending := &state.pendingEntries[varID]
  1379  	if empty {
  1380  		state.writePendingEntry(varID, b.ID, v.ID)
  1381  		pending.clear()
  1382  		return
  1383  	}
  1384  
  1385  	// Extend the previous entry if possible.
  1386  	if pending.present {
  1387  		merge := true
  1388  		for i, slotID := range state.varSlots[varID] {
  1389  			if !canMerge(pending.pieces[i], curLoc[slotID]) {
  1390  				merge = false
  1391  				break
  1392  			}
  1393  		}
  1394  		if merge {
  1395  			return
  1396  		}
  1397  	}
  1398  
  1399  	state.writePendingEntry(varID, b.ID, v.ID)
  1400  	pending.present = true
  1401  	pending.startBlock = b.ID
  1402  	pending.startValue = v.ID
  1403  	for i, slot := range state.varSlots[varID] {
  1404  		pending.pieces[i] = curLoc[slot]
  1405  	}
  1406  }
  1407  
  1408  // writePendingEntry writes out the pending entry for varID, if any,
  1409  // terminated at endBlock/Value.
  1410  func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
  1411  	pending := state.pendingEntries[varID]
  1412  	if !pending.present {
  1413  		return
  1414  	}
  1415  
  1416  	// Skip zero-width entries where start and end coordinates are identical.
  1417  	if pending.startBlock == endBlock && pending.startValue == endValue {
  1418  		if state.loggingLevel > 1 {
  1419  			state.logf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name)
  1420  		}
  1421  		return
  1422  	}
  1423  
  1424  	if state.loggingLevel > 1 {
  1425  		var partStrs []string
  1426  		for i, slot := range state.varSlots[varID] {
  1427  			partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i])))
  1428  		}
  1429  		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, " "))
  1430  	}
  1431  
  1432  	// Build the DWARF location expression.
  1433  	var expr []byte
  1434  	for i, slotID := range state.varSlots[varID] {
  1435  		loc := pending.pieces[i]
  1436  		slot := state.slots[slotID]
  1437  
  1438  		if !loc.absent() {
  1439  			if loc.onStack() {
  1440  				if loc.stackOffsetValue() == 0 {
  1441  					expr = append(expr, dwarf.DW_OP_call_frame_cfa)
  1442  				} else {
  1443  					expr = append(expr, dwarf.DW_OP_fbreg)
  1444  					expr = dwarf.AppendSleb128(expr, int64(loc.stackOffsetValue()))
  1445  				}
  1446  			} else {
  1447  				regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()]
  1448  				if regnum < 32 {
  1449  					expr = append(expr, dwarf.DW_OP_reg0+byte(regnum))
  1450  				} else {
  1451  					expr = append(expr, dwarf.DW_OP_regx)
  1452  					expr = dwarf.AppendUleb128(expr, uint64(regnum))
  1453  				}
  1454  			}
  1455  		}
  1456  
  1457  		if len(state.varSlots[varID]) > 1 {
  1458  			expr = append(expr, dwarf.DW_OP_piece)
  1459  			expr = dwarf.AppendUleb128(expr, uint64(slot.Type.Size()))
  1460  		}
  1461  	}
  1462  
  1463  	entry := LocListEntry{
  1464  		StartBlock: pending.startBlock,
  1465  		StartValue: pending.startValue,
  1466  		EndBlock:   endBlock,
  1467  		EndValue:   endValue,
  1468  		Expr:       expr,
  1469  	}
  1470  	state.lists[varID] = append(state.lists[varID], entry)
  1471  }
  1472  
  1473  // PutLocationList adds entries (a location list in structured form)
  1474  // to listSym, encoding it in the appropriate DWARF format.
  1475  func (debugInfo *FuncDebug) PutLocationList(entries []LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
  1476  	if buildcfg.Experiment.Dwarf5 {
  1477  		debugInfo.PutLocationListDwarf5(entries, ctxt, listSym, startPC)
  1478  	} else {
  1479  		debugInfo.PutLocationListDwarf4(entries, ctxt, listSym, startPC)
  1480  	}
  1481  }
  1482  
  1483  // PutLocationListDwarf5 adds entries (a location list in structured form)
  1484  // to listSym in DWARF 5 format.
  1485  func (debugInfo *FuncDebug) PutLocationListDwarf5(entries []LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
  1486  	getPC := debugInfo.GetPC
  1487  
  1488  	// base address entry
  1489  	listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_base_addressx)
  1490  	listSym.WriteDwTxtAddrx(ctxt, listSym.Size, startPC, ctxt.DwTextCount*2)
  1491  
  1492  	var stbuf, enbuf [10]byte
  1493  	for _, entry := range entries {
  1494  		begin := getPC(entry.StartBlock, entry.StartValue)
  1495  		end := getPC(entry.EndBlock, entry.EndValue)
  1496  
  1497  		// Write LLE_offset_pair tag followed by payload (ULEB for start
  1498  		// and then end).
  1499  		listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_offset_pair)
  1500  		stb := stbuf[:0]
  1501  		enb := enbuf[:0]
  1502  		stb = dwarf.AppendUleb128(stb, uint64(begin))
  1503  		enb = dwarf.AppendUleb128(enb, uint64(end))
  1504  		listSym.WriteBytes(ctxt, listSym.Size, stb)
  1505  		listSym.WriteBytes(ctxt, listSym.Size, enb)
  1506  
  1507  		// DWARF5 uses ULEB128-encoded length for the location expression.
  1508  		stb = stbuf[:0]
  1509  		stb = dwarf.AppendUleb128(stb, uint64(len(entry.Expr)))
  1510  		listSym.WriteBytes(ctxt, listSym.Size, stb)
  1511  		listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
  1512  	}
  1513  
  1514  	// Terminator
  1515  	listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_end_of_list)
  1516  }
  1517  
  1518  // PutLocationListDwarf4 adds entries (a location list in structured form)
  1519  // to listSym in DWARF 4 format.
  1520  func (debugInfo *FuncDebug) PutLocationListDwarf4(entries []LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
  1521  	getPC := debugInfo.GetPC
  1522  
  1523  	if ctxt.UseBASEntries {
  1524  		listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0)
  1525  		listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
  1526  	}
  1527  
  1528  	for _, entry := range entries {
  1529  		begin := getPC(entry.StartBlock, entry.StartValue)
  1530  		end := getPC(entry.EndBlock, entry.EndValue)
  1531  
  1532  		// Horrible hack. If a range contains only zero-width
  1533  		// instructions, e.g. an Arg, and it's at the beginning of the
  1534  		// function, this would be indistinguishable from an
  1535  		// end entry. Fudge it.
  1536  		if begin == 0 && end == 0 {
  1537  			end = 1
  1538  		}
  1539  
  1540  		if ctxt.UseBASEntries {
  1541  			listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, begin)
  1542  			listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, end)
  1543  		} else {
  1544  			listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, begin)
  1545  			listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, end)
  1546  		}
  1547  
  1548  		// Write 2-byte length prefix followed by the location expression.
  1549  		listSym.WriteInt(ctxt, listSym.Size, 2, int64(len(entry.Expr)))
  1550  		listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
  1551  	}
  1552  
  1553  	// End entry.
  1554  	listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
  1555  	listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
  1556  }
  1557  
  1558  // locatePrologEnd walks the entry block of a function with incoming
  1559  // register arguments and locates the last instruction in the prolog
  1560  // that spills a register arg. It returns the ID of that instruction,
  1561  // and (where appropriate) the prolog's lowered closure ptr store inst.
  1562  //
  1563  // Example:
  1564  //
  1565  //	b1:
  1566  //	    v3 = ArgIntReg <int> {p1+0} [0] : AX
  1567  //	    ... more arg regs ..
  1568  //	    v4 = ArgFloatReg <float32> {f1+0} [0] : X0
  1569  //	    v52 = MOVQstore <mem> {p1} v2 v3 v1
  1570  //	    ... more stores ...
  1571  //	    v68 = MOVSSstore <mem> {f4} v2 v67 v66
  1572  //	    v38 = MOVQstoreconst <mem> {blob} [val=0,off=0] v2 v32
  1573  //
  1574  // Important: locatePrologEnd is expected to work properly only with
  1575  // optimization turned off (e.g. "-N"). If optimization is enabled
  1576  // we can't be assured of finding all input arguments spilled in the
  1577  // entry block prolog.
  1578  func locatePrologEnd(f *Func, needCloCtx bool) (ID, *Value) {
  1579  
  1580  	// returns true if this instruction looks like it moves an ABI
  1581  	// register (or context register for rangefunc bodies) to the
  1582  	// stack, along with the value being stored.
  1583  	isRegMoveLike := func(v *Value) (bool, ID) {
  1584  		n, ok := v.Aux.(*ir.Name)
  1585  		var r ID
  1586  		if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
  1587  			return false, r
  1588  		}
  1589  		regInputs, memInputs, spInputs := 0, 0, 0
  1590  		for _, a := range v.Args {
  1591  			if a.Op == OpArgIntReg || a.Op == OpArgFloatReg ||
  1592  				(needCloCtx && a.Op.isLoweredGetClosurePtr()) {
  1593  				regInputs++
  1594  				r = a.ID
  1595  			} else if a.Type.IsMemory() {
  1596  				memInputs++
  1597  			} else if a.Op == OpSP {
  1598  				spInputs++
  1599  			} else {
  1600  				return false, r
  1601  			}
  1602  		}
  1603  		return v.Type.IsMemory() && memInputs == 1 &&
  1604  			regInputs == 1 && spInputs == 1, r
  1605  	}
  1606  
  1607  	// OpArg*Reg values we've seen so far on our forward walk,
  1608  	// for which we have not yet seen a corresponding spill.
  1609  	regArgs := make([]ID, 0, 32)
  1610  
  1611  	// removeReg tries to remove a value from regArgs, returning true
  1612  	// if found and removed, or false otherwise.
  1613  	removeReg := func(r ID) bool {
  1614  		for i := 0; i < len(regArgs); i++ {
  1615  			if regArgs[i] == r {
  1616  				regArgs = slices.Delete(regArgs, i, i+1)
  1617  				return true
  1618  			}
  1619  		}
  1620  		return false
  1621  	}
  1622  
  1623  	// Walk forwards through the block. When we see OpArg*Reg, record
  1624  	// the value it produces in the regArgs list. When see a store that uses
  1625  	// the value, remove the entry. When we hit the last store (use)
  1626  	// then we've arrived at the end of the prolog.
  1627  	var cloRegStore *Value
  1628  	for k, v := range f.Entry.Values {
  1629  		if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
  1630  			regArgs = append(regArgs, v.ID)
  1631  			continue
  1632  		}
  1633  		if needCloCtx && v.Op.isLoweredGetClosurePtr() {
  1634  			regArgs = append(regArgs, v.ID)
  1635  			cloRegStore = v
  1636  			continue
  1637  		}
  1638  		if ok, r := isRegMoveLike(v); ok {
  1639  			if removed := removeReg(r); removed {
  1640  				if len(regArgs) == 0 {
  1641  					// Found our last spill; return the value after
  1642  					// it. Note that it is possible that this spill is
  1643  					// the last instruction in the block. If so, then
  1644  					// return the "end of block" sentinel.
  1645  					if k < len(f.Entry.Values)-1 {
  1646  						return f.Entry.Values[k+1].ID, cloRegStore
  1647  					}
  1648  					return BlockEnd.ID, cloRegStore
  1649  				}
  1650  			}
  1651  		}
  1652  		if v.Op.IsCall() {
  1653  			// if we hit a call, we've gone too far.
  1654  			return v.ID, cloRegStore
  1655  		}
  1656  	}
  1657  	// nothing found
  1658  	return ID(-1), cloRegStore
  1659  }
  1660  
  1661  // isNamedRegParam returns true if the param corresponding to "p"
  1662  // is a named, non-blank input parameter assigned to one or more
  1663  // registers.
  1664  func isNamedRegParam(p abi.ABIParamAssignment) bool {
  1665  	if p.Name == nil {
  1666  		return false
  1667  	}
  1668  	n := p.Name
  1669  	if n.Sym() == nil || n.Sym().IsBlank() {
  1670  		return false
  1671  	}
  1672  	if len(p.Registers) == 0 {
  1673  		return false
  1674  	}
  1675  	return true
  1676  }
  1677  
  1678  // BuildFuncDebugNoOptimized populates a FuncDebug object "rval" with
  1679  // entries corresponding to the register-resident input parameters for
  1680  // the function "f"; it is used when we are compiling without
  1681  // optimization but the register ABI is enabled. For each reg param,
  1682  // it constructs a 2-element location list: the first element holds
  1683  // the input register, and the second element holds the stack location
  1684  // of the param (the assumption being that when optimization is off,
  1685  // each input param reg will be spilled in the prolog). In addition
  1686  // to the register params, here we also build location lists (where
  1687  // appropriate for the ".closureptr" compiler-synthesized variable
  1688  // needed by the debugger for range func bodies.
  1689  func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
  1690  	needCloCtx := f.CloSlot != nil
  1691  	pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
  1692  
  1693  	// Look to see if we have any named register-promoted parameters,
  1694  	// and/or whether we need location info for the ".closureptr"
  1695  	// synthetic variable; if not bail early and let the caller sort
  1696  	// things out for the remainder of the params/locals.
  1697  	numRegParams := 0
  1698  	for _, inp := range pri.InParams() {
  1699  		if isNamedRegParam(inp) {
  1700  			numRegParams++
  1701  		}
  1702  	}
  1703  	if numRegParams == 0 && !needCloCtx {
  1704  		return
  1705  	}
  1706  
  1707  	state := debugState{f: f}
  1708  
  1709  	if loggingEnabled {
  1710  		state.logf("generating -N reg param loc lists for func %q\n", f.Name)
  1711  	}
  1712  
  1713  	// cloReg stores the obj register num that the context register
  1714  	// appears in within the function prolog, where appropriate.
  1715  	var cloReg int16
  1716  
  1717  	extraForCloCtx := 0
  1718  	if needCloCtx {
  1719  		extraForCloCtx = 1
  1720  	}
  1721  
  1722  	// Allocate location lists.
  1723  	rval.LocationLists = make([][]LocListEntry, numRegParams+extraForCloCtx)
  1724  
  1725  	// Locate the value corresponding to the last spill of
  1726  	// an input register.
  1727  	afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
  1728  
  1729  	if needCloCtx {
  1730  		reg, _ := state.f.getHome(cloRegStore.ID).(*Register)
  1731  		cloReg = reg.ObjNum()
  1732  		if loggingEnabled {
  1733  			state.logf("needCloCtx is true for func %q, cloreg=%v\n",
  1734  				f.Name, reg)
  1735  		}
  1736  	}
  1737  
  1738  	addVarSlot := func(name *ir.Name, typ *types.Type) {
  1739  		sl := LocalSlot{N: name, Type: typ, Off: 0}
  1740  		rval.Vars = append(rval.Vars, name)
  1741  		rval.Slots = append(rval.Slots, sl)
  1742  		slid := len(rval.VarSlots)
  1743  		rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
  1744  	}
  1745  
  1746  	// Make an initial pass to populate the vars/slots for our return
  1747  	// value, covering first the input parameters and then (if needed)
  1748  	// the special ".closureptr" var for rangefunc bodies.
  1749  	params := []abi.ABIParamAssignment{}
  1750  	for _, inp := range pri.InParams() {
  1751  		if !isNamedRegParam(inp) {
  1752  			// will be sorted out elsewhere
  1753  			continue
  1754  		}
  1755  		if !IsVarWantedForDebug(inp.Name) {
  1756  			continue
  1757  		}
  1758  		addVarSlot(inp.Name, inp.Type)
  1759  		params = append(params, inp)
  1760  	}
  1761  	if needCloCtx {
  1762  		addVarSlot(f.CloSlot, f.CloSlot.Type())
  1763  		cloAssign := abi.ABIParamAssignment{
  1764  			Type:      f.CloSlot.Type(),
  1765  			Name:      f.CloSlot,
  1766  			Registers: []abi.RegIndex{0}, // dummy
  1767  		}
  1768  		params = append(params, cloAssign)
  1769  	}
  1770  
  1771  	// Walk the input params again and process the register-resident elements.
  1772  	pidx := 0
  1773  	for _, inp := range params {
  1774  		if !isNamedRegParam(inp) {
  1775  			// will be sorted out elsewhere
  1776  			continue
  1777  		}
  1778  		if !IsVarWantedForDebug(inp.Name) {
  1779  			continue
  1780  		}
  1781  
  1782  		sl := rval.Slots[pidx]
  1783  		n := rval.Vars[pidx]
  1784  
  1785  		if afterPrologVal == ID(-1) {
  1786  			// This can happen for degenerate functions with infinite
  1787  			// loops such as that in issue 45948. In such cases, leave
  1788  			// the var/slot set up for the param, but don't try to
  1789  			// emit a location list.
  1790  			if loggingEnabled {
  1791  				state.logf("locatePrologEnd failed, skipping %v\n", n)
  1792  			}
  1793  			pidx++
  1794  			continue
  1795  		}
  1796  
  1797  		// Param is arriving in one or more registers. We need a 2-element
  1798  		// location expression for it. First entry in location list
  1799  		// will correspond to lifetime in input registers.
  1800  		if loggingEnabled {
  1801  			state.logf("param %v:\n  [<entry>, %d]:\n", n, afterPrologVal)
  1802  		}
  1803  		var regExpr []byte
  1804  		rtypes, _ := inp.RegisterTypesAndOffsets()
  1805  		padding := make([]uint64, 0, 32)
  1806  		padding = inp.ComputePadding(padding)
  1807  		for k, r := range inp.Registers {
  1808  			var reg int16
  1809  			if n == f.CloSlot {
  1810  				reg = cloReg
  1811  			} else {
  1812  				reg = ObjRegForAbiReg(r, f.Config)
  1813  			}
  1814  			dwreg := ctxt.Arch.DWARFRegisters[reg]
  1815  			if dwreg < 32 {
  1816  				regExpr = append(regExpr, dwarf.DW_OP_reg0+byte(dwreg))
  1817  			} else {
  1818  				regExpr = append(regExpr, dwarf.DW_OP_regx)
  1819  				regExpr = dwarf.AppendUleb128(regExpr, uint64(dwreg))
  1820  			}
  1821  			if loggingEnabled {
  1822  				state.logf("    piece %d -> dwreg %d", k, dwreg)
  1823  			}
  1824  			if len(inp.Registers) > 1 {
  1825  				regExpr = append(regExpr, dwarf.DW_OP_piece)
  1826  				ts := rtypes[k].Size()
  1827  				regExpr = dwarf.AppendUleb128(regExpr, uint64(ts))
  1828  				if padding[k] > 0 {
  1829  					if loggingEnabled {
  1830  						state.logf(" [pad %d bytes]", padding[k])
  1831  					}
  1832  					regExpr = append(regExpr, dwarf.DW_OP_piece)
  1833  					regExpr = dwarf.AppendUleb128(regExpr, padding[k])
  1834  				}
  1835  			}
  1836  			if loggingEnabled {
  1837  				state.logf("\n")
  1838  			}
  1839  		}
  1840  		rval.LocationLists[pidx] = append(rval.LocationLists[pidx], LocListEntry{
  1841  			StartBlock: f.Entry.ID,
  1842  			StartValue: BlockStart.ID,
  1843  			EndBlock:   f.Entry.ID,
  1844  			EndValue:   afterPrologVal,
  1845  			Expr:       regExpr,
  1846  		})
  1847  
  1848  		// Second entry in the location list will be the stack home
  1849  		// of the param, once it has been spilled.  Emit that now.
  1850  		var stackExpr []byte
  1851  		soff := stackOffset(sl)
  1852  		if soff == 0 {
  1853  			stackExpr = append(stackExpr, dwarf.DW_OP_call_frame_cfa)
  1854  		} else {
  1855  			stackExpr = append(stackExpr, dwarf.DW_OP_fbreg)
  1856  			stackExpr = dwarf.AppendSleb128(stackExpr, int64(soff))
  1857  		}
  1858  		if loggingEnabled {
  1859  			state.logf("  [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
  1860  		}
  1861  
  1862  		rval.LocationLists[pidx] = append(rval.LocationLists[pidx], LocListEntry{
  1863  			StartBlock: f.Entry.ID,
  1864  			StartValue: afterPrologVal,
  1865  			EndBlock:   f.Entry.ID,
  1866  			EndValue:   FuncEnd.ID,
  1867  			Expr:       stackExpr,
  1868  		})
  1869  
  1870  		pidx++
  1871  	}
  1872  }
  1873  
  1874  // IsVarWantedForDebug returns true if the debug info for the node should
  1875  // be generated.
  1876  // For example, internal variables for range-over-func loops have little
  1877  // value to users, so we don't generate debug info for them.
  1878  func IsVarWantedForDebug(n ir.Node) bool {
  1879  	name := n.Sym().Name
  1880  	if len(name) > 0 && name[0] == '&' {
  1881  		name = name[1:]
  1882  	}
  1883  	if len(name) > 0 && name[0] == '#' {
  1884  		// #yield is used by delve.
  1885  		return strings.HasPrefix(name, "#yield")
  1886  	}
  1887  	return true
  1888  }
  1889  

View as plain text