github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/internal/task/task_stack_cortexm.S (about) 1 //go:build tinygo 2 3 // Only generate .debug_frame, don't generate .eh_frame. 4 .cfi_sections .debug_frame 5 6 .section .text.tinygo_startTask 7 .global tinygo_startTask 8 .type tinygo_startTask, %function 9 tinygo_startTask: 10 .cfi_startproc 11 // Small assembly stub for starting a goroutine. This is already run on the 12 // new stack, with the callee-saved registers already loaded. 13 // Most importantly, r4 contains the pc of the to-be-started function and r5 14 // contains the only argument it is given. Multiple arguments are packed 15 // into one by storing them in a new allocation. 16 17 // Indicate to the unwinder that there is nothing to unwind, this is the 18 // root frame. It avoids the following (bogus) error message in GDB: 19 // Backtrace stopped: previous frame identical to this frame (corrupt stack?) 20 .cfi_undefined lr 21 22 // Set the first argument of the goroutine start wrapper, which contains all 23 // the arguments. 24 mov r0, r5 25 26 // Branch to the "goroutine start" function. By using blx instead of bx, 27 // we'll return here instead of tail calling. 28 blx r4 29 30 // After return, exit this goroutine. This is a tail call. 31 bl tinygo_pause 32 .cfi_endproc 33 .size tinygo_startTask, .-tinygo_startTask 34 35 .section .text.tinygo_switchToScheduler 36 .global tinygo_switchToScheduler 37 .type tinygo_switchToScheduler, %function 38 tinygo_switchToScheduler: 39 .cfi_startproc 40 // r0 = sp *uintptr 41 42 // Currently on the task stack (SP=PSP). We need to store the position on 43 // the stack where the in-use registers will be stored. 44 mov r1, sp 45 subs r1, #36 46 str r1, [r0] 47 48 b tinygo_swapTask 49 .cfi_endproc 50 .size tinygo_switchToScheduler, .-tinygo_switchToScheduler 51 52 .section .text.tinygo_switchToTask 53 .global tinygo_switchToTask 54 .type tinygo_switchToTask, %function 55 tinygo_switchToTask: 56 .cfi_startproc 57 // r0 = sp uintptr 58 59 // Currently on the scheduler stack (SP=MSP). We'll have to update the PSP, 60 // and then we can invoke swapTask. 61 msr PSP, r0 62 63 b.n tinygo_swapTask 64 .cfi_endproc 65 .size tinygo_switchToTask, .-tinygo_switchToTask 66 67 .section .text.tinygo_swapTask 68 .global tinygo_swapTask 69 .type tinygo_swapTask, %function 70 tinygo_swapTask: 71 .cfi_startproc 72 // This function stores the current register state to the stack, switches to 73 // the other stack (MSP/PSP), and loads the register state from the other 74 // stack. Apart from saving and restoring all relevant callee-saved 75 // registers, it also ends with branching to the last program counter (saved 76 // as the lr register, to follow the ARM calling convention). 77 78 // On pre-Thumb2 CPUs (Cortex-M0 in particular), registers r8-r15 cannot be 79 // used directly. Only very few operations work on them, such as mov. That's 80 // why the higher register values are first stored in the temporary register 81 // r3 when loading/storing them. 82 // It is possible to reduce the swapTask by two instructions (~2 cycles) on 83 // Cortex-M0 by reordering the layout of the pushed registers from {r4-r11, 84 // lr} to {r8-r11, r4-r8, lr}. However, that also requires a change on the 85 // Go side (depending on thumb1/thumb2!) and so is not really worth the 86 // complexity. 87 88 // Store state to old task. It saves the lr instead of the pc, because that 89 // will be the pc after returning back to the old task (in a different 90 // invocation of swapTask). 91 #if defined(__thumb2__) 92 push {r4-r11, lr} 93 .cfi_def_cfa_offset 9*4 94 #else 95 mov r0, r8 96 mov r1, r9 97 mov r2, r10 98 mov r3, r11 99 push {r0-r3, lr} 100 .cfi_def_cfa_offset 5*4 101 push {r4-r7} 102 .cfi_def_cfa_offset 9*4 103 #endif 104 105 // Switch the stack. This could either switch from PSP to MSP, or from MSP 106 // to PSP. By using an XOR (eor), it will just switch to the other stack. 107 mrs r0, CONTROL // load CONTROL register 108 movs r3, #2 109 eors r0, r0, r3 // flip the SPSEL (active stack pointer) bit 110 msr CONTROL, r0 // store CONTROL register 111 isb // required to flush the pipeline 112 113 // Load state from new task and branch to the previous position in the 114 // program. 115 #if defined(__thumb2__) 116 pop {r4-r11, pc} 117 #else 118 pop {r4-r7} 119 .cfi_def_cfa_offset 5*4 120 pop {r0-r3} 121 .cfi_def_cfa_offset 1*4 122 mov r8, r0 123 mov r9, r1 124 mov r10, r2 125 mov r11, r3 126 pop {pc} 127 #endif 128 .cfi_endproc 129 .size tinygo_swapTask, .-tinygo_swapTask