xco

Concurrency for C
git clone https://git.ryansepassi.com/git/xco.git
Log | Files | Refs | README

xco_platform.c (4961B)


      1 /*
      2  * platform/arm64/xco_platform.c — AArch64 init + switch + xco_trampoline thunk.
      3  *
      4  * The switch primitive and the xco_trampoline thunk are written as
      5  * file-scope global asm rather than __attribute__((naked)) functions:
      6  * GCC silently ignores `naked` on AArch64, so naked-function inline
      7  * asm only works under Clang. File-scope __asm__ is portable across
      8  * both GCC and Clang and keeps everything in one .c file.
      9  *
     10  * Layout offsets used in the asm:
     11  *     0   regs[0..9]    x19-x28
     12  *    80   regs[10..11]  fp (x29), lr (x30)
     13  *    96   regs[12]      sp
     14  *   104   fp_regs[0..7] d8-d15
     15  *
     16  * Symbol naming: asm labels are built with the compiler-provided
     17  * __USER_LABEL_PREFIX__ (empty on ELF, "_" on Mach-O) so the labels
     18  * emitted here match what the C compiler generates for references to
     19  * xco_platform_switch / xco_platform_trampoline_thunk on either OS.
     20  */
     21 
     22 #include "xco_platform_internal.h"
     23 #include "xco_platform.h"
     24 
     25 #include <assert.h>
     26 #include <stddef.h>
     27 #include <stdint.h>
     28 #include <string.h>
     29 
     30 /* The platform context type forward-declared in xco_platform.h. */
     31 struct xco_platform_ctx {
     32     uintptr_t regs[13];
     33     uint64_t  fp_regs[8];
     34 } __attribute__((aligned(16)));
     35 
     36 /* ---- layout checks --------------------------------------------------
     37  *
     38  * xco.c sized its embedded ctx buffer using these macros — verify
     39  * they still match the real struct, and verify the byte offsets the
     40  * asm hardcodes are still correct.
     41  */
     42 _Static_assert(sizeof(struct xco_platform_ctx)   == XCO__CTX_SIZE,
     43                "XCO__CTX_SIZE out of sync with struct layout");
     44 _Static_assert(_Alignof(struct xco_platform_ctx) == XCO__CTX_ALIGN,
     45                "XCO__CTX_ALIGN out of sync with struct layout");
     46 _Static_assert(sizeof(uintptr_t) == 8,                                "AArch64");
     47 _Static_assert(offsetof(struct xco_platform_ctx, regs)    == 0,       "");
     48 _Static_assert(offsetof(struct xco_platform_ctx, fp_regs) == 104,     "");
     49 
     50 /* Forward declaration: the symbol is defined by the file-scope asm
     51  * below. Its address is taken in xco_platform_init. */
     52 extern void xco_platform_trampoline_thunk(void);
     53 
     54 /* ---- xco_platform_init --------------------------------------------- */
     55 
     56 void xco_platform_init(xco_platform_ctx_t *ctx,
     57                        void *stack_base, size_t stack_len,
     58                        void (*entry)(uintptr_t)) {
     59     assert(((uintptr_t)stack_base & (XCO_STACK_ALIGN - 1)) == 0);
     60 
     61     /* AArch64 stacks grow down. Compute and align the top. */
     62     uintptr_t top = (uintptr_t)stack_base + stack_len;
     63     top &= ~(uintptr_t)(XCO_STACK_ALIGN - 1);
     64 
     65     memset(ctx, 0, sizeof(*ctx));
     66 
     67     /* The switch primitive will load these into x19, fp, lr, sp. */
     68     ctx->regs[0]  = (uintptr_t)entry;                         /* x19 */
     69     ctx->regs[10] = 0;                                         /* fp  */
     70     ctx->regs[11] = (uintptr_t)xco_platform_trampoline_thunk;  /* lr  */
     71     ctx->regs[12] = top;                                       /* sp  */
     72 }
     73 
     74 /* ---- xco_platform_switch and xco_trampoline thunk (file-scope asm) -----
     75  *
     76  * xco_platform_switch(from x0, to x1, value x2) -> x0:
     77  *   saves callee-saved state into *from, restores from *to, and
     78  *   delivers `value` to the destination as x0 — which is the first-arg
     79  *   register on the xco_trampoline thunk's first run, and the return-value
     80  *   register when resuming a previously-suspended switch.
     81  *
     82  * xco_platform_trampoline_thunk():
     83  *   On entry x0 = value, x19 = entry; tail-calls entry(value); brk if
     84  *   it ever returns.
     85  */
     86 
     87 /* Stringify-after-expand so __USER_LABEL_PREFIX__ (a token, possibly
     88  * empty) becomes a string literal we can concatenate into the asm. */
     89 #define XCO_STR_(x)   #x
     90 #define XCO_STR(x)    XCO_STR_(x)
     91 #define XCO_SYM(name) XCO_STR(__USER_LABEL_PREFIX__) #name
     92 
     93 __asm__ (
     94     ".text\n"
     95     ".align 4\n"
     96 
     97     ".globl " XCO_SYM(xco_platform_switch) "\n"
     98     XCO_SYM(xco_platform_switch) ":\n"
     99     "    stp x19, x20, [x0, #0]\n"
    100     "    stp x21, x22, [x0, #16]\n"
    101     "    stp x23, x24, [x0, #32]\n"
    102     "    stp x25, x26, [x0, #48]\n"
    103     "    stp x27, x28, [x0, #64]\n"
    104     "    stp fp,  lr,  [x0, #80]\n"
    105     "    mov x9, sp\n"
    106     "    str x9,       [x0, #96]\n"
    107     "    stp d8,  d9,  [x0, #104]\n"
    108     "    stp d10, d11, [x0, #120]\n"
    109     "    stp d12, d13, [x0, #136]\n"
    110     "    stp d14, d15, [x0, #152]\n"
    111 
    112     "    ldp d8,  d9,  [x1, #104]\n"
    113     "    ldp d10, d11, [x1, #120]\n"
    114     "    ldp d12, d13, [x1, #136]\n"
    115     "    ldp d14, d15, [x1, #152]\n"
    116     "    ldp x19, x20, [x1, #0]\n"
    117     "    ldp x21, x22, [x1, #16]\n"
    118     "    ldp x23, x24, [x1, #32]\n"
    119     "    ldp x25, x26, [x1, #48]\n"
    120     "    ldp x27, x28, [x1, #64]\n"
    121     "    ldp fp,  lr,  [x1, #80]\n"
    122     "    ldr x9,       [x1, #96]\n"
    123     "    mov sp, x9\n"
    124 
    125     "    mov x0, x2\n"
    126     "    ret\n"
    127 
    128     ".globl " XCO_SYM(xco_platform_trampoline_thunk) "\n"
    129     XCO_SYM(xco_platform_trampoline_thunk) ":\n"
    130     "    blr x19\n"
    131     "    brk #0\n"
    132 );