Skip to content

Commit ee3f918

Browse files
committed
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.
1 parent 9760dc3 commit ee3f918

3 files changed

Lines changed: 19 additions & 22 deletions

File tree

builder/sizes_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
4242
// This is a small number of very diverse targets that we want to test.
4343
tests := []sizeTest{
4444
// microcontrollers
45-
{"hifive1b", "examples/echo", 3765, 307, 0, 2260},
46-
{"microbit", "examples/serial", 2824, 368, 8, 2256},
47-
{"wioterminal", "examples/pininterrupt", 8041, 1663, 132, 7488},
45+
{"hifive1b", "examples/echo", 3771, 309, 0, 2260},
46+
{"microbit", "examples/serial", 2832, 368, 8, 2256},
47+
{"wioterminal", "examples/pininterrupt", 8053, 1663, 132, 7488},
4848

4949
// TODO: also check wasm. Right now this is difficult, because
5050
// wasm binaries are run through wasm-opt and therefore the

compiler/testdata/defer-cortex-m-qemu.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ source_filename = "defer.go"
33
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
44
target triple = "thumbv7m-unknown-unknown-eabi"
55

6-
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface, ptr, i1 }
6+
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface, ptr }
77
%runtime._interface = type { ptr, ptr }
88

99
; Function Attrs: nounwind
@@ -111,9 +111,9 @@ rundefers.end3: ; preds = %rundefers.loophead6
111111
; Function Attrs: nocallback nofree nosync nounwind willreturn
112112
declare ptr @llvm.stacksave.p0() #2
113113

114-
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(32), ptr, ptr) #1
114+
declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(28), ptr, ptr) #1
115115

116-
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(32), ptr) #1
116+
declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(28), ptr) #1
117117

118118
; Function Attrs: nounwind
119119
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #0 {

src/runtime/panic.go

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,15 @@ type deferFrame struct {
3838
JumpPC unsafe.Pointer // pc to return to
3939
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
4040
Previous *deferFrame // previous recover buffer pointer
41-
Panicking panicState // not panicking, panicking, or in Goexit
41+
Panicking panicState // panic/Goexit state
4242
PanicValue interface{} // panic value, might be nil for panic(nil) for example
4343
DeferPtr unsafe.Pointer // head of the stack-allocated defer list
44-
Goexit bool // whether Goexit is still pending after a recovered deferred panic
4544
}
4645

4746
type panicState uint8
4847

4948
const (
50-
panicFalse panicState = iota
51-
panicTrue
49+
panicTrue panicState = 1 << iota
5250
panicGoexit
5351
)
5452

@@ -66,10 +64,10 @@ func panicOrGoexit(message interface{}, panicking panicState) {
6664
if supportsRecover() && !interrupt.In() {
6765
frame := (*deferFrame)(task.Current().DeferFrame)
6866
if frame != nil {
69-
if panicking == panicGoexit {
70-
frame.Goexit = true
71-
}
7267
frame.PanicValue = message
68+
if panicking&panicTrue != 0 {
69+
panicking |= frame.Panicking & panicGoexit
70+
}
7371
frame.Panicking = panicking
7472
tinygo_longjmp(frame)
7573
// unreachable
@@ -103,7 +101,7 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
103101
// Use the normal panic mechanism so that this runtime error
104102
// can be recovered with recover().
105103
frame.PanicValue = plainError(msg)
106-
frame.Panicking = panicTrue
104+
frame.Panicking = panicTrue | (frame.Panicking & panicGoexit)
107105
tinygo_longjmp(frame)
108106
// unreachable
109107
}
@@ -140,9 +138,8 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
140138
currentTask := task.Current()
141139
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
142140
frame.JumpSP = jumpSP
143-
frame.Panicking = panicFalse
141+
frame.Panicking = 0
144142
frame.DeferPtr = nil
145-
frame.Goexit = false
146143
currentTask.DeferFrame = unsafe.Pointer(frame)
147144
}
148145

@@ -154,12 +151,12 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
154151
//go:nobounds
155152
func destroyDeferFrame(frame *deferFrame) {
156153
task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
157-
if frame.Panicking != panicFalse {
154+
if frame.Panicking&panicTrue != 0 {
158155
// We're still panicking!
159156
// Re-raise the panic now.
160-
panicOrGoexit(frame.PanicValue, frame.Panicking)
157+
panicOrGoexit(frame.PanicValue, panicTrue)
161158
}
162-
if frame.Goexit {
159+
if frame.Panicking&panicGoexit != 0 {
163160
// A deferred function panicked during Goexit, and that panic was
164161
// recovered. Continue the original Goexit instead of returning.
165162
panicOrGoexit(nil, panicGoexit)
@@ -194,15 +191,15 @@ func _recover(useParentFrame bool) interface{} {
194191
// already), but instead from the previous frame.
195192
frame = frame.Previous
196193
}
197-
if frame != nil && frame.Panicking != panicFalse {
198-
if frame.Panicking == panicGoexit {
194+
if frame != nil && frame.Panicking != 0 {
195+
if frame.Panicking&panicTrue == 0 {
199196
// Special value that indicates we're exiting the goroutine using
200197
// Goexit(). Therefore, make this recover call a no-op.
201198
return nil
202199
}
203200
// Only the first call to recover returns the panic value. It also stops
204201
// the panicking sequence, hence setting panicking to false.
205-
frame.Panicking = panicFalse
202+
frame.Panicking &^= panicTrue
206203
return frame.PanicValue
207204
}
208205
// Not panicking, so return a nil interface.

0 commit comments

Comments
 (0)