panic.mx raw

   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