17#ifndef XTC_CORO_PREEMPT_H
18#define XTC_CORO_PREEMPT_H
31#if defined(__x86_64__) && defined(__linux__) && !defined(__APPLE__) && \
32 !defined(XTC_AMALGAMATION) && \
33 (!defined(XTC_CORO_FCTX) || defined(__GLIBC__))
34# define XTC_CORO_REDIRECT_X86_64 1
44#if defined(__aarch64__) && defined(__linux__) && !defined(__APPLE__) && \
45 !defined(XTC_AMALGAMATION)
46# define XTC_CORO_REDIRECT_AARCH64 1
48#if defined(XTC_CORO_REDIRECT_X86_64) || defined(XTC_CORO_REDIRECT_AARCH64)
52extern int __xtc_preempt_pc_ok(uintptr_t pc);
76static XTC_THREAD_LOCAL
volatile int g_in_preempt_tls;
85__attribute__((noinline))
87__in_preempt_slot(
void)
89 volatile int *
volatile slot = &g_in_preempt_tls;
92#define g_in_preempt (*__in_preempt_slot())
94#if defined(XTC_CORO_REDIRECT_X86_64)
118extern uint64_t __xtc_preempt_xsave_mask __attribute__((visibility(
"hidden")));
119extern uint64_t __xtc_preempt_xsave_size __attribute__((visibility(
"hidden")));
120static _Atomic
int g_xsave_probed;
133extern XTC_THREAD_LOCAL uintptr_t __xtc_preempt_pc
134 __attribute__((visibility(
"hidden")));
136#define XTC_XSAVE_WANT 0x2E7ull
139__preempt_xsave_probe(
void)
141 unsigned a, b, c, d, i, lo, hi;
142 uint64_t mask, size = 576;
144 if (atomic_load_explicit(&g_xsave_probed, memory_order_acquire))
147 if (__get_cpuid(1, &a, &b, &c, &d) && (c & bit_OSXSAVE) != 0 &&
148 __get_cpuid_max(0, NULL) >= 0xD) {
149 __asm__
volatile(
"xgetbv" :
"=a"(lo),
"=d"(hi) :
"c"(0));
150 mask = (((uint64_t)hi << 32) | lo) & XTC_XSAVE_WANT;
151 for (i = 2; i < 10; i++) {
152 if ((mask & (1ull << i)) == 0)
154 __cpuid_count(0xD, i, a, b, c, d);
155 if ((uint64_t)a + b > size)
156 size = (uint64_t)a + b;
158 size = (size + 63) & ~63ull;
160 __xtc_preempt_xsave_size = size;
161 atomic_signal_fence(memory_order_release);
162 __xtc_preempt_xsave_mask = mask;
163 atomic_store_explicit(&g_xsave_probed, 1, memory_order_release);
167#if defined(XTC_CORO_REDIRECT_X86_64)
174__coro_preempt_redirect_x86_64(
void *uctx)
198 extern void __xtc_preempt_trampoline(
void);
199 extern void __xtc_preempt_trampoline_end(
void);
200 ucontext_t *uc = (ucontext_t *)uctx;
201 greg_t orig_pc, orig_sp, new_sp;
203 if (__xtc_current_coro == NULL)
205 if (g_in_preempt > 0)
207 __preempt_xsave_probe();
209 orig_pc = uc->uc_mcontext.gregs[REG_RIP];
210 orig_sp = uc->uc_mcontext.gregs[REG_RSP];
220 if (orig_pc >= (greg_t)(uintptr_t)&__xtc_preempt_trampoline &&
221 orig_pc < (greg_t)(uintptr_t)&__xtc_preempt_trampoline_end)
224 if (!__xtc_preempt_pc_ok((uintptr_t)orig_pc))
235 new_sp = orig_sp - 136;
236 __xtc_preempt_pc = (uintptr_t)orig_pc;
239 uc->uc_mcontext.gregs[REG_RSP] = new_sp;
240 uc->uc_mcontext.gregs[REG_RIP] =
241 (greg_t)(uintptr_t)&__xtc_preempt_trampoline;
246#if defined(XTC_CORO_REDIRECT_AARCH64)
265_Static_assert(
sizeof(siginfo_t) == 128,
"rt_sigframe: siginfo size");
266_Static_assert(offsetof(ucontext_t, uc_stack) == 16,
"uc_stack");
267_Static_assert(offsetof(ucontext_t, uc_sigmask) == 40,
"uc_sigmask");
268_Static_assert(offsetof(ucontext_t, uc_mcontext.sp) == 432,
"mc sp");
269_Static_assert(offsetof(ucontext_t, uc_mcontext.pc) == 440,
"mc pc");
270_Static_assert(offsetof(ucontext_t, uc_mcontext.pstate) == 448,
"mc pstate");
272#define CORO_A64_PSR_BTYPE 0xC00ull
275__coro_preempt_redirect_aarch64(
void *uctx)
277 extern void __xtc_preempt_trampoline(
void);
278 extern void __xtc_preempt_trampoline_end(
void);
279 ucontext_t *uc = (ucontext_t *)uctx;
280 struct xtc_coro *c = __xtc_current_coro;
281 unsigned long long pc, frame, lo, hi, *stash;
285 if (g_in_preempt > 0)
287 pc = uc->uc_mcontext.pc;
288 if (pc >= (
unsigned long long)(uintptr_t)&__xtc_preempt_trampoline &&
289 pc < (
unsigned long long)(uintptr_t)&__xtc_preempt_trampoline_end)
291 if (!__xtc_preempt_pc_ok((uintptr_t)pc))
297 frame = (
unsigned long long)(uintptr_t)uctx -
sizeof(siginfo_t);
298 lo = (
unsigned long long)(uintptr_t)c->stack;
299 hi = lo + c->stack_sz + 65536;
300 if ((frame & 15) != 0 || frame >= uc->uc_mcontext.sp || frame <= lo ||
303 stash = (
unsigned long long *)(uintptr_t)frame;
305 stash[1] = uc->uc_mcontext.sp;
306 stash[2] = uc->uc_mcontext.pstate;
309 uc->uc_mcontext.pstate &= ~CORO_A64_PSR_BTYPE;
310 uc->uc_mcontext.sp = frame;
312 (
unsigned long long)(uintptr_t)&__xtc_preempt_trampoline;