mirror of https://github.com/golang/go.git
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:
parent
4256ec1661
commit
a130fb6309
|
|
@ -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
|
// locatePrologEnd walks the entry block of a function with incoming
|
||||||
// register arguments and locates the last instruction in the prolog
|
// 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:
|
// Example:
|
||||||
//
|
//
|
||||||
// b1:
|
// 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
|
// optimization turned off (e.g. "-N"). If optimization is enabled
|
||||||
// we can't be assured of finding all input arguments spilled in the
|
// we can't be assured of finding all input arguments spilled in the
|
||||||
// entry block prolog.
|
// 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
|
// 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) {
|
isRegMoveLike := func(v *Value) (bool, ID) {
|
||||||
n, ok := v.Aux.(*ir.Name)
|
n, ok := v.Aux.(*ir.Name)
|
||||||
var r ID
|
var r ID
|
||||||
if !ok || n.Class != ir.PPARAM {
|
if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
|
||||||
return false, r
|
return false, r
|
||||||
}
|
}
|
||||||
regInputs, memInputs, spInputs := 0, 0, 0
|
regInputs, memInputs, spInputs := 0, 0, 0
|
||||||
for _, a := range v.Args {
|
for _, a := range v.Args {
|
||||||
if a.Op == OpArgIntReg || a.Op == OpArgFloatReg {
|
if a.Op == OpArgIntReg || a.Op == OpArgFloatReg ||
|
||||||
|
(needCloCtx && a.Op.isLoweredGetClosurePtr()) {
|
||||||
regInputs++
|
regInputs++
|
||||||
r = a.ID
|
r = a.ID
|
||||||
} else if a.Type.IsMemory() {
|
} 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 it produces in the regArgs list. When see a store that uses
|
||||||
// the value, remove the entry. When we hit the last store (use)
|
// the value, remove the entry. When we hit the last store (use)
|
||||||
// then we've arrived at the end of the prolog.
|
// then we've arrived at the end of the prolog.
|
||||||
|
var cloRegStore *Value
|
||||||
for k, v := range f.Entry.Values {
|
for k, v := range f.Entry.Values {
|
||||||
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
|
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
|
||||||
regArgs = append(regArgs, v.ID)
|
regArgs = append(regArgs, v.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if needCloCtx && v.Op.isLoweredGetClosurePtr() {
|
||||||
|
regArgs = append(regArgs, v.ID)
|
||||||
|
cloRegStore = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
if ok, r := isRegMoveLike(v); ok {
|
if ok, r := isRegMoveLike(v); ok {
|
||||||
if removed := removeReg(r); removed {
|
if removed := removeReg(r); removed {
|
||||||
if len(regArgs) == 0 {
|
if len(regArgs) == 0 {
|
||||||
|
|
@ -1715,19 +1725,19 @@ func locatePrologEnd(f *Func) ID {
|
||||||
// the last instruction in the block. If so, then
|
// the last instruction in the block. If so, then
|
||||||
// return the "end of block" sentinel.
|
// return the "end of block" sentinel.
|
||||||
if k < len(f.Entry.Values)-1 {
|
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 v.Op.IsCall() {
|
||||||
// if we hit a call, we've gone too far.
|
// if we hit a call, we've gone too far.
|
||||||
return v.ID
|
return v.ID, cloRegStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// nothing found
|
// nothing found
|
||||||
return ID(-1)
|
return ID(-1), cloRegStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// isNamedRegParam returns true if the param corresponding to "p"
|
// 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
|
// it constructs a 2-element location list: the first element holds
|
||||||
// the input register, and the second element holds the stack location
|
// the input register, and the second element holds the stack location
|
||||||
// of the param (the assumption being that when optimization is off,
|
// 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) {
|
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)
|
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
|
||||||
|
|
||||||
// Look to see if we have any named register-promoted parameters.
|
// Look to see if we have any named register-promoted parameters,
|
||||||
// If there are none, bail early and let the caller sort things
|
// and/or whether we need location info for the ".closureptr"
|
||||||
// out for the remainder of the params/locals.
|
// synthetic variable; if not bail early and let the caller sort
|
||||||
|
// things out for the remainder of the params/locals.
|
||||||
numRegParams := 0
|
numRegParams := 0
|
||||||
for _, inp := range pri.InParams() {
|
for _, inp := range pri.InParams() {
|
||||||
if isNamedRegParam(inp) {
|
if isNamedRegParam(inp) {
|
||||||
numRegParams++
|
numRegParams++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if numRegParams == 0 {
|
if numRegParams == 0 && !needCloCtx {
|
||||||
return
|
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)
|
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.
|
// Allocate location lists.
|
||||||
rval.LocationLists = make([][]byte, numRegParams)
|
rval.LocationLists = make([][]byte, numRegParams+extraForCloCtx)
|
||||||
|
|
||||||
// Locate the value corresponding to the last spill of
|
// Locate the value corresponding to the last spill of
|
||||||
// an input register.
|
// an input register.
|
||||||
afterPrologVal := locatePrologEnd(f)
|
afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
|
||||||
|
|
||||||
// Walk the input params again and process the register-resident elements.
|
if needCloCtx {
|
||||||
pidx := 0
|
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() {
|
for _, inp := range pri.InParams() {
|
||||||
if !isNamedRegParam(inp) {
|
if !isNamedRegParam(inp) {
|
||||||
// will be sorted out elsewhere
|
// will be sorted out elsewhere
|
||||||
continue
|
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
|
// Walk the input params again and process the register-resident elements.
|
||||||
sl := LocalSlot{N: n, Type: inp.Type, Off: 0}
|
pidx := 0
|
||||||
rval.Vars = append(rval.Vars, n)
|
for _, inp := range params {
|
||||||
rval.Slots = append(rval.Slots, sl)
|
if !isNamedRegParam(inp) {
|
||||||
slid := len(rval.VarSlots)
|
// will be sorted out elsewhere
|
||||||
rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sl := rval.Slots[pidx]
|
||||||
|
n := rval.Vars[pidx]
|
||||||
|
|
||||||
if afterPrologVal == ID(-1) {
|
if afterPrologVal == ID(-1) {
|
||||||
// This can happen for degenerate functions with infinite
|
// 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 := make([]uint64, 0, 32)
|
||||||
padding = inp.ComputePadding(padding)
|
padding = inp.ComputePadding(padding)
|
||||||
for k, r := range inp.Registers {
|
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]
|
dwreg := ctxt.Arch.DWARFRegisters[reg]
|
||||||
if dwreg < 32 {
|
if dwreg < 32 {
|
||||||
list = append(list, dwarf.DW_OP_reg0+byte(dwreg))
|
list = append(list, dwarf.DW_OP_reg0+byte(dwreg))
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ type Func struct {
|
||||||
RegArgs []Spill
|
RegArgs []Spill
|
||||||
// OwnAux describes parameters and results for this function.
|
// OwnAux describes parameters and results for this function.
|
||||||
OwnAux *AuxCall
|
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.
|
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.
|
freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil.
|
||||||
|
|
|
||||||
|
|
@ -524,6 +524,7 @@ func buildssa(fn *ir.Func, worker int, isPgoHot bool) *ssa.Func {
|
||||||
cloSlot.SetUsed(true)
|
cloSlot.SetUsed(true)
|
||||||
cloSlot.SetEsc(ir.EscNever)
|
cloSlot.SetEsc(ir.EscNever)
|
||||||
cloSlot.SetAddrtaken(true)
|
cloSlot.SetAddrtaken(true)
|
||||||
|
s.f.CloSlot = cloSlot
|
||||||
s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, cloSlot, s.mem(), false)
|
s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, cloSlot, s.mem(), false)
|
||||||
addr := s.addr(cloSlot)
|
addr := s.addr(cloSlot)
|
||||||
s.store(s.f.Config.Types.BytePtr, addr, clo)
|
s.store(s.f.Config.Types.BytePtr, addr, clo)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue