From 361d51a6b58bccaab0559e06737c918018a7a5fa Mon Sep 17 00:00:00 2001 From: Youlin Feng Date: Fri, 31 Oct 2025 10:45:26 +0800 Subject: [PATCH] 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 LUCI-TryBot-Result: Go LUCI Reviewed-by: Keith Randall Reviewed-by: Michael Knyszek --- src/cmd/compile/internal/ssagen/ssa.go | 3 +-- src/runtime/heapdump.go | 1 - src/runtime/panic.go | 25 ++++++++----------------- src/runtime/runtime2.go | 1 - 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index ae7d57566f..db2ffb5752 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -7797,7 +7797,7 @@ func callTargetLSym(callee *ir.Name) *obj.LSym { } // deferStructFnField is the field index of _defer.fn. -const deferStructFnField = 4 +const deferStructFnField = 3 var deferType *types.Type @@ -7817,7 +7817,6 @@ func deferstruct() *types.Type { makefield("heap", types.Types[types.TBOOL]), makefield("rangefunc", types.Types[types.TBOOL]), makefield("sp", types.Types[types.TUINTPTR]), - makefield("pc", types.Types[types.TUINTPTR]), // Note: the types here don't really matter. Defer structures // are always scanned explicitly during stack copying and GC, // so we make them uintptr type even though they are real pointers. diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 3671c65ab7..3a8c374fc0 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -382,7 +382,6 @@ func dumpgoroutine(gp *g) { dumpint(uint64(uintptr(unsafe.Pointer(d)))) dumpint(uint64(uintptr(unsafe.Pointer(gp)))) dumpint(uint64(d.sp)) - dumpint(uint64(d.pc)) fn := *(**funcval)(unsafe.Pointer(&d.fn)) dumpint(uint64(uintptr(unsafe.Pointer(fn)))) if d.fn == nil { diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 175452fec9..aee1cd0eed 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -354,7 +354,6 @@ func deferproc(fn func()) { d.link = gp._defer gp._defer = d d.fn = fn - d.pc = sys.GetCallerPC() // We must not be preempted between calling GetCallerSP and // storing it to d.sp because GetCallerSP's result is a // uintptr stack pointer. @@ -458,7 +457,6 @@ func deferrangefunc() any { d := newdefer() d.link = gp._defer gp._defer = d - d.pc = sys.GetCallerPC() // We must not be preempted between calling GetCallerSP and // storing it to d.sp because GetCallerSP's result is a // uintptr stack pointer. @@ -518,7 +516,6 @@ func deferconvert(d0 *_defer) { } for d1 := d; ; d1 = d1.link { d1.sp = d0.sp - d1.pc = d0.pc if d1.link == nil { d1.link = tail break @@ -547,7 +544,6 @@ func deferprocStack(d *_defer) { d.heap = false d.rangefunc = false d.sp = sys.GetCallerSP() - d.pc = sys.GetCallerPC() // The lines below implement: // d.panic = nil // d.fd = nil @@ -977,8 +973,6 @@ func (p *_panic) nextDefer() (func(), bool) { fn := d.fn - p.retpc = d.pc - // Unlink and free. popDefer(gp) @@ -1018,6 +1012,12 @@ func (p *_panic) nextFrame() (ok bool) { // it's non-zero. 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 } @@ -1273,15 +1273,6 @@ func recovery(gp *g) { pc, sp, fp := p.retpc, uintptr(p.sp), uintptr(p.fp) 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. for ; p != nil && uintptr(p.startSP) < sp; p = p.link { // 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 // worthwhile though. 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 break } @@ -1367,7 +1358,7 @@ func recovery(gp *g) { // branch directly to the deferreturn gp.sched.sp = sp - gp.sched.pc = gotoPc + gp.sched.pc = pc gp.sched.lr = 0 // Restore the bp on platforms that support frame pointers. // N.B. It's fine to not set anything for platforms that don't diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index b346337d60..3672b19f76 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -1090,7 +1090,6 @@ type _defer struct { heap bool rangefunc bool // true for rangefunc list sp uintptr // sp at time of defer - pc uintptr // pc at time of defer fn func() // can be nil for open-coded defers link *_defer // next defer on G; can point to either heap or stack! -- 2.52.0