Skip to content

Commit e631e70

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 197fd7a commit e631e70

2 files changed

Lines changed: 15 additions & 17 deletions

File tree

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: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,16 @@ 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+
panicFalse panicState = 0
50+
panicTrue panicState = 1 << iota
5251
panicGoexit
5352
)
5453

@@ -66,10 +65,10 @@ func panicOrGoexit(message interface{}, panicking panicState) {
6665
if supportsRecover() && !interrupt.In() {
6766
frame := (*deferFrame)(task.Current().DeferFrame)
6867
if frame != nil {
69-
if panicking == panicGoexit {
70-
frame.Goexit = true
71-
}
7268
frame.PanicValue = message
69+
if panicking&panicTrue != 0 {
70+
panicking |= frame.Panicking & panicGoexit
71+
}
7372
frame.Panicking = panicking
7473
tinygo_longjmp(frame)
7574
// unreachable
@@ -103,7 +102,7 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
103102
// Use the normal panic mechanism so that this runtime error
104103
// can be recovered with recover().
105104
frame.PanicValue = plainError(msg)
106-
frame.Panicking = panicTrue
105+
frame.Panicking = panicTrue | (frame.Panicking & panicGoexit)
107106
tinygo_longjmp(frame)
108107
// unreachable
109108
}
@@ -142,7 +141,6 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
142141
frame.JumpSP = jumpSP
143142
frame.Panicking = panicFalse
144143
frame.DeferPtr = nil
145-
frame.Goexit = false
146144
currentTask.DeferFrame = unsafe.Pointer(frame)
147145
}
148146

@@ -154,12 +152,12 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
154152
//go:nobounds
155153
func destroyDeferFrame(frame *deferFrame) {
156154
task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
157-
if frame.Panicking != panicFalse {
155+
if frame.Panicking&panicTrue != 0 {
158156
// We're still panicking!
159157
// Re-raise the panic now.
160-
panicOrGoexit(frame.PanicValue, frame.Panicking)
158+
panicOrGoexit(frame.PanicValue, panicTrue)
161159
}
162-
if frame.Goexit {
160+
if frame.Panicking&panicGoexit != 0 {
163161
// A deferred function panicked during Goexit, and that panic was
164162
// recovered. Continue the original Goexit instead of returning.
165163
panicOrGoexit(nil, panicGoexit)
@@ -195,14 +193,14 @@ func _recover(useParentFrame bool) interface{} {
195193
frame = frame.Previous
196194
}
197195
if frame != nil && frame.Panicking != panicFalse {
198-
if frame.Panicking == panicGoexit {
196+
if frame.Panicking&panicTrue == 0 {
199197
// Special value that indicates we're exiting the goroutine using
200198
// Goexit(). Therefore, make this recover call a no-op.
201199
return nil
202200
}
203201
// Only the first call to recover returns the panic value. It also stops
204202
// the panicking sequence, hence setting panicking to false.
205-
frame.Panicking = panicFalse
203+
frame.Panicking &^= panicTrue
206204
return frame.PanicValue
207205
}
208206
// Not panicking, so return a nil interface.

0 commit comments

Comments
 (0)