1
0
mirror of https://github.com/golang/go synced 2024-11-14 13:50:23 -07:00

cmd/compile/internal/ssa: fix DWARF location expr for .closureptr

CL 586975 added support to the compiler back end to emit a synthetic
".closureptr" variable in range func bodies, plus code to spill the
incoming context pointer to that variable's location on the stack.

This patch fixes up the code in the back end that generates DWARF
location lists for incoming parameters (which sometimes arrive in
registers) in the "-l -N" no-optimization case to also create a
correct DWARF location list for ".closureptr", a two-piece list
reflecting the fact that its value arrives in a register and then is
spilled to the stack in the prolog.

Fixes #67918.

Change-Id: I029305b5248b8140253fdeb6821b877916fbb87a
Reviewed-on: https://go-review.googlesource.com/c/go/+/591595
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
Than McIntosh 2024-06-10 14:25:22 +00:00
parent 4256ec1661
commit a130fb6309
3 changed files with 93 additions and 25 deletions

View File

@ -1639,7 +1639,9 @@ func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int)
// locatePrologEnd walks the entry block of a function with incoming
// register arguments and locates the last instruction in the prolog
// that spills a register arg. It returns the ID of that instruction
// that spills a register arg. It returns the ID of that instruction,
// and (where appropriate) the prolog's lowered closure ptr store inst.
//
// Example:
//
// b1:
@ -1655,19 +1657,21 @@ func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int)
// optimization turned off (e.g. "-N"). If optimization is enabled
// we can't be assured of finding all input arguments spilled in the
// entry block prolog.
func locatePrologEnd(f *Func) ID {
func locatePrologEnd(f *Func, needCloCtx bool) (ID, *Value) {
// returns true if this instruction looks like it moves an ABI
// register to the stack, along with the value being stored.
// register (or context register for rangefunc bodies) to the
// stack, along with the value being stored.
isRegMoveLike := func(v *Value) (bool, ID) {
n, ok := v.Aux.(*ir.Name)
var r ID
if !ok || n.Class != ir.PPARAM {
if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
return false, r
}
regInputs, memInputs, spInputs := 0, 0, 0
for _, a := range v.Args {
if a.Op == OpArgIntReg || a.Op == OpArgFloatReg {
if a.Op == OpArgIntReg || a.Op == OpArgFloatReg ||
(needCloCtx && a.Op.isLoweredGetClosurePtr()) {
regInputs++
r = a.ID
} else if a.Type.IsMemory() {
@ -1702,11 +1706,17 @@ func locatePrologEnd(f *Func) ID {
// the value it produces in the regArgs list. When see a store that uses
// the value, remove the entry. When we hit the last store (use)
// then we've arrived at the end of the prolog.
var cloRegStore *Value
for k, v := range f.Entry.Values {
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
regArgs = append(regArgs, v.ID)
continue
}
if needCloCtx && v.Op.isLoweredGetClosurePtr() {
regArgs = append(regArgs, v.ID)
cloRegStore = v
continue
}
if ok, r := isRegMoveLike(v); ok {
if removed := removeReg(r); removed {
if len(regArgs) == 0 {
@ -1715,19 +1725,19 @@ func locatePrologEnd(f *Func) ID {
// the last instruction in the block. If so, then
// return the "end of block" sentinel.
if k < len(f.Entry.Values)-1 {
return f.Entry.Values[k+1].ID
return f.Entry.Values[k+1].ID, cloRegStore
}
return BlockEnd.ID
return BlockEnd.ID, cloRegStore
}
}
}
if v.Op.IsCall() {
// if we hit a call, we've gone too far.
return v.ID
return v.ID, cloRegStore
}
}
// nothing found
return ID(-1)
return ID(-1), cloRegStore
}
// isNamedRegParam returns true if the param corresponding to "p"
@ -1754,21 +1764,26 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
// it constructs a 2-element location list: the first element holds
// the input register, and the second element holds the stack location
// of the param (the assumption being that when optimization is off,
// each input param reg will be spilled in the prolog).
// each input param reg will be spilled in the prolog). In addition
// to the register params, here we also build location lists (where
// appropriate for the ".closureptr" compiler-synthesized variable
// needed by the debugger for range func bodies.
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
needCloCtx := f.CloSlot != nil
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
// Look to see if we have any named register-promoted parameters.
// If there are none, bail early and let the caller sort things
// out for the remainder of the params/locals.
// Look to see if we have any named register-promoted parameters,
// and/or whether we need location info for the ".closureptr"
// synthetic variable; if not bail early and let the caller sort
// things out for the remainder of the params/locals.
numRegParams := 0
for _, inp := range pri.InParams() {
if isNamedRegParam(inp) {
numRegParams++
}
}
if numRegParams == 0 {
if numRegParams == 0 && !needCloCtx {
return
}
@ -1778,27 +1793,71 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
state.logf("generating -N reg param loc lists for func %q\n", f.Name)
}
// cloReg stores the obj register num that the context register
// appears in within the function prolog, where appropriate.
var cloReg int16
extraForCloCtx := 0
if needCloCtx {
extraForCloCtx = 1
}
// Allocate location lists.
rval.LocationLists = make([][]byte, numRegParams)
rval.LocationLists = make([][]byte, numRegParams+extraForCloCtx)
// Locate the value corresponding to the last spill of
// an input register.
afterPrologVal := locatePrologEnd(f)
afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
// Walk the input params again and process the register-resident elements.
pidx := 0
if needCloCtx {
reg, _ := state.f.getHome(cloRegStore.ID).(*Register)
cloReg = reg.ObjNum()
if loggingEnabled {
state.logf("needCloCtx is true for func %q, cloreg=%v\n",
f.Name, reg)
}
}
addVarSlot := func(name *ir.Name, typ *types.Type) {
sl := LocalSlot{N: name, Type: typ, Off: 0}
rval.Vars = append(rval.Vars, name)
rval.Slots = append(rval.Slots, sl)
slid := len(rval.VarSlots)
rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
}
// Make an initial pass to populate the vars/slots for our return
// value, covering first the input parameters and then (if needed)
// the special ".closureptr" var for rangefunc bodies.
params := []abi.ABIParamAssignment{}
for _, inp := range pri.InParams() {
if !isNamedRegParam(inp) {
// will be sorted out elsewhere
continue
}
addVarSlot(inp.Name, inp.Type)
params = append(params, inp)
}
if needCloCtx {
addVarSlot(f.CloSlot, f.CloSlot.Type())
cloAssign := abi.ABIParamAssignment{
Type: f.CloSlot.Type(),
Name: f.CloSlot,
Registers: []abi.RegIndex{0}, // dummy
}
params = append(params, cloAssign)
}
n := inp.Name
sl := LocalSlot{N: n, Type: inp.Type, Off: 0}
rval.Vars = append(rval.Vars, n)
rval.Slots = append(rval.Slots, sl)
slid := len(rval.VarSlots)
rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
// Walk the input params again and process the register-resident elements.
pidx := 0
for _, inp := range params {
if !isNamedRegParam(inp) {
// will be sorted out elsewhere
continue
}
sl := rval.Slots[pidx]
n := rval.Vars[pidx]
if afterPrologVal == ID(-1) {
// This can happen for degenerate functions with infinite
@ -1828,7 +1887,12 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
padding := make([]uint64, 0, 32)
padding = inp.ComputePadding(padding)
for k, r := range inp.Registers {
reg := ObjRegForAbiReg(r, f.Config)
var reg int16
if n == f.CloSlot {
reg = cloReg
} else {
reg = ObjRegForAbiReg(r, f.Config)
}
dwreg := ctxt.Arch.DWARFRegisters[reg]
if dwreg < 32 {
list = append(list, dwarf.DW_OP_reg0+byte(dwreg))

View File

@ -67,6 +67,9 @@ type Func struct {
RegArgs []Spill
// OwnAux describes parameters and results for this function.
OwnAux *AuxCall
// CloSlot holds the compiler-synthesized name (".closureptr")
// where we spill the closure pointer for range func bodies.
CloSlot *ir.Name
freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil.
freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil.

View File

@ -524,6 +524,7 @@ func buildssa(fn *ir.Func, worker int, isPgoHot bool) *ssa.Func {
cloSlot.SetUsed(true)
cloSlot.SetEsc(ir.EscNever)
cloSlot.SetAddrtaken(true)
s.f.CloSlot = cloSlot
s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, cloSlot, s.mem(), false)
addr := s.addr(cloSlot)
s.store(s.f.Config.Types.BytePtr, addr, clo)