Source file src/cmd/compile/internal/ssa/ssadebug/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 ssadebug
     6  
     7  import (
     8  	"cmp"
     9  	"internal/buildcfg"
    10  	"slices"
    11  
    12  	"cmd/compile/internal/abi"
    13  	"cmd/compile/internal/ir"
    14  	"cmd/compile/internal/ssa"
    15  	"cmd/compile/internal/ssa/ssabase"
    16  	"cmd/compile/internal/ssa/ssaop"
    17  	"cmd/compile/internal/types"
    18  	"cmd/internal/dwarf"
    19  	"cmd/internal/obj"
    20  	"cmd/internal/src"
    21  )
    22  
    23  // A FuncDebug contains all the debug information for the variables in a
    24  // function. Variables are identified by their LocalSlot, which may be
    25  // the result of decomposing a larger variable.
    26  type FuncDebug struct {
    27  	// Slots is all the slots used in the debug info, indexed by their SlotID.
    28  	Slots []ssa.LocalSlot
    29  	// The user variables, indexed by VarID.
    30  	Vars []*ir.Name
    31  	// The slots that make up each variable, indexed by VarID.
    32  	VarSlots [][]ssa.SlotID
    33  	// The location list data, indexed by VarID. Must be processed by PutLocationList.
    34  	LocationLists [][]ssa.LocListEntry
    35  	// Register-resident output parameters for the function. This is filled in at
    36  	// SSA generation time.
    37  	RegOutputParams []*ir.Name
    38  	// Variable declarations that were removed during optimization
    39  	OptDcl []*ir.Name
    40  	// The ssa.Func.EntryID value, used to build location lists for
    41  	// return values promoted to heap in later DWARF generation.
    42  	EntryID ssa.ID
    43  
    44  	// Filled in by the user. Translates Block and Value ID to PC.
    45  	//
    46  	// NOTE: block is only used if value is BlockStart.ID or BlockEnd.ID.
    47  	// Otherwise, it is ignored.
    48  	GetPC func(block, value ssa.ID) int64
    49  }
    50  
    51  // slotCanonicalizer is a table used to lookup and canonicalize
    52  // LocalSlot's in a type insensitive way (e.g. taking into account the
    53  // base name, offset, and width of the slot, but ignoring the slot
    54  // type).
    55  type slotCanonicalizer struct {
    56  	slmap  map[slotKey]SlKeyIdx
    57  	slkeys []ssa.LocalSlot
    58  }
    59  
    60  func newSlotCanonicalizer() *slotCanonicalizer {
    61  	return &slotCanonicalizer{
    62  		slmap:  make(map[slotKey]SlKeyIdx),
    63  		slkeys: []ssa.LocalSlot{ssa.LocalSlot{N: nil}},
    64  	}
    65  }
    66  
    67  type SlKeyIdx uint32
    68  
    69  const noSlot = SlKeyIdx(0)
    70  
    71  // slotKey is a type-insensitive encapsulation of a LocalSlot; it
    72  // is used to key a map within slotCanonicalizer.
    73  type slotKey struct {
    74  	name        *ir.Name
    75  	offset      int64
    76  	width       int64
    77  	splitOf     SlKeyIdx // idx in slkeys slice in slotCanonicalizer
    78  	splitOffset int64
    79  }
    80  
    81  // lookup looks up a LocalSlot in the slot canonicalizer "sc", returning
    82  // a canonical index for the slot, and adding it to the table if need
    83  // be. Return value is the canonical slot index, and a boolean indicating
    84  // whether the slot was found in the table already (TRUE => found).
    85  func (sc *slotCanonicalizer) lookup(ls ssa.LocalSlot) (SlKeyIdx, bool) {
    86  	split := noSlot
    87  	if ls.SplitOf != nil {
    88  		split, _ = sc.lookup(*ls.SplitOf)
    89  	}
    90  	k := slotKey{
    91  		name: ls.N, offset: ls.Off, width: ls.Type.Size(),
    92  		splitOf: split, splitOffset: ls.SplitOffset,
    93  	}
    94  	if idx, ok := sc.slmap[k]; ok {
    95  		return idx, true
    96  	}
    97  	rv := SlKeyIdx(len(sc.slkeys))
    98  	sc.slkeys = append(sc.slkeys, ls)
    99  	sc.slmap[k] = rv
   100  	return rv, false
   101  }
   102  
   103  func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) ssa.LocalSlot {
   104  	return sc.slkeys[idx]
   105  }
   106  
   107  // PopulateABIInRegArgOps examines the entry block of the function
   108  // and looks for incoming parameters that have missing or partial
   109  // OpArg{Int,Float}Reg values, inserting additional values in
   110  // cases where they are missing. Example:
   111  //
   112  //	func foo(s string, used int, notused int) int {
   113  //	  return len(s) + used
   114  //	}
   115  //
   116  // In the function above, the incoming parameter "used" is fully live,
   117  // "notused" is not live, and "s" is partially live (only the length
   118  // field of the string is used). At the point where debug value
   119  // analysis runs, we might expect to see an entry block with:
   120  //
   121  //	b1:
   122  //	  v4 = ArgIntReg <uintptr> {s+8} [0] : BX
   123  //	  v5 = ArgIntReg <int> {used} [0] : CX
   124  //
   125  // While this is an accurate picture of the live incoming params,
   126  // we also want to have debug locations for non-live params (or
   127  // their non-live pieces), e.g. something like
   128  //
   129  //	b1:
   130  //	  v9 = ArgIntReg <*uint8> {s+0} [0] : AX
   131  //	  v4 = ArgIntReg <uintptr> {s+8} [0] : BX
   132  //	  v5 = ArgIntReg <int> {used} [0] : CX
   133  //	  v10 = ArgIntReg <int> {unused} [0] : DI
   134  //
   135  // This function examines the live OpArg{Int,Float}Reg values and
   136  // synthesizes new (dead) values for the non-live params or the
   137  // non-live pieces of partially live params.
   138  func PopulateABIInRegArgOps(f *ssa.Func) {
   139  	pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
   140  
   141  	// When manufacturing new slots that correspond to splits of
   142  	// composite parameters, we want to avoid creating a new sub-slot
   143  	// that differs from some existing sub-slot only by type, since
   144  	// the debug location analysis will treat that slot as a separate
   145  	// entity. To achieve this, create a lookup table of existing
   146  	// slots that is type-insenstitive.
   147  	sc := newSlotCanonicalizer()
   148  	for _, sl := range f.Names {
   149  		sc.lookup(sl)
   150  	}
   151  
   152  	// Add slot -> value entry to f.NamedValues if not already present.
   153  	addToNV := func(v *ssa.Value, sl ssa.LocalSlot) {
   154  		values, ok := f.NamedValues[sl]
   155  		if !ok {
   156  			// Haven't seen this slot yet.
   157  			f.Names = append(f.Names, sl)
   158  		} else {
   159  			for _, ev := range values {
   160  				if v == ev {
   161  					return
   162  				}
   163  			}
   164  		}
   165  		values = append(values, v)
   166  		f.NamedValues[sl] = values
   167  	}
   168  
   169  	newValues := []*ssa.Value{}
   170  
   171  	abiRegIndexToRegister := func(reg abi.RegIndex) int8 {
   172  		i := f.ABISelf.FloatIndexFor(reg)
   173  		if i >= 0 { // float PR
   174  			return f.Config.FloatParamRegs[i]
   175  		} else {
   176  			return f.Config.IntParamRegs[reg]
   177  		}
   178  	}
   179  
   180  	// Helper to construct a new OpArg{Float,Int}Reg op value.
   181  	var pos src.XPos
   182  	if len(f.Entry.Values) != 0 {
   183  		pos = f.Entry.Values[0].Pos
   184  	}
   185  	synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl ssa.LocalSlot) *ssa.Value {
   186  		aux := &ssa.AuxNameOffset{Name: n, Offset: sl.Off}
   187  		op, auxInt := ssa.ArgOpAndRegisterFor(reg, f.ABISelf)
   188  		v := f.NewValueNoBlock(op, t, pos)
   189  		v.AuxInt = auxInt
   190  		v.Aux = aux
   191  		v.Args = nil
   192  		v.Block = f.Entry
   193  		newValues = append(newValues, v)
   194  		addToNV(v, sl)
   195  		f.SetHome(v, &f.Config.Registers[abiRegIndexToRegister(reg)])
   196  		return v
   197  	}
   198  
   199  	// Make a pass through the entry block looking for
   200  	// OpArg{Int,Float}Reg ops. Record the slots they use in a table
   201  	// ("sc"). We use a type-insensitive lookup for the slot table,
   202  	// since the type we get from the ABI analyzer won't always match
   203  	// what the compiler uses when creating OpArg{Int,Float}Reg ops.
   204  	for _, v := range f.Entry.Values {
   205  		if v.Op == ssaop.OpArgIntReg || v.Op == ssaop.OpArgFloatReg {
   206  			aux := v.Aux.(*ssa.AuxNameOffset)
   207  			sl := ssa.LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset}
   208  			// install slot in lookup table
   209  			idx, _ := sc.lookup(sl)
   210  			// add to f.NamedValues if not already present
   211  			addToNV(v, sc.canonSlot(idx))
   212  		} else if v.Op.IsCall() {
   213  			// if we hit a call, we've gone too far.
   214  			break
   215  		}
   216  	}
   217  
   218  	// Now make a pass through the ABI in-params, looking for params
   219  	// or pieces of params that we didn't encounter in the loop above.
   220  	for _, inp := range pri.InParams() {
   221  		if !isNamedRegParam(inp) {
   222  			continue
   223  		}
   224  		n := inp.Name
   225  
   226  		// Param is spread across one or more registers. Walk through
   227  		// each piece to see whether we've seen an arg reg op for it.
   228  		types, offsets := inp.RegisterTypesAndOffsets()
   229  		for k, t := range types {
   230  			// Note: this recipe for creating a LocalSlot is designed
   231  			// to be compatible with the one used in expand_calls.go
   232  			// as opposed to decompose.go. The expand calls code just
   233  			// takes the base name and creates an offset into it,
   234  			// without using the SplitOf/SplitOffset fields. The code
   235  			// in decompose.go does the opposite -- it creates a
   236  			// LocalSlot object with "Off" set to zero, but with
   237  			// SplitOf pointing to a parent slot, and SplitOffset
   238  			// holding the offset into the parent object.
   239  			pieceSlot := ssa.LocalSlot{N: n, Type: t, Off: offsets[k]}
   240  
   241  			// Look up this piece to see if we've seen a reg op
   242  			// for it. If not, create one.
   243  			_, found := sc.lookup(pieceSlot)
   244  			if !found {
   245  				// This slot doesn't appear in the map, meaning it
   246  				// corresponds to an in-param that is not live, or
   247  				// a portion of an in-param that is not live/used.
   248  				// Add a new dummy OpArg{Int,Float}Reg for it.
   249  				synthesizeOpIntFloatArg(n, t, inp.Registers[k],
   250  					pieceSlot)
   251  			}
   252  		}
   253  	}
   254  
   255  	// Insert the new values into the head of the block.
   256  	f.Entry.Values = append(newValues, f.Entry.Values...)
   257  }
   258  
   259  // BuildFuncDebug builds debug information for f, placing the results
   260  // in "rval". f must be fully processed, so that each Value is where it
   261  // will be when machine code is emitted.
   262  func BuildFuncDebug(ctxt *obj.Link, f *ssa.Func, loggingLevel int, stackOffset func(ssa.LocalSlot) int32, rval *FuncDebug) {
   263  	if f.RegAlloc == nil {
   264  		f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
   265  	}
   266  	state := &f.Cache.DebugState
   267  	state.LoggingLevel = loggingLevel % 1000
   268  
   269  	// A specific number demands exactly that many iterations. Under
   270  	// particular circumstances it make require more than the total of
   271  	// 2 passes implied by a single run through liveness and a single
   272  	// run through location list generation.
   273  	state.ConvergeCount = loggingLevel / 1000
   274  	state.F = f
   275  	state.Registers = f.Config.Registers
   276  	state.StackOffset = stackOffset
   277  	state.Ctxt = ctxt
   278  
   279  	if buildcfg.Experiment.RegabiArgs {
   280  		PopulateABIInRegArgOps(f)
   281  	}
   282  
   283  	if state.LoggingLevel > 0 {
   284  		state.Logf("Generating location lists for function %q\n", f.Name)
   285  	}
   286  
   287  	if state.VarParts == nil {
   288  		state.VarParts = make(map[*ir.Name][]ssa.SlotID)
   289  	} else {
   290  		clear(state.VarParts)
   291  	}
   292  
   293  	// Recompose any decomposed variables, and establish the canonical
   294  	// IDs for each var and slot by filling out state.vars and state.slots.
   295  
   296  	state.Slots = state.Slots[:0]
   297  	state.Vars = state.Vars[:0]
   298  	for i, slot := range f.Names {
   299  		state.Slots = append(state.Slots, slot)
   300  		if ir.IsSynthetic(slot.N) || !ssa.IsVarWantedForDebug(slot.N) {
   301  			continue
   302  		}
   303  
   304  		topSlot := slot
   305  		for topSlot.SplitOf != nil {
   306  			topSlot = *topSlot.SplitOf
   307  		}
   308  		if _, ok := state.VarParts[topSlot.N]; !ok {
   309  			state.Vars = append(state.Vars, topSlot.N)
   310  		}
   311  		state.VarParts[topSlot.N] = append(state.VarParts[topSlot.N], ssa.SlotID(i))
   312  	}
   313  
   314  	// Recreate the LocalSlot for each stack-only variable.
   315  	// This would probably be better as an output from stackframe.
   316  	for _, b := range f.Blocks {
   317  		for _, v := range b.Values {
   318  			if v.Op == ssaop.OpVarDef {
   319  				n := v.Aux.(*ir.Name)
   320  				if ir.IsSynthetic(n) || !ssa.IsVarWantedForDebug(n) {
   321  					continue
   322  				}
   323  
   324  				if _, ok := state.VarParts[n]; !ok {
   325  					slot := ssa.LocalSlot{N: n, Type: v.Type, Off: 0}
   326  					state.Slots = append(state.Slots, slot)
   327  					state.VarParts[n] = []ssa.SlotID{ssa.SlotID(len(state.Slots) - 1)}
   328  					state.Vars = append(state.Vars, n)
   329  				}
   330  			}
   331  		}
   332  	}
   333  
   334  	// Fill in the var<->slot mappings.
   335  	if cap(state.VarSlots) < len(state.Vars) {
   336  		state.VarSlots = make([][]ssa.SlotID, len(state.Vars))
   337  	} else {
   338  		state.VarSlots = state.VarSlots[:len(state.Vars)]
   339  		for i := range state.VarSlots {
   340  			state.VarSlots[i] = state.VarSlots[i][:0]
   341  		}
   342  	}
   343  	if cap(state.SlotVars) < len(state.Slots) {
   344  		state.SlotVars = make([]ssa.VarID, len(state.Slots))
   345  	} else {
   346  		state.SlotVars = state.SlotVars[:len(state.Slots)]
   347  	}
   348  
   349  	for varID, n := range state.Vars {
   350  		parts := state.VarParts[n]
   351  		slices.SortFunc(parts, func(a, b ssa.SlotID) int {
   352  			return cmp.Compare(varOffset(state.Slots[a]), varOffset(state.Slots[b]))
   353  		})
   354  
   355  		state.VarSlots[varID] = parts
   356  		for _, slotID := range parts {
   357  			state.SlotVars[slotID] = ssa.VarID(varID)
   358  		}
   359  	}
   360  
   361  	state.InitializeCache(f, len(state.VarParts), len(state.Slots))
   362  
   363  	for i, slot := range f.Names {
   364  		if ir.IsSynthetic(slot.N) || !ssa.IsVarWantedForDebug(slot.N) {
   365  			continue
   366  		}
   367  		for _, value := range f.NamedValues[slot] {
   368  			state.ValueNames[value.ID] = append(state.ValueNames[value.ID], ssa.SlotID(i))
   369  		}
   370  	}
   371  
   372  	blockLocs := state.Liveness()
   373  	state.BuildLocationLists(blockLocs)
   374  
   375  	// Populate "rval" with what we've computed.
   376  	rval.Slots = state.Slots
   377  	rval.VarSlots = state.VarSlots
   378  	rval.Vars = state.Vars
   379  	rval.LocationLists = state.Lists
   380  }
   381  
   382  // varOffset returns the offset of slot within the user variable it was
   383  // decomposed from. This has nothing to do with its stack offset.
   384  func varOffset(slot ssa.LocalSlot) int64 {
   385  	offset := slot.Off
   386  	s := &slot
   387  	for ; s.SplitOf != nil; s = s.SplitOf {
   388  		offset += s.SplitOffset
   389  	}
   390  	return offset
   391  }
   392  
   393  // PutLocationList adds entries (a location list in structured form)
   394  // to listSym, encoding it in the appropriate DWARF format.
   395  func (debugInfo *FuncDebug) PutLocationList(entries []ssa.LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
   396  	if buildcfg.Experiment.Dwarf5 {
   397  		debugInfo.PutLocationListDwarf5(entries, ctxt, listSym, startPC)
   398  	} else {
   399  		debugInfo.PutLocationListDwarf4(entries, ctxt, listSym, startPC)
   400  	}
   401  }
   402  
   403  // PutLocationListDwarf5 adds entries (a location list in structured form)
   404  // to listSym in DWARF 5 format.
   405  func (debugInfo *FuncDebug) PutLocationListDwarf5(entries []ssa.LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
   406  	getPC := debugInfo.GetPC
   407  
   408  	// base address entry
   409  	listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_base_addressx)
   410  	listSym.WriteDwTxtAddrx(ctxt, listSym.Size, startPC, ctxt.DwTextCount*2)
   411  
   412  	var stbuf, enbuf [10]byte
   413  	for _, entry := range entries {
   414  		begin := getPC(entry.StartBlock, entry.StartValue)
   415  		end := getPC(entry.EndBlock, entry.EndValue)
   416  
   417  		// Write LLE_offset_pair tag followed by payload (ULEB for start
   418  		// and then end).
   419  		listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_offset_pair)
   420  		stb := stbuf[:0]
   421  		enb := enbuf[:0]
   422  		stb = dwarf.AppendUleb128(stb, uint64(begin))
   423  		enb = dwarf.AppendUleb128(enb, uint64(end))
   424  		listSym.WriteBytes(ctxt, listSym.Size, stb)
   425  		listSym.WriteBytes(ctxt, listSym.Size, enb)
   426  
   427  		// DWARF5 uses ULEB128-encoded length for the location expression.
   428  		stb = stbuf[:0]
   429  		stb = dwarf.AppendUleb128(stb, uint64(len(entry.Expr)))
   430  		listSym.WriteBytes(ctxt, listSym.Size, stb)
   431  		listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
   432  	}
   433  
   434  	// Terminator
   435  	listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_end_of_list)
   436  }
   437  
   438  // PutLocationListDwarf4 adds entries (a location list in structured form)
   439  // to listSym in DWARF 4 format.
   440  func (debugInfo *FuncDebug) PutLocationListDwarf4(entries []ssa.LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
   441  	getPC := debugInfo.GetPC
   442  
   443  	if ctxt.UseBASEntries {
   444  		listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0)
   445  		listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
   446  	}
   447  
   448  	for _, entry := range entries {
   449  		begin := getPC(entry.StartBlock, entry.StartValue)
   450  		end := getPC(entry.EndBlock, entry.EndValue)
   451  
   452  		// Horrible hack. If a range contains only zero-width
   453  		// instructions, e.g. an Arg, and it's at the beginning of the
   454  		// function, this would be indistinguishable from an
   455  		// end entry. Fudge it.
   456  		if begin == 0 && end == 0 {
   457  			end = 1
   458  		}
   459  
   460  		if ctxt.UseBASEntries {
   461  			listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, begin)
   462  			listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, end)
   463  		} else {
   464  			listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, begin)
   465  			listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, end)
   466  		}
   467  
   468  		// Write 2-byte length prefix followed by the location expression.
   469  		listSym.WriteInt(ctxt, listSym.Size, 2, int64(len(entry.Expr)))
   470  		listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
   471  	}
   472  
   473  	// End entry.
   474  	listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
   475  	listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
   476  }
   477  
   478  // locatePrologEnd walks the entry block of a function with incoming
   479  // register arguments and locates the last instruction in the prolog
   480  // that spills a register arg. It returns the ID of that instruction,
   481  // and (where appropriate) the prolog's lowered closure ptr store inst.
   482  //
   483  // Example:
   484  //
   485  //	b1:
   486  //	    v3 = ArgIntReg <int> {p1+0} [0] : AX
   487  //	    ... more arg regs ..
   488  //	    v4 = ArgFloatReg <float32> {f1+0} [0] : X0
   489  //	    v52 = MOVQstore <mem> {p1} v2 v3 v1
   490  //	    ... more stores ...
   491  //	    v68 = MOVSSstore <mem> {f4} v2 v67 v66
   492  //	    v38 = MOVQstoreconst <mem> {blob} [val=0,off=0] v2 v32
   493  //
   494  // Important: locatePrologEnd is expected to work properly only with
   495  // optimization turned off (e.g. "-N"). If optimization is enabled
   496  // we can't be assured of finding all input arguments spilled in the
   497  // entry block prolog.
   498  func locatePrologEnd(f *ssa.Func, needCloCtx bool) (ssa.ID, *ssa.Value) {
   499  
   500  	// returns true if this instruction looks like it moves an ABI
   501  	// register (or context register for rangefunc bodies) to the
   502  	// stack, along with the value being stored.
   503  	isRegMoveLike := func(v *ssa.Value) (bool, ssa.ID) {
   504  		n, ok := v.Aux.(*ir.Name)
   505  		var r ssa.ID
   506  		if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
   507  			return false, r
   508  		}
   509  		regInputs, memInputs, spInputs := 0, 0, 0
   510  		for _, a := range v.Args {
   511  			if a.Op == ssaop.OpArgIntReg || a.Op == ssaop.OpArgFloatReg ||
   512  				(needCloCtx && a.Op.IsLoweredGetClosurePtr()) {
   513  				regInputs++
   514  				r = a.ID
   515  			} else if a.Type.IsMemory() {
   516  				memInputs++
   517  			} else if a.Op == ssaop.OpSP {
   518  				spInputs++
   519  			} else {
   520  				return false, r
   521  			}
   522  		}
   523  		return v.Type.IsMemory() && memInputs == 1 &&
   524  			regInputs == 1 && spInputs == 1, r
   525  	}
   526  
   527  	// OpArg*Reg values we've seen so far on our forward walk,
   528  	// for which we have not yet seen a corresponding spill.
   529  	regArgs := make([]ssa.ID, 0, 32)
   530  
   531  	// removeReg tries to remove a value from regArgs, returning true
   532  	// if found and removed, or false otherwise.
   533  	removeReg := func(r ssa.ID) bool {
   534  		for i := 0; i < len(regArgs); i++ {
   535  			if regArgs[i] == r {
   536  				regArgs = slices.Delete(regArgs, i, i+1)
   537  				return true
   538  			}
   539  		}
   540  		return false
   541  	}
   542  
   543  	// Walk forwards through the block. When we see OpArg*Reg, record
   544  	// the value it produces in the regArgs list. When see a store that uses
   545  	// the value, remove the entry. When we hit the last store (use)
   546  	// then we've arrived at the end of the prolog.
   547  	var cloRegStore *ssa.Value
   548  	for k, v := range f.Entry.Values {
   549  		if v.Op == ssaop.OpArgIntReg || v.Op == ssaop.OpArgFloatReg {
   550  			regArgs = append(regArgs, v.ID)
   551  			continue
   552  		}
   553  		if needCloCtx && v.Op.IsLoweredGetClosurePtr() {
   554  			regArgs = append(regArgs, v.ID)
   555  			cloRegStore = v
   556  			continue
   557  		}
   558  		if ok, r := isRegMoveLike(v); ok {
   559  			if removed := removeReg(r); removed {
   560  				if len(regArgs) == 0 {
   561  					// Found our last spill; return the value after
   562  					// it. Note that it is possible that this spill is
   563  					// the last instruction in the block. If so, then
   564  					// return the "end of block" sentinel.
   565  					if k < len(f.Entry.Values)-1 {
   566  						return f.Entry.Values[k+1].ID, cloRegStore
   567  					}
   568  					return ssa.BlockEnd.ID, cloRegStore
   569  				}
   570  			}
   571  		}
   572  		if v.Op.IsCall() {
   573  			// if we hit a call, we've gone too far.
   574  			return v.ID, cloRegStore
   575  		}
   576  	}
   577  	// nothing found
   578  	return ssa.ID(-1), cloRegStore
   579  }
   580  
   581  // isNamedRegParam returns true if the param corresponding to "p"
   582  // is a named, non-blank input parameter assigned to one or more
   583  // registers.
   584  func isNamedRegParam(p abi.ABIParamAssignment) bool {
   585  	if p.Name == nil {
   586  		return false
   587  	}
   588  	n := p.Name
   589  	if n.Sym() == nil || n.Sym().IsBlank() {
   590  		return false
   591  	}
   592  	if len(p.Registers) == 0 {
   593  		return false
   594  	}
   595  	return true
   596  }
   597  
   598  // BuildFuncDebugNoOptimized populates a FuncDebug object "rval" with
   599  // entries corresponding to the register-resident input parameters for
   600  // the function "f"; it is used when we are compiling without
   601  // optimization but the register ABI is enabled. For each reg param,
   602  // it constructs a 2-element location list: the first element holds
   603  // the input register, and the second element holds the stack location
   604  // of the param (the assumption being that when optimization is off,
   605  // each input param reg will be spilled in the prolog). In addition
   606  // to the register params, here we also build location lists (where
   607  // appropriate for the ".closureptr" compiler-synthesized variable
   608  // needed by the debugger for range func bodies.
   609  func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *ssa.Func, loggingEnabled bool, stackOffset func(ssa.LocalSlot) int32, rval *FuncDebug) {
   610  	needCloCtx := f.CloSlot != nil
   611  	pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
   612  
   613  	// Look to see if we have any named register-promoted parameters,
   614  	// and/or whether we need location info for the ".closureptr"
   615  	// synthetic variable; if not bail early and let the caller sort
   616  	// things out for the remainder of the params/locals.
   617  	numRegParams := 0
   618  	for _, inp := range pri.InParams() {
   619  		if isNamedRegParam(inp) {
   620  			numRegParams++
   621  		}
   622  	}
   623  	if numRegParams == 0 && !needCloCtx {
   624  		return
   625  	}
   626  
   627  	state := ssa.DebugState{F: f}
   628  
   629  	if loggingEnabled {
   630  		state.Logf("generating -N reg param loc lists for func %q\n", f.Name)
   631  	}
   632  
   633  	// cloReg stores the obj register num that the context register
   634  	// appears in within the function prolog, where appropriate.
   635  	var cloReg int16
   636  
   637  	extraForCloCtx := 0
   638  	if needCloCtx {
   639  		extraForCloCtx = 1
   640  	}
   641  
   642  	// Allocate location lists.
   643  	rval.LocationLists = make([][]ssa.LocListEntry, numRegParams+extraForCloCtx)
   644  
   645  	// Locate the value corresponding to the last spill of
   646  	// an input register.
   647  	afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
   648  
   649  	if needCloCtx {
   650  		reg, _ := state.F.GetHome(cloRegStore.ID).(*ssabase.Register)
   651  		cloReg = reg.ObjNum
   652  		if loggingEnabled {
   653  			state.Logf("needCloCtx is true for func %q, cloreg=%v\n",
   654  				f.Name, reg)
   655  		}
   656  	}
   657  
   658  	addVarSlot := func(name *ir.Name, typ *types.Type) {
   659  		sl := ssa.LocalSlot{N: name, Type: typ, Off: 0}
   660  		rval.Vars = append(rval.Vars, name)
   661  		rval.Slots = append(rval.Slots, sl)
   662  		slid := len(rval.VarSlots)
   663  		rval.VarSlots = append(rval.VarSlots, []ssa.SlotID{ssa.SlotID(slid)})
   664  	}
   665  
   666  	// Make an initial pass to populate the vars/slots for our return
   667  	// value, covering first the input parameters and then (if needed)
   668  	// the special ".closureptr" var for rangefunc bodies.
   669  	params := []abi.ABIParamAssignment{}
   670  	for _, inp := range pri.InParams() {
   671  		if !isNamedRegParam(inp) {
   672  			// will be sorted out elsewhere
   673  			continue
   674  		}
   675  		if !ssa.IsVarWantedForDebug(inp.Name) {
   676  			continue
   677  		}
   678  		addVarSlot(inp.Name, inp.Type)
   679  		params = append(params, inp)
   680  	}
   681  	if needCloCtx {
   682  		addVarSlot(f.CloSlot, f.CloSlot.Type())
   683  		cloAssign := abi.ABIParamAssignment{
   684  			Type:      f.CloSlot.Type(),
   685  			Name:      f.CloSlot,
   686  			Registers: []abi.RegIndex{0}, // dummy
   687  		}
   688  		params = append(params, cloAssign)
   689  	}
   690  
   691  	// Walk the input params again and process the register-resident elements.
   692  	pidx := 0
   693  	for _, inp := range params {
   694  		if !isNamedRegParam(inp) {
   695  			// will be sorted out elsewhere
   696  			continue
   697  		}
   698  		if !ssa.IsVarWantedForDebug(inp.Name) {
   699  			continue
   700  		}
   701  
   702  		sl := rval.Slots[pidx]
   703  		n := rval.Vars[pidx]
   704  
   705  		if afterPrologVal == ssa.ID(-1) {
   706  			// This can happen for degenerate functions with infinite
   707  			// loops such as that in issue 45948. In such cases, leave
   708  			// the var/slot set up for the param, but don't try to
   709  			// emit a location list.
   710  			if loggingEnabled {
   711  				state.Logf("locatePrologEnd failed, skipping %v\n", n)
   712  			}
   713  			pidx++
   714  			continue
   715  		}
   716  
   717  		// Param is arriving in one or more registers. We need a 2-element
   718  		// location expression for it. First entry in location list
   719  		// will correspond to lifetime in input registers.
   720  		if loggingEnabled {
   721  			state.Logf("param %v:\n  [<entry>, %d]:\n", n, afterPrologVal)
   722  		}
   723  		var regExpr []byte
   724  		rtypes, _ := inp.RegisterTypesAndOffsets()
   725  		padding := make([]uint64, 0, 32)
   726  		padding = inp.ComputePadding(padding)
   727  		for k, r := range inp.Registers {
   728  			var reg int16
   729  			if n == f.CloSlot {
   730  				reg = cloReg
   731  			} else {
   732  				reg = ssa.ObjRegForAbiReg(r, f.Config)
   733  			}
   734  			dwreg := ctxt.Arch.DWARFRegisters[reg]
   735  			if dwreg < 32 {
   736  				regExpr = append(regExpr, dwarf.DW_OP_reg0+byte(dwreg))
   737  			} else {
   738  				regExpr = append(regExpr, dwarf.DW_OP_regx)
   739  				regExpr = dwarf.AppendUleb128(regExpr, uint64(dwreg))
   740  			}
   741  			if loggingEnabled {
   742  				state.Logf("    piece %d -> dwreg %d", k, dwreg)
   743  			}
   744  			if len(inp.Registers) > 1 {
   745  				regExpr = append(regExpr, dwarf.DW_OP_piece)
   746  				ts := rtypes[k].Size()
   747  				regExpr = dwarf.AppendUleb128(regExpr, uint64(ts))
   748  				if padding[k] > 0 {
   749  					if loggingEnabled {
   750  						state.Logf(" [pad %d bytes]", padding[k])
   751  					}
   752  					regExpr = append(regExpr, dwarf.DW_OP_piece)
   753  					regExpr = dwarf.AppendUleb128(regExpr, padding[k])
   754  				}
   755  			}
   756  			if loggingEnabled {
   757  				state.Logf("\n")
   758  			}
   759  		}
   760  		rval.LocationLists[pidx] = append(rval.LocationLists[pidx], ssa.LocListEntry{
   761  			StartBlock: f.Entry.ID,
   762  			StartValue: ssa.BlockStart.ID,
   763  			EndBlock:   f.Entry.ID,
   764  			EndValue:   afterPrologVal,
   765  			Expr:       regExpr,
   766  		})
   767  
   768  		// Second entry in the location list will be the stack home
   769  		// of the param, once it has been spilled.  Emit that now.
   770  		var stackExpr []byte
   771  		soff := stackOffset(sl)
   772  		if soff == 0 {
   773  			stackExpr = append(stackExpr, dwarf.DW_OP_call_frame_cfa)
   774  		} else {
   775  			stackExpr = append(stackExpr, dwarf.DW_OP_fbreg)
   776  			stackExpr = dwarf.AppendSleb128(stackExpr, int64(soff))
   777  		}
   778  		if loggingEnabled {
   779  			state.Logf("  [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
   780  		}
   781  
   782  		rval.LocationLists[pidx] = append(rval.LocationLists[pidx], ssa.LocListEntry{
   783  			StartBlock: f.Entry.ID,
   784  			StartValue: afterPrologVal,
   785  			EndBlock:   f.Entry.ID,
   786  			EndValue:   ssa.FuncEnd.ID,
   787  			Expr:       stackExpr,
   788  		})
   789  
   790  		pidx++
   791  	}
   792  }
   793  

View as plain text