Skip to content

Commit 0a98ace

Browse files
authored
Merge pull request #476 from PedroBatista/arm64
Native support for ARM64/Apple Silicon
2 parents 7113659 + 1c8c662 commit 0a98ace

File tree

10 files changed

+229
-13
lines changed

10 files changed

+229
-13
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,5 @@ install_manifest.txt
9595
Makefile
9696
IoInstallPrefix.h
9797
xcuserdata
98-
build
98+
build
99+
.idea

CMakeLists.txt

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
# Require CMake 2.8. I know for sure that this will not work with CMake 2.6
2222
# due to the use of the FILE command we use when creating the bundle
2323
# hierarchy.
24-
cmake_minimum_required(VERSION 2.8.12)
24+
#cmake_minimum_required(VERSION 2.8.12)
25+
cmake_minimum_required(VERSION 3.5)
2526

2627
# Mac OS X: Setting policy CMP0042 to the new behavior generates dylibs with
2728
# RPATH-relative install name that is better suited for Mac OS X applications
@@ -34,15 +35,23 @@ endif()
3435
# spaces, or anything silly like that please.
3536
project(IoLanguage C)
3637

38+
# Enable ASM language for ARM64 coroutine support
39+
enable_language(ASM)
40+
3741
# Default config when building with gcc variants
3842
IF(CMAKE_COMPILER_IS_GNUCC OR (CMAKE_C_COMPILER_ID MATCHES "Clang"))
39-
SET(CMAKE_BUILD_TYPE_DebugFast)
40-
SET(CMAKE_CXX_FLAGS_DEBUGFAST "-g -O0")
41-
SET(CMAKE_C_FLAGS_DEBUGFAST "-g -O0")
42-
SET(CMAKE_C_FLAGS "-msse2")
43-
if(NOT CMAKE_BUILD_TYPE)
44-
SET(CMAKE_BUILD_TYPE "DebugFast")
45-
endif(NOT CMAKE_BUILD_TYPE)
43+
SET(CMAKE_BUILD_TYPE_DebugFast)
44+
SET(CMAKE_CXX_FLAGS_DEBUGFAST "-g -O0")
45+
SET(CMAKE_C_FLAGS_DEBUGFAST "-g -O0")
46+
47+
# Only use -msse2 flag for x86/x86_64 architectures.
48+
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
49+
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2")
50+
endif()
51+
52+
if(NOT CMAKE_BUILD_TYPE)
53+
SET(CMAKE_BUILD_TYPE "DebugFast")
54+
endif(NOT CMAKE_BUILD_TYPE)
4655
ENDIF(CMAKE_COMPILER_IS_GNUCC OR (CMAKE_C_COMPILER_ID MATCHES "Clang"))
4756

4857
MESSAGE(STATUS "Configuration set to: ${CMAKE_BUILD_TYPE}")

