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