Skip to content

error in paste mode #10298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dhalbert opened this issue Apr 28, 2025 · 2 comments
Open

error in paste mode #10298

dhalbert opened this issue Apr 28, 2025 · 2 comments

Comments

@dhalbert
Copy link
Collaborator

dhalbert commented Apr 28, 2025

Reproducible on ESP32-C3 and ESP32-C6:

A code error during paste mode or raw mode causes a hard crash when exiting the mode with ctrl-D.

This does not occur when using the regular REPL input mode.

Simplest example (x is an unassigned variable):

Adafruit CircuitPython 10.0.0-alpha.2 on 2025-04-04; Adafruit QT Py ESP32C3 with ESP32-C3FN4
>>> 
paste mode; Ctrl-C to cancel, Ctrl-D to finish
=== x
=== [type ctrl-D here]
ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0xc (RTC_SW_CPU_RST),boot:0xd (SPI_FAST_FLASH_BOOT)
Saved PC:0x40382ab0
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd5820,len:0x120
load:0x403cc710,len:0x900
load:0x403ce710,len:0x2178
entry 0x403cc710

Auto-reload is off.
Running in safe mode! Not running saved code.

You are in safe mode because:
CircuitPython core code crashed hard. Whoops!
Hard fault: memory access or instruction error.
Please file an issue with your program at github.com/adafruit/circuitpython/issues.
Press reset to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.

One hypothesis is that the ctrl-D causes an EOF exception during read, and so there is a double exception (the first is due to the code error).

This does not occur on boards with native USB CDC REPL. The only such boards I have to test right now are ESP32-C3 and ESP32-C6, which are RISCV, but that may be a coincidence. Or it may be due to nlr handling on RISCV.

EDITS:

Could not reproduce on ESP32 Feather HUZZAH32 or V2, or micro:bit V2, so it's not just serial-only boards.

1/0 is also a very simple error. Interestingly, raise ValueError or similar does not provoke the crash.

@eightycc
Copy link
Collaborator

eightycc commented May 1, 2025

Bisected from working 9.2.7 to failing 10-alpha.2. Found this commit was causing the failure: 04622e7. There are two changed lines in shared/runtime/pyexec.c:parse_compile_execute() that add protection against running a function that failed to compile. The first change adds assignment of mp_const_none to the definition of module_fun.

Including this change alone from the commit causes the failure. Examination of the generated object code with and without this change shows extensive changes to the arrangement of the stack frame. Here is the change in context:

nlr_buf_t nlr;
nlr.ret_val = NULL;
if (nlr_push(&nlr) == 0) {
mp_obj_t module_fun = mp_const_none;
// CIRCUITPY-CHANGE
#if CIRCUITPY_ATEXIT
if (!(exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT))
#endif

Moving the definition/assignment out of the scope of the if statement (i.e., between lines 92 and 93) retains the intent of the code change but does not cause the failure.

This issue affects only Risc-V.

@eightycc
Copy link
Collaborator

eightycc commented May 3, 2025

The workaround described above stopped working with the introduction of gcc 14.2.0 by ESP-IDF 5.4.1. The reason this happened was a change in the ordering of generated RISC-V instructions in function parse_compile_execute when compiled with gcc 14.2.0:

0x42057e62 <parse_compile_execute+100>:	lw	a4,28(sp)
   0x42057e64 <parse_compile_execute+102>:	li	a5,1
   0x42057e66 <parse_compile_execute+104>:	lw	s3,0(s1)
   0x42057e6a <parse_compile_execute+108>:	beq	a4,a5,0x42057f90 <parse_compile_execute+402>
   0x42057e6e <parse_compile_execute+112>:	lw	a1,28(sp)
   0x42057e70 <parse_compile_execute+114>:	mv	a0,s1
   0x42057e72 <parse_compile_execute+116>:	jal	0x42004baa <mp_parse>
   0x42057e76 <parse_compile_execute+120>:	lw	a5,16(sp)
   0x42057e78 <parse_compile_execute+122>:	sw	a0,56(sp)
   0x42057e7a <parse_compile_execute+124>:	sw	a1,60(sp)
   0x42057e7c <parse_compile_execute+126>:	srli	a2,a5,0x2
   0x42057e80 <parse_compile_execute+130>:	andi	a2,a2,1
   0x42057e82 <parse_compile_execute+132>:	mv	a1,s3
   0x42057e84 <parse_compile_execute+134>:	addi	a0,sp,56
   0x42057e86 <parse_compile_execute+136>:	jal	0x420082da <mp_compile>
   0x42057e8a <parse_compile_execute+140>:	lw	a4,28(sp)
   0x42057e8c <parse_compile_execute+142>:	li	a5,1
   *** Pointer to compiled function returned by mp_compile copied to s1 ***
   0x42057e8e <parse_compile_execute+144>:	mv	s1,a0
   0x42057e90 <parse_compile_execute+146>:	bne	a4,a5,0x42057e98 <parse_compile_execute+154>
   0x42057e94 <parse_compile_execute+150>:	jal	0x4201fb8e <gc_collect>
   *** Store into module_fun on stack frame after call to gc_collect ***
   0x42057e98 <parse_compile_execute+154>:	sw	s1,24(sp)
   0x42057e9a <parse_compile_execute+156>:	li	a0,3
   0x42057e9c <parse_compile_execute+158>:	jal	0x42057d64 <mp_hal_set_interrupt_char>
   0x42057ea0 <parse_compile_execute+162>:	beqz	s2,0x42057fa2 <parse_compile_execute+420>
   0x42057ea4 <parse_compile_execute+166>:	lw	a5,12(sp)
   0x42057ea6 <parse_compile_execute+168>:	lw	a3,12(a5)
   0x42057ea8 <parse_compile_execute+170>:	lw	a2,4(a5)
   0x42057eaa <parse_compile_execute+172>:	lw	a1,0(a5)
   0x42057eac <parse_compile_execute+174>:	lw	a0,8(a5)
   0x42057eae <parse_compile_execute+176>:	jal	0x4200aa94 <mp_call_function_n_kw>

The call to mp_compile returns a pointer to the compiled function object in a0. a0 is copied to s1 and then gc_collect is called. Subsequent to calling gc_collect, s1 is stored into the stack frame. Because the pointer to the compiled function is not yet in the stack frame when gc_collect is called, gc_collect frees it, resulting in the memory occupied by the executing function's compiled code getting re-allocated and overlayed while it is executing.

Making module_fun volatile prevents gcc's RISC-V codegen from delaying its store into the stack frame, durably correcting the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants