1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/**
* Cem Runtime - Portable Context Switching
*
* This module provides fast, portable context switching for the Cem runtime.
* It replaces the deprecated ucontext API with custom assembly implementations.
*
* Supported Platforms:
* - macOS ARM64 (Apple Silicon) - IMPLEMENTED ✅
* - Linux x86-64 (Intel/AMD) - IMPLEMENTED ✅
* - macOS x86-64 (Intel) - TODO (trivial port from Linux x86-64)
* - Linux ARM64 - TODO (trivial port from macOS ARM64)
*
* Design Philosophy:
* - Single unified API across all platforms
* - Platform-specific implementations via conditional compilation
* - Minimal overhead (~10-20ns context switch vs ~500ns for ucontext)
* - Callee-saved registers only (caller-saved are preserved by C ABI)
* - Thread-safe design ready for future work-stealing scheduler
*/
// Platform Detection
// Detect architecture
// Detect OS
// Check implementation status
// Context Structure
/**
* Platform-specific CPU context
*
* This stores all callee-saved registers needed for context switching.
* The structure layout is architecture-specific and matches what the
* assembly code expects.
*/
/**
* ARM64 Context Layout (AArch64 calling convention)
*
* Callee-saved registers that must be preserved:
* - x19-x28: General purpose registers (10 registers)
* - x29: Frame pointer (FP)
* - x30: Link register (LR)
* - sp: Stack pointer
* - d8-d15: Floating point registers (8 registers)
*
* Total: 13 integer registers + 8 FP registers = 21 registers
* Size: 13*8 + 8*8 = 104 + 64 = 168 bytes
*/
typedef struct cem_context_t;
/**
* x86-64 Context Layout (System V AMD64 ABI)
*
* Callee-saved registers that must be preserved:
* - rbx, rbp, r12, r13, r14, r15: General purpose (6 registers)
* - rsp: Stack pointer
*
* Note: x86-64 doesn't require saving XMM registers as callee-saved
* in the base ABI, but we may need to save MXCSR for FP state.
*
* Total: 7 registers
* Size: 7*8 = 56 bytes
*/
typedef struct cem_context_t;
// Context Helper Macros
/**
* Get stack pointer from context (architecture-independent)
*
* ARM64 uses 'sp', x86-64 uses 'rsp'. This macro provides a uniform interface.
*/
// Configuration Constants
/**
* Minimum stack size for safe execution (Phase 3: Dynamic Growth)
*
* 4KB initial allocation per strand provides enough space for most
* operations while keeping memory overhead low. Stacks grow dynamically
* by doubling when needed.
*/
/**
* Minimum free stack space to maintain (Phase 3)
*
* If free space falls below this threshold at a context switch checkpoint,
* the stack will be grown proactively. This prevents sudden allocations
* (large local arrays, deep recursion) from overflowing.
*
* 2KB provides headroom for typical function calls with local variables.
* This must be LESS than CEM_INITIAL_STACK_SIZE to avoid immediate growth.
*
* Note: The 75% usage threshold (CEM_STACK_GROWTH_THRESHOLD_PERCENT) provides
* additional protection, so this threshold is primarily for catching sudden
* large allocations (e.g., VLAs, large structs on stack).
*/
/**
* Stack usage threshold for proactive growth (Phase 3)
*
* If stack usage exceeds this percentage of total size, growth is triggered
* at the next checkpoint even if free space is above CEM_MIN_FREE_STACK.
*
* 75% provides a good balance between memory efficiency and preventing
* overflow.
*/
/**
* Maximum stack size (safety limit)
*
* Stacks will not grow beyond this size. If a strand needs more,
* it will trigger a runtime error. This prevents runaway stack growth
* from consuming all system memory.
*
* 1MB is generous for most strand operations while protecting against
* pathological cases (infinite recursion, etc.)
*/
/**
* Legacy compatibility constant
*
* CEM_MIN_STACK_SIZE is now an alias for CEM_INITIAL_STACK_SIZE.
* Kept for backward compatibility with Phase 2b code.
*/
// Context Switching API
/**
* Save current context and switch to target context
*
* This is the core context switching primitive. It:
* 1. Saves all callee-saved registers to `save_ctx`
* 2. Restores all callee-saved registers from `restore_ctx`
* 3. Continues execution from where `restore_ctx` was saved
*
* Assembly implementation is in context_<arch>.s
*
* @param save_ctx - Where to save current context
* @param restore_ctx - Context to restore and switch to
*/
void ;
/**
* Initialize a context for a new strand
*
* INTERNAL API: This function should ONLY be called from strand_spawn().
* Direct calls from user code are not supported and may cause undefined
* behavior.
*
* This sets up a context to start executing `func` with the given
* stack. When `func` returns, control passes to `return_func`.
*
* C implementation is in context.c
*
* @param ctx - Context to initialize (must be non-NULL)
* @param stack_base - Starting address of the C stack allocation (low address).
* NOTE: Despite the name "base", this is the LOW address
* of the stack memory. On ARM64/x86-64, stacks grow
* downward, so the stack pointer will be set to (stack_base + stack_size) which
* is the high address. Must be non-NULL.
* @param stack_size - Size of the C stack in bytes (minimum
* CEM_MIN_STACK_SIZE). Must be positive.
* @param func - Function to execute (receives no args, returns void). Must be
* non-NULL.
* @param return_func - Function to call when func returns. Currently unused
* because strand_spawn() uses strand_entry_trampoline which handles cleanup.
* This parameter exists for potential future use.
*/
void ;
// Platform-Specific Notes
/*
* ARM64 macOS Notes:
* - Stack must be 16-byte aligned at function entry
* - Stack grows downward (high address to low address)
* - x29 (FP) and x30 (LR) are used for stack frames and return addresses
* - We store LR so we can return to the correct location on context switch
*
* x86-64 Notes (for future implementation):
* - Stack must be 16-byte aligned before CALL instruction
* - Red zone: 128 bytes below rsp can be used without adjustment
* - Return address is on stack (not in register like ARM64)
*/
// CEM_RUNTIME_CONTEXT_H