aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorDavid Chase <drchase@google.com>2025-02-19 16:47:31 -0500
committerDavid Chase <drchase@google.com>2025-02-25 08:35:38 -0800
commitc2ae5c7443fc8bda1d2b06390d4b439e81fb4b09 (patch)
treeac228294c6dd1555af397d362b135aa6695e32f3 /src/runtime
parent6adf08f747aff60810e754ca74e1bef381cbae86 (diff)
downloadgo-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.s4
-rw-r--r--src/runtime/asm_amd64.s5
-rw-r--r--src/runtime/asm_arm.s4
-rw-r--r--src/runtime/asm_arm64.s4
-rw-r--r--src/runtime/asm_loong64.s4
-rw-r--r--src/runtime/asm_mips64x.s4
-rw-r--r--src/runtime/asm_mipsx.s4
-rw-r--r--src/runtime/asm_ppc64x.s4
-rw-r--r--src/runtime/asm_riscv64.s5
-rw-r--r--src/runtime/asm_s390x.s4
-rw-r--r--src/runtime/asm_wasm.s4
-rw-r--r--src/runtime/panic.go48
-rw-r--r--src/runtime/stubs.go7
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.