| /* ----------------------------------------------------------------------- |
| sysv.S - Copyright (c) 2013 Tensilica, Inc. |
| |
| XTENSA Foreign Function Interface |
| |
| 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. |
| ----------------------------------------------------------------------- */ |
| |
| #define LIBFFI_ASM |
| #include <fficonfig.h> |
| #include <ffi.h> |
| |
| #define ENTRY(name) .text; .globl name; .type name,@function; .align 4; name: |
| #define END(name) .size name , . - name |
| |
| /* Assert that the table below is in sync with ffi.h. */ |
| |
| #if FFI_TYPE_UINT8 != 5 \ |
| || FFI_TYPE_SINT8 != 6 \ |
| || FFI_TYPE_UINT16 != 7 \ |
| || FFI_TYPE_SINT16 != 8 \ |
| || FFI_TYPE_UINT32 != 9 \ |
| || FFI_TYPE_SINT32 != 10 \ |
| || FFI_TYPE_UINT64 != 11 |
| #error "xtensa/sysv.S out of sync with ffi.h" |
| #endif |
| |
| |
| /* ffi_call_SYSV (rvalue, rbytes, flags, (*fnaddr)(), bytes, ecif) |
| void *rvalue; a2 |
| unsigned long rbytes; a3 |
| unsigned flags; a4 |
| void (*fnaddr)(); a5 |
| unsigned long bytes; a6 |
| extended_cif* ecif) a7 |
| */ |
| |
| ENTRY(ffi_call_SYSV) |
| |
| entry a1, 32 # 32 byte frame for using call8 below |
| |
| mov a10, a7 # a10(->arg0): ecif |
| sub a11, a1, a6 # a11(->arg1): stack pointer |
| mov a7, a1 # fp |
| movsp a1, a11 # set new sp = old_sp - bytes |
| |
| movi a8, ffi_prep_args |
| callx8 a8 # ffi_prep_args(ecif, stack) |
| |
| # prepare to move stack pointer back up to 6 arguments |
| # note that 'bytes' is already aligned |
| |
| movi a10, 6*4 |
| sub a11, a6, a10 |
| movgez a6, a10, a11 |
| add a6, a1, a6 |
| |
| |
| # we can pass up to 6 arguments in registers |
| # for simplicity, just load 6 arguments |
| # (the stack size is at least 32 bytes, so no risk to cross boundaries) |
| |
| l32i a10, a1, 0 |
| l32i a11, a1, 4 |
| l32i a12, a1, 8 |
| l32i a13, a1, 12 |
| l32i a14, a1, 16 |
| l32i a15, a1, 20 |
| |
| # move stack pointer |
| |
| movsp a1, a6 |
| |
| callx8 a5 # (*fn)(args...) |
| |
| # Handle return value(s) |
| |
| beqz a2, .Lexit |
| |
| movi a5, FFI_TYPE_STRUCT |
| bne a4, a5, .Lstore |
| movi a5, 16 |
| blt a5, a3, .Lexit |
| |
| s32i a10, a2, 0 |
| blti a3, 5, .Lexit |
| addi a3, a3, -1 |
| s32i a11, a2, 4 |
| blti a3, 8, .Lexit |
| s32i a12, a2, 8 |
| blti a3, 12, .Lexit |
| s32i a13, a2, 12 |
| |
| .Lexit: retw |
| |
| .Lstore: |
| addi a4, a4, -FFI_TYPE_UINT8 |
| bgei a4, 7, .Lexit # should never happen |
| movi a6, store_calls |
| add a4, a4, a4 |
| addx4 a6, a4, a6 # store_table + idx * 8 |
| jx a6 |
| |
| .align 8 |
| store_calls: |
| # UINT8 |
| s8i a10, a2, 0 |
| retw |
| |
| # SINT8 |
| .align 8 |
| s8i a10, a2, 0 |
| retw |
| |
| # UINT16 |
| .align 8 |
| s16i a10, a2, 0 |
| retw |
| |
| # SINT16 |
| .align 8 |
| s16i a10, a2, 0 |
| retw |
| |
| # UINT32 |
| .align 8 |
| s32i a10, a2, 0 |
| retw |
| |
| # SINT32 |
| .align 8 |
| s32i a10, a2, 0 |
| retw |
| |
| # UINT64 |
| .align 8 |
| s32i a10, a2, 0 |
| s32i a11, a2, 4 |
| retw |
| |
| END(ffi_call_SYSV) |
| |
| |
| /* |
| * void ffi_cacheflush (unsigned long start, unsigned long end) |
| */ |
| |
| #define EXTRA_ARGS_SIZE 24 |
| |
| ENTRY(ffi_cacheflush) |
| |
| entry a1, 16 |
| |
| 1: dhwbi a2, 0 |
| ihi a2, 0 |
| addi a2, a2, 4 |
| blt a2, a3, 1b |
| |
| retw |
| |
| END(ffi_cacheflush) |
| |
| /* ffi_trampoline is copied to the stack */ |
| |
| ENTRY(ffi_trampoline) |
| |
| entry a1, 16 + (FFI_REGISTER_NARGS * 4) + (4 * 4) # [ 0] |
| j 2f # [ 3] |
| .align 4 # [ 6] |
| 1: .long 0 # [ 8] |
| 2: l32r a15, 1b # [12] |
| _mov a14, a0 # [15] |
| callx0 a15 # [18] |
| # [21] |
| END(ffi_trampoline) |
| |
| /* |
| * ffi_closure() |
| * |
| * a0: closure + 21 |
| * a14: return address (a0) |
| */ |
| |
| ENTRY(ffi_closure_SYSV) |
| |
| /* intentionally omitting entry here */ |
| |
| # restore return address (a0) and move pointer to closure to a10 |
| addi a10, a0, -21 |
| mov a0, a14 |
| |
| # allow up to 4 arguments as return values |
| addi a11, a1, 4 * 4 |
| |
| # save up to 6 arguments to stack (allocated by entry below) |
| s32i a2, a11, 0 |
| s32i a3, a11, 4 |
| s32i a4, a11, 8 |
| s32i a5, a11, 12 |
| s32i a6, a11, 16 |
| s32i a7, a11, 20 |
| |
| movi a8, ffi_closure_SYSV_inner |
| mov a12, a1 |
| callx8 a8 # .._inner(*closure, **avalue, *rvalue) |
| |
| # load up to four return arguments |
| l32i a2, a1, 0 |
| l32i a3, a1, 4 |
| l32i a4, a1, 8 |
| l32i a5, a1, 12 |
| |
| # (sign-)extend return value |
| movi a11, FFI_TYPE_UINT8 |
| bne a10, a11, 1f |
| extui a2, a2, 0, 8 |
| retw |
| |
| 1: movi a11, FFI_TYPE_SINT8 |
| bne a10, a11, 1f |
| sext a2, a2, 7 |
| retw |
| |
| 1: movi a11, FFI_TYPE_UINT16 |
| bne a10, a11, 1f |
| extui a2, a2, 0, 16 |
| retw |
| |
| 1: movi a11, FFI_TYPE_SINT16 |
| bne a10, a11, 1f |
| sext a2, a2, 15 |
| |
| 1: retw |
| |
| END(ffi_closure_SYSV) |