riscv32.c (9283B)
1 /* 2 * lib/coro/riscv32.c -- RISC-V 32-bit (ILP32/ILP32F/ILP32D) implementations of 3 * setjmp / longjmp (<setjmp.h>) 4 * __kit_coro_ctx_init / __kit_coro_switch / trampoline (<kit/coro.h>) 5 * 6 * Per-target context layout (matches xOS rv32 tick_coro_ctx): 7 * 8 * regs[0]: ra 9 * regs[1]: sp 10 * regs[2..13]: s0-s11 11 * fp_regs[0..11]: fs0-fs11 12 * 13 * The fp_regs slots are always allocated (12 * 8 = 96 bytes at offset 14 * 56) so the struct layout is constant regardless of the F/D extension. 15 * The save/restore code is conditional on __riscv_flen: 16 * __riscv_flen == 64 -> fsd/fld (64-bit, fills slots fully) 17 * __riscv_flen == 32 -> fsw/flw (32-bit, packs into the low halves) 18 * else -> no FP save/restore 19 * 20 * Field bytes = 14*4 + 12*8 = 152; sizeof = 160 after 16-byte align 21 * tail padding. Fits in the 256-byte storage carved out by jmp_buf 22 * and coro_ctx. 23 * 24 * SAVE_/RESTORE_ are C string-concat macros so the same byte sequence 25 * is emitted in setjmp, longjmp, and __kit_coro_switch without duplication. 26 */ 27 28 #include <kit/coro.h> 29 #include <setjmp.h> 30 #include <stddef.h> 31 #include <stdint.h> 32 33 struct __kit_riscv32_ctx { 34 uintptr_t regs[14]; 35 uint64_t fp_regs[12]; 36 } __attribute__((aligned(16))); 37 38 _Static_assert(sizeof(struct __kit_riscv32_ctx) == 160, "layout"); 39 _Static_assert(_Alignof(struct __kit_riscv32_ctx) == 16, "align"); 40 _Static_assert(offsetof(struct __kit_riscv32_ctx, fp_regs) == 56, "fp off"); 41 _Static_assert(sizeof(struct __kit_riscv32_ctx) <= sizeof(coro_ctx), 42 "fits coro_ctx"); 43 _Static_assert(sizeof(struct __kit_riscv32_ctx) <= sizeof(jmp_buf), 44 "fits jmp_buf"); 45 _Static_assert(_Alignof(coro_ctx) >= _Alignof(struct __kit_riscv32_ctx), 46 "align coro_ctx"); 47 48 extern void __kit_coro_trampoline(void); 49 50 void __kit_coro_ctx_init(coro_ctx* ctx, void* stack_base, size_t stack_len, 51 void (*entry)(uintptr_t)) { 52 struct __kit_riscv32_ctx* c = (struct __kit_riscv32_ctx*)ctx; 53 54 /* RISC-V stacks grow down; align top to 16. */ 55 uintptr_t top = (uintptr_t)stack_base + stack_len; 56 top &= ~(uintptr_t)(CORO_STACK_ALIGN - 1); 57 58 for (size_t i = 0; i < sizeof(*c) / sizeof(uintptr_t); ++i) 59 ((uintptr_t*)c)[i] = 0; 60 61 c->regs[0] = (uintptr_t)__kit_coro_trampoline; /* ra */ 62 c->regs[1] = top; /* sp */ 63 c->regs[2] = (uintptr_t)entry; /* s0 -- entry fn */ 64 } 65 66 #define STR_(x) #x 67 #define STR(x) STR_(x) 68 #define SYM(n) STR(__USER_LABEL_PREFIX__) #n 69 70 /* Integer save: ra, sp, s0-s11 into regs[0..13] at offsets 0..52. */ 71 #define SAVE_GPR(reg) \ 72 " sw ra, 0(" reg \ 73 ")\n" \ 74 " sw sp, 4(" reg \ 75 ")\n" \ 76 " sw s0, 8(" reg \ 77 ")\n" \ 78 " sw s1, 12(" reg \ 79 ")\n" \ 80 " sw s2, 16(" reg \ 81 ")\n" \ 82 " sw s3, 20(" reg \ 83 ")\n" \ 84 " sw s4, 24(" reg \ 85 ")\n" \ 86 " sw s5, 28(" reg \ 87 ")\n" \ 88 " sw s6, 32(" reg \ 89 ")\n" \ 90 " sw s7, 36(" reg \ 91 ")\n" \ 92 " sw s8, 40(" reg \ 93 ")\n" \ 94 " sw s9, 44(" reg \ 95 ")\n" \ 96 " sw s10, 48(" reg \ 97 ")\n" \ 98 " sw s11, 52(" reg ")\n" 99 100 #define RESTORE_GPR(reg) \ 101 " lw ra, 0(" reg \ 102 ")\n" \ 103 " lw sp, 4(" reg \ 104 ")\n" \ 105 " lw s0, 8(" reg \ 106 ")\n" \ 107 " lw s1, 12(" reg \ 108 ")\n" \ 109 " lw s2, 16(" reg \ 110 ")\n" \ 111 " lw s3, 20(" reg \ 112 ")\n" \ 113 " lw s4, 24(" reg \ 114 ")\n" \ 115 " lw s5, 28(" reg \ 116 ")\n" \ 117 " lw s6, 32(" reg \ 118 ")\n" \ 119 " lw s7, 36(" reg \ 120 ")\n" \ 121 " lw s8, 40(" reg \ 122 ")\n" \ 123 " lw s9, 44(" reg \ 124 ")\n" \ 125 " lw s10, 48(" reg \ 126 ")\n" \ 127 " lw s11, 52(" reg ")\n" 128 129 #if __riscv_flen == 64 130 #define SAVE_FPR(reg) \ 131 " fsd fs0, 56(" reg \ 132 ")\n" \ 133 " fsd fs1, 64(" reg \ 134 ")\n" \ 135 " fsd fs2, 72(" reg \ 136 ")\n" \ 137 " fsd fs3, 80(" reg \ 138 ")\n" \ 139 " fsd fs4, 88(" reg \ 140 ")\n" \ 141 " fsd fs5, 96(" reg \ 142 ")\n" \ 143 " fsd fs6, 104(" reg \ 144 ")\n" \ 145 " fsd fs7, 112(" reg \ 146 ")\n" \ 147 " fsd fs8, 120(" reg \ 148 ")\n" \ 149 " fsd fs9, 128(" reg \ 150 ")\n" \ 151 " fsd fs10, 136(" reg \ 152 ")\n" \ 153 " fsd fs11, 144(" reg ")\n" 154 #define RESTORE_FPR(reg) \ 155 " fld fs0, 56(" reg \ 156 ")\n" \ 157 " fld fs1, 64(" reg \ 158 ")\n" \ 159 " fld fs2, 72(" reg \ 160 ")\n" \ 161 " fld fs3, 80(" reg \ 162 ")\n" \ 163 " fld fs4, 88(" reg \ 164 ")\n" \ 165 " fld fs5, 96(" reg \ 166 ")\n" \ 167 " fld fs6, 104(" reg \ 168 ")\n" \ 169 " fld fs7, 112(" reg \ 170 ")\n" \ 171 " fld fs8, 120(" reg \ 172 ")\n" \ 173 " fld fs9, 128(" reg \ 174 ")\n" \ 175 " fld fs10, 136(" reg \ 176 ")\n" \ 177 " fld fs11, 144(" reg ")\n" 178 #elif __riscv_flen == 32 179 #define SAVE_FPR(reg) \ 180 " fsw fs0, 56(" reg \ 181 ")\n" \ 182 " fsw fs1, 60(" reg \ 183 ")\n" \ 184 " fsw fs2, 64(" reg \ 185 ")\n" \ 186 " fsw fs3, 68(" reg \ 187 ")\n" \ 188 " fsw fs4, 72(" reg \ 189 ")\n" \ 190 " fsw fs5, 76(" reg \ 191 ")\n" \ 192 " fsw fs6, 80(" reg \ 193 ")\n" \ 194 " fsw fs7, 84(" reg \ 195 ")\n" \ 196 " fsw fs8, 88(" reg \ 197 ")\n" \ 198 " fsw fs9, 92(" reg \ 199 ")\n" \ 200 " fsw fs10, 96(" reg \ 201 ")\n" \ 202 " fsw fs11, 100(" reg ")\n" 203 #define RESTORE_FPR(reg) \ 204 " flw fs0, 56(" reg \ 205 ")\n" \ 206 " flw fs1, 60(" reg \ 207 ")\n" \ 208 " flw fs2, 64(" reg \ 209 ")\n" \ 210 " flw fs3, 68(" reg \ 211 ")\n" \ 212 " flw fs4, 72(" reg \ 213 ")\n" \ 214 " flw fs5, 76(" reg \ 215 ")\n" \ 216 " flw fs6, 80(" reg \ 217 ")\n" \ 218 " flw fs7, 84(" reg \ 219 ")\n" \ 220 " flw fs8, 88(" reg \ 221 ")\n" \ 222 " flw fs9, 92(" reg \ 223 ")\n" \ 224 " flw fs10, 96(" reg \ 225 ")\n" \ 226 " flw fs11, 100(" reg ")\n" 227 #else 228 #define SAVE_FPR(reg) "" 229 #define RESTORE_FPR(reg) "" 230 #endif 231 232 /* Save: int first, FP second (matches xOS rv32 pattern, and rv64 here). 233 Restore: FP first, int second -- mirror order, minimizes register 234 reuse window. Note none of these loads write to the address-base 235 register, so the integer/FP order is purely cosmetic. */ 236 #define SAVE_INTO(reg) SAVE_GPR(reg) SAVE_FPR(reg) 237 #define RESTORE_FROM(reg) RESTORE_FPR(reg) RESTORE_GPR(reg) 238 239 __asm__ ( 240 ".text\n" 241 ".align 2\n" 242 243 /* setjmp(env) -- env=a0. ra at function entry is the caller's 244 return address, exactly what longjmp must restore. */ 245 ".weak " SYM(setjmp) "\n" 246 ".type " SYM(setjmp) ", @function\n" 247 SYM(setjmp) ":\n" 248 SAVE_INTO("a0") 249 " li a0, 0\n" 250 " ret\n" 251 ".size " SYM(setjmp) ", .-" SYM(setjmp) "\n" 252 253 /* longjmp(env, val) -- env=a0, val=a1. 254 longjmp(_, 0) must deliver 1 (C11 7.13.2.1p4); branch-free: 255 seqz t0, a1 ; t0 = (a1 == 0) 256 add a0, a1, t0 257 so a0 = a1 if a1 != 0, else 1. */ 258 ".weak " SYM(longjmp) "\n" 259 ".type " SYM(longjmp) ", @function\n" 260 SYM(longjmp) ":\n" 261 RESTORE_FROM("a0") 262 " seqz t0, a1\n" 263 " add a0, a1, t0\n" 264 " ret\n" 265 ".size " SYM(longjmp) ", .-" SYM(longjmp) "\n" 266 267 /* __kit_coro_switch(from, to, value) -- a0=from, a1=to, a2=value. 268 Save into [a0], restore from [a1], deliver a2 in a0 (which is 269 both the return register and the trampoline's first-arg reg 270 on a fresh context's first run). */ 271 ".globl " SYM(__kit_coro_switch) "\n" 272 ".type " SYM(__kit_coro_switch) ", @function\n" 273 SYM(__kit_coro_switch) ":\n" 274 SAVE_INTO("a0") 275 RESTORE_FROM("a1") 276 " mv a0, a2\n" 277 " ret\n" 278 ".size " SYM(__kit_coro_switch) ", .-" SYM(__kit_coro_switch) "\n" 279 280 /* __kit_coro_trampoline -- on first entry: a0=value (delivered 281 by __kit_coro_switch's `mv a0, a2`), s0=entry (set by __kit_coro_ctx_init via 282 regs[2]), sp=stack_top. ebreak if entry returns. */ 283 ".globl " SYM(__kit_coro_trampoline) "\n" 284 ".type " SYM(__kit_coro_trampoline) ", @function\n" 285 SYM(__kit_coro_trampoline) ":\n" 286 " jalr s0\n" 287 " ebreak\n" 288 ".size " SYM(__kit_coro_trampoline) ", .-" SYM(__kit_coro_trampoline) "\n" 289 290 ".section .note.GNU-stack,\"\",@progbits\n" 291 );