kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

riscv64.c (8174B)


      1 /*
      2  * lib/coro/riscv64.c -- RISC-V 64-bit (LP64D) implementations of
      3  *   setjmp / longjmp                       (<setjmp.h>)
      4  *   __kit_coro_ctx_init / __kit_coro_switch / trampoline   (<kit/coro.h>)
      5  *
      6  * RISC-V LP64D callee-saved set:
      7  *   ra  (x1)              -- saved manually so longjmp/__kit_coro_switch can
      8  *                            "return" to the original call site
      9  *   sp  (x2)
     10  *   s0-s11 (x8-x9, x18-x27)
     11  *   fs0-fs11 (f8-f9, f18-f27)
     12  *
     13  * Layout (matches xOS rv64 tick_coro_ctx):
     14  *
     15  *   regs[0]:        ra
     16  *   regs[1]:        sp
     17  *   regs[2..13]:    s0-s11
     18  *   fp_regs[0..11]: fs0-fs11   (offset 112)
     19  *
     20  * sizeof = 14*8 + 12*8 = 208, 16-byte aligned. Fits in the 256-byte
     21  * storage carved out by jmp_buf and coro_ctx.
     22  *
     23  *   setjmp(env)             a0=env
     24  *   longjmp(env, val)       a0=env, a1=val
     25  *   __kit_coro_switch(f, t, val)  a0=from, a1=to, a2=val
     26  *
     27  * Value-passing trick: the destination context "returns" via
     28  *     ld ra, 0(a1); ... ret
     29  * where `ret` is `jalr x0, 0(ra)`. By moving the value into a0 just
     30  * before `ret`, both a fresh trampoline (entry(value)) and a previously
     31  * suspended __kit_coro_switch (= the value its switch call returned) see it
     32  * as the a0 return register.
     33  *
     34  * SAVE_/RESTORE_ are C string-concat macros so the same byte sequence
     35  * is emitted in setjmp, longjmp, and __kit_coro_switch without duplication.
     36  *
     37  * Symbol naming uses __USER_LABEL_PREFIX__ so labels match the C
     38  * compiler's call-site mangling (empty on RISC-V ELF).
     39  */
     40 
     41 #include <kit/coro.h>
     42 #include <setjmp.h>
     43 #include <stddef.h>
     44 #include <stdint.h>
     45 
     46 struct __kit_riscv64_ctx {
     47   uintptr_t regs[14];
     48   uint64_t fp_regs[12];
     49 } __attribute__((aligned(16)));
     50 
     51 _Static_assert(sizeof(struct __kit_riscv64_ctx) == 208, "layout");
     52 _Static_assert(_Alignof(struct __kit_riscv64_ctx) == 16, "align");
     53 _Static_assert(offsetof(struct __kit_riscv64_ctx, fp_regs) == 112, "fp off");
     54 _Static_assert(sizeof(struct __kit_riscv64_ctx) <= sizeof(coro_ctx),
     55                "fits coro_ctx");
     56 _Static_assert(sizeof(struct __kit_riscv64_ctx) <= sizeof(jmp_buf),
     57                "fits jmp_buf");
     58 _Static_assert(_Alignof(coro_ctx) >= _Alignof(struct __kit_riscv64_ctx),
     59                "align coro_ctx");
     60 
     61 extern void __kit_coro_trampoline(void);
     62 
     63 void __kit_coro_ctx_init(coro_ctx* ctx, void* stack_base, size_t stack_len,
     64                          void (*entry)(uintptr_t)) {
     65   struct __kit_riscv64_ctx* c = (struct __kit_riscv64_ctx*)ctx;
     66 
     67   /* RISC-V stacks grow down; align top to 16. */
     68   uintptr_t top = (uintptr_t)stack_base + stack_len;
     69   top &= ~(uintptr_t)(CORO_STACK_ALIGN - 1);
     70 
     71   for (size_t i = 0; i < sizeof(*c) / sizeof(uintptr_t); ++i)
     72     ((uintptr_t*)c)[i] = 0;
     73 
     74   c->regs[0] = (uintptr_t)__kit_coro_trampoline; /* ra */
     75   c->regs[1] = top;                              /* sp */
     76   c->regs[2] = (uintptr_t)entry;                 /* s0 -- entry fn */
     77 }
     78 
     79 #define STR_(x) #x
     80 #define STR(x) STR_(x)
     81 #define SYM(n) STR(__USER_LABEL_PREFIX__) #n
     82 
     83 /* Save callee-saved state into [reg]. reg is a register name string,
     84    e.g. "a0". Emits straight-line sd/fsd; no scratch register needed. */
     85 #define SAVE_INTO(reg)     \
     86   "    sd  ra,    0(" reg  \
     87   ")\n"                    \
     88   "    sd  sp,    8(" reg  \
     89   ")\n"                    \
     90   "    sd  s0,   16(" reg  \
     91   ")\n"                    \
     92   "    sd  s1,   24(" reg  \
     93   ")\n"                    \
     94   "    sd  s2,   32(" reg  \
     95   ")\n"                    \
     96   "    sd  s3,   40(" reg  \
     97   ")\n"                    \
     98   "    sd  s4,   48(" reg  \
     99   ")\n"                    \
    100   "    sd  s5,   56(" reg  \
    101   ")\n"                    \
    102   "    sd  s6,   64(" reg  \
    103   ")\n"                    \
    104   "    sd  s7,   72(" reg  \
    105   ")\n"                    \
    106   "    sd  s8,   80(" reg  \
    107   ")\n"                    \
    108   "    sd  s9,   88(" reg  \
    109   ")\n"                    \
    110   "    sd  s10,  96(" reg  \
    111   ")\n"                    \
    112   "    sd  s11, 104(" reg  \
    113   ")\n"                    \
    114   "    fsd fs0,  112(" reg \
    115   ")\n"                    \
    116   "    fsd fs1,  120(" reg \
    117   ")\n"                    \
    118   "    fsd fs2,  128(" reg \
    119   ")\n"                    \
    120   "    fsd fs3,  136(" reg \
    121   ")\n"                    \
    122   "    fsd fs4,  144(" reg \
    123   ")\n"                    \
    124   "    fsd fs5,  152(" reg \
    125   ")\n"                    \
    126   "    fsd fs6,  160(" reg \
    127   ")\n"                    \
    128   "    fsd fs7,  168(" reg \
    129   ")\n"                    \
    130   "    fsd fs8,  176(" reg \
    131   ")\n"                    \
    132   "    fsd fs9,  184(" reg \
    133   ")\n"                    \
    134   "    fsd fs10, 192(" reg \
    135   ")\n"                    \
    136   "    fsd fs11, 200(" reg ")\n"
    137 
    138 /* Restore callee-saved state from [reg]. */
    139 #define RESTORE_FROM(reg)  \
    140   "    fld fs0,  112(" reg \
    141   ")\n"                    \
    142   "    fld fs1,  120(" reg \
    143   ")\n"                    \
    144   "    fld fs2,  128(" reg \
    145   ")\n"                    \
    146   "    fld fs3,  136(" reg \
    147   ")\n"                    \
    148   "    fld fs4,  144(" reg \
    149   ")\n"                    \
    150   "    fld fs5,  152(" reg \
    151   ")\n"                    \
    152   "    fld fs6,  160(" reg \
    153   ")\n"                    \
    154   "    fld fs7,  168(" reg \
    155   ")\n"                    \
    156   "    fld fs8,  176(" reg \
    157   ")\n"                    \
    158   "    fld fs9,  184(" reg \
    159   ")\n"                    \
    160   "    fld fs10, 192(" reg \
    161   ")\n"                    \
    162   "    fld fs11, 200(" reg \
    163   ")\n"                    \
    164   "    ld  ra,    0(" reg  \
    165   ")\n"                    \
    166   "    ld  sp,    8(" reg  \
    167   ")\n"                    \
    168   "    ld  s0,   16(" reg  \
    169   ")\n"                    \
    170   "    ld  s1,   24(" reg  \
    171   ")\n"                    \
    172   "    ld  s2,   32(" reg  \
    173   ")\n"                    \
    174   "    ld  s3,   40(" reg  \
    175   ")\n"                    \
    176   "    ld  s4,   48(" reg  \
    177   ")\n"                    \
    178   "    ld  s5,   56(" reg  \
    179   ")\n"                    \
    180   "    ld  s6,   64(" reg  \
    181   ")\n"                    \
    182   "    ld  s7,   72(" reg  \
    183   ")\n"                    \
    184   "    ld  s8,   80(" reg  \
    185   ")\n"                    \
    186   "    ld  s9,   88(" reg  \
    187   ")\n"                    \
    188   "    ld  s10,  96(" reg  \
    189   ")\n"                    \
    190   "    ld  s11, 104(" reg ")\n"
    191 
    192 __asm__ (
    193     ".text\n"
    194     ".align 2\n"
    195 
    196     /* setjmp(env) -- env in a0. ra at call time is the caller's return
    197        address, which is exactly what longjmp must restore. */
    198     ".weak " SYM(setjmp) "\n"
    199     ".type "  SYM(setjmp) ", @function\n"
    200     SYM(setjmp) ":\n"
    201     SAVE_INTO("a0")
    202     "    li   a0, 0\n"
    203     "    ret\n"
    204     ".size "  SYM(setjmp) ", .-" SYM(setjmp) "\n"
    205 
    206     /* longjmp(env, val) -- env in a0, val in a1.
    207        longjmp(_, 0) must deliver 1 (C11 7.13.2.1p4). Branch-free:
    208        seqz t0, a1 -> t0 = (a1==0); a0 = a1 + t0. RESTORE_FROM
    209        doesn't touch t0/a0/a1, so the seqz/add can run after it and
    210        write a0 directly -- one fewer instruction than munging a1
    211        first and mv'ing later. */
    212     ".weak " SYM(longjmp) "\n"
    213     ".type "  SYM(longjmp) ", @function\n"
    214     SYM(longjmp) ":\n"
    215     RESTORE_FROM("a0")
    216     "    seqz t0, a1\n"
    217     "    add  a0, a1, t0\n"
    218     "    ret\n"
    219     ".size "  SYM(longjmp) ", .-" SYM(longjmp) "\n"
    220 
    221     /* __kit_coro_switch(from, to, value) -- a0=from, a1=to, a2=value.
    222        Save into [a0], restore from [a1] (which clobbers a0 and a1's
    223        roles -- ra/sp/s* are loaded from the to-context), then deliver
    224        value in a0 just before ret. */
    225     ".globl " SYM(__kit_coro_switch) "\n"
    226     ".type "  SYM(__kit_coro_switch) ", @function\n"
    227     SYM(__kit_coro_switch) ":\n"
    228     SAVE_INTO("a0")
    229     RESTORE_FROM("a1")
    230     "    mv   a0, a2\n"
    231     "    ret\n"
    232     ".size "  SYM(__kit_coro_switch) ", .-" SYM(__kit_coro_switch) "\n"
    233 
    234     /* __kit_coro_trampoline -- on first entry: a0=value (delivered),
    235        s0=entry fn (set by __kit_coro_ctx_init), sp aligned to 16. ebreak if entry
    236        returns. */
    237     ".globl " SYM(__kit_coro_trampoline) "\n"
    238     ".type "  SYM(__kit_coro_trampoline) ", @function\n"
    239     SYM(__kit_coro_trampoline) ":\n"
    240     "    jalr s0\n"
    241     "    ebreak\n"
    242     ".size "  SYM(__kit_coro_trampoline) ", .-" SYM(__kit_coro_trampoline) "\n"
    243 
    244     ".section .note.GNU-stack,\"\",%progbits\n"
    245 );