fx.reflection: implement dynamic function calling on linux-x86_64
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
.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
|
||||
|
||||
mov %rdi, %r11
|
||||
mov %rsi, %r12
|
||||
|
||||
# First, set up the fixed arguments
|
||||
# %r13 = fixed arg count
|
||||
movq (%r12), %r13
|
||||
|
||||
# arg[0]
|
||||
cmp $0, %r13
|
||||
jle .idone
|
||||
movq 48(%r12), %rdi
|
||||
|
||||
# arg[1]
|
||||
cmp $1, %r13
|
||||
jle .idone
|
||||
movq 56(%r12), %rsi
|
||||
|
||||
# arg[2]
|
||||
cmp $2, %r13
|
||||
jle .idone
|
||||
movq 64(%r12), %rdx
|
||||
|
||||
# arg[3]
|
||||
cmp $3, %r13
|
||||
jle .idone
|
||||
movq 72(%r12), %rcx
|
||||
|
||||
# arg[4]
|
||||
cmp $4, %r13
|
||||
jle .idone
|
||||
movq 80(%r12), %r8
|
||||
|
||||
# arg[5]
|
||||
cmp $5, %r13
|
||||
jle .idone
|
||||
movq 88(%r12), %r9
|
||||
|
||||
.idone:
|
||||
# Next, set up the fixed double arguments
|
||||
# r13 = fixed double arg count
|
||||
movq 8(%r12), %r13
|
||||
|
||||
# arg[0]
|
||||
cmp $0, %r13
|
||||
jle .vdone
|
||||
movq 96(%r12), %xmm0
|
||||
|
||||
# arg[1]
|
||||
cmp $1, %r13
|
||||
jle .vdone
|
||||
movq 104(%r12), %xmm1
|
||||
|
||||
# arg[2]
|
||||
cmp $2, %r13
|
||||
jle .vdone
|
||||
movq 112(%r12), %xmm2
|
||||
|
||||
# arg[3]
|
||||
cmp $3, %r13
|
||||
jle .vdone
|
||||
movq 120(%r12), %xmm3
|
||||
|
||||
# arg[4]
|
||||
cmp $4, %r13
|
||||
jle .vdone
|
||||
movq 128(%r12), %xmm4
|
||||
|
||||
# arg[5]
|
||||
cmp $5, %r13
|
||||
jle .vdone
|
||||
movq 136(%r12), %xmm5
|
||||
|
||||
# arg[6]
|
||||
cmp $6, %r13
|
||||
jle .vdone
|
||||
movq 144(%r12), %xmm6
|
||||
|
||||
# arg[7]
|
||||
cmp $7, %r13
|
||||
jle .vdone
|
||||
movq 152(%r12), %xmm7
|
||||
|
||||
.vdone:
|
||||
# Finally, set up the variable arguments
|
||||
|
||||
# x8 = excess arg count
|
||||
movq 32(%r12), %r13
|
||||
|
||||
# calculate the amount of stack space needed for the varargs
|
||||
shl $3, %r13
|
||||
|
||||
andq $0xFFFFFFFFFFFFFFF0, %r13
|
||||
addq $0x10, %r13
|
||||
|
||||
# allocate the stack space
|
||||
sub %r13, %rsp
|
||||
mov %rsp, %r14
|
||||
|
||||
# convert buffer size back to number of arguments
|
||||
movq 32(%r12), %r13
|
||||
|
||||
# r13: number of var args (decrements with every iteration)
|
||||
# r14: var arg dest pointer (increments with every iteration)
|
||||
# r15: arg src buffer (increments with every iteration)
|
||||
mov 160(%r12), %r15
|
||||
|
||||
.loop:
|
||||
cmp $0, %r13
|
||||
je .loop_end
|
||||
|
||||
# read the arg value from the src pointer
|
||||
mov (%r15), %rax
|
||||
|
||||
# write it to the stack, and increment the dest pointer
|
||||
mov %rax, (%r14)
|
||||
add $8, %r14
|
||||
|
||||
# increment the src pointer, decrement the arg count
|
||||
sub $1, %r13
|
||||
add $8, %r15
|
||||
|
||||
jmp .loop
|
||||
.loop_end:
|
||||
|
||||
# call the function implementation
|
||||
mov 168(%r12), %rax
|
||||
call *%r11
|
||||
|
||||
# de-allocate the stack varargs buffer (the size is now stored in x19)
|
||||
movq 32(%r12), %r13
|
||||
shl $3, %r13
|
||||
andq $0xFFFFFFFFFFFFFFF0, %r13
|
||||
addq $0x10, %r13
|
||||
addq %r13, %rsp
|
||||
|
||||
pop %r15
|
||||
pop %r14
|
||||
pop %r13
|
||||
pop %r12
|
||||
|
||||
# restore the saved stack frame and link pointer
|
||||
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
|
||||
@@ -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;
|
||||
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)
|
||||
{
|
||||
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,37 @@
|
||||
#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 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_value_type return_type);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user