Introduction
With the Windowed Register option the sixteen visible registers a0 to a15 are a window onto a
larger physical file (64 registers on the ESP32, ESP32-S2 and ESP32-S3). A call moves the window up by 4, 8 or 12 registers; the
callee's low registers are the caller's high ones, so arguments pass without copying. The ESP8266 has no windows: it uses the
call0 ABI, where the compiler saves registers on the stack itself.
The state is in two special registers: WINDOWBASE (where the window starts, in groups of 4)
and WINDOWSTART (one bit for each group that begins a live frame), and in the
CALLINC and WOE fields of PS. A call whose new window would run into a live frame raises a
window overflow exception; a return to a frame that is no longer in the file raises a window underflow exception. The handlers
spill and reload registers with S32E and L32E and return with RFWO and RFWU.
Calls, entry and return
A windowed call puts the return address in the callee's a0 with the increment (1, 2 or 3) in its top two bits,
sets PS.CALLINC and jumps. ENTRY in the callee rotates the window by CALLINC groups and allocates the
stack frame. RETW reads the increment back from the top bits of a0, rotates the window back and jumps.
The call target of CALL4, CALL8 and CALL12 is word-aligned like CALL0:
((pc & ~3) + 4) + (offset18 << 2). ENTRY must be at a word-aligned address.
| Instruction | Encoding | What it does |
|---|---|---|
call4 target | o o o o oo01 5 | Windowed call; the callee's window starts 4 registers up (its a0 is the caller's a4). |
call8 target | o o o o oo10 5 | Windowed call; the window moves 8. |
call12 target | o o o o oo11 5 | Windowed call; the window moves 12. |
callx4 as | 0 0 0 s d 0 | Windowed call to the address in as; the window moves 4. |
callx8 as | 0 0 0 s e 0 | Windowed call to the address in as; the window moves 8. |
callx12 as | 0 0 0 s f 0 | Windowed call to the address in as; the window moves 12. |
entry as, size | i i i s 3 6 | Rotate the window by CALLINC and subtract the frame size (i times 8, up to 32,760) from as to make the callee's stack pointer. |
retw | 0 0 0 0 9 0 | Windowed return. |
retw.n | f 0 1 d | Windowed return, 2 bytes (Code Density). |
movsp at, as | 0 0 1 s t 0 | Move to the stack pointer; raises a window underflow exception if the frame was spilled, so the move waits for the reload. |
rotw n | 4 0 8 0 t 0 | Rotate the window by n groups of 4 (n from -8 to 7); privileged. |
Window overflow and underflow
| Instruction | Encoding | What it does |
|---|---|---|
l32e at, as, offset | 0 9 r s t 0 | Load a word using the privilege of the window owner; the offset is -64 to -4 (r minus 16, times 4). |
s32e at, as, offset | 4 9 r s t 0 | Store a word in the same way. |
rfwo | 0 0 3 4 0 0 | Return from window overflow exception. |
rfwu | 0 0 3 5 0 0 | Return from window underflow exception. |
The windowed ABI in practice
In compiled ESP-IDF code a function begins entry a1, N, receives its arguments in a2 to a7 and
returns a value in a2; the caller puts outgoing arguments in a10 upward for a call8
(the callee sees them as a2). a1 is the stack pointer. Interrupt and exception code uses
call0 because the window handlers cannot run inside a window exception.
Not described
Known, not yet covered
- Reference: the window overflow and underflow vectors and their handlers in detail.
Unknown
- Reference: the prose is from the ISA manual as remembered; objdump confirms the encodings and operands, not the descriptions of what the window does.
Links
Reference
- ESP-IDF documentation – Espressif's documentation