From bf7d8cfb75bd90948ca4bddd9b83877d177b947c Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 4 May 2026 16:33:16 +0100 Subject: [PATCH] fx.reflection: implement dynamic function calling --- fx.reflection/function-context.c | 86 +++++++ fx.reflection/function.c | 140 +++++++++++ .../include/fx/reflection/function.h | 83 +++++++ fx.reflection/sys/darwin/arm64/callvm.c | 110 +++++++++ fx.reflection/sys/darwin/arm64/dyn-dispatch.S | 178 ++++++++++++++ .../sys/darwin/arm64/dyn-dispatch.old | 221 ++++++++++++++++++ .../darwin/arm64/include/platform/callvm.h | 36 +++ 7 files changed, 854 insertions(+) create mode 100644 fx.reflection/function-context.c create mode 100644 fx.reflection/function.c create mode 100644 fx.reflection/include/fx/reflection/function.h create mode 100644 fx.reflection/sys/darwin/arm64/callvm.c create mode 100644 fx.reflection/sys/darwin/arm64/dyn-dispatch.S create mode 100644 fx.reflection/sys/darwin/arm64/dyn-dispatch.old create mode 100644 fx.reflection/sys/darwin/arm64/include/platform/callvm.h diff --git a/fx.reflection/function-context.c b/fx.reflection/function-context.c new file mode 100644 index 0000000..33a9a5c --- /dev/null +++ b/fx.reflection/function-context.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include + +struct fx_function_context_p { + struct callvm ctx_vm; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ + +static fx_status function_context_reset( + const struct fx_function_context_p *func) +{ + return FX_SUCCESS; +} + +static fx_status function_context_push_arg( + const struct fx_function_context_p *func, + const fx_value *arg) +{ + return FX_SUCCESS; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +FX_API fx_function *fx_function_context_create(void) +{ + fx_function_context *ctx + = fx_object_create(FX_REFLECTION_TYPE_FUNCTION_CONTEXT); + if (!ctx) { + return NULL; + } + + struct fx_function_context_p *p = fx_object_get_private( + ctx, + FX_REFLECTION_TYPE_FUNCTION_CONTEXT); + + return ctx; +} + +FX_API void fx_function_context_reset(fx_function_context *ctx) +{ + FX_CLASS_DISPATCH_STATIC_V0( + FX_REFLECTION_TYPE_FUNCTION_CONTEXT, + function_context_reset, + ctx); +} + +FX_API void fx_function_context_push_arg( + fx_function_context *ctx, + const fx_value *value) +{ + FX_CLASS_DISPATCH_STATIC_V( + FX_REFLECTION_TYPE_FUNCTION_CONTEXT, + function_context_push_arg, + ctx, + value); +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void function_context_init(fx_object *obj, void *priv) +{ +} + +static void function_context_fini(fx_object *obj, void *priv) +{ +} + +/*** CLASS DEFINITION *********************************************************/ + +FX_TYPE_CLASS_DEFINITION_BEGIN(fx_function_context) + FX_TYPE_CLASS_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_CLASS_INTERFACE_END(fx_object, FX_TYPE_OBJECT) +FX_TYPE_CLASS_DEFINITION_END(fx_function_context) + +FX_TYPE_DEFINITION_BEGIN(fx_function_context) + FX_TYPE_ID(0x97d98a1a, 0x6312, 0x409e, 0xb47d, 0xc5d80daf9e50); + FX_TYPE_CLASS(fx_function_context_class); + FX_TYPE_INSTANCE_PRIVATE(struct fx_function_context_p); + FX_TYPE_INSTANCE_INIT(function_context_init); + FX_TYPE_INSTANCE_FINI(function_context_fini); +FX_TYPE_DEFINITION_END(fx_function_context) diff --git a/fx.reflection/function.c b/fx.reflection/function.c new file mode 100644 index 0000000..73d7626 --- /dev/null +++ b/fx.reflection/function.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include + +struct fx_function_p { + char *func_name; + fx_function_flags func_flags; + fx_function_impl func_impl; + fx_value_type func_return_type; + fx_value_type *func_arg_types; + /* number of explicit arguments that the function takes. + * if the FX_FUNCTION_F_VARARG flag is set, the function supports a + * variable number of arguments, which may be handled differently by the + * ABI */ + size_t func_nr_args; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ + +extern intptr_t fx_function_invoke_i( + fx_function_impl impl, + const fx_value *args, + size_t nr_args); + +static fx_status function_invoke( + const struct fx_function_p *func, + const fx_value *args, + size_t nr_args, + fx_value *return_value) +{ + if (nr_args < func->func_nr_args) { + return FX_ERR_INVALID_ARGUMENT; + } + + if (nr_args > func->func_nr_args + && !(func->func_flags & FX_FUNCTION_F_VARARG)) { + return FX_ERR_INVALID_ARGUMENT; + } + + size_t nr_fixed_args = func->func_nr_args; + size_t nr_var_args = nr_args - nr_fixed_args; + + struct callvm vm = {0}; + callvm_reset(&vm, nr_fixed_args); + + for (size_t i = 0; i < nr_args; i++) { + const fx_value *arg = &args[i]; + callvm_push(&vm, arg); + } + + *return_value + = callvm_invoke(&vm, func->func_impl, func->func_return_type); + + callvm_reset(&vm, 0); + return FX_SUCCESS; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +FX_API fx_function *fx_function_create( + const char *name, + fx_function_flags flags, + fx_function_impl impl, + const fx_value_type *args, + size_t nr_args, + fx_value_type return_type) +{ + fx_function *func = fx_object_create(FX_REFLECTION_TYPE_FUNCTION); + if (!func) { + return NULL; + } + + struct fx_function_p *p + = fx_object_get_private(func, FX_REFLECTION_TYPE_FUNCTION); + + p->func_name = fx_strdup(name); + p->func_flags = flags; + p->func_impl = impl; + p->func_nr_args = nr_args; + p->func_return_type = return_type; + p->func_arg_types = calloc(nr_args, sizeof *args); + for (size_t i = 0; i < nr_args; i++) { + p->func_arg_types[i] = args[i]; + } + + return func; +} + +FX_API fx_status fx_function_invoke( + const fx_function *func, + const fx_value *args, + size_t nr_args, + fx_value *return_value) +{ + FX_CLASS_DISPATCH_STATIC( + FX_REFLECTION_TYPE_FUNCTION, + function_invoke, + func, + args, + nr_args, + return_value); +} + +/*** VIRTUAL FUNCTIONS + * ********************************************************/ + +static void function_init(fx_object *obj, void *priv) +{ +} + +static void function_fini(fx_object *obj, void *priv) +{ + struct fx_function_p *p = priv; + if (p->func_name) { + free(p->func_name); + } + + if (p->func_arg_types) { + free(p->func_arg_types); + } +} + +/*** CLASS DEFINITION + * *********************************************************/ + +FX_TYPE_CLASS_DEFINITION_BEGIN(fx_function) + FX_TYPE_CLASS_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_CLASS_INTERFACE_END(fx_object, FX_TYPE_OBJECT) +FX_TYPE_CLASS_DEFINITION_END(fx_function) + +FX_TYPE_DEFINITION_BEGIN(fx_function) + FX_TYPE_ID(0x09e40174, 0x7443, 0x486e, 0xad21, 0xcc9374762e7e); + FX_TYPE_CLASS(fx_function_class); + FX_TYPE_INSTANCE_PRIVATE(struct fx_function_p); + FX_TYPE_INSTANCE_INIT(function_init); + FX_TYPE_INSTANCE_FINI(function_fini); +FX_TYPE_DEFINITION_END(fx_function) diff --git a/fx.reflection/include/fx/reflection/function.h b/fx.reflection/include/fx/reflection/function.h new file mode 100644 index 0000000..03d78ec --- /dev/null +++ b/fx.reflection/include/fx/reflection/function.h @@ -0,0 +1,83 @@ +#ifndef FX_REFLECTION_FUNCTION_H_ +#define FX_REFLECTION_FUNCTION_H_ + +#include +#include +#include + +FX_DECLS_BEGIN; + +#define FX_REFLECTION_TYPE_FUNCTION (fx_function_get_type()) +#define FX_REFLECTION_TYPE_FUNCTION_CONTEXT (fx_function_context_get_type()) + +typedef enum fx_function_flags { + FX_FUNCTION_F_NONE = 0x00u, + FX_FUNCTION_F_STATIC = 0x01u, + FX_FUNCTION_F_VIRTUAL = 0x02u, + FX_FUNCTION_F_VARARG = 0x04u, +} fx_function_flags; + +#if 0 +#define FX_FUNCTION_VALUE(type) \ + ((fx_function_value) { \ + .v_type = FX_FUNCTION_V_PRIMITIVE, \ + .v_primitive = (type), \ + }) + +typedef enum fx_function_value_type { + FX_FUNCTION_V_NONE = 0, + FX_FUNCTION_V_PRIMITIVE, + FX_FUNCTION_V_OBJECT, +} fx_function_value_type; + +typedef struct fx_function_value { + fx_function_value_type v_type; + union { + fx_value_type v_primitive; + fx_type v_object; + }; +} fx_function_value; +#endif + +typedef void (*fx_function_impl)(); + +FX_DECLARE_TYPE(fx_function); +FX_DECLARE_TYPE(fx_function_context); + +FX_TYPE_CLASS_DECLARATION_BEGIN(fx_function) +FX_TYPE_CLASS_DECLARATION_END(fx_function) + +FX_TYPE_CLASS_DECLARATION_BEGIN(fx_function_context) +FX_TYPE_CLASS_DECLARATION_END(fx_function_context) + +FX_API fx_type fx_function_get_type(); +FX_API fx_type fx_function_context_get_type(); + +FX_API fx_function_context *fx_function_context_create(void); +FX_API void fx_function_context_reset(fx_function_context *ctx); +FX_API void fx_function_context_push_arg( + fx_function_context *ctx, + const fx_value *value); + +FX_API fx_function *fx_function_create( + const char *name, + fx_function_flags flags, + fx_function_impl impl, + const fx_value_type *args, + size_t nr_args, + fx_value_type return_type); +FX_API fx_status +fx_function_bind(const fx_function *func, const fx_value *args, size_t nr_args); +FX_API fx_status fx_function_invoke( + const fx_function *func, + const fx_value *args, + size_t nr_args, + fx_value *return_value); +FX_API fx_status fx_function_invoke_with_context( + const fx_function *func, + const fx_function_context *ctx, + fx_value *return_value); + +FX_DECLS_END; + +#endif diff --git a/fx.reflection/sys/darwin/arm64/callvm.c b/fx.reflection/sys/darwin/arm64/callvm.c new file mode 100644 index 0000000..2246e93 --- /dev/null +++ b/fx.reflection/sys/darwin/arm64/callvm.c @@ -0,0 +1,110 @@ +#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; +} + +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++; +} + +void callvm_push(struct callvm *vm, const fx_value *value) +{ + if (vm->vm_arg_count >= vm->vm_arg_fixed) { + push_excess(vm, (uintptr_t)value->v_pointer); + return; + } + + switch (value->v_type.t_primitive) { + case FX_VALUE_TYPE_DOUBLE: + push_double(vm, value->v_double); + break; + default: + push_int(vm, (uintptr_t)value->v_pointer); + 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_value_type return_type) +{ + switch (return_type) { + case FX_VALUE_TYPE_NONE: + callvm_invoke_v(impl, vm); + break; + case FX_VALUE_TYPE_DOUBLE: + return FX_VALUE_DOUBLE(callvm_invoke_d(impl, vm)); + default: + return FX_VALUE_INT(callvm_invoke_i(impl, vm)); + } + + return FX_VALUE_EMPTY; +} diff --git a/fx.reflection/sys/darwin/arm64/dyn-dispatch.S b/fx.reflection/sys/darwin/arm64/dyn-dispatch.S new file mode 100644 index 0000000..8af5497 --- /dev/null +++ b/fx.reflection/sys/darwin/arm64/dyn-dispatch.S @@ -0,0 +1,178 @@ +.global _callvm_invoke_i +.align 4 + +# x0 = (function ptr) impl +# x1 = (struct callvm) context +_callvm_invoke_i: + # save the stack frame and link pointer + stp x29, x30, [sp, #-16]! + mov x29, sp + + # store function pointer for later + mov x16, x0 + mov x15, x1 + + # First, set up the fixed arguments + # x8 = fixed arg count + ldr x8, [x15, #0] + + # arg[0] + cmp x8, #0 + ble .iv.idone + ldr x0, [x15, #48] + + # arg[1] + cmp x8, #1 + ble .iv.idone + ldr x1, [x15, #56] + + # arg[2] + cmp x8, #2 + ble .iv.idone + ldr x2, [x15, #64] + + # arg[3] + cmp x8, #3 + ble .iv.idone + ldr x3, [x15, #72] + + # arg[4] + cmp x8, #4 + ble .iv.idone + ldr x4, [x15, #80] + + # arg[5] + cmp x8, #5 + ble .iv.idone + ldr x5, [x15, #88] + + # arg[6] + cmp x8, #6 + ble .iv.idone + ldr x6, [x15, #96] + + # arg[7] + cmp x8, #7 + ble .iv.idone + ldr x7, [x15, #104] + +.iv.idone: + # Next, set up the fixed double arguments + # x8 = fixed double arg count + ldr x8, [x15, #8] + + # arg[0] + cmp x8, #0 + ble .iv.vdone + ldr d0, [x15, #112] + + # arg[1] + cmp x8, #1 + ble .iv.vdone + ldr d1, [x15, #120] + + # arg[2] + cmp x8, #2 + ble .iv.vdone + ldr d2, [x15, #128] + + # arg[3] + cmp x8, #3 + ble .iv.vdone + ldr d3, [x15, #136] + + # arg[4] + cmp x8, #4 + ble .iv.vdone + ldr d4, [x15, #144] + + # arg[5] + cmp x8, #5 + ble .iv.vdone + ldr d5, [x15, #152] + + # arg[6] + cmp x8, #6 + ble .iv.vdone + ldr d6, [x15, #160] + + # arg[7] + cmp x8, #7 + ble .iv.vdone + ldr d7, [x15, #168] + +.iv.vdone: + # Finally, set up the variable arguments + + # x8 = excess arg count + ldr x8, [x15, #32] + + # calculate the amount of stack space needed for the varargs + mov x10, #0x10 + mul x12, x8, x10 + + # x19 is caller-saved, and we'll need x12 later + str x19, [sp, #-16]! + mov x19, x12 + + # allocate the stack space + sub sp, sp, x19 + mov x13, sp + + # get a pointer to the end of the stack vararg buffer + #mov x14, #8 + #mul x14, x14, x11 + #add x13, x13, x14 + + # x8: number of var args (decrements with every iteration) + # x9: arg src buffer (increments with every iteration) + # x13: var arg dest pointer (starts at the end, decrements with every iteration) + ldr x9, [x15, #176] + +.iv.loop: + cmp x8, #0 + beq .iv.loop.end + + # read the arg value from the src pointer + ldr x14, [x9] + + # write it to the stack, and increment the dest pointer + str x14, [x13] + add x13, x13, #8 + + # increment the src pointer, decrement the arg count + sub x8, x8, #1 + add x9, x9, #8 + b .iv.loop +.iv.loop.end: + + # call the function implementation + blr x16 + + # de-allocate the stack varargs buffer (the size is now stored in x19) + add sp, sp, x19 + + # restore the saved value of x19, + ldr x19, [sp], #16 + + # restore the saved stack frame and link pointer + ldp x29,x30, [sp], #16 + ret + + +.global _callvm_invoke_d +.align 4 + +# x0 = (function ptr) impl +# x1 = (struct callvm) context +_callvm_invoke_d: + b _callvm_invoke_i + + +.global _callvm_invoke_v +.align 4 + +# x0 = (function ptr) impl +# x1 = (struct callvm) context +_callvm_invoke_v: + b _callvm_invoke_i diff --git a/fx.reflection/sys/darwin/arm64/dyn-dispatch.old b/fx.reflection/sys/darwin/arm64/dyn-dispatch.old new file mode 100644 index 0000000..18d40d8 --- /dev/null +++ b/fx.reflection/sys/darwin/arm64/dyn-dispatch.old @@ -0,0 +1,221 @@ +.global _fx_function_invoke_i +.align 4 + +# x0 = (function ptr) impl +# x1 = (fx_value[]) args +# x2 = (size_t) nr args +_fx_function_invoke_i: + # save the stack frame and link pointer + stp x29, x30, [sp, #-16]! + + # store function pointer for later + str x0, [sp, #-16]! + # move arg array of of x1 and x2, so that they can be initialised for + # the function call + mov x8, x1 + mov x9, x2 + + # arg[0] + cmp x9, #0 + ble .i.done + ldr x0, [x8, #8] + + # arg[1] + cmp x9, #1 + ble .i.done + ldr x1, [x8, #24] + + # arg[2] + cmp x9, #2 + ble .i.done + ldr x2, [x8, #40] + + # arg[3] + cmp x9, #3 + ble .i.done + ldr x3, [x8, #56] + + # arg[4] + cmp x9, #4 + ble .i.done + ldr x4, [x8, #72] + + # arg[5] + cmp x9, #5 + ble .i.done + ldr x5, [x8, #88] + + # arg[6] + cmp x9, #6 + ble .i.done + ldr x6, [x8, #104] + + # arg[7] + cmp x9, #7 + ble .i.done + ldr x7, [x8, #120] + +.i.done: + ldr x8, [sp], #16 + blr x8 + + # restore the saved stack frame and link pointer + ldp x29,x30, [sp], #16 + ret + + +.global _fx_function_invoke_d +.align 4 + +_fx_function_invoke_d: + mov x0, 888 + ret + + +.global _fx_function_invoke_v +.align 4 + +_fx_function_invoke_v: + ret + + + +.global _fx_function_invoke_iv +.align 4 + +# x0 = (function ptr) impl +# x1 = (fx_value[]) args +# x2 = (size_t) nr fixed args +# x3 = (size_t) nr var args +_fx_function_invoke_iv: + # save the stack frame and link pointer + stp x29, x30, [sp, #-16]! + mov x29, sp + + # store function pointer for later + mov x15, x0 + # move arg array of of x1,x2,x3, so that they can be initialised for + # the function call + mov x9, x1 + mov x10, x2 + mov x11, x3 + + # First, set up the fixed arguments + + # arg[0] + cmp x10, #0 + ble .iv.done + ldr x0, [x9, #8] + add x9, x9, #16 + + # arg[1] + cmp x10, #1 + ble .iv.done + ldr x1, [x9, #8] + add x9, x9, #16 + + # arg[2] + cmp x10, #2 + ble .iv.done + ldr x2, [x9, #8] + add x9, x9, #16 + + # arg[3] + cmp x10, #3 + ble .iv.done + ldr x3, [x9, #8] + add x9, x9, #16 + + # arg[4] + cmp x10, #4 + ble .iv.done + ldr x4, [x9, #8] + add x9, x9, #16 + + # arg[5] + cmp x10, #5 + ble .iv.done + ldr x5, [x9, #8] + add x9, x9, #16 + + # arg[6] + cmp x10, #6 + ble .iv.done + ldr x6, [x9, #8] + add x9, x9, #16 + + # arg[7] + cmp x10, #7 + ble .iv.done + ldr x7, [x9, #8] + add x9, x9, #16 + +.iv.done: + # Next, set up the variable arguments + + # calculate the amount of stack space needed for the varargs + mov x10, #0x10 + mul x12, x11, x10 + #and x12, x12, #0x10 + #mov x10, #0x10 + #add x12, x12, x10 + + # x19 is caller-saved, and we'll need x12 later + str x19, [sp, #-16]! + mov x19, x12 + + # allocate the stack space + sub sp, sp, x19 + mov x13, sp + + # get a pointer to the end of the stack vararg buffer + #mov x14, #8 + #mul x14, x14, x11 + #add x13, x13, x14 + + # x9: arg src buffer (starts at varargs, increments with every iteration) + # x11: number of var args (decrements with every iteration) + # x13: var arg dest pointer (starts at the end, decrements with every iteration) +.iv.loop: + cmp x11, #0 + beq .iv.loop.end + + # read the arg value from the src pointer + ldr x14, [x9, #8] + + # write it to the stack, and increment the dest pointer + str x14, [x13] + add x13, x13, #8 + + # increment the src pointer, decrement the arg count + sub x11, x11, #1 + add x9, x9, #16 + b .iv.loop +.iv.loop.end: + + # call the function implementation + blr x15 + + # de-allocate the stack varargs buffer (the size is now stored in x19) + add sp, sp, x19 + + # restore the saved value of x19, + ldr x19, [sp], #16 + + # restore the saved stack frame and link pointer + ldp x29,x30, [sp], #16 + ret + + +.global _fx_function_invoke_dv +.align 4 + +_fx_function_invoke_dv: + ret + + +.global _fx_function_invoke_vv +.align 4 + +_fx_function_invoke_vv: + ret diff --git a/fx.reflection/sys/darwin/arm64/include/platform/callvm.h b/fx.reflection/sys/darwin/arm64/include/platform/callvm.h new file mode 100644 index 0000000..a75c1aa --- /dev/null +++ b/fx.reflection/sys/darwin/arm64/include/platform/callvm.h @@ -0,0 +1,36 @@ +#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 8 + +/* 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; +}; + +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_value_type return_type); + +#endif