libs/CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ set(IO_SRCS
4646

4747
# Hackery for CMake's horrible ASM support
4848
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
49-
set(ASM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/coroutine/source/asm.S)
50-
set_source_files_properties(${ASM_SOURCES} PROPERTIES LANGUAGE C)
51-
endif(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
49+
# Always include asm.S for all architectures
50+
set(ASM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/coroutine/source/asm.S)
51+
set_source_files_properties(${ASM_SOURCES} PROPERTIES LANGUAGE C)
52+
endif()
5253

5354
# Object files from every lib. Used to create iovmall static library.
5455
file(GLOB COROUTINE_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/coroutine/source/*.c")

libs/coroutine/source/Coro.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ typedef struct CallbackBlock {
6565
#endif
6666
} CallbackBlock;
6767

68+
#if !defined(__arm64__) && !defined(__aarch64__)
6869
Coro *Coro_new(void) {
6970
Coro *self = (Coro *)io_calloc(1, sizeof(Coro));
7071
self->requestedStackSize = CORO_DEFAULT_STACK_SIZE;
@@ -566,6 +567,7 @@ end : {
566567

567568
#endif
568569

570+
#endif
569571
// old code
570572

571573
/*

libs/coroutine/source/Coro.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
#define USE_UCONTEXT 1
1010
#endif
1111

12+
#if defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__))
13+
#include "arm64-ucontext.h"
14+
#define USE_UCONTEXT 1
15+
#endif
16+
1217
#if defined(__FreeBSD__)
1318
#define HAS_UCONTEXT 1
1419
#endif

libs/coroutine/source/Coro_arm64.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#if defined(__arm64__) || defined(__aarch64__)
2+
#include "Coro.h"
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
// External functions implemented in asm.S
8+
extern int coro_arm64_getcontext(void *context) __asm("coro_arm64_getcontext");
9+
extern int coro_arm64_setcontext(void *context) __asm("coro_arm64_setcontext");
10+
11+
// Custom context implementation for ARM64
12+
typedef struct {
13+
unsigned long x19_x20[2]; // x19, x20
14+
unsigned long x21_x22[2]; // x21, x22
15+
unsigned long x23_x24[2]; // x23, x24
16+
unsigned long x25_x26[2]; // x25, x26
17+
unsigned long x27_x28[2]; // x27, x28
18+
unsigned long fp_lr[2]; // x29 (fp), x30 (lr)
19+
unsigned long sp; // stack pointer
20+
} arm64_context_t;
21+
22+
typedef struct CallbackBlock {
23+
void *context;
24+
CoroStartCallback *func;
25+
} CallbackBlock;
26+
27+
static CallbackBlock globalCallbackBlock;
28+
29+
static void Coro_StartWithArg(void) {
30+
//fprintf(stderr, "Coro_StartWithArg called\n");
31+
CallbackBlock *block = &globalCallbackBlock;
32+
//fprintf(stderr, "Function pointer: %p\n", block->func);
33+
block->func(block->context);
34+
fprintf(stderr, "Scheduler error: returned from coro start function\n");
35+
exit(-1);
36+
}
37+
38+
void Coro_free(Coro *self) {
39+
if (self->stack) {
40+
free(self->stack);
41+
}
42+
free(self);
43+
}
44+
45+
void Coro_initializeMainCoro(Coro *self) {
46+
self->isMain = 1;
47+
}
48+
49+
Coro *Coro_new(void) {
50+
Coro *c = (Coro *)calloc(1, sizeof(Coro));
51+
if (c) {
52+
c->requestedStackSize = CORO_DEFAULT_STACK_SIZE;
53+
c->allocatedStackSize = 0;
54+
c->stack = NULL;
55+
}
56+
return c;
57+
}
58+
59+
void Coro_setStackSize_(Coro *self, size_t size) {
60+
self->requestedStackSize = size;
61+
}
62+
63+
static void Coro_allocStackIfNeeded(Coro *self) {
64+
if (self->stack && self->requestedStackSize < self->allocatedStackSize) {
65+
free(self->stack);
66+
self->stack = NULL;
67+
self->allocatedStackSize = 0;
68+
}
69+
if (!self->stack) {
70+
// Make sure stack is 16-byte aligned for ARM64
71+
self->stack = calloc(1, self->requestedStackSize + 16);
72+
self->allocatedStackSize = self->requestedStackSize;
73+
}
74+
}
75+
76+
// This function initializes the context with a new stack and entry point
77+
void Coro_setup(Coro *self, void *arg) {
78+
arm64_context_t *context = (arm64_context_t *)&self->env;
79+
memset(context, 0, sizeof(*context));
80+
81+
Coro_allocStackIfNeeded(self);
82+
83+
// Initialize stack pointer to top of stack (ARM64 full descending stack)
84+
unsigned long sp = (unsigned long)self->stack + self->allocatedStackSize - 16;
85+
// Ensure 16-byte alignment
86+
sp &= ~15UL;
87+
88+
// Store stack pointer in context
89+
context->sp = sp;
90+
91+
// Store entry point in link register (x30)
92+
context->fp_lr[1] = (unsigned long)Coro_StartWithArg;
93+
}
94+
95+
int Coro_stackSpaceAlmostGone(Coro *self) {
96+
arm64_context_t *context = (arm64_context_t *)&self->env;
97+
unsigned long sp = context->sp;
98+
unsigned long stack_base = (unsigned long)self->stack;
99+
100+
// Check if we have less than 1KB of stack space left
101+
return (sp - stack_base) < 1024;
102+
}
103+
104+
size_t Coro_bytesLeftOnStack(Coro *self) {
105+
arm64_context_t *context = (arm64_context_t *)&self->env;
106+
unsigned long sp = context->sp;
107+
unsigned long stack_base = (unsigned long)self->stack;
108+
109+
// Return number of bytes between stack pointer and stack base
110+
if (sp > stack_base) {
111+
return sp - stack_base;
112+
}
113+
114+
// Fallback if stack info not available
115+
return 1024 * 1024;
116+
}
117+
118+
void Coro_startCoro_(Coro *self, Coro *other, void *context, CoroStartCallback *callback) {
119+
globalCallbackBlock.context = context;
120+
globalCallbackBlock.func = callback;
121+
Coro_setup(other, &globalCallbackBlock);
122+
Coro_switchTo_(self, other);
123+
}
124+
125+
void Coro_switchTo_(Coro *self, Coro *next) {
126+
// Get the context pointers
127+
arm64_context_t *from_context = (arm64_context_t *)&self->env;
128+
arm64_context_t *to_context = (arm64_context_t *)&next->env;
129+
130+
// Save current context, if successful (returns 0), then restore the next context
131+
if (coro_arm64_getcontext((void*)from_context) == 0) {
132+
coro_arm64_setcontext((void*)to_context);
133+
}
134+
135+
}
136+
#endif
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// arm64-ucontext.h: Apple Silicon (ARM64) macOS context switching using ucontext
2+
#ifndef ARM64_UCONTEXT_H
3+
#define ARM64_UCONTEXT_H
4+
5+
// Required for ucontext on modern macOS
6+
#ifndef _XOPEN_SOURCE
7+
#define _XOPEN_SOURCE 700
8+
#endif
9+
10+
#include <ucontext.h>
11+
12+
typedef ucontext_t arm64_ucontext_t;
13+
14+
#endif // ARM64_UCONTEXT_H

libs/coroutine/source/asm.S

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#define NEEDAMD64CONTEXT 1
2222
#define SET _setmcontext
2323
#define GET _getmcontext
24+
#elif defined(__arm64__) || defined(__aarch64__) || defined(__ARM64_ARCH_8__)
25+
#define NEEDARM64CONTEXT 1
26+
#define SET coro_arm64_setcontext
27+
#define GET coro_arm64_getcontext
2428
#else
2529
#define NEEDPOWERCONTEXT 1
2630
#define SET __setmcontext
@@ -262,3 +266,32 @@ SET:
262266
ldr r0, [r0]
263267
movpc, lr
264268
#endif
269+
270+
#ifdef NEEDARM64CONTEXT
271+
.globl coro_arm64_getcontext
272+
coro_arm64_getcontext:
273+
// x0 contains the context pointer
274+
stp x19, x20, [x0, #0]
275+
stp x21, x22, [x0, #16]
276+
stp x23, x24, [x0, #32]
277+
stp x25, x26, [x0, #48]
278+
stp x27, x28, [x0, #64]
279+
stp x29, x30, [x0, #80]
280+
mov x2, sp
281+
str x2, [x0, #96]
282+
mov x0, #0
283+
ret
284+
285+
.globl coro_arm64_setcontext
286+
coro_arm64_setcontext:
287+
ldp x19, x20, [x0, #0]
288+
ldp x21, x22, [x0, #16]
289+
ldp x23, x24, [x0, #32]
290+
ldp x25, x26, [x0, #48]
291+
ldp x27, x28, [x0, #64]
292+
ldp x29, x30, [x0, #80]
293+
ldr x2, [x0, #96]
294+
mov sp, x2
295+
mov x0, #1
296+
ret
297+
#endif

libs/coroutine/source/context.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */
22

3+
#include <stdio.h>
34
#include "taskimpl.h"
45

56
#if defined(__APPLE__)
@@ -9,6 +10,9 @@
910
#elif defined(__x86_64__)
1011
#define NEEDAMD64MAKECONTEXT
1112
#define NEEDSWAPCONTEXT
13+
#elif defined(__arm64__) || defined(__aarch64__)
14+
#define NEEDARM64MAKECONTEXT
15+
#define NEEDSWAPCONTEXT
1216
#else
1317
#define NEEDPOWERMAKECONTEXT
1418
#define NEEDSWAPCONTEXT
@@ -116,6 +120,7 @@ void makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) {
116120
}
117121
#endif
118122

123+
119124
#ifdef NEEDSWAPCONTEXT
120125
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp) {
121126
if (getcontext(oucp) == 0)

libs/coroutine/source/taskimpl.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,25 @@ extern void makecontext(ucontext_t *, void (*)(), int, ...);
110110
#endif
111111

112112
#if defined(__APPLE__)
113+
#if defined(__i386__)
113114
#define mcontext libthread_mcontext
114115
#define mcontext_t libthread_mcontext_t
115116
#define ucontext libthread_ucontext
116117
#define ucontext_t libthread_ucontext_t
117-
#if defined(__i386__)
118118
#include "386-ucontext.h"
119119
#elif defined(__x86_64__)
120+
#define mcontext libthread_mcontext
121+
#define mcontext_t libthread_mcontext_t
122+
#define ucontext libthread_ucontext
123+
#define ucontext_t libthread_ucontext_t
120124
#include "amd64-ucontext.h"
125+
#elif defined(__arm64__) || defined(__aarch64__)
126+
#include "arm64-ucontext.h"
121127
#else
128+
#define mcontext libthread_mcontext
129+
#define mcontext_t libthread_mcontext_t
130+
#define ucontext libthread_ucontext
131+
#define ucontext_t libthread_ucontext_t
122132
#include "power-ucontext.h"
123133
#endif
124134
#endif

0 commit comments

Comments
 (0)