build: add module to handle querying platform details
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
.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
|
||||
|
||||
# preserve callee-saved registers
|
||||
stp x19, x20, [sp, #-16]!
|
||||
|
||||
# store function pointer and callvm context for later
|
||||
mov x19, x0
|
||||
mov x20, x1
|
||||
|
||||
# First, set up the variable arguments
|
||||
|
||||
### calculate the amount of stack space needed for the varargs
|
||||
# store the original stack pointer, so it can be restored later
|
||||
mov x0, sp
|
||||
str x0, [sp, #-16]!
|
||||
# x8 = excess arg count
|
||||
ldr x8, [x20, #32]
|
||||
# x8 = excess arg buffer size (8 bytes per value)
|
||||
lsl x8, x8, #3
|
||||
# allocate the stack space
|
||||
sub sp, sp, x8
|
||||
|
||||
# re-align the stack, and take a pointer to the newly-allocated buffer
|
||||
mov x0, sp
|
||||
and x0, x0, #0xFFFFFFFFFFFFFFF0
|
||||
mov sp, x0
|
||||
|
||||
ldr x1, [x20, #176]
|
||||
mov x2, x8
|
||||
|
||||
# memcpy(stack vararg buffer, callvm excess buffer, arg size)
|
||||
bl _memcpy
|
||||
|
||||
# Next, set up the fixed integer arguments
|
||||
ldr x0, [x20, #48]
|
||||
ldr x1, [x20, #56]
|
||||
ldr x2, [x20, #64]
|
||||
ldr x3, [x20, #72]
|
||||
ldr x4, [x20, #80]
|
||||
ldr x5, [x20, #88]
|
||||
ldr x6, [x20, #96]
|
||||
ldr x7, [x20, #104]
|
||||
|
||||
# Finally, set up the fixed double arguments
|
||||
ldr d0, [x20, #112]
|
||||
ldr d1, [x20, #120]
|
||||
ldr d2, [x20, #128]
|
||||
ldr d3, [x20, #136]
|
||||
ldr d4, [x20, #144]
|
||||
ldr d5, [x20, #152]
|
||||
ldr d6, [x20, #160]
|
||||
ldr d7, [x20, #168]
|
||||
|
||||
# call the function implementation
|
||||
blr x19
|
||||
|
||||
# restore the saved stack pointer, and de-allocate the stack varargs buffer
|
||||
ldr x9, [x29, #-32]
|
||||
mov sp, x9
|
||||
|
||||
# restore callee-saved registers
|
||||
ldp x19, x20, [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,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,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