diff --git a/fx.reflection/sys/linux/x86_64/callvm.S b/fx.reflection/sys/linux/x86_64/callvm.S new file mode 100644 index 0000000..a19fd88 --- /dev/null +++ b/fx.reflection/sys/linux/x86_64/callvm.S @@ -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 diff --git a/fx.reflection/sys/linux/x86_64/callvm.c b/fx.reflection/sys/linux/x86_64/callvm.c new file mode 100644 index 0000000..749167d --- /dev/null +++ b/fx.reflection/sys/linux/x86_64/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; + 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; +} diff --git a/fx.reflection/sys/linux/x86_64/include/platform/callvm.h b/fx.reflection/sys/linux/x86_64/include/platform/callvm.h new file mode 100644 index 0000000..acc3ec6 --- /dev/null +++ b/fx.reflection/sys/linux/x86_64/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_value_type return_type); + +#endif