mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
runtime: encode pending Goexit in panic state
Treat panicState as a bitmask so a recovered panic during Goexit can clear the panic bit while preserving the pending Goexit bit. This removes the separate deferFrame Goexit field and restores the previous defer frame size.
This commit is contained in:
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
|
||||
// This is a small number of very diverse targets that we want to test.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 3765, 307, 0, 2260},
|
||||
{"microbit", "examples/serial", 2824, 368, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 8041, 1663, 132, 7488},
|
||||
{"hifive1b", "examples/echo", 3771, 309, 0, 2260},
|
||||
{"microbit", "examples/serial", 2832, 368, 8, 2256},
|
||||
{"wioterminal", "examples/pininterrupt", 8053, 1663, 132, 7488},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@ source_filename = "defer.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
||||
|
||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface, ptr, i1 }
|
||||
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface, ptr }
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
|
||||
; Function Attrs: nounwind
|
||||
@@ -111,9 +111,9 @@ rundefers.end3: ; preds = %rundefers.loophead6
|
||||
; Function Attrs: nocallback nofree nosync nounwind willreturn
|
||||
declare ptr @llvm.stacksave.p0() #2
|
||||
|
||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(32), ptr, ptr) #1
|
||||
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(28), ptr, ptr) #1
|
||||
|
||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(32), ptr) #1
|
||||
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(28), ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 {
|
||||
|
||||
+13
-16
@@ -38,17 +38,15 @@ type deferFrame struct {
|
||||
JumpPC unsafe.Pointer // pc to return to
|
||||
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
|
||||
Previous *deferFrame // previous recover buffer pointer
|
||||
Panicking panicState // not panicking, panicking, or in Goexit
|
||||
Panicking panicState // panic/Goexit state
|
||||
PanicValue interface{} // panic value, might be nil for panic(nil) for example
|
||||
DeferPtr unsafe.Pointer // head of the stack-allocated defer list
|
||||
Goexit bool // whether Goexit is still pending after a recovered deferred panic
|
||||
}
|
||||
|
||||
type panicState uint8
|
||||
|
||||
const (
|
||||
panicFalse panicState = iota
|
||||
panicTrue
|
||||
panicTrue panicState = 1 << iota
|
||||
panicGoexit
|
||||
)
|
||||
|
||||
@@ -66,10 +64,10 @@ func panicOrGoexit(message interface{}, panicking panicState) {
|
||||
if supportsRecover() && !interrupt.In() {
|
||||
frame := (*deferFrame)(task.Current().DeferFrame)
|
||||
if frame != nil {
|
||||
if panicking == panicGoexit {
|
||||
frame.Goexit = true
|
||||
}
|
||||
frame.PanicValue = message
|
||||
if panicking&panicTrue != 0 {
|
||||
panicking |= frame.Panicking & panicGoexit
|
||||
}
|
||||
frame.Panicking = panicking
|
||||
tinygo_longjmp(frame)
|
||||
// unreachable
|
||||
@@ -103,7 +101,7 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
|
||||
// Use the normal panic mechanism so that this runtime error
|
||||
// can be recovered with recover().
|
||||
frame.PanicValue = plainError(msg)
|
||||
frame.Panicking = panicTrue
|
||||
frame.Panicking = panicTrue | (frame.Panicking & panicGoexit)
|
||||
tinygo_longjmp(frame)
|
||||
// unreachable
|
||||
}
|
||||
@@ -140,9 +138,8 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
|
||||
currentTask := task.Current()
|
||||
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
|
||||
frame.JumpSP = jumpSP
|
||||
frame.Panicking = panicFalse
|
||||
frame.Panicking = 0
|
||||
frame.DeferPtr = nil
|
||||
frame.Goexit = false
|
||||
currentTask.DeferFrame = unsafe.Pointer(frame)
|
||||
}
|
||||
|
||||
@@ -154,12 +151,12 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
|
||||
//go:nobounds
|
||||
func destroyDeferFrame(frame *deferFrame) {
|
||||
task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
|
||||
if frame.Panicking != panicFalse {
|
||||
if frame.Panicking&panicTrue != 0 {
|
||||
// We're still panicking!
|
||||
// Re-raise the panic now.
|
||||
panicOrGoexit(frame.PanicValue, frame.Panicking)
|
||||
panicOrGoexit(frame.PanicValue, panicTrue)
|
||||
}
|
||||
if frame.Goexit {
|
||||
if frame.Panicking&panicGoexit != 0 {
|
||||
// A deferred function panicked during Goexit, and that panic was
|
||||
// recovered. Continue the original Goexit instead of returning.
|
||||
panicOrGoexit(nil, panicGoexit)
|
||||
@@ -194,15 +191,15 @@ func _recover(useParentFrame bool) interface{} {
|
||||
// already), but instead from the previous frame.
|
||||
frame = frame.Previous
|
||||
}
|
||||
if frame != nil && frame.Panicking != panicFalse {
|
||||
if frame.Panicking == panicGoexit {
|
||||
if frame != nil && frame.Panicking != 0 {
|
||||
if frame.Panicking&panicTrue == 0 {
|
||||
// Special value that indicates we're exiting the goroutine using
|
||||
// Goexit(). Therefore, make this recover call a no-op.
|
||||
return nil
|
||||
}
|
||||
// Only the first call to recover returns the panic value. It also stops
|
||||
// the panicking sequence, hence setting panicking to false.
|
||||
frame.Panicking = panicFalse
|
||||
frame.Panicking &^= panicTrue
|
||||
return frame.PanicValue
|
||||
}
|
||||
// Not panicking, so return a nil interface.
|
||||
|
||||
Reference in New Issue
Block a user