fx.reflection: implement dynamic function calling
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
#include <fx/macros.h>
|
||||
#include <fx/reflection/function.h>
|
||||
#include <fx/string.h>
|
||||
#include <fx/value.h>
|
||||
#include <platform/callvm.h>
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <fx/macros.h>
|
||||
#include <fx/reflection/function.h>
|
||||
#include <fx/string.h>
|
||||
#include <fx/value.h>
|
||||
#include <platform/callvm.h>
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef FX_REFLECTION_FUNCTION_H_
|
||||
#define FX_REFLECTION_FUNCTION_H_
|
||||
|
||||
#include <fx/macros.h>
|
||||
#include <fx/status.h>
|
||||
#include <fx/value.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <platform/callvm.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef FX_REFLECTION_DARWIN_ARM64_CALLVM_H_
|
||||
#define FX_REFLECTION_DARWIN_ARM64_CALLVM_H_
|
||||
|
||||
#include <fx/reflection/function.h>
|
||||
#include <fx/value.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user