From 8c0a31e19bccbbcf566234a5050d17cd07f44289 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sat, 30 May 2026 10:09:55 +0100 Subject: [PATCH] meta: update rosetta support --- CMakeLists.txt | 6 +- cmake/Platform.cmake | 4 +- cmake/Templates.cmake | 22 +- fx.reflection/sys/rosetta/amd64/callvm.S | 95 +++++++ fx.reflection/sys/rosetta/amd64/callvm.c | 243 ++++++++++++++++++ .../rosetta/amd64/include/platform/callvm.h | 37 +++ fx/cstr.c | 8 + fx/encoding.c | 2 +- fx/sys/rosetta/bitop.c | 2 +- fx/test/randomise.c | 2 + test/hello.c | 4 +- 11 files changed, 407 insertions(+), 18 deletions(-) create mode 100644 fx.reflection/sys/rosetta/amd64/callvm.S create mode 100644 fx.reflection/sys/rosetta/amd64/callvm.c create mode 100644 fx.reflection/sys/rosetta/amd64/include/platform/callvm.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9302c84..5d3b725 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.25) project(fx C ASM) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include (TestBigEndian) include(Templates) include(Platform) @@ -59,4 +59,6 @@ foreach (assembly ${fx_assemblies}) add_subdirectory(assemblies/${assembly}) endforeach (assembly) -add_subdirectory(test) +if (fx_enable_tests) + add_subdirectory(test) +endif () diff --git a/cmake/Platform.cmake b/cmake/Platform.cmake index 6646191..385e67b 100644 --- a/cmake/Platform.cmake +++ b/cmake/Platform.cmake @@ -19,7 +19,7 @@ function(get_platform_details) elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") set(system_name "win32") else () - message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}") + string(TOLOWER "${CMAKE_SYSTEM_NAME}" system_name) endif () if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") @@ -27,7 +27,7 @@ function(get_platform_details) elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64") set(system_arch "aarch64") else () - message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") + string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" system_arch) endif () set(${arg_SYSTEM} ${system_name} PARENT_SCOPE) diff --git a/cmake/Templates.cmake b/cmake/Templates.cmake index 6bae678..23d537f 100644 --- a/cmake/Templates.cmake +++ b/cmake/Templates.cmake @@ -47,16 +47,18 @@ function(add_fx_assembly) target_compile_definitions(${assembly_target_name} PRIVATE ${def}) endforeach (def) - foreach (ns ${arg_NAMESPACES}) - file(GLOB test_sources ${fx_source_root}/${ns}/test/*.c) - foreach (test_file ${test_sources}) - get_filename_component(test_name ${test_file} NAME_WE) - set(test_name ${ns}-${test_name}) - string(REPLACE "." "-" test_name ${test_name}) - add_executable(${test_name} ${test_file}) - target_link_libraries(${test_name} ${assembly_target_name}) - endforeach (test_file) - endforeach (ns) + if (fx_enable_tests) + foreach (ns ${arg_NAMESPACES}) + file(GLOB test_sources ${fx_source_root}/${ns}/test/*.c) + foreach (test_file ${test_sources}) + get_filename_component(test_name ${test_file} NAME_WE) + set(test_name ${ns}-${test_name}) + string(REPLACE "." "-" test_name ${test_name}) + add_executable(${test_name} ${test_file}) + target_link_libraries(${test_name} ${assembly_target_name}) + endforeach (test_file) + endforeach (ns) + endif () set_target_properties(${assembly_target_name} PROPERTIES FOLDER "${assembly_name}") diff --git a/fx.reflection/sys/rosetta/amd64/callvm.S b/fx.reflection/sys/rosetta/amd64/callvm.S new file mode 100644 index 0000000..95411ab --- /dev/null +++ b/fx.reflection/sys/rosetta/amd64/callvm.S @@ -0,0 +1,95 @@ +.extern memcpy +.type memcpy, @function + +.global callvm_invoke_i +.type callvm_invoke_i, @function + +# %rdi = (function ptr) impl +# %rsi = (struct callvm) context +callvm_invoke_i: + # save the stack frame pointer + push %rbp + mov %rsp, %rbp + + # store function pointer for later + push %r12 + push %r13 + push %r14 + push %r15 + + # move our parameters out of the way + mov %rdi, %r11 + mov %rsi, %r12 + + # calculate the amount of stack space needed for the varargs + movq 32(%r12), %r13 + shl $3, %r13 + + # allocate the stack space + push %rsp + sub %r13, %rsp + andq $0xFFFFFFFFFFFFFFF0, %rsp # re-align the stack + + # copy the excess args to the stack + mov %rsp, %rdi + mov 160(%r12), %rsi + mov %r13, %rdx + call memcpy + + # Next, set up the fixed integer arguments + + movq 48(%r12), %rdi # int arg 0 + movq 56(%r12), %rsi # int arg 1 + movq 64(%r12), %rdx # int arg 2 + movq 72(%r12), %rcx # int arg 3 + movq 80(%r12), %r8 # int arg 4 + movq 88(%r12), %r9 # int arg 5 + + # Finally, set up the fixed double arguments + + movq 96(%r12), %xmm0 # double arg 0 + movq 104(%r12), %xmm1 # double arg 1 + movq 112(%r12), %xmm2 # double arg 2 + movq 120(%r12), %xmm3 # double arg 3 + movq 128(%r12), %xmm4 # double arg 4 + movq 136(%r12), %xmm5 # double arg 5 + movq 144(%r12), %xmm6 # double arg 6 + movq 152(%r12), %xmm7 # double arg 7 + + # set the number of vararg double parameters + # as required by the ABI + mov 168(%r12), %rax + + # call the function implementation + call *%r11 + + # Restore the stack pointer (deallocating the varargs buffer) + mov -40(%rbp), %rsp + + # Restore callee-saved registers + pop %r15 + pop %r14 + pop %r13 + pop %r12 + + # restore the saved stack frame + pop %rbp + ret + + +.global callvm_invoke_d +.type callvm_invoke_d, @function + +# %rdi = (function ptr) impl +# %rsi = (struct callvm *) context +callvm_invoke_d: + jmp callvm_invoke_i + + +.global callvm_invoke_v +.type callvm_invoke_v, @function + +# %rdi = (function ptr) impl +# %rsi = (struct callvm *) context +callvm_invoke_v: + jmp callvm_invoke_i diff --git a/fx.reflection/sys/rosetta/amd64/callvm.c b/fx.reflection/sys/rosetta/amd64/callvm.c new file mode 100644 index 0000000..c107b12 --- /dev/null +++ b/fx.reflection/sys/rosetta/amd64/callvm.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include + +#if 0 + switch (arg->v_type.t_primitive) { + case FX_VALUE_TYPE_DOUBLE: + break; + default: + callvm_push_int(&vm, (uintptr_t)arg->v_pointer); + break; + } +#endif + +void callvm_reset(struct callvm *vm, unsigned int max_fixed_args) +{ + vm->vm_arg_int_count = 0; + vm->vm_arg_double_count = 0; + vm->vm_arg_fixed = max_fixed_args; + vm->vm_arg_excess_count = 0; + vm->vm_double_excess_count = 0; +} + +static void expand_excess(struct callvm *vm) +{ + size_t new_capacity = vm->vm_arg_excess_max * 2; + if (!new_capacity) { + new_capacity = 4; + } + + void *buf = realloc( + vm->vm_arg_excess, + new_capacity * sizeof *vm->vm_arg_excess); + + if (!buf) { + return; + } + + vm->vm_arg_excess = buf; + vm->vm_arg_excess_max = new_capacity; +} + +static void push_excess(struct callvm *vm, uintptr_t value) +{ + if (vm->vm_arg_excess_count + 1 > vm->vm_arg_excess_max) { + expand_excess(vm); + } + + vm->vm_arg_excess[vm->vm_arg_excess_count++] = value; + vm->vm_arg_count++; +} + +static void push_int(struct callvm *vm, uintptr_t value) +{ + if (vm->vm_arg_int_count >= MAX_INT_ARGS) { + push_excess(vm, value); + return; + } + + vm->vm_arg_int[vm->vm_arg_int_count++] = value; + vm->vm_arg_count++; +} + +static void push_double(struct callvm *vm, double value) +{ + if (vm->vm_arg_double_count >= MAX_DOUBLE_ARGS) { + push_excess(vm, *(uintptr_t *)&value); + return; + } + + vm->vm_arg_double[vm->vm_arg_double_count++] = value; + vm->vm_arg_count++; + + if (vm->vm_arg_count > vm->vm_arg_fixed) { + vm->vm_double_excess_count++; + } +} + +void callvm_push(struct callvm *vm, const fx_value *value) +{ + if (!fx_type_is_value_type(value->v_type)) { + push_int(vm, (uintptr_t)value->v_object); + return; + } + + unsigned int value_type = __fx_type_get_value_type(value->v_type); + + switch (value_type) { + case __FX_VALUE_TYPE_BOOL: + push_int(vm, value->v_bool); + break; + case __FX_VALUE_TYPE_I16: + push_int(vm, value->v_i16); + break; + case __FX_VALUE_TYPE_U16: + push_int(vm, value->v_u16); + break; + case __FX_VALUE_TYPE_I32: + push_int(vm, value->v_i32); + break; + case __FX_VALUE_TYPE_U32: + push_int(vm, value->v_u32); + break; + case __FX_VALUE_TYPE_I64: + push_int(vm, value->v_i64); + break; + case __FX_VALUE_TYPE_U64: + push_int(vm, value->v_u64); + break; + case __FX_VALUE_TYPE_IPTR: + push_int(vm, value->v_iptr); + break; + case __FX_VALUE_TYPE_UPTR: + push_int(vm, value->v_uptr); + break; + case __FX_VALUE_TYPE_SBYTE: + push_int(vm, value->v_sbyte); + break; + case __FX_VALUE_TYPE_BYTE: + push_int(vm, value->v_byte); + break; + case __FX_VALUE_TYPE_SHORT: + push_int(vm, value->v_short); + break; + case __FX_VALUE_TYPE_USHORT: + push_int(vm, value->v_ushort); + break; + case __FX_VALUE_TYPE_INT: + push_int(vm, value->v_int); + break; + case __FX_VALUE_TYPE_UINT: + push_int(vm, value->v_uint); + break; + case __FX_VALUE_TYPE_LONG: + push_int(vm, value->v_long); + break; + case __FX_VALUE_TYPE_ULONG: + push_int(vm, value->v_ulong); + break; + case __FX_VALUE_TYPE_LONGLONG: + push_int(vm, value->v_longlong); + break; + case __FX_VALUE_TYPE_ULONGLONG: + push_int(vm, value->v_ulonglong); + break; + case __FX_VALUE_TYPE_SIZE: + push_int(vm, value->v_size); + break; + case __FX_VALUE_TYPE_FLOAT: + push_double(vm, value->v_float); + break; + case __FX_VALUE_TYPE_DOUBLE: + push_double(vm, value->v_double); + break; + case __FX_VALUE_TYPE_CSTR: + push_int(vm, (uintptr_t)value->v_cstr); + break; + case __FX_VALUE_TYPE_POINTER: + push_int(vm, (uintptr_t)value->v_pointer); + break; + default: + break; + } +} + +extern uintptr_t callvm_invoke_i(fx_function_impl impl, struct callvm *vm); +extern double callvm_invoke_d(fx_function_impl impl, struct callvm *vm); +extern void callvm_invoke_v(fx_function_impl impl, struct callvm *vm); + +fx_value callvm_invoke( + struct callvm *vm, + fx_function_impl impl, + fx_type_id return_type) +{ + if (!fx_type_is_value_type(return_type)) { + uintptr_t v = callvm_invoke_i(impl, vm); + fx_value result = { + .v_type = return_type, + .v_object = (fx_object *)v, + }; + return result; + } + + unsigned int value_type = __fx_type_get_value_type(return_type); + + switch (value_type) { + case __FX_VALUE_TYPE_BOOL: + return FX_BOOL(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_I16: + return FX_I16(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_U16: + return FX_U16(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_I32: + return FX_I32(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_U32: + return FX_U32(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_I64: + return FX_I64(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_U64: + return FX_U64(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_IPTR: + return FX_IPTR(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_UPTR: + return FX_UPTR(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_SBYTE: + return FX_SBYTE(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_BYTE: + return FX_BYTE(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_SHORT: + return FX_SHORT(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_USHORT: + return FX_USHORT(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_INT: + return FX_INT(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_UINT: + return FX_UINT(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_LONG: + return FX_LONG(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_ULONG: + return FX_ULONG(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_LONGLONG: + return FX_LONGLONG(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_ULONGLONG: + return FX_ULONGLONG(callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_SIZE: + return FX_SIZE(callvm_invoke_i(impl, vm)); +#if FX_ENABLE_FLOATING_POINT + case __FX_VALUE_TYPE_FLOAT: + return FX_FLOAT(callvm_invoke_d(impl, vm)); + case __FX_VALUE_TYPE_DOUBLE: + return FX_DOUBLE(callvm_invoke_d(impl, vm)); +#endif + case __FX_VALUE_TYPE_CSTR: + return FX_CSTR((const char *)callvm_invoke_i(impl, vm)); + case __FX_VALUE_TYPE_POINTER: + return FX_POINTER((void *)callvm_invoke_i(impl, vm)); + default: + return FX_VALUE_EMPTY; + } + + return FX_VALUE_EMPTY; +} diff --git a/fx.reflection/sys/rosetta/amd64/include/platform/callvm.h b/fx.reflection/sys/rosetta/amd64/include/platform/callvm.h new file mode 100644 index 0000000..a13c49d --- /dev/null +++ b/fx.reflection/sys/rosetta/amd64/include/platform/callvm.h @@ -0,0 +1,37 @@ +#ifndef FX_REFLECTION_DARWIN_ARM64_CALLVM_H_ +#define FX_REFLECTION_DARWIN_ARM64_CALLVM_H_ + +#include +#include +#include + +#define MAX_FIXED_ARGS ((unsigned int)-1) +#define MAX_DOUBLE_ARGS 8 +#define MAX_INT_ARGS 6 + +/* dyn-dispatch.S depends on the layout of this struct */ +struct callvm { + uint64_t vm_arg_int_count; + uint64_t vm_arg_double_count; + + /* any args pushed after this limit is reached will be stored in the + * excess buffer. used for calling varargs functions */ + uint64_t vm_arg_count, vm_arg_fixed; + + uint64_t vm_arg_excess_count; + uint64_t vm_arg_excess_max; + + uintptr_t vm_arg_int[MAX_INT_ARGS]; + double vm_arg_double[MAX_DOUBLE_ARGS]; + uintptr_t *vm_arg_excess; + uint64_t vm_double_excess_count; +}; + +extern void callvm_reset(struct callvm *vm, unsigned int max_fixed_args); +extern void callvm_push(struct callvm *vm, const fx_value *value); +extern fx_value callvm_invoke( + struct callvm *vm, + fx_function_impl impl, + fx_type_id return_type); + +#endif diff --git a/fx/cstr.c b/fx/cstr.c index 97b70ef..3105781 100644 --- a/fx/cstr.c +++ b/fx/cstr.c @@ -369,6 +369,7 @@ fx_status cstr_to_size(const char *in, size_t *out) fx_status cstr_to_float(const char *in, float *out) { +#if FX_ENABLE_FLOATING_POINT char *ep; float v = strtof(in, &ep); if (*ep) { @@ -377,10 +378,14 @@ fx_status cstr_to_float(const char *in, float *out) *out = v; return FX_SUCCESS; +#else + return FX_ERR_NOT_SUPPORTED; +#endif } fx_status cstr_to_double(const char *in, double *out) { +#if FX_ENABLE_FLOATING_POINT char *ep; double v = strtod(in, &ep); if (*ep) { @@ -389,6 +394,9 @@ fx_status cstr_to_double(const char *in, double *out) *out = v; return FX_SUCCESS; +#else + return FX_ERR_NOT_SUPPORTED; +#endif } fx_status cstr_to_cstr(const char *in, const char **out) diff --git a/fx/encoding.c b/fx/encoding.c index 19d9e7d..1ad2683 100644 --- a/fx/encoding.c +++ b/fx/encoding.c @@ -3,7 +3,7 @@ bool fx_wchar_is_number(fx_wchar c) { - return iswdigit((wchar_t)c); + return (c >= '0' && c <= '9'); } bool fx_wchar_is_alpha(fx_wchar c) diff --git a/fx/sys/rosetta/bitop.c b/fx/sys/rosetta/bitop.c index 3653953..98ba2e7 100644 --- a/fx/sys/rosetta/bitop.c +++ b/fx/sys/rosetta/bitop.c @@ -1,4 +1,4 @@ -#include +#include int fx_popcountl(long v) { diff --git a/fx/test/randomise.c b/fx/test/randomise.c index 516ebef..25d8e88 100644 --- a/fx/test/randomise.c +++ b/fx/test/randomise.c @@ -29,11 +29,13 @@ int main(void) printf("%02x", bytes[i % 16]); } +#if FX_ENABLE_FLOATING_POINT printf("\n\ngenerating %d random doubles:\n", NRAND_DOUBLES); for (int i = 0; i < NRAND_DOUBLES; i++) { double v = fx_random_next_double(&random); printf(" %lf\n", v); } +#endif return 0; } diff --git a/test/hello.c b/test/hello.c index 5b88f72..879069d 100644 --- a/test/hello.c +++ b/test/hello.c @@ -1,10 +1,10 @@ #include #include -static double another_function(int a, double b, int c) +static int another_function(int a, double b, int c) { printf("a=%d, b=%lf, c=%d\n", a, b, c); - return 1.2; + return 2; } static int test_function(int a, int b, int c, ...)