x86_64.c (5061B)
1 /* 2 * lib/coro/x86_64.c -- x86_64 System V ABI implementations of 3 * setjmp / longjmp (<setjmp.h>) 4 * __kit_coro_ctx_init / __kit_coro_switch / trampoline (<kit/coro.h>) 5 * 6 * Callee-saved set on SysV: rbx, rbp, r12-r15. (No callee-saved xmm 7 * regs -- those are MS-ABI specific; see x86_64_win.c.) 8 * 9 * regs[0]: rbx regs[4]: r14 10 * regs[1]: rbp regs[5]: r15 11 * regs[2]: r12 regs[6]: rsp 12 * regs[3]: r13 regs[7]: rip 13 * 14 * sizeof = 64, 16-byte aligned. 15 * 16 * setjmp(env) %rdi=env 17 * longjmp(env, val) %rdi=env, %esi=val 18 * __kit_coro_switch(f, t, val) %rdi=from, %rsi=to, %rdx=val 19 * 20 * The "save rsp/rip" trick: at function entry, (%rsp) holds the 21 * caller's return address (just pushed by `call`); 8(%rsp) is the 22 * caller's pre-call rsp. Saving those two lets longjmp/__kit_coro_switch 23 * "land" at the call site exactly as if the function had returned. 24 */ 25 26 #include <kit/coro.h> 27 #include <setjmp.h> 28 #include <stddef.h> 29 #include <stdint.h> 30 31 struct __kit_x86_64_ctx { 32 uintptr_t regs[8]; 33 } __attribute__((aligned(16))); 34 35 _Static_assert(sizeof(struct __kit_x86_64_ctx) == 64, "layout"); 36 _Static_assert(_Alignof(struct __kit_x86_64_ctx) == 16, "align"); 37 _Static_assert(sizeof(struct __kit_x86_64_ctx) <= sizeof(coro_ctx), 38 "fits coro_ctx"); 39 _Static_assert(sizeof(struct __kit_x86_64_ctx) <= sizeof(jmp_buf), 40 "fits jmp_buf"); 41 _Static_assert(_Alignof(coro_ctx) >= _Alignof(struct __kit_x86_64_ctx), 42 "align coro_ctx"); 43 44 extern void __kit_coro_trampoline(void); 45 46 void __kit_coro_ctx_init(coro_ctx* ctx, void* stack_base, size_t stack_len, 47 void (*entry)(uintptr_t)) { 48 struct __kit_x86_64_ctx* c = (struct __kit_x86_64_ctx*)ctx; 49 50 /* x86_64 stacks grow down; align top to 16. */ 51 uintptr_t top = (uintptr_t)stack_base + stack_len; 52 top &= ~(uintptr_t)(CORO_STACK_ALIGN - 1); 53 54 for (size_t i = 0; i < sizeof(*c) / sizeof(uintptr_t); ++i) 55 ((uintptr_t*)c)[i] = 0; 56 57 c->regs[1] = 0; /* rbp */ 58 c->regs[3] = (uintptr_t)entry; /* r13 -- entry fn */ 59 c->regs[6] = top; /* rsp */ 60 c->regs[7] = (uintptr_t)__kit_coro_trampoline; /* rip */ 61 } 62 63 #define STR_(x) #x 64 #define STR(x) STR_(x) 65 #define SYM(n) STR(__USER_LABEL_PREFIX__) #n 66 67 /* Save callee-saved + (caller's) rsp + rip into [reg]; clobbers %rax. 68 Used at function-entry stack discipline: (%rsp)=ret-addr, 8(%rsp)=pre-call 69 rsp. */ 70 #define SAVE_INTO(reg) \ 71 " movq %rbx, 0(" reg \ 72 ")\n" \ 73 " movq %rbp, 8(" reg \ 74 ")\n" \ 75 " movq %r12, 16(" reg \ 76 ")\n" \ 77 " movq %r13, 24(" reg \ 78 ")\n" \ 79 " movq %r14, 32(" reg \ 80 ")\n" \ 81 " movq %r15, 40(" reg \ 82 ")\n" \ 83 " leaq 8(%rsp), %rax\n" \ 84 " movq %rax, 48(" reg \ 85 ")\n" \ 86 " movq (%rsp), %rax\n" \ 87 " movq %rax, 56(" reg ")\n" 88 89 /* Restore callee-saved + rsp from [reg], leave rip in %rcx ready to 90 jmp. Caller delivers the destination value in %rax beforehand. */ 91 #define RESTORE_FROM(reg) \ 92 " movq 0(" reg \ 93 "), %rbx\n" \ 94 " movq 8(" reg \ 95 "), %rbp\n" \ 96 " movq 16(" reg \ 97 "), %r12\n" \ 98 " movq 24(" reg \ 99 "), %r13\n" \ 100 " movq 32(" reg \ 101 "), %r14\n" \ 102 " movq 40(" reg \ 103 "), %r15\n" \ 104 " movq 48(" reg \ 105 "), %rsp\n" \ 106 " movq 56(" reg "), %rcx\n" 107 108 __asm__ ( 109 ".text\n" 110 ".p2align 4\n" 111 112 /* setjmp(env) -- env=%rdi */ 113 ".weak " SYM(setjmp) "\n" 114 SYM(setjmp) ":\n" 115 SAVE_INTO("%rdi") 116 " xorl %eax, %eax\n" 117 " ret\n" 118 119 /* longjmp(env, val) -- env=%rdi, val=%esi. 120 longjmp(_, 0) must deliver 1 (C11 7.13.2.1p4). */ 121 ".weak " SYM(longjmp) "\n" 122 SYM(longjmp) ":\n" 123 " movslq %esi, %rax\n" /* sign-extend int → long */ 124 " testq %rax, %rax\n" 125 " movl $1, %edx\n" 126 " cmoveq %rdx, %rax\n" 127 RESTORE_FROM("%rdi") 128 " jmpq *%rcx\n" 129 130 /* __kit_coro_switch(from, to, value) -- from=%rdi, to=%rsi, value=%rdx. */ 131 ".globl " SYM(__kit_coro_switch) "\n" 132 SYM(__kit_coro_switch) ":\n" 133 SAVE_INTO("%rdi") 134 " movq %rdx, %rax\n" /* deliver value as return reg */ 135 RESTORE_FROM("%rsi") 136 " jmpq *%rcx\n" 137 138 /* __kit_coro_trampoline -- on first entry: %rax=value, 139 %r13=entry, %rsp=stack_top (no return addr pushed -- __kit_coro_switch 140 reaches here via jmp). System V wants %rsp+8 ≡ 16 (mod 16) at 141 function entry; the andq below makes that hold defensively. */ 142 ".globl " SYM(__kit_coro_trampoline) "\n" 143 SYM(__kit_coro_trampoline) ":\n" 144 " andq $-16, %rsp\n" 145 " movq %rax, %rdi\n" /* value → first arg */ 146 " callq *%r13\n" /* entry(value) */ 147 " ud2\n" 148 );