|  | /* 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__) */ |