| /* Copyright (c) 2020 Kalray | |
| Permission is hereby granted, free of charge, to any person obtaining | |
| a copy of this software and associated documentation files (the | |
| ``Software''), to deal in the Software without restriction, including | |
| without limitation the rights to use, copy, modify, merge, publish, | |
| distribute, sublicense, and/or sell copies of the Software, and to | |
| permit persons to whom the Software is furnished to do so, subject to | |
| the following conditions: | |
| The above copyright notice and this permission notice shall be | |
| included in all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, | |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
| CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
| TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
| SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ | |
| #if defined(__kvx__) | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <fficonfig.h> | |
| #include <ffi.h> | |
| #include "ffi_common.h" | |
| #include "asm.h" | |
| #define ALIGN(x, a) ALIGN_MASK(x, (typeof(x))(a) - 1) | |
| #define ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) | |
| #define KVX_ABI_STACK_ALIGNMENT (32) | |
| #define KVX_ABI_STACK_ARG_ALIGNMENT (8) | |
| #define max(a,b) ((a) > (b) ? (a) : (b)) | |
| #ifdef FFI_DEBUG | |
| #define DEBUG_PRINT(...) do{ fprintf( stderr, __VA_ARGS__ ); } while(0) | |
| #else | |
| #define DEBUG_PRINT(...) | |
| #endif | |
| struct ret_value { | |
| unsigned long int r0; | |
| unsigned long int r1; | |
| unsigned long int r2; | |
| unsigned long int r3; | |
| }; | |
| extern struct ret_value ffi_call_SYSV(unsigned total_size, | |
| unsigned size, | |
| extended_cif *ecif, | |
| unsigned *rvalue_addr, | |
| void *fn, | |
| unsigned int_ext_method); | |
| /* Perform machine dependent cif processing */ | |
| ffi_status ffi_prep_cif_machdep(ffi_cif *cif) | |
| { | |
| cif->flags = cif->rtype->size; | |
| return FFI_OK; | |
| } | |
| /* ffi_prep_args is called by the assembly routine once stack space | |
| has been allocated for the function's arguments */ | |
| void *ffi_prep_args(char *stack, unsigned int arg_slots_size, extended_cif *ecif) | |
| { | |
| char *stacktemp = stack; | |
| char *current_arg_passed_by_value = stack + arg_slots_size; | |
| int i, s; | |
| ffi_type **arg; | |
| int count = 0; | |
| ffi_cif *cif = ecif->cif; | |
| void **argv = ecif->avalue; | |
| arg = cif->arg_types; | |
| DEBUG_PRINT("stack: %p\n", stack); | |
| DEBUG_PRINT("arg_slots_size: %u\n", arg_slots_size); | |
| DEBUG_PRINT("current_arg_passed_by_value: %p\n", current_arg_passed_by_value); | |
| DEBUG_PRINT("ecif: %p\n", ecif); | |
| DEBUG_PRINT("ecif->avalue: %p\n", ecif->avalue); | |
| for (i = 0; i < cif->nargs; i++) { | |
| s = KVX_ABI_SLOT_SIZE; | |
| switch((*arg)->type) { | |
| case FFI_TYPE_SINT8: | |
| case FFI_TYPE_UINT8: | |
| case FFI_TYPE_SINT16: | |
| case FFI_TYPE_UINT16: | |
| case FFI_TYPE_SINT32: | |
| case FFI_TYPE_UINT32: | |
| case FFI_TYPE_FLOAT: | |
| case FFI_TYPE_DOUBLE: | |
| case FFI_TYPE_UINT64: | |
| case FFI_TYPE_SINT64: | |
| case FFI_TYPE_POINTER: | |
| DEBUG_PRINT("INT64/32/16/8/FLOAT/DOUBLE or POINTER @%p\n", stack); | |
| *(uint64_t *) stack = *(uint64_t *)(* argv); | |
| break; | |
| case FFI_TYPE_COMPLEX: | |
| if ((*arg)->size == 8) | |
| *(_Complex float *) stack = *(_Complex float *)(* argv); | |
| else if ((*arg)->size == 16) { | |
| *(_Complex double *) stack = *(_Complex double *)(* argv); | |
| s = 16; | |
| } else | |
| abort(); | |
| break; | |
| case FFI_TYPE_STRUCT: { | |
| char *value; | |
| unsigned int written_size = 0; | |
| DEBUG_PRINT("struct by value @%p\n", stack); | |
| if ((*arg)->size > KVX_ABI_MAX_AGGREGATE_IN_REG_SIZE) { | |
| DEBUG_PRINT("big struct\n"); | |
| *(uint64_t *) stack = (uintptr_t)current_arg_passed_by_value; | |
| value = current_arg_passed_by_value; | |
| current_arg_passed_by_value += (*arg)->size; | |
| written_size = KVX_ABI_SLOT_SIZE; | |
| } else { | |
| value = stack; | |
| written_size = (*arg)->size; | |
| } | |
| memcpy(value, *argv, (*arg)->size); | |
| s = ALIGN(written_size, KVX_ABI_STACK_ARG_ALIGNMENT); | |
| break; | |
| } | |
| default: | |
| printf("Error: unsupported arg type %d\n", (*arg)->type); | |
| abort(); | |
| break; | |
| } | |
| stack += s; | |
| count += s; | |
| argv++; | |
| arg++; | |
| } | |
| #ifdef FFI_DEBUG | |
| FFI_ASSERT(((intptr_t)(stacktemp + REG_ARGS_SIZE) & (KVX_ABI_STACK_ALIGNMENT-1)) == 0); | |
| #endif | |
| return stacktemp + REG_ARGS_SIZE; | |
| } | |
| /* Perform machine dependent cif processing when we have a variadic function */ | |
| ffi_status ffi_prep_cif_machdep_var(ffi_cif *cif, unsigned int nfixedargs, | |
| unsigned int ntotalargs) | |
| { | |
| cif->flags = cif->rtype->size; | |
| return FFI_OK; | |
| } | |
| static unsigned long handle_small_int_ext(kvx_intext_method *int_ext_method, | |
| const ffi_type *rtype) | |
| { | |
| switch (rtype->type) { | |
| case FFI_TYPE_SINT8: | |
| *int_ext_method = KVX_RET_SXBD; | |
| return KVX_REGISTER_SIZE; | |
| case FFI_TYPE_SINT16: | |
| *int_ext_method = KVX_RET_SXHD; | |
| return KVX_REGISTER_SIZE; | |
| case FFI_TYPE_SINT32: | |
| *int_ext_method = KVX_RET_SXWD; | |
| return KVX_REGISTER_SIZE; | |
| case FFI_TYPE_UINT8: | |
| *int_ext_method = KVX_RET_ZXBD; | |
| return KVX_REGISTER_SIZE; | |
| case FFI_TYPE_UINT16: | |
| *int_ext_method = KVX_RET_ZXHD; | |
| return KVX_REGISTER_SIZE; | |
| case FFI_TYPE_UINT32: | |
| *int_ext_method = KVX_RET_ZXWD; | |
| return KVX_REGISTER_SIZE; | |
| default: | |
| *int_ext_method = KVX_RET_NONE; | |
| return rtype->size; | |
| } | |
| } | |
| void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) | |
| { | |
| int i; | |
| unsigned long int slot_fitting_args_size = 0; | |
| unsigned long int total_size = 0; | |
| unsigned long int big_struct_size = 0; | |
| kvx_intext_method int_extension_method; | |
| ffi_type **arg; | |
| struct ret_value local_rvalue = {0}; | |
| size_t wb_size; | |
| /* Calculate size to allocate on stack */ | |
| for (i = 0, arg = cif->arg_types; i < cif->nargs; i++, arg++) { | |
| DEBUG_PRINT("argument %d, type %d, size %lu\n", i, (*arg)->type, (*arg)->size); | |
| if (((*arg)->type == FFI_TYPE_STRUCT) || ((*arg)->type == FFI_TYPE_COMPLEX)) { | |
| if ((*arg)->size <= KVX_ABI_MAX_AGGREGATE_IN_REG_SIZE) { | |
| slot_fitting_args_size += ALIGN((*arg)->size, KVX_ABI_SLOT_SIZE); | |
| } else { | |
| slot_fitting_args_size += KVX_ABI_SLOT_SIZE; /* aggregate passed by reference */ | |
| big_struct_size += ALIGN((*arg)->size, KVX_ABI_SLOT_SIZE); | |
| } | |
| } else if ((*arg)->size <= KVX_ABI_SLOT_SIZE) { | |
| slot_fitting_args_size += KVX_ABI_SLOT_SIZE; | |
| } else { | |
| printf("Error: unsupported arg size %ld arg type %d\n", (*arg)->size, (*arg)->type); | |
| abort(); /* should never happen? */ | |
| } | |
| } | |
| extended_cif ecif; | |
| ecif.cif = cif; | |
| ecif.avalue = avalue; | |
| ecif.rvalue = rvalue; | |
| /* This implementation allocates anyway for all register based args */ | |
| slot_fitting_args_size = max(slot_fitting_args_size, REG_ARGS_SIZE); | |
| total_size = slot_fitting_args_size + big_struct_size; | |
| total_size = ALIGN(total_size, KVX_ABI_STACK_ALIGNMENT); | |
| /* wb_size: write back size, the size we will need to write back to user | |
| * provided buffer. In theory it should always be cif->flags which is | |
| * cif->rtype->size. But libffi API mandates that for integral types | |
| * of size <= system register size, then we *MUST* write back | |
| * the size of system register size. | |
| * in our case, if size <= 8 bytes we must write back 8 bytes. | |
| * floats, complex and structs are not affected, only integrals. | |
| */ | |
| wb_size = handle_small_int_ext(&int_extension_method, cif->rtype); | |
| switch (cif->abi) { | |
| case FFI_SYSV: | |
| DEBUG_PRINT("total_size: %lu\n", total_size); | |
| DEBUG_PRINT("slot fitting args size: %lu\n", slot_fitting_args_size); | |
| DEBUG_PRINT("rvalue: %p\n", rvalue); | |
| DEBUG_PRINT("fn: %p\n", fn); | |
| DEBUG_PRINT("rsize: %u\n", cif->flags); | |
| DEBUG_PRINT("wb_size: %u\n", wb_size); | |
| DEBUG_PRINT("int_extension_method: %u\n", int_extension_method); | |
| local_rvalue = ffi_call_SYSV(total_size, slot_fitting_args_size, | |
| &ecif, rvalue, fn, int_extension_method); | |
| if ((cif->flags <= KVX_ABI_MAX_AGGREGATE_IN_REG_SIZE) | |
| && (cif->rtype->type != FFI_TYPE_VOID)) | |
| memcpy(rvalue, &local_rvalue, wb_size); | |
| break; | |
| default: | |
| abort(); | |
| break; | |
| } | |
| } | |
| /* Closures not supported yet */ | |
| ffi_status | |
| ffi_prep_closure_loc (ffi_closure* closure, | |
| ffi_cif* cif, | |
| void (*fun)(ffi_cif*,void*,void**,void*), | |
| void *user_data, | |
| void *codeloc) | |
| { | |
| return FFI_BAD_ABI; | |
| } | |
| #endif /* (__kvx__) */ |