]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/ssa: fix DWARF location expr for .closureptr
authorThan McIntosh <thanm@google.com>
Mon, 10 Jun 2024 14:25:22 +0000 (14:25 +0000)
committerThan McIntosh <thanm@google.com>
Mon, 10 Jun 2024 19:44:43 +0000 (19:44 +0000)
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>
src/cmd/compile/internal/ssa/debug.go
src/cmd/compile/internal/ssa/func.go
src/cmd/compile/internal/ssagen/ssa.go

index 05a72787f345fa01672f71c137714e580a786cde..4abe5a98924f49e1067a47a12dbc0face3a83e78 100644 (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)
+
+       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)
+       }
 
        // Walk the input params again and process the register-resident elements.
        pidx := 0
-       for _, inp := range pri.InParams() {
+       for _, inp := range params {
                if !isNamedRegParam(inp) {
                        // will be sorted out elsewhere
                        continue
                }
 
-               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)})
+               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))
index 2bb34a41cbe0fc921c3e2dc6af65c2f215af7b89..62472cc94eb8a557ab9a56f36e97c3ce95744b53 100644 (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.
index 0d2693ea33931820d447fcb25d1d367673147faa..26d236dcacafb4d073a42664d435d150b3b68910 100644 (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)