1 package runtime
2 3 import (
4 "internal/task"
5 "runtime/interrupt"
6 "moxie"
7 "unsafe"
8 )
9 10 // trap is a compiler hint that this function cannot be executed. It is
11 // translated into either a trap instruction or a call to abort().
12 //
13 //export llvm.trap
14 func trap()
15 16 // Inline assembly stub. It is essentially C longjmp but modified a bit for the
17 // purposes of Moxie. It restores the stack pointer and jumps to the given pc.
18 //
19 //export moxie_longjmp
20 func moxie_longjmp(frame *deferFrame)
21 22 // Compiler intrinsic.
23 // Returns whether recover is supported on the current architecture.
24 func supportsRecover() bool
25 26 // Compile intrinsic.
27 // Returns which strategy is used. This is usually "print" but can be changed
28 // using the -panic= compiler flag.
29 func panicStrategy() uint8
30 31 // DeferFrame is a stack allocated object that stores information for the
32 // current "defer frame", which is used in functions that use the `defer`
33 // keyword.
34 // The compiler knows about the JumpPC struct offset, so it should not be moved
35 // without also updating compiler/defer.go.
36 type deferFrame struct {
37 JumpSP unsafe.Pointer // stack pointer to return to
38 JumpPC unsafe.Pointer // pc to return to
39 ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
40 Previous *deferFrame // previous recover buffer pointer
41 Panicking panicState // not panicking, panicking, or in Goexit
42 PanicValue interface{} // panic value, might be nil for panic(nil) for example
43 }
44 45 type panicState uint8
46 47 const (
48 panicFalse panicState = iota
49 panicTrue
50 panicGoexit
51 )
52 53 // Builtin function panic(msg), used as a compiler intrinsic.
54 func _panic(message interface{}) {
55 panicOrGoexit(message, panicTrue)
56 }
57 58 func panicOrGoexit(message interface{}, panicking panicState) {
59 if panicStrategy() == moxie.PanicStrategyTrap {
60 trap()
61 }
62 // Note: recover is not supported inside interrupts.
63 // (This could be supported, like defer, but we currently don't).
64 if supportsRecover() && !interrupt.In() {
65 frame := (*deferFrame)(task.Current().DeferFrame)
66 if frame != nil {
67 frame.PanicValue = message
68 frame.Panicking = panicking
69 moxie_longjmp(frame)
70 // unreachable
71 }
72 }
73 if panicking == panicGoexit {
74 // Call to Goexit() instead of a panic.
75 // Exit the goroutine instead of printing a panic message.
76 deadlock()
77 }
78 printstring("panic: ")
79 printitf(message)
80 printnl()
81 abort()
82 }
83 84 // Cause a runtime panic, which is (currently) always a string.
85 func runtimePanic(msg string) {
86 // As long as this function is inined, llvm.returnaddress(0) will return
87 // something sensible.
88 runtimePanicAt(returnAddress(0), msg)
89 }
90 91 func runtimePanicAt(addr unsafe.Pointer, msg string) {
92 if panicStrategy() == moxie.PanicStrategyTrap {
93 trap()
94 }
95 if hasReturnAddr {
96 // Note: the string "panic: runtime error at " is also used in
97 // runtime_cortexm_hardfault.go. It is kept the same so that the string
98 // can be deduplicated by the compiler.
99 printstring("panic: runtime error at ")
100 printptr(uintptr(addr) - callInstSize)
101 printstring(": ")
102 } else {
103 printstring("panic: runtime error: ")
104 }
105 printstring(msg)
106 printnl()
107 abort()
108 }
109 110 // Called at the start of a function that includes a deferred call.
111 // It gets passed in the stack-allocated defer frame and configures it.
112 // Note that the frame is not zeroed yet, so we need to initialize all values
113 // that will be used.
114 //
115 //go:inline
116 //go:nobounds
117 func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
118 if interrupt.In() {
119 // Defer is not currently allowed in interrupts.
120 // We could add support for this, but since defer might also allocate
121 // (especially in loops) it might not be a good idea anyway.
122 runtimePanicAt(returnAddress(0), "defer in interrupt")
123 }
124 currentTask := task.Current()
125 frame.Previous = (*deferFrame)(currentTask.DeferFrame)
126 frame.JumpSP = jumpSP
127 frame.Panicking = panicFalse
128 currentTask.DeferFrame = unsafe.Pointer(frame)
129 }
130 131 // Called right before the return instruction. It pops the defer frame from the
132 // linked list of defer frames. It also re-raises a panic if the goroutine is
133 // still panicking.
134 //
135 //go:inline
136 //go:nobounds
137 func destroyDeferFrame(frame *deferFrame) {
138 task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
139 if frame.Panicking != panicFalse {
140 // We're still panicking!
141 // Re-raise the panic now.
142 panicOrGoexit(frame.PanicValue, frame.Panicking)
143 }
144 }
145 146 // _recover is the built-in recover() function. It tries to recover a currently
147 // panicking goroutine.
148 // useParentFrame is set when the caller of runtime._recover has a defer frame
149 // itself. In that case, recover() shouldn't check that frame but one frame up.
150 func _recover(useParentFrame bool) interface{} {
151 if !supportsRecover() || interrupt.In() {
152 // Either we're compiling without stack unwinding support, or we're
153 // inside an interrupt where panic/recover is not supported. Either way,
154 // make this a no-op since panic() won't do any long jumps to a deferred
155 // function.
156 return nil
157 }
158 // TODO: somehow check that recover() is called directly by a deferred
159 // function in a panicking goroutine. Maybe this can be done by comparing
160 // the frame pointer?
161 frame := (*deferFrame)(task.Current().DeferFrame)
162 if useParentFrame {
163 // Don't recover panic from the current frame (which can't be panicking
164 // already), but instead from the previous frame.
165 frame = frame.Previous
166 }
167 if frame != nil && frame.Panicking != panicFalse {
168 if frame.Panicking == panicGoexit {
169 // Special value that indicates we're exiting the goroutine using
170 // Goexit(). Therefore, make this recover call a no-op.
171 return nil
172 }
173 // Only the first call to recover returns the panic value. It also stops
174 // the panicking sequence, hence setting panicking to false.
175 frame.Panicking = panicFalse
176 return frame.PanicValue
177 }
178 // Not panicking, so return a nil interface.
179 return nil
180 }
181 182 // Panic when trying to dereference a nil pointer.
183 func nilPanic() {
184 runtimePanicAt(returnAddress(0), "nil pointer dereference")
185 }
186 187 // Panic when trying to add an entry to a nil map
188 func nilMapPanic() {
189 runtimePanicAt(returnAddress(0), "assignment to entry in nil map")
190 }
191 192 // Panic when trying to access an array or slice out of bounds.
193 func lookupPanic() {
194 runtimePanicAt(returnAddress(0), "index out of range")
195 }
196 197 // Panic when trying to slice a slice out of bounds.
198 func slicePanic() {
199 runtimePanicAt(returnAddress(0), "slice out of range")
200 }
201 202 // Panic when trying to convert a slice to an array pointer (Go 1.17+) and the
203 // slice is shorter than the array.
204 func sliceToArrayPointerPanic() {
205 runtimePanicAt(returnAddress(0), "slice smaller than array")
206 }
207 208 // Panic when calling unsafe.Slice() (Go 1.17+) or unsafe.String() (Go 1.20+)
209 // with a len that's too large (which includes if the ptr is nil and len is
210 // nonzero).
211 func unsafeSlicePanic() {
212 runtimePanicAt(returnAddress(0), "unsafe.Slice/String: len out of range")
213 }
214 215 // Panic when trying to create a new channel that is too big.
216 func chanMakePanic() {
217 runtimePanicAt(returnAddress(0), "new channel is too big")
218 }
219 220 // Panic when a shift value is negative.
221 func negativeShiftPanic() {
222 runtimePanicAt(returnAddress(0), "negative shift")
223 }
224 225 // Panic when there is a divide by zero.
226 func divideByZeroPanic() {
227 runtimePanicAt(returnAddress(0), "divide by zero")
228 }
229 230 func blockingPanic() {
231 runtimePanicAt(returnAddress(0), "trying to do blocking operation in exported function")
232 }
233 234 //go:linkname fips_fatal crypto/internal/fips140.fatal
235 func fips_fatal(msg string) {
236 runtimePanic(msg)
237 }
238