runtime: remove the pc field of _defer struct

Since we always can get the address of `CALL runtime.deferreturn(SB)`
from the unwinder, so it is not necessary to record the caller's pc
in the _defer struct. For the stack allocated _defer, this CL makes
the frame smaller.

Change-Id: I0fd347e4bc07cf8a9b954816323df30fc52552b6
Reviewed-on: https://go-review.googlesource.com/c/go/+/716720
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
Youlin Feng 2025-10-31 10:45:26 +08:00 committed by Keith Randall
parent 00ee1860ce
commit 361d51a6b5
4 changed files with 9 additions and 21 deletions

View file

@ -7797,7 +7797,7 @@ func callTargetLSym(callee *ir.Name) *obj.LSym {
} }
// deferStructFnField is the field index of _defer.fn. // deferStructFnField is the field index of _defer.fn.
const deferStructFnField = 4 const deferStructFnField = 3
var deferType *types.Type var deferType *types.Type
@ -7817,7 +7817,6 @@ func deferstruct() *types.Type {
makefield("heap", types.Types[types.TBOOL]), makefield("heap", types.Types[types.TBOOL]),
makefield("rangefunc", types.Types[types.TBOOL]), makefield("rangefunc", types.Types[types.TBOOL]),
makefield("sp", types.Types[types.TUINTPTR]), makefield("sp", types.Types[types.TUINTPTR]),
makefield("pc", types.Types[types.TUINTPTR]),
// Note: the types here don't really matter. Defer structures // Note: the types here don't really matter. Defer structures
// are always scanned explicitly during stack copying and GC, // are always scanned explicitly during stack copying and GC,
// so we make them uintptr type even though they are real pointers. // so we make them uintptr type even though they are real pointers.

View file

@ -382,7 +382,6 @@ func dumpgoroutine(gp *g) {
dumpint(uint64(uintptr(unsafe.Pointer(d)))) dumpint(uint64(uintptr(unsafe.Pointer(d))))
dumpint(uint64(uintptr(unsafe.Pointer(gp)))) dumpint(uint64(uintptr(unsafe.Pointer(gp))))
dumpint(uint64(d.sp)) dumpint(uint64(d.sp))
dumpint(uint64(d.pc))
fn := *(**funcval)(unsafe.Pointer(&d.fn)) fn := *(**funcval)(unsafe.Pointer(&d.fn))
dumpint(uint64(uintptr(unsafe.Pointer(fn)))) dumpint(uint64(uintptr(unsafe.Pointer(fn))))
if d.fn == nil { if d.fn == nil {

View file

@ -354,7 +354,6 @@ func deferproc(fn func()) {
d.link = gp._defer d.link = gp._defer
gp._defer = d gp._defer = d
d.fn = fn d.fn = fn
d.pc = sys.GetCallerPC()
// We must not be preempted between calling GetCallerSP and // We must not be preempted between calling GetCallerSP and
// storing it to d.sp because GetCallerSP's result is a // storing it to d.sp because GetCallerSP's result is a
// uintptr stack pointer. // uintptr stack pointer.
@ -458,7 +457,6 @@ func deferrangefunc() any {
d := newdefer() d := newdefer()
d.link = gp._defer d.link = gp._defer
gp._defer = d gp._defer = d
d.pc = sys.GetCallerPC()
// We must not be preempted between calling GetCallerSP and // We must not be preempted between calling GetCallerSP and
// storing it to d.sp because GetCallerSP's result is a // storing it to d.sp because GetCallerSP's result is a
// uintptr stack pointer. // uintptr stack pointer.
@ -518,7 +516,6 @@ func deferconvert(d0 *_defer) {
} }
for d1 := d; ; d1 = d1.link { for d1 := d; ; d1 = d1.link {
d1.sp = d0.sp d1.sp = d0.sp
d1.pc = d0.pc
if d1.link == nil { if d1.link == nil {
d1.link = tail d1.link = tail
break break
@ -547,7 +544,6 @@ func deferprocStack(d *_defer) {
d.heap = false d.heap = false
d.rangefunc = false d.rangefunc = false
d.sp = sys.GetCallerSP() d.sp = sys.GetCallerSP()
d.pc = sys.GetCallerPC()
// The lines below implement: // The lines below implement:
// d.panic = nil // d.panic = nil
// d.fd = nil // d.fd = nil
@ -977,8 +973,6 @@ func (p *_panic) nextDefer() (func(), bool) {
fn := d.fn fn := d.fn
p.retpc = d.pc
// Unlink and free. // Unlink and free.
popDefer(gp) popDefer(gp)
@ -1018,6 +1012,12 @@ func (p *_panic) nextFrame() (ok bool) {
// it's non-zero. // it's non-zero.
if u.frame.sp == limit { if u.frame.sp == limit {
f := u.frame.fn
if f.deferreturn == 0 {
throw("no deferreturn")
}
p.retpc = f.entry() + uintptr(f.deferreturn)
break // found a frame with linked defers break // found a frame with linked defers
} }
@ -1273,15 +1273,6 @@ func recovery(gp *g) {
pc, sp, fp := p.retpc, uintptr(p.sp), uintptr(p.fp) pc, sp, fp := p.retpc, uintptr(p.sp), uintptr(p.fp)
p0, saveOpenDeferState := p, p.deferBitsPtr != nil && *p.deferBitsPtr != 0 p0, saveOpenDeferState := p, p.deferBitsPtr != nil && *p.deferBitsPtr != 0
// The linker records the f-relative address of a call to deferreturn in f's funcInfo.
// Assuming a "normal" call to recover() inside one of f's deferred functions
// invoked for a panic, that is the desired PC for exiting f.
f := findfunc(pc)
if f.deferreturn == 0 {
throw("no deferreturn")
}
gotoPc := f.entry() + uintptr(f.deferreturn)
// Unwind the panic stack. // Unwind the panic stack.
for ; p != nil && uintptr(p.startSP) < sp; p = p.link { for ; p != nil && uintptr(p.startSP) < sp; p = p.link {
// Don't allow jumping past a pending Goexit. // Don't allow jumping past a pending Goexit.
@ -1304,7 +1295,7 @@ func recovery(gp *g) {
// With how subtle defer handling is, this might not actually be // With how subtle defer handling is, this might not actually be
// worthwhile though. // worthwhile though.
if p.goexit { if p.goexit {
gotoPc, sp = p.startPC, uintptr(p.startSP) pc, sp = p.startPC, uintptr(p.startSP)
saveOpenDeferState = false // goexit is unwinding the stack anyway saveOpenDeferState = false // goexit is unwinding the stack anyway
break break
} }
@ -1367,7 +1358,7 @@ func recovery(gp *g) {
// branch directly to the deferreturn // branch directly to the deferreturn
gp.sched.sp = sp gp.sched.sp = sp
gp.sched.pc = gotoPc gp.sched.pc = pc
gp.sched.lr = 0 gp.sched.lr = 0
// Restore the bp on platforms that support frame pointers. // Restore the bp on platforms that support frame pointers.
// N.B. It's fine to not set anything for platforms that don't // N.B. It's fine to not set anything for platforms that don't

View file

@ -1090,7 +1090,6 @@ type _defer struct {
heap bool heap bool
rangefunc bool // true for rangefunc list rangefunc bool // true for rangefunc list
sp uintptr // sp at time of defer sp uintptr // sp at time of defer
pc uintptr // pc at time of defer
fn func() // can be nil for open-coded defers fn func() // can be nil for open-coded defers
link *_defer // next defer on G; can point to either heap or stack! link *_defer // next defer on G; can point to either heap or stack!