44#include <stdbool.h>
55#include <string.h>
66
7+ #if _WIN32
8+ #define WIN32_LEAN_AND_MEAN
9+ #include <windows.h>
10+ #else
711#include <poll.h>
812#include <unistd.h>
913#include <sys/mman.h>
14+ #endif
1015
1116#include "coroutine.h"
1217
18+
19+ #if _WIN32
20+ int getpagesize () {
21+ SYSTEM_INFO si ;
22+ GetSystemInfo (& si );
23+ return si .dwPageSize ;
24+ }
25+ #endif
26+
1327// TODO: make the STACK_CAPACITY customizable by the user
1428//#define STACK_CAPACITY (4*1024)
1529#define STACK_CAPACITY (1024*getpagesize())
@@ -60,7 +74,11 @@ typedef struct {
6074} Indices ;
6175
6276typedef struct {
77+ #if _WIN32
78+ char * items ; // not supported yet
79+ #else
6380 struct pollfd * items ;
81+ #endif
6482 size_t count ;
6583 size_t capacity ;
6684} Polls ;
@@ -82,12 +100,33 @@ typedef enum {
82100 SM_WRITE ,
83101} Sleep_Mode ;
84102
85- // Linux x86_64 call convention
103+ // Linux x86_64 calling convention
86104// %rdi, %rsi, %rdx, %rcx, %r8, and %r9
87105
106+ // https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions?view=msvc-170#x64-register-usage
107+ // Windows x64 calling convention: RCX, RDX, R8, R9
108+ // Windows x64 ABI considers registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-XMM15 nonvolatile.
109+ // They must be saved and restored by a function that uses them.
110+
88111void __attribute__((naked )) coroutine_yield (void )
89112{
90113 // @arch
114+ #if _WIN32
115+ asm(
116+ " pushq %rcx\n"
117+ " pushq %rbx\n"
118+ " pushq %rbp\n"
119+ " pushq %rdi\n"
120+ " pushq %rsi\n"
121+ " pushq %r12\n"
122+ " pushq %r13\n"
123+ " pushq %r14\n"
124+ " pushq %r15\n"
125+ // TODO: push XMM6-XMM15
126+ " movq %rsp, %rcx\n" // rsp
127+ " movq $0, %rdx\n" // sm = SM_READ
128+ " jmp coroutine_switch_context\n" );
129+ #else
91130 asm(
92131 " pushq %rdi\n"
93132 " pushq %rbp\n"
@@ -99,12 +138,32 @@ void __attribute__((naked)) coroutine_yield(void)
99138 " movq %rsp, %rdi\n" // rsp
100139 " movq $0, %rsi\n" // sm = SM_NONE
101140 " jmp coroutine_switch_context\n" );
141+ #endif
102142}
103143
104144void __attribute__((naked )) coroutine_sleep_read (int fd )
105145{
146+ #if !defined(__clang__ )
106147 (void ) fd ;
148+ #endif
107149 // @arch
150+ #if _WIN32
151+ asm(
152+ " pushq %rcx\n"
153+ " pushq %rbx\n"
154+ " pushq %rbp\n"
155+ " pushq %rdi\n"
156+ " pushq %rsi\n"
157+ " pushq %r12\n"
158+ " pushq %r13\n"
159+ " pushq %r14\n"
160+ " pushq %r15\n"
161+ // TODO: push XMM6-XMM15
162+ " movq %rcx, %r8\n" // fd
163+ " movq %rsp, %rcx\n" // rsp
164+ " movq $1, %rdx\n" // sm = SM_READ
165+ " jmp coroutine_switch_context\n" );
166+ #else
108167 asm(
109168 " pushq %rdi\n"
110169 " pushq %rbp\n"
@@ -117,12 +176,32 @@ void __attribute__((naked)) coroutine_sleep_read(int fd)
117176 " movq %rsp, %rdi\n" // rsp
118177 " movq $1, %rsi\n" // sm = SM_READ
119178 " jmp coroutine_switch_context\n" );
179+ #endif
120180}
121181
122182void __attribute__((naked )) coroutine_sleep_write (int fd )
123183{
184+ #if !defined(__clang__ )
124185 (void ) fd ;
186+ #endif
125187 // @arch
188+ #if _WIN32
189+ asm(
190+ " pushq %rcx\n"
191+ " pushq %rbx\n"
192+ " pushq %rbp\n"
193+ " pushq %rdi\n"
194+ " pushq %rsi\n"
195+ " pushq %r12\n"
196+ " pushq %r13\n"
197+ " pushq %r14\n"
198+ " pushq %r15\n"
199+ // TODO: push XMM6-XMM15
200+ " movq %rcx, %r8\n" // fd
201+ " movq %rsp, %rcx\n" // rsp
202+ " movq $2, %rdx\n" // sm = SM_READ
203+ " jmp coroutine_switch_context\n" );
204+ #else
126205 asm(
127206 " pushq %rdi\n"
128207 " pushq %rbp\n"
@@ -135,12 +214,30 @@ void __attribute__((naked)) coroutine_sleep_write(int fd)
135214 " movq %rsp, %rdi\n" // rsp
136215 " movq $2, %rsi\n" // sm = SM_WRITE
137216 " jmp coroutine_switch_context\n" );
217+ #endif
138218}
139219
140220void __attribute__((naked )) coroutine_restore_context (void * rsp )
141221{
142222 // @arch
223+ #if !defined(__clang__ )
143224 (void )rsp ;
225+ #endif
226+ #if _WIN32
227+ asm(
228+ " movq %rcx, %rsp\n"
229+ // TODO: pop XMM15-XMM6
230+ " popq %r15\n"
231+ " popq %r14\n"
232+ " popq %r13\n"
233+ " popq %r12\n"
234+ " popq %rsi\n"
235+ " popq %rdi\n"
236+ " popq %rbp\n"
237+ " popq %rbx\n"
238+ " popq %rcx\n"
239+ " ret\n" );
240+ #else
144241 asm(
145242 " movq %rdi, %rsp\n"
146243 " popq %r15\n"
@@ -151,6 +248,7 @@ void __attribute__((naked)) coroutine_restore_context(void *rsp)
151248 " popq %rbp\n"
152249 " popq %rdi\n"
153250 " ret\n" );
251+ #endif
154252}
155253
156254void coroutine_switch_context (void * rsp , Sleep_Mode sm , int fd )
@@ -160,23 +258,36 @@ void coroutine_switch_context(void *rsp, Sleep_Mode sm, int fd)
160258 switch (sm ) {
161259 case SM_NONE : current += 1 ; break ;
162260 case SM_READ : {
261+ #if _WIN32
262+ (void )fd ;
263+ TODO ("polling is not implemented for windows" );
264+ #else
163265 da_append (& asleep , active .items [current ]);
164266 struct pollfd pfd = {.fd = fd , .events = POLLRDNORM ,};
165267 da_append (& polls , pfd );
166268 da_remove_unordered (& active , current );
269+ #endif
167270 } break ;
168271
169272 case SM_WRITE : {
273+ #if _WIN32
274+ (void )fd ;
275+ TODO ("polling is not implemented for windows" );
276+ #else
170277 da_append (& asleep , active .items [current ]);
171278 struct pollfd pfd = {.fd = fd , .events = POLLWRNORM ,};
172279 da_append (& polls , pfd );
173280 da_remove_unordered (& active , current );
281+ #endif
174282 } break ;
175283
176284 default : UNREACHABLE ("coroutine_switch_context" );
177285 }
178286
179287 if (polls .count > 0 ) {
288+ #if _WIN32
289+ TODO ("polling is not implemented for windows" );
290+ #else
180291 int timeout = active .count == 0 ? -1 : 0 ;
181292 int result = poll (polls .items , polls .count , timeout );
182293 if (result < 0 ) TODO ("poll" );
@@ -191,6 +302,7 @@ void coroutine_switch_context(void *rsp, Sleep_Mode sm, int fd)
191302 ++ i ;
192303 }
193304 }
305+ #endif
194306 }
195307
196308 assert (active .count > 0 );
@@ -216,6 +328,9 @@ void coroutine__finish_current(void)
216328 da_remove_unordered (& active , current );
217329
218330 if (polls .count > 0 ) {
331+ #if _WIN32
332+ TODO ("polling is not implemented for windows" );
333+ #else
219334 int timeout = active .count == 0 ? -1 : 0 ;
220335 int result = poll (polls .items , polls .count , timeout );
221336 if (result < 0 ) TODO ("poll" );
@@ -230,6 +345,7 @@ void coroutine__finish_current(void)
230345 ++ i ;
231346 }
232347 }
348+ #endif
233349 }
234350
235351 assert (active .count > 0 );
@@ -245,21 +361,40 @@ void coroutine_go(void (*f)(void*), void *arg)
245361 } else {
246362 da_append (& contexts , ((Context ){0 }));
247363 id = contexts .count - 1 ;
364+ #if _WIN32
365+ void * base = contexts .items [id ].stack_base = VirtualAlloc (NULL , STACK_CAPACITY , MEM_COMMIT | MEM_RESERVE , PAGE_READWRITE );
366+ assert (base != NULL );
367+ // TODO: add VirtualProtect with PAGE_NOACCESS for overflow and underflow
368+ #else
248369 contexts .items [id ].stack_base = mmap (NULL , STACK_CAPACITY , PROT_WRITE |PROT_READ , MAP_PRIVATE |MAP_STACK |MAP_ANONYMOUS |MAP_GROWSDOWN , -1 , 0 );
249370 assert (contexts .items [id ].stack_base != MAP_FAILED );
371+ #endif
250372 }
251373
252374 void * * rsp = (void * * )((char * )contexts .items [id ].stack_base + STACK_CAPACITY );
253375 // @arch
254376 * (-- rsp ) = coroutine__finish_current ;
255377 * (-- rsp ) = f ;
378+ #if _WIN32
379+ * (-- rsp ) = arg ; // push rcx
380+ * (-- rsp ) = 0 ; // push rbx
381+ * (-- rsp ) = 0 ; // push rbp
382+ * (-- rsp ) = 0 ; // push rdi
383+ * (-- rsp ) = 0 ; // push rsi
384+ * (-- rsp ) = 0 ; // push r12
385+ * (-- rsp ) = 0 ; // push r13
386+ * (-- rsp ) = 0 ; // push r14
387+ * (-- rsp ) = 0 ; // push r15
388+ // TODO: push XMM6-XMM15
389+ #else
256390 * (-- rsp ) = arg ; // push rdi
257391 * (-- rsp ) = 0 ; // push rbx
258392 * (-- rsp ) = 0 ; // push rbp
259393 * (-- rsp ) = 0 ; // push r12
260394 * (-- rsp ) = 0 ; // push r13
261395 * (-- rsp ) = 0 ; // push r14
262396 * (-- rsp ) = 0 ; // push r15
397+ #endif
263398 contexts .items [id ].rsp = rsp ;
264399
265400 da_append (& active , id );
0 commit comments