aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/panic.go
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2021-03-30 17:55:22 -0400
committerAustin Clements <austin@google.com>2021-03-31 13:52:45 +0000
commitc847932804467f511b0e123cf29b72dd0d509306 (patch)
treef14ba730784e978de68edabdd676aca9b693834b /src/runtime/panic.go
parent135c9f45ecaec719bcc297ec9f9f19042bc9adf6 (diff)
downloadgo-c847932804467f511b0e123cf29b72dd0d509306.tar.gz
go-c847932804467f511b0e123cf29b72dd0d509306.zip
runtime: replace reflectcall of defers with direct call
With GOEXPERIMENT=regabidefer, all deferred functions take no arguments and have no results (their signature is always func()). Since the signature is fixed, we can replace all of the reflectcalls in the defer code with direct closure calls. For #40724. Change-Id: I3acd6742fe665610608a004c675f473b9d0e65ee Reviewed-on: https://go-review.googlesource.com/c/go/+/306010 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Than McIntosh <thanm@google.com>
Diffstat (limited to 'src/runtime/panic.go')
-rw-r--r--src/runtime/panic.go83
1 files changed, 69 insertions, 14 deletions
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index c265a5af79..bbf3ea473a 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -382,6 +382,19 @@ func deferArgs(d *_defer) unsafe.Pointer {
return add(unsafe.Pointer(d), unsafe.Sizeof(*d))
}
+// deferFunc returns d's deferred function. This is temporary while we
+// support both modes of GOEXPERIMENT=regabidefer. Once we commit to
+// that experiment, we should change the type of d.fn.
+//go:nosplit
+func deferFunc(d *_defer) func() {
+ if !experimentRegabiDefer {
+ throw("requires experimentRegabiDefer")
+ }
+ var fn func()
+ *(**funcval)(unsafe.Pointer(&fn)) = d.fn
+ return fn
+}
+
var deferType *_type // type of _defer struct
func init() {
@@ -635,10 +648,15 @@ func Goexit() {
addOneOpenDeferFrame(gp, 0, nil)
}
} else {
-
- // Save the pc/sp in reflectcallSave(), so we can "recover" back to this
- // loop if necessary.
- reflectcallSave(&p, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz))
+ if experimentRegabiDefer {
+ // Save the pc/sp in deferCallSave(), so we can "recover" back to this
+ // loop if necessary.
+ deferCallSave(&p, deferFunc(d))
+ } else {
+ // Save the pc/sp in reflectcallSave(), so we can "recover" back to this
+ // loop if necessary.
+ reflectcallSave(&p, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz))
+ }
}
if p.aborted {
// We had a recursive panic in the defer d we started, and
@@ -860,7 +878,11 @@ func runOpenDeferFrame(gp *g, d *_defer) bool {
deferBits = deferBits &^ (1 << i)
*(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset))) = deferBits
p := d._panic
- reflectcallSave(p, unsafe.Pointer(closure), deferArgs, argWidth)
+ if experimentRegabiDefer {
+ deferCallSave(p, deferFunc(d))
+ } else {
+ reflectcallSave(p, unsafe.Pointer(closure), deferArgs, argWidth)
+ }
if p != nil && p.aborted {
break
}
@@ -880,17 +902,20 @@ func runOpenDeferFrame(gp *g, d *_defer) bool {
// panic record. This allows the runtime to return to the Goexit defer processing
// loop, in the unusual case where the Goexit may be bypassed by a successful
// recover.
+//
+// This is marked as a wrapper by the compiler so it doesn't appear in
+// tracebacks.
func reflectcallSave(p *_panic, fn, arg unsafe.Pointer, argsize uint32) {
+ if experimentRegabiDefer {
+ throw("not allowed with experimentRegabiDefer")
+ }
if p != nil {
p.argp = unsafe.Pointer(getargp(0))
p.pc = getcallerpc()
p.sp = unsafe.Pointer(getcallersp())
}
- // Pass a dummy RegArgs for now since no function actually implements
- // the register-based ABI.
- //
- // TODO(mknyszek): Implement this properly, setting up arguments in
- // registers as necessary in the caller.
+ // Pass a dummy RegArgs since we'll only take this path if
+ // we're not using the register ABI.
var regs abi.RegArgs
reflectcall(nil, fn, arg, argsize, argsize, argsize, &regs)
if p != nil {
@@ -899,6 +924,29 @@ func reflectcallSave(p *_panic, fn, arg unsafe.Pointer, argsize uint32) {
}
}
+// deferCallSave calls fn() after saving the caller's pc and sp in the
+// panic record. This allows the runtime to return to the Goexit defer
+// processing loop, in the unusual case where the Goexit may be
+// bypassed by a successful recover.
+//
+// This is marked as a wrapper by the compiler so it doesn't appear in
+// tracebacks.
+func deferCallSave(p *_panic, fn func()) {
+ if !experimentRegabiDefer {
+ throw("only allowed with experimentRegabiDefer")
+ }
+ if p != nil {
+ p.argp = unsafe.Pointer(getargp(0))
+ p.pc = getcallerpc()
+ p.sp = unsafe.Pointer(getcallersp())
+ }
+ fn()
+ if p != nil {
+ p.pc = 0
+ p.sp = unsafe.Pointer(nil)
+ }
+}
+
// The implementation of the predeclared function panic.
func gopanic(e interface{}) {
gp := getg()
@@ -970,7 +1018,7 @@ func gopanic(e interface{}) {
// Mark defer as started, but keep on list, so that traceback
// can find and update the defer's argument frame if stack growth
- // or a garbage collection happens before reflectcall starts executing d.fn.
+ // or a garbage collection happens before executing d.fn.
d.started = true
// Record the panic that is running the defer.
@@ -987,12 +1035,19 @@ func gopanic(e interface{}) {
} else {
p.argp = unsafe.Pointer(getargp(0))
- var regs abi.RegArgs
- reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz), uint32(d.siz), &regs)
+ if experimentRegabiDefer {
+ fn := deferFunc(d)
+ fn()
+ } else {
+ // Pass a dummy RegArgs since we'll only take this path if
+ // we're not using the register ABI.
+ var regs abi.RegArgs
+ reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz), uint32(d.siz), &regs)
+ }
}
p.argp = nil
- // reflectcall did not panic. Remove d.
+ // Deferred function did not panic. Remove d.
if gp._defer != d {
throw("bad defer entry in panic")
}