diff options
| author | David Chase <drchase@google.com> | 2025-02-19 16:47:31 -0500 |
|---|---|---|
| committer | David Chase <drchase@google.com> | 2025-02-25 08:35:38 -0800 |
| commit | c2ae5c7443fc8bda1d2b06390d4b439e81fb4b09 (patch) | |
| tree | ac228294c6dd1555af397d362b135aa6695e32f3 /src/runtime | |
| parent | 6adf08f747aff60810e754ca74e1bef381cbae86 (diff) | |
| download | go-c2ae5c7443fc8bda1d2b06390d4b439e81fb4b09.tar.xz | |
cmd/compile, runtime: use PC of deferreturn for panic transfer
this removes the old conditional-on-register-value
handshake from the deferproc/deferprocstack logic.
The "line" for the recovery-exit frame itself (not the defers
that it runs) is the closing brace of the function.
Reduces code size slightly (e.g. go command is 0.2% smaller)
Sample output showing effect of this change, also what sort of
code it requires to observe the effect:
```
package main
import "os"
func main() {
g(len(os.Args) - 1) // stack[0]
}
var gi int
var pi *int = &gi
//go:noinline
func g(i int) {
switch i {
case 0:
defer func() {
println("g0", i)
q() // stack[2] if i == 0
}()
for j := *pi; j < 1; j++ {
defer func() {
println("recover0", recover().(string))
}()
}
default:
for j := *pi; j < 1; j++ {
defer func() {
println("g1", i)
q() // stack[2] if i == 1
}()
}
defer func() {
println("recover1", recover().(string))
}()
}
p()
} // stack[1] (deferreturn)
//go:noinline
func p() {
panic("p()")
}
//go:noinline
func q() {
panic("q()") // stack[3]
}
/* Sample output for "./foo foo":
recover1 p()
g1 1
panic: q()
goroutine 1 [running]:
main.q()
.../main.go:46 +0x2c
main.g.func3()
.../main.go:29 +0x48
main.g(0x1?)
.../main.go:37 +0x68
main.main()
.../main.go:6 +0x28
*/
```
Change-Id: Ie39ea62ecc244213500380ea06d44024cadc2317
Reviewed-on: https://go-review.googlesource.com/c/go/+/650795
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/runtime')
| -rw-r--r-- | src/runtime/asm_386.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_amd64.s | 5 | ||||
| -rw-r--r-- | src/runtime/asm_arm.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_arm64.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_loong64.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_mips64x.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_mipsx.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_ppc64x.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_riscv64.s | 5 | ||||
| -rw-r--r-- | src/runtime/asm_s390x.s | 4 | ||||
| -rw-r--r-- | src/runtime/asm_wasm.s | 4 | ||||
| -rw-r--r-- | src/runtime/panic.go | 48 | ||||
| -rw-r--r-- | src/runtime/stubs.go | 7 |
13 files changed, 14 insertions, 87 deletions
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 5aafe14be9..b4818723e5 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -1373,10 +1373,6 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 SETEQ ret+0(FP) RET -TEXT runtime·return0(SB), NOSPLIT, $0 - MOVL $0, AX - RET - // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. TEXT _cgo_topofstack(SB),NOSPLIT,$0 diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index cdf9874a7f..4b630b5ecc 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -1679,11 +1679,6 @@ DATA shifts<>+0xf0(SB)/8, $0x0807060504030201 DATA shifts<>+0xf8(SB)/8, $0xff0f0e0d0c0b0a09 GLOBL shifts<>(SB),RODATA,$256 -TEXT runtime·return0(SB), NOSPLIT, $0 - MOVL $0, AX - RET - - // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. TEXT _cgo_topofstack(SB),NOSPLIT,$0 diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 4d57ec6062..7c39b4a3e2 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -846,10 +846,6 @@ TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-12 TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-12 JMP runtime·memhash64Fallback(SB) -TEXT runtime·return0(SB),NOSPLIT,$0 - MOVW $0, R0 - RET - TEXT runtime·procyield(SB),NOSPLIT|NOFRAME,$0 MOVW cycles+0(FP), R1 MOVW $0, R0 diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s index bf9ab6bcbc..238eaf2789 100644 --- a/src/runtime/asm_arm64.s +++ b/src/runtime/asm_arm64.s @@ -1263,10 +1263,6 @@ TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0 MOVD (R0), R0 UNDEF -TEXT runtime·return0(SB), NOSPLIT, $0 - MOVW $0, R0 - RET - // The top-most function running on a goroutine // returns to goexit+PCQuantum. TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0 diff --git a/src/runtime/asm_loong64.s b/src/runtime/asm_loong64.s index 1c5ced4512..de64f8acbc 100644 --- a/src/runtime/asm_loong64.s +++ b/src/runtime/asm_loong64.s @@ -679,10 +679,6 @@ TEXT runtime·memhash32<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash64<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24 JMP runtime·memhash64Fallback<ABIInternal>(SB) -TEXT runtime·return0(SB), NOSPLIT, $0 - MOVW $0, R19 - RET - // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. TEXT _cgo_topofstack(SB),NOSPLIT,$16 diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s index 80cd87c4af..cfb9950e17 100644 --- a/src/runtime/asm_mips64x.s +++ b/src/runtime/asm_mips64x.s @@ -644,10 +644,6 @@ TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 JMP runtime·memhash64Fallback(SB) -TEXT runtime·return0(SB), NOSPLIT, $0 - MOVW $0, R1 - RET - // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. TEXT _cgo_topofstack(SB),NOSPLIT,$16 diff --git a/src/runtime/asm_mipsx.s b/src/runtime/asm_mipsx.s index ca95f22bd6..33afa2e5c5 100644 --- a/src/runtime/asm_mipsx.s +++ b/src/runtime/asm_mipsx.s @@ -634,10 +634,6 @@ TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-12 TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-12 JMP runtime·memhash64Fallback(SB) -TEXT runtime·return0(SB),NOSPLIT,$0 - MOVW $0, R1 - RET - // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. TEXT _cgo_topofstack(SB),NOSPLIT|NOFRAME,$0 diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s index 2b8c4d42a3..268e0c01c1 100644 --- a/src/runtime/asm_ppc64x.s +++ b/src/runtime/asm_ppc64x.s @@ -980,10 +980,6 @@ TEXT runtime·memhash32<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash64<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24 JMP runtime·memhash64Fallback<ABIInternal>(SB) -TEXT runtime·return0(SB), NOSPLIT, $0 - MOVW $0, R3 - RET - // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. #ifdef GOOS_aix diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s index 71b32304d7..20c9fdf7ff 100644 --- a/src/runtime/asm_riscv64.s +++ b/src/runtime/asm_riscv64.s @@ -247,11 +247,6 @@ TEXT runtime·memhash32<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash64<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24 JMP runtime·memhash64Fallback<ABIInternal>(SB) -// func return0() -TEXT runtime·return0(SB), NOSPLIT, $0 - MOV $0, A0 - RET - // restore state from Gobuf; longjmp // func gogo(buf *gobuf) diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s index f2354a6d53..6758175fc2 100644 --- a/src/runtime/asm_s390x.s +++ b/src/runtime/asm_s390x.s @@ -767,10 +767,6 @@ TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 JMP runtime·memhash64Fallback(SB) -TEXT runtime·return0(SB), NOSPLIT, $0 - MOVW $0, R3 - RET - // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. TEXT _cgo_topofstack(SB),NOSPLIT|NOFRAME,$0 diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index 69da583a1d..247368d127 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -195,10 +195,6 @@ TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 JMP runtime·memhash64Fallback(SB) -TEXT runtime·return0(SB), NOSPLIT, $0-0 - MOVD $0, RET0 - RET - TEXT runtime·asminit(SB), NOSPLIT, $0-0 // No per-thread init. RET diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 1ed2503320..c31cfd6e1a 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -285,16 +285,6 @@ func deferproc(fn func()) { // storing it to d.sp because GetCallerSP's result is a // uintptr stack pointer. d.sp = sys.GetCallerSP() - - // deferproc returns 0 normally. - // a deferred func that stops a panic - // makes the deferproc return 1. - // the code the compiler generates always - // checks the return value and jumps to the - // end of the function if deferproc returns != 0. - return0() - // No code can go here - the C return register has - // been set and must not be clobbered. } var rangeDoneError = error(errorString("range function continued iteration after function for loop body returned false")) @@ -391,15 +381,10 @@ func deferrangefunc() any { throw("defer on system stack") } - fn := findfunc(sys.GetCallerPC()) - if fn.deferreturn == 0 { - throw("no deferreturn") - } - d := newdefer() d.link = gp._defer gp._defer = d - d.pc = fn.entry() + uintptr(fn.deferreturn) + 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. @@ -434,9 +419,6 @@ func deferprocat(fn func(), frame any) { break } } - - // Must be last - see deferproc above. - return0() } // deferconvert converts the rangefunc defer list of d0 into an ordinary list @@ -484,6 +466,7 @@ func deferprocStack(d *_defer) { // go code on the system stack can't defer throw("defer on system stack") } + // fn is already set. // The other fields are junk on entry to deferprocStack and // are initialized here. @@ -506,10 +489,6 @@ func deferprocStack(d *_defer) { *(*uintptr)(unsafe.Pointer(&d.link)) = uintptr(unsafe.Pointer(gp._defer)) *(*uintptr)(unsafe.Pointer(&d.head)) = 0 *(*uintptr)(unsafe.Pointer(&gp._defer)) = uintptr(unsafe.Pointer(d)) - - return0() - // No code can go here - the C return register has - // been set and must not be clobbered. } // Each P holds a pool for defers. @@ -927,9 +906,6 @@ func (p *_panic) nextDefer() (func(), bool) { fn := d.fn - // TODO(mdempsky): Instead of having each deferproc call have - // its own "deferreturn(); return" sequence, we should just make - // them reuse the one we emit for open-coded defers. p.retpc = d.pc // Unlink and free. @@ -1159,6 +1135,15 @@ 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. @@ -1181,7 +1166,7 @@ func recovery(gp *g) { // With how subtle defer handling is, this might not actually be // worthwhile though. if p.goexit { - pc, sp = p.startPC, uintptr(p.startSP) + gotoPc, sp = p.startPC, uintptr(p.startSP) saveOpenDeferState = false // goexit is unwinding the stack anyway break } @@ -1242,11 +1227,9 @@ func recovery(gp *g) { throw("bad recovery") } - // Make the deferproc for this d return again, - // this time returning 1. The calling function will - // jump to the standard return epilogue. + // branch directly to the deferreturn gp.sched.sp = sp - gp.sched.pc = pc + gp.sched.pc = gotoPc 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 @@ -1263,9 +1246,6 @@ func recovery(gp *g) { // only gets us to the caller's fp. gp.sched.bp = sp - goarch.PtrSize } - // The value in ret is delivered IN A REGISTER, even if there is a - // stack ABI. - gp.sched.ret = 1 gogo(&gp.sched) } diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index ecf97666d7..20fc1c59ad 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -326,13 +326,6 @@ func morestack_noctxt() func rt0_go() -// return0 is a stub used to return 0 from deferproc. -// It is called at the very end of deferproc to signal -// the calling Go function that it should not jump -// to deferreturn. -// in asm_*.s -func return0() - // in asm_*.s // not called directly; definitions here supply type information for traceback. // These must have the same signature (arg pointer map) as reflectcall. |
