diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..9206052c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,157 @@ +version: 2.1 + +defaults: &defaults + working_directory: ~/repo + docker: + # Built from: + # https://github.com/pyodide/pyodide/blob/2ab4b0ab6aefe99fd994bb4f9ab086e5c0aebb7b/Dockerfile + - image: pyodide/pyodide-env:20230126-chrome109-firefox109-py311 + +jobs: + install-emsdk: + <<: *defaults + steps: + - checkout + - run: + name: install emsdk + command: | + git clone https://github.com/emscripten-core/emsdk.git --depth=1 + cd emsdk + ./emsdk install 3.1.30 + ./emsdk activate 3.1.30 + + - persist_to_workspace: + root: . + paths: + - emsdk + + build: + parameters: + wasm-bigint: + description: Should we build with wasm-bigint? + type: string + default: "" + environment: + WASM_BIGINT: << parameters.wasm-bigint >> + <<: *defaults + steps: + - checkout + - attach_workspace: + at: . + + - run: + name: build + command: | + source ./emsdk/emsdk_env.sh + ./testsuite/emscripten/build.sh + + - run: + name: build tests + command: | + source ./emsdk/emsdk_env.sh + cp -r testsuite/libffi.call testsuite/libffi.call.test + cp -r testsuite/libffi.closures testsuite/libffi.closures.test + ./testsuite/emscripten/build-tests.sh testsuite/libffi.call.test + ./testsuite/emscripten/build-tests.sh testsuite/libffi.closures.test + + - persist_to_workspace: + root: . + paths: + - target + - testsuite + + test: + parameters: + test-params: + description: The tests to run. + type: string + <<: *defaults + steps: + - checkout + + - attach_workspace: + at: . + + - run: + name: run tests + command: | + git reset --hard + cd testsuite/emscripten/ + mkdir test-results + pytest \ + --junitxml=test-results/junit.xml \ + test_libffi.py \ + << parameters.test-params >> + - store_test_results: + path: testsuite/emscripten/test-results + + test-dejagnu: + parameters: + params: + description: Parameters to node-tests + type: string + default: "" + + <<: *defaults + steps: + - checkout + + - attach_workspace: + at: . + + - run: + name: run tests + command: | + source ./emsdk/emsdk_env.sh + python3 testsuite/emscripten/quiet_emcc_info.py + testsuite/emscripten/node-tests.sh << parameters.params >> + +workflows: + version: 2 + build-and-test: + jobs: + - install-emsdk + + - build: + name: build + requires: + - install-emsdk + + - build: + name: build-bigint + wasm-bigint: "true" + requires: + - install-emsdk + + - test: + name: test-firefox + test-params: -k firefox + requires: + - build + - test: + name: test-chrome + test-params: -k chrome + requires: + - build + + - test: + name: test-firefox-bigint + test-params: -k firefox + requires: + - build-bigint + + - test: + name: test-chrome-bigint + test-params: -k chrome + requires: + - build-bigint + + - test-dejagnu: + name: test-dejagnu + requires: + - install-emsdk + - test-dejagnu: + name: test-dejagnu-bigint + params: --wasm-bigint + requires: + - install-emsdk diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml new file mode 100644 index 00000000..75a7c21a --- /dev/null +++ b/.github/workflows/emscripten.yml @@ -0,0 +1,177 @@ +name: CI emscripten + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + push: + branches: [ master ] + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + + +env: + PYODIDE_VERSION: 0.22.1 + # PYTHON_VERSION and EMSCRIPTEN_VERSION are determined by PYODIDE_VERSION. + # The appropriate versions can be found in the Pyodide repodata.json + # "info" field, or in Makefile.envs: + # https://github.com/pyodide/pyodide/blob/main/Makefile.envs#L2 + PYTHON_VERSION: 3.10.7 + EMSCRIPTEN_VERSION: 3.1.27 + EM_CACHE_FOLDER: emsdk-cache + +jobs: + setup-emsdk-cache: + runs-on: ubuntu-22.04 + steps: + - name: Setup cache + id: cache-system-libraries + uses: actions/cache@v3 + with: + path: ${{env.EM_CACHE_FOLDER}} + key: ${{env.EMSCRIPTEN_VERSION}} + + - uses: mymindstorm/setup-emsdk@v12 + with: + version: ${{ env.EMSCRIPTEN_VERSION }} + actions-cache-folder: ${{env.EM_CACHE_FOLDER}} + + test-dejagnu: + strategy: + matrix: + bigint: ["--wasm-bigint", ""] + runs-on: ubuntu-22.04 + needs: [setup-emsdk-cache] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: set up python + id: setup-python + uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Setup cache + id: cache-system-libraries + uses: actions/cache@v3 + with: + path: ${{env.EM_CACHE_FOLDER}} + key: ${{env.EMSCRIPTEN_VERSION}} + + - uses: mymindstorm/setup-emsdk@v12 + with: + version: ${{ env.EMSCRIPTEN_VERSION }} + actions-cache-folder: ${{env.EM_CACHE_FOLDER}} + + - name: Install dejagnu + shell: bash -l {0} + run: | + sudo apt install -y dejagnu + + - name: run tests + run: | + testsuite/emscripten/node-tests.sh ${{ matrix.bigint }} + + - name: Install rlgl and run + run: | + wget -qO - https://rl.gl/cli/rlgl-linux-amd64.tgz | \ + tar --strip-components=2 -xvzf - ./rlgl/rlgl; + ./rlgl l --key=0LIBFFI-0LIBFFI-0LIBFFI-0LIBFFI https://rl.gl + ./rlgl e -l project=libffi -l sha=${GITHUB_SHA:0:7} -l CC='emcc' -l host=wasm32-emscripten --policy=https://github.com/libffi/rlgl-policy.git testsuite/libffi.log + exit $? + + build: + strategy: + matrix: + bigint: ["--wasm-bigint", ""] + env: + WASM_BIGINT: ${{ matrix.bigint }} + runs-on: ubuntu-22.04 + needs: [setup-emsdk-cache] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup cache + id: cache-system-libraries + uses: actions/cache@v3 + with: + path: ${{env.EM_CACHE_FOLDER}} + key: ${{env.EMSCRIPTEN_VERSION}} + + - uses: mymindstorm/setup-emsdk@v12 + with: + version: ${{ env.EMSCRIPTEN_VERSION }} + actions-cache-folder: ${{env.EM_CACHE_FOLDER}} + + - name: build + run: | + ./testsuite/emscripten/build.sh ${{ matrix.bigint }} + + - name: build tests + run: | + cp -r testsuite/libffi.call testsuite/libffi.call.test + cp -r testsuite/libffi.closures testsuite/libffi.closures.test + ./testsuite/emscripten/build-tests.sh testsuite/libffi.call.test ${{ matrix.bigint }} + ./testsuite/emscripten/build-tests.sh testsuite/libffi.closures.test ${{ matrix.bigint }} + + - name: Store artifacts + uses: actions/upload-artifact@v3 + with: + name: built-tests + path: | + ./testsuite/libffi.c*/ + + test: + strategy: + matrix: + bigint: ["--wasm-bigint", ""] + browser: ["firefox", "chrome"] + runs-on: ubuntu-22.04 + needs: [build] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Download build artifact + uses: actions/download-artifact@v3 + with: + name: built-tests + path: ./testsuite/ + + - uses: conda-incubator/setup-miniconda@v2 + with: + activate-environment: pyodide-env + python-version: "3.11" + channels: conda-forge + - name: Install test dependencies + run: | + pip install pytest-pyodide==0.23.2 + + - name: + run: | + cd testsuite/emscripten/ + mkdir test-results + pytest \ + --junitxml=test-results/junit.xml \ + test_libffi.py \ + -k ${{ matrix.browser }} \ + -s + + - name: Test Report + uses: dorny/test-reporter@v1 + if: success() || failure() + with: + name: pytest results ${{ matrix.bigint }} ${{ matrix.browser }} + path: testsuite/emscripten/test-results/junit.xml + reporter: java-junit + + - name: Store test results + uses: actions/upload-artifact@v3 + if: success() || failure() + with: + name: test-results + path: testsuite/emscripten/test-results diff --git a/.gitignore b/.gitignore index f33ca3da..dea0b1bb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ .deps *.o *.lo +*.wasm +*.js +*.test +*.log .dirstamp *.la Makefile @@ -35,3 +39,8 @@ build_*/ darwin_*/ src/arm/trampoline.S **/texinfo.tex +target/ +__pycache__ +.docker_home +emsdk +test-results diff --git a/Makefile.am b/Makefile.am index a4a4887c..3de0bea4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,6 +62,7 @@ noinst_HEADERS = src/aarch64/ffitarget.h src/aarch64/internal.h \ src/s390/ffitarget.h src/s390/internal.h src/sh/ffitarget.h \ src/sh64/ffitarget.h src/sparc/ffitarget.h \ src/sparc/internal.h src/tile/ffitarget.h src/vax/ffitarget.h \ + src/wasm32/ffitarget.h \ src/x86/ffitarget.h src/x86/internal.h src/x86/internal64.h \ src/x86/asmnames.h src/xtensa/ffitarget.h src/dlmalloc.c \ src/kvx/ffitarget.h src/kvx/asm.h \ @@ -81,7 +82,7 @@ EXTRA_libffi_la_SOURCES = src/aarch64/ffi.c src/aarch64/sysv.S \ src/mips/n32.S src/moxie/ffi.c src/moxie/eabi.S \ src/nios2/ffi.c src/nios2/sysv.S src/or1k/ffi.c \ src/or1k/sysv.S src/pa/ffi.c src/pa/linux.S src/pa/hpux32.S \ - src/powerpc/ffi.c src/powerpc/ffi_sysv.c \ + src/pa/hpux64.S src/powerpc/ffi.c src/powerpc/ffi_sysv.c \ src/powerpc/ffi_linux64.c src/powerpc/sysv.S \ src/powerpc/linux64.S src/powerpc/linux64_closure.S \ src/powerpc/ppc_closure.S src/powerpc/aix.S \ @@ -92,6 +93,7 @@ EXTRA_libffi_la_SOURCES = src/aarch64/ffi.c src/aarch64/sysv.S \ src/sh64/sysv.S src/sparc/ffi.c src/sparc/ffi64.c \ src/sparc/v8.S src/sparc/v9.S src/tile/ffi.c src/tile/tile.S \ src/vax/ffi.c src/vax/elfbsd.S src/x86/ffi.c src/x86/sysv.S \ + src/wasm32/ffi.c \ src/x86/ffiw64.c src/x86/win64.S src/x86/ffi64.c \ src/x86/unix64.S src/x86/sysv_intel.S src/x86/win64_intel.S \ src/xtensa/ffi.c src/xtensa/sysv.S src/kvx/ffi.c \ diff --git a/README.md b/README.md index a5b60e84..62c4263d 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ tested: | Blackfin | uClinux | GCC | | CSKY | Linux | GCC | | HPPA | HPUX | GCC | +| HPPA64 | HPUX | GCC | | KVX | Linux | GCC | | IA-64 | Linux | GCC | | LoongArch64 | Linux | GCC | @@ -197,6 +198,11 @@ History See the git log for details at http://github.com/libffi/libffi. + TBD - TBD + Add support for HPPA64. + Many HPPA fixes. + Many x86 Darwin fixes. + 3.4.4 Oct-23-2022 Important aarch64 fixes, including support for linux builds with Link Time Optimization (-flto). @@ -474,6 +480,7 @@ developers: nios ii Sandra Loosemore openrisc Sebastian Macke pa Randolph Chung, Dave Anglin, Andreas Tobler + pa64 Dave Anglin powerpc Geoffrey Keating, Andreas Tobler, David Edelsohn, John Hornkvist powerpc64 Jakub Jelinek diff --git a/configure.host b/configure.host index f23716f7..b291bd0e 100644 --- a/configure.host +++ b/configure.host @@ -73,6 +73,7 @@ case "${host}" in ;; hppa*64-*-hpux*) TARGET=PA64_HPUX; TARGETDIR=pa + SOURCES="ffi64.c hpux64.S" ;; hppa*-*-hpux*) TARGET=PA_HPUX; TARGETDIR=pa @@ -264,6 +265,11 @@ case "${host}" in SOURCES="ffi.c elfbsd.S" ;; + wasm32-*-*) + TARGET=wasm32; TARGETDIR=wasm32 + SOURCES="ffi.c" + ;; + xtensa*-*) TARGET=XTENSA; TARGETDIR=xtensa SOURCES="ffi.c sysv.S" diff --git a/include/ffi.h.in b/include/ffi.h.in index 84671357..e7fe9633 100644 --- a/include/ffi.h.in +++ b/include/ffi.h.in @@ -361,14 +361,6 @@ typedef struct { FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); -#if defined(PA_LINUX) || defined(PA_HPUX) -#define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) -#define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) -#else -#define FFI_CLOSURE_PTR(X) (X) -#define FFI_RESTORE_PTR(X) (X) -#endif - FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, @@ -515,8 +507,14 @@ FFI_API ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets); -/* Useful for eliminating compiler warnings. */ +/* Convert between closure and function pointers. */ +#if defined(PA_LINUX) || defined(PA_HPUX) +#define FFI_FN(f) ((void (*)(void))((unsigned int)(f) | 2)) +#define FFI_CL(f) ((void *)((unsigned int)(f) & ~3)) +#else #define FFI_FN(f) ((void (*)(void))f) +#define FFI_CL(f) ((void *)(f)) +#endif /* ---- Definitions shared with assembly code ---------------------------- */ diff --git a/include/ffi_common.h b/include/ffi_common.h index 2bd31b03..c53a7949 100644 --- a/include/ffi_common.h +++ b/include/ffi_common.h @@ -128,6 +128,10 @@ void *ffi_data_to_code_pointer (void *data) FFI_HIDDEN; static trampoline. */ int ffi_tramp_is_present (void *closure) FFI_HIDDEN; +/* Return a file descriptor of a temporary zero-sized file in a + writable and executable filesystem. */ +int open_temp_exec_file(void) FFI_HIDDEN; + /* Extended cif, used in callback from assembly routine */ typedef struct { diff --git a/src/closures.c b/src/closures.c index 9aafbec4..0f2d6197 100644 --- a/src/closures.c +++ b/src/closures.c @@ -31,6 +31,8 @@ #define _GNU_SOURCE 1 #endif +#ifndef __EMSCRIPTEN__ + #include #include #include @@ -795,7 +797,7 @@ open_temp_exec_file (void) static int allocate_space (int fd, off_t offset, off_t len) { - static size_t page_size; + static long page_size; /* Obtain system page size. */ if (!page_size) @@ -993,23 +995,23 @@ ffi_closure_alloc (size_t size, void **code) if (!code) return NULL; - ptr = FFI_CLOSURE_PTR (dlmalloc (size)); + ptr = dlmalloc (size); if (ptr) { msegmentptr seg = segment_holding (gm, ptr); - *code = add_segment_exec_offset (ptr, seg); + *code = FFI_FN (add_segment_exec_offset (ptr, seg)); if (!ffi_tramp_is_supported ()) return ptr; ftramp = ffi_tramp_alloc (0); if (ftramp == NULL) { - dlfree (FFI_RESTORE_PTR (ptr)); + dlfree (ptr); return NULL; } - *code = ffi_tramp_get_addr (ftramp); + *code = FFI_FN (ffi_tramp_get_addr (ftramp)); ((ffi_closure *) ptr)->ftramp = ftramp; } @@ -1050,7 +1052,7 @@ ffi_closure_free (void *ptr) if (ffi_tramp_is_supported ()) ffi_tramp_free (((ffi_closure *) ptr)->ftramp); - dlfree (FFI_RESTORE_PTR (ptr)); + dlfree (ptr); } int @@ -1070,16 +1072,20 @@ ffi_tramp_is_present (void *ptr) void * ffi_closure_alloc (size_t size, void **code) { + void *c; + if (!code) return NULL; - return *code = FFI_CLOSURE_PTR (malloc (size)); + c = malloc (size); + *code = FFI_FN (c); + return c; } void ffi_closure_free (void *ptr) { - free (FFI_RESTORE_PTR (ptr)); + free (ptr); } void * @@ -1098,3 +1104,4 @@ ffi_tramp_is_present (__attribute__((unused)) void *ptr) #endif /* FFI_CLOSURES */ #endif /* NetBSD with PROT_MPROTECT */ +#endif /* __EMSCRIPTEN__ */ diff --git a/src/pa/ffi.c b/src/pa/ffi.c index 186bf694..f6012397 100644 --- a/src/pa/ffi.c +++ b/src/pa/ffi.c @@ -56,27 +56,12 @@ static inline int ffi_struct_type(ffi_type *t) size_t sz = t->size; /* Small structure results are passed in registers, - larger ones are passed by pointer. Note that - small structures of size 2, 4 and 8 differ from - the corresponding integer types in that they have - different alignment requirements. */ - - if (sz <= 1) - return FFI_TYPE_UINT8; - else if (sz == 2) - return FFI_TYPE_SMALL_STRUCT2; - else if (sz == 3) - return FFI_TYPE_SMALL_STRUCT3; - else if (sz == 4) - return FFI_TYPE_SMALL_STRUCT4; - else if (sz == 5) - return FFI_TYPE_SMALL_STRUCT5; - else if (sz == 6) - return FFI_TYPE_SMALL_STRUCT6; - else if (sz == 7) - return FFI_TYPE_SMALL_STRUCT7; - else if (sz <= 8) - return FFI_TYPE_SMALL_STRUCT8; + larger ones are passed by pointer. Note that small + structures differ from the corresponding integer + types in that they have different alignment requirements. */ + + if (sz <= 8) + return -sz; else return FFI_TYPE_STRUCT; /* else, we pass it by pointer. */ } @@ -445,7 +430,6 @@ ffi_status ffi_closure_inner_pa32(ffi_closure *closure, UINT32 *stack) int i, avn; unsigned int slot = FIRST_ARG_SLOT; register UINT32 r28 asm("r28"); - ffi_closure *c = (ffi_closure *)FFI_RESTORE_PTR (closure); cif = closure->cif; @@ -548,7 +532,7 @@ ffi_status ffi_closure_inner_pa32(ffi_closure *closure, UINT32 *stack) } /* Invoke the closure. */ - (c->fun) (cif, rvalue, avalue, c->user_data); + (closure->fun) (cif, rvalue, avalue, closure->user_data); debug(3, "after calling function, ret[0] = %08x, ret[1] = %08x\n", u.ret[0], u.ret[1]); @@ -557,16 +541,16 @@ ffi_status ffi_closure_inner_pa32(ffi_closure *closure, UINT32 *stack) switch (cif->flags) { case FFI_TYPE_UINT8: - *(stack - FIRST_ARG_SLOT) = (UINT8)(u.ret[0] >> 24); + *(stack - FIRST_ARG_SLOT) = (UINT8)u.ret[0]; break; case FFI_TYPE_SINT8: - *(stack - FIRST_ARG_SLOT) = (SINT8)(u.ret[0] >> 24); + *(stack - FIRST_ARG_SLOT) = (SINT8)u.ret[0]; break; case FFI_TYPE_UINT16: - *(stack - FIRST_ARG_SLOT) = (UINT16)(u.ret[0] >> 16); + *(stack - FIRST_ARG_SLOT) = (UINT16)u.ret[0]; break; case FFI_TYPE_SINT16: - *(stack - FIRST_ARG_SLOT) = (SINT16)(u.ret[0] >> 16); + *(stack - FIRST_ARG_SLOT) = (SINT16)u.ret[0]; break; case FFI_TYPE_INT: case FFI_TYPE_SINT32: @@ -591,6 +575,7 @@ ffi_status ffi_closure_inner_pa32(ffi_closure *closure, UINT32 *stack) /* Don't need a return value, done by caller. */ break; + case FFI_TYPE_SMALL_STRUCT1: case FFI_TYPE_SMALL_STRUCT2: case FFI_TYPE_SMALL_STRUCT3: case FFI_TYPE_SMALL_STRUCT4: @@ -649,8 +634,6 @@ ffi_prep_closure_loc (ffi_closure* closure, void *user_data, void *codeloc) { - ffi_closure *c = (ffi_closure *)FFI_RESTORE_PTR (closure); - /* The layout of a function descriptor. A function pointer with the PLABEL bit set points to a function descriptor. */ struct pa32_fd @@ -676,14 +659,14 @@ ffi_prep_closure_loc (ffi_closure* closure, fd = (struct pa32_fd *)((UINT32)ffi_closure_pa32 & ~3); /* Setup trampoline. */ - tramp = (struct ffi_pa32_trampoline_struct *)c->tramp; + tramp = (struct ffi_pa32_trampoline_struct *)closure->tramp; tramp->code_pointer = fd->code_pointer; tramp->fake_gp = (UINT32)codeloc & ~3; tramp->real_gp = fd->gp; - c->cif = cif; - c->user_data = user_data; - c->fun = fun; + closure->cif = cif; + closure->user_data = user_data; + closure->fun = fun; return FFI_OK; } diff --git a/src/pa/ffi64.c b/src/pa/ffi64.c new file mode 100644 index 00000000..08807c8a --- /dev/null +++ b/src/pa/ffi64.c @@ -0,0 +1,614 @@ +/* ----------------------------------------------------------------------- + ffi64.c - (c) 2022 John David Anglin + + HPPA Foreign Function Interface + PA 64-Bit ABI support + + 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. + ----------------------------------------------------------------------- */ + +#include +#include + +#include +#include + +#define ROUND_UP(v, a) (((size_t)(v) + (a) - 1) & ~((a) - 1)) + +#define FIRST_ARG_SLOT 0 +#define DEBUG_LEVEL 0 + +#define fldw(addr, fpreg) \ + __asm__ volatile ("fldw 4(%0), %%" #fpreg "R" : : "r"(addr) : #fpreg) +#define fstw(fpreg, addr) \ + __asm__ volatile ("fstw %%" #fpreg "R, 4(%0)" : : "r"(addr)) +#define fldd(addr, fpreg) \ + __asm__ volatile ("fldd 0(%0), %%" #fpreg "L" : : "r"(addr) : #fpreg) +#define fstd(fpreg, addr) \ + __asm__ volatile ("fstd %%" #fpreg "L, 0(%0)" : : "r"(addr)) + +#define debug(lvl, x...) do { if (lvl <= DEBUG_LEVEL) { printf(x); } } while (0) + +static inline int ffi_struct_type(ffi_type *t) +{ + int sz = t->size; + + /* Small structure results are returned in registers 28 and 29, + larger ones are in a buffer allocated by the callee. The + address of the buffer is passed in r28. The buffer is supposed + to be aligned on a 16-byte boundary. Register return values are + padded on the right. The pad bits on the right are undefined. */ + + if (sz <= 16) + return -sz; + else + return FFI_TYPE_STRUCT; +} + +/* PA has a downward growing stack, which looks like this. Stack + arguments are offset from the argument ponter (AP) in r29. + + Offset + [ Fixed args ] + AP-64 arg word 0 (r26, fr4) + AP-56 arg word 1 (r25, fr5) + AP-48 arg word 2 (r24, fr6) + AP-40 arg word 3 (r23, fr7) + AP-32 arg word 4 (r22, fr8) + AP-24 arg word 5 (r21, fr9) + AP-16 arg word 6 (r20, fr10) + AP-8 arg word 7 (r19, fr11) + [ Variable args; AP = SP-16 if there are no variable args ] + AP stack arg 0 + AP+8 stack arg 1 + ... + [ Frame marker ] + SP-16 RP + SP-8 previous SP + + The first eight argument words on the stack are reserved for use by + the callee. Instead, the general and floating registers replace + the first four argument slots. Non FP arguments are passed solely + in the general registers. Single and double FP arguments are passed + in both general and floating registers when using libffi. + + The registers are allocated in the same manner as stack slots. + This allows the callee to save its arguments on the stack if + necessary: + + arg word 0 -> gr26 or fr4L or fr4R + arg word 1 -> gr25 or fr5L or fr5R + arg word 2 -> gr24 or fr6L or fr6R + arg word 3 -> gr23 or fr7L or fr7R + ... + + Single Single-precision floating-point parameters, when passed in + floating-point registers, are passed in the right halves of the + floating point registers; the left halves are unused. + + Quad-precision floating-point parameters within the first 64 bytes of + the parameter list are always passed in general registers. + + The rest of the arguments are passed on the stack starting at AP. + + This means we can have holes either in the register allocation, + or in the stack. */ + +/* ffi_prep_args is called by the assembly routine once stack space + has been allocated for the function's arguments + + The following code will put everything into the stack frame + (which was allocated by the asm routine), and on return + the asm routine will load the arguments that should be + passed by register into the appropriate registers + + NOTE: We load floating point args in this function... that means we + assume gcc will not mess with fp regs in here. */ + +void ffi_prep_args_pa64(UINT64 *stack, extended_cif *ecif, unsigned bytes) +{ + register unsigned int i; + register ffi_type **p_arg; + register void **p_argv; + unsigned int slot = FIRST_ARG_SLOT; + size_t len; + + debug(1, "%s: stack = %p, ecif = %p, bytes = %u\n", __FUNCTION__, stack, + ecif, bytes); + + p_arg = ecif->cif->arg_types; + p_argv = ecif->avalue; + + for (i = 0; i < ecif->cif->nargs; i++) + { + int type = (*p_arg)->type; + + len = (*p_arg)->size; + + switch (type) + { + case FFI_TYPE_SINT8: + *(SINT64 *)(stack + slot) = *(SINT8 *)(*p_argv); + break; + + case FFI_TYPE_UINT8: + *(UINT64 *)(stack + slot) = *(UINT8 *)(*p_argv); + break; + + case FFI_TYPE_SINT16: + *(SINT64 *)(stack + slot) = *(SINT16 *)(*p_argv); + break; + + case FFI_TYPE_UINT16: + *(UINT64 *)(stack + slot) = *(UINT16 *)(*p_argv); + break; + + case FFI_TYPE_SINT32: + *(SINT64 *)(stack + slot) = *(SINT32 *)(*p_argv); + break; + + case FFI_TYPE_UINT32: + *(UINT64 *)(stack + slot) = *(UINT32 *)(*p_argv); + break; + + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + case FFI_TYPE_POINTER: + debug(3, "Storing UINT64 %lu in slot %u\n", *(UINT64 *)(*p_argv), + slot); + *(UINT64 *)(stack + slot) = *(UINT64 *)(*p_argv); + break; + + case FFI_TYPE_FLOAT: + /* First 8 args go in fr4L - fr11L. */ + debug(3, "Storing UINT32(float) in slot %u\n", slot); + *(UINT64 *)(stack + slot) = *(UINT32 *)(*p_argv); + switch (slot - FIRST_ARG_SLOT) + { + /* First 4 args go in fr4L - fr7L. */ + case 0: fldw(stack + slot, fr4); break; + case 1: fldw(stack + slot, fr5); break; + case 2: fldw(stack + slot, fr6); break; + case 3: fldw(stack + slot, fr7); break; + case 4: fldw(stack + slot, fr8); break; + case 5: fldw(stack + slot, fr9); break; + case 6: fldw(stack + slot, fr10); break; + case 7: fldw(stack + slot, fr11); break; + } + break; + + case FFI_TYPE_DOUBLE: + debug(3, "Storing UINT64(double) at slot %u\n", slot); + *(UINT64 *)(stack + slot) = *(UINT64 *)(*p_argv); + switch (slot - FIRST_ARG_SLOT) + { + /* First 8 args go in fr4 to fr11. */ + case 0: fldd(stack + slot, fr4); break; + case 1: fldd(stack + slot, fr5); break; + case 2: fldd(stack + slot, fr6); break; + case 3: fldd(stack + slot, fr7); break; + case 4: fldd(stack + slot, fr8); break; + case 5: fldd(stack + slot, fr9); break; + case 6: fldd(stack + slot, fr10); break; + case 7: fldd(stack + slot, fr11); break; + } + break; + +#ifdef PA64_HPUX + case FFI_TYPE_LONGDOUBLE: + /* Align slot to a 16-byte boundary. */ + slot += (slot & 1); + *(UINT64 *)(stack + slot) = *(UINT64 *)(*p_argv); + *(UINT64 *)(stack + slot + 1) = *(UINT64 *)(*p_argv + 8); + break; +#endif + + case FFI_TYPE_STRUCT: + /* Structs larger than 8 bytes are aligned on a 16-byte boundary. */ + if (len > 8) + slot += (slot & 1); + memcpy((char *)(stack + slot), (char *)*p_argv, len); + break; + + default: + FFI_ASSERT(0); + } + + slot += ROUND_UP (len, 8) >> 3; + p_arg++; + p_argv++; + } + + FFI_ASSERT(slot * 8 <= bytes); + + return; +} + +static void ffi_size_stack_pa64(ffi_cif *cif) +{ + ffi_type **ptr; + int i; + int z = 0; /* # stack slots */ + + for (ptr = cif->arg_types, i = 0; i < cif->nargs; ptr++, i++) + { + int type = (*ptr)->type; + int size = (*ptr)->size; + + switch (type) + { +#ifdef PA64_HPUX + case FFI_TYPE_LONGDOUBLE: + z += 2 + (z & 1); + break; +#endif + + case FFI_TYPE_STRUCT: + if (size > 8) + z += (z & 1); + z += ROUND_UP (size, 8) >> 3; + break; + + default: /* 64-bit values */ + z++; + } + } + + /* We need a minimum of 8 argument slots. Stack must be 16-byte + aligned. */ + if (z <= 8) + z = 8; + else + z += (z & 1); + + /* Add 16 bytes for frame marker. */ + cif->bytes = z * 8 + 64; + debug(3, "Calculated stack size is %u bytes\n", cif->bytes); +} + +/* Perform machine dependent cif processing. */ +ffi_status ffi_prep_cif_machdep(ffi_cif *cif) +{ + /* Set the return type flag for jump table. */ + switch (cif->rtype->type) + { + case FFI_TYPE_COMPLEX: + case FFI_TYPE_STRUCT: + /* For the return type we have to check the size of the structures. + If the size is smaller or equal 8 bytes, the result is given back + in one register. If the size is smaller or equal 16 bytes than we + return the result in two registers. If the size is bigger than + 16 bytes, the return is in a buffer allocated by the caller. */ + cif->flags = ffi_struct_type(cif->rtype); + break; + + default: + cif->flags = (unsigned) cif->rtype->type; + break; + } + + /* Lucky us, because of the unique PA ABI we get to do our + own stack sizing. */ + switch (cif->abi) + { + case FFI_PA64: + ffi_size_stack_pa64(cif); + break; + + default: + FFI_ASSERT(0); + break; + } + + return FFI_OK; +} + +extern void ffi_call_pa64(void (*)(UINT64 *, extended_cif *, unsigned), + extended_cif *, unsigned, unsigned, unsigned *, + void (*fn)(void)); + +void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) +{ + extended_cif ecif; + + ecif.cif = cif; + ecif.avalue = avalue; + + /* If the return value is a struct and we don't have a return + value address then we need to make one. */ + + if (rvalue == NULL + && (cif->rtype->type == FFI_TYPE_STRUCT + || cif->rtype->type == FFI_TYPE_COMPLEX) + && cif->rtype->size > 16) + ecif.rvalue = alloca(ROUND_UP (cif->rtype->size, 16)); + else + ecif.rvalue = rvalue; + + + switch (cif->abi) + { + case FFI_PA64: + debug(3, "Calling ffi_call_pa64: ecif=%p, bytes=%u, flags=%u, rvalue=%p, fn=%p\n", &ecif, cif->bytes, cif->flags, ecif.rvalue, (void *)fn); + ffi_call_pa64(ffi_prep_args_pa64, &ecif, cif->bytes, + cif->flags, ecif.rvalue, fn); + break; + + default: + FFI_ASSERT(0); + break; + } +} + +#if FFI_CLOSURES +/* This is more-or-less an inverse of ffi_call -- we have arguments on + the stack, and we need to fill them into a cif structure and invoke + the user function. This really ought to be in asm to make sure + the compiler doesn't do things we don't expect. */ +ffi_status ffi_closure_inner_pa64(ffi_closure *closure, UINT64 *stack) +{ + ffi_cif *cif; + void **avalue; + void *rvalue; + /* Functions can return up to 128-bits in registers. Return address + must be double word aligned. */ + union { long double rld; UINT64 ret[2]; } u; + ffi_type **p_arg; + char *tmp; + int i, avn; + unsigned int slot = FIRST_ARG_SLOT; + register UINT64 r28 asm("r28"); + + cif = closure->cif; + + /* If returning via structure, callee will write to our pointer. */ + if (cif->flags == FFI_TYPE_STRUCT) + rvalue = (void *)r28; + else + rvalue = &u; + + avalue = (void **)alloca(cif->nargs * FFI_SIZEOF_ARG); + avn = cif->nargs; + p_arg = cif->arg_types; + + for (i = 0; i < avn; i++) + { + int type = (*p_arg)->type; + + switch (type) + { + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT8: + avalue[i] = (void *)(stack + slot) + 7; + break; + + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT16: + avalue[i] = (void *)(stack + slot) + 6; + break; + + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: + avalue[i] = (void *)(stack + slot) + 4; + break; + + case FFI_TYPE_POINTER: + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + avalue[i] = (void *)(stack + slot); + break; + + case FFI_TYPE_FLOAT: + /* The closure call is indirect. In Linux, floating point + arguments in indirect calls with a prototype are passed + in the floating point registers instead of the general + registers. So, we need to replace what was previously + stored in the current slot with the value in the + corresponding floating point register. */ + switch (slot + FIRST_ARG_SLOT) + { + case 0: fstw(fr4, (void *)(stack + slot)); break; + case 1: fstw(fr5, (void *)(stack + slot)); break; + case 2: fstw(fr6, (void *)(stack + slot)); break; + case 3: fstw(fr7, (void *)(stack + slot)); break; + case 4: fstw(fr8, (void *)(stack + slot)); break; + case 5: fstw(fr9, (void *)(stack + slot)); break; + case 6: fstw(fr10, (void *)(stack + slot)); break; + case 7: fstw(fr11, (void *)(stack + slot)); break; + } + avalue[i] = (void *)(stack + slot) + 4; + break; + + case FFI_TYPE_DOUBLE: + /* See previous comment for FFI_TYPE_FLOAT. */ + switch (slot + FIRST_ARG_SLOT) + { + case 0: fstd(fr4, (void *)(stack + slot)); break; + case 1: fstd(fr5, (void *)(stack + slot)); break; + case 2: fstd(fr6, (void *)(stack + slot)); break; + case 3: fstd(fr7, (void *)(stack + slot)); break; + case 4: fstd(fr8, (void *)(stack + slot)); break; + case 5: fstd(fr9, (void *)(stack + slot)); break; + case 6: fstd(fr10, (void *)(stack + slot)); break; + case 7: fstd(fr11, (void *)(stack + slot)); break; + } + avalue[i] = (void *)(stack + slot); + break; + +#ifdef PA64_HPUX + case FFI_TYPE_LONGDOUBLE: + /* Long doubles are treated like a big structure. */ + slot += (slot & 1); + avalue[i] = (void *)(stack + slot); + break; +#endif + + case FFI_TYPE_STRUCT: + /* All structs are passed in registers. Structs larger + than 8 bytes are aligned on a 16-byte boundary. */ + if((*p_arg)->size > 8) + slot += (slot & 1); + avalue[i] = (void *) (stack + slot); + break; + + default: + FFI_ASSERT(0); + } + + slot += (ROUND_UP ((*p_arg)->size, 8) >> 3); + p_arg++; + } + + /* Invoke the closure. */ + (closure->fun) (cif, rvalue, avalue, closure->user_data); + + debug(3, "after calling function, ret[0] = %16lx, ret[1] = %16lx\n", u.ret[0], + u.ret[1]); + + /* Store the result using the lower 2 bytes of the flags. */ + switch (cif->flags) + { + case FFI_TYPE_UINT8: + *(stack + FIRST_ARG_SLOT) = (UINT8)u.ret[0]; + break; + case FFI_TYPE_SINT8: + *(stack + FIRST_ARG_SLOT) = (SINT8)u.ret[0]; + break; + case FFI_TYPE_UINT16: + *(stack + FIRST_ARG_SLOT) = (UINT16)u.ret[0]; + break; + case FFI_TYPE_SINT16: + *(stack + FIRST_ARG_SLOT) = (SINT16)u.ret[0]; + break; + case FFI_TYPE_INT: + case FFI_TYPE_SINT32: + *(stack + FIRST_ARG_SLOT) = (SINT32)u.ret[0]; + break; + case FFI_TYPE_UINT32: + *(stack - FIRST_ARG_SLOT) = (UINT32)u.ret[0]; + break; + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + case FFI_TYPE_POINTER: + *(stack - FIRST_ARG_SLOT) = u.ret[0]; + break; + + case FFI_TYPE_LONGDOUBLE: + *(stack + FIRST_ARG_SLOT) = u.ret[0]; + *(stack + FIRST_ARG_SLOT + 1) = u.ret[1]; + break; + + case FFI_TYPE_DOUBLE: + fldd(rvalue, fr4); + break; + + case FFI_TYPE_FLOAT: + /* Adjust for address adjustment in fldw macro. */ + fldw(rvalue - 4, fr4); + break; + + case FFI_TYPE_STRUCT: + /* Don't need a return value, done by caller. */ + break; + + case -1: + case -2: + case -3: + case -4: + case -5: + case -6: + case -7: + case -8: + case -9: + case -10: + case -11: + case -12: + case -13: + case -14: + case -15: + case -16: + tmp = (void*)(stack + FIRST_ARG_SLOT); + memcpy((void*)tmp, &u, cif->rtype->size); + break; + + case FFI_TYPE_VOID: + break; + + default: + debug(0, "assert with cif->flags: %d\n",cif->flags); + FFI_ASSERT(0); + break; + } + return FFI_OK; +} + +/* Fill in a closure to refer to the specified fun and user_data. + cif specifies the argument and result types for fun. + The cif must already be prep'ed. */ + +extern void ffi_closure_pa64(void); + +ffi_status +ffi_prep_closure_loc (ffi_closure* closure, + ffi_cif* cif, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data, + void *codeloc) +{ + /* The layout of a function descriptor. */ + struct pa64_fd + { + UINT64 tmp1; + UINT64 tmp2; + UINT64 code_pointer; + UINT64 gp; + }; + + struct ffi_pa64_trampoline_struct + { + UINT64 real_gp; /* Real gp value. */ + UINT64 tmp2; + UINT64 code_pointer; /* Pointer to ffi_closure_unix. */ + UINT64 fake_gp; /* Pointer to closure, installed as gp. */ + }; + + struct ffi_pa64_trampoline_struct *tramp; + struct pa64_fd *fd; + + if (cif->abi != FFI_PA64) + return FFI_BAD_ABI; + + /* Get function descriptor address for ffi_closure_pa64. */ + fd = (struct pa64_fd *)((UINT64)ffi_closure_pa64); + + /* Setup trampoline. */ + tramp = (struct ffi_pa64_trampoline_struct *)closure->tramp; + tramp->code_pointer = fd->code_pointer; + tramp->fake_gp = (UINT64)codeloc; + tramp->real_gp = fd->gp; + + closure->cif = cif; + closure->user_data = user_data; + closure->fun = fun; + + return FFI_OK; +} +#endif diff --git a/src/pa/ffitarget.h b/src/pa/ffitarget.h index df1209eb..dae854a6 100644 --- a/src/pa/ffitarget.h +++ b/src/pa/ffitarget.h @@ -54,7 +54,6 @@ typedef enum ffi_abi { #endif #ifdef PA64_HPUX -#error "PA64_HPUX FFI is not yet implemented" FFI_PA64, FFI_LAST_ABI, FFI_DEFAULT_ABI = FFI_PA64 @@ -68,13 +67,28 @@ typedef enum ffi_abi { #define FFI_CLOSURES 1 #define FFI_NATIVE_RAW_API 0 +#if defined(PA64_HPUX) +#define FFI_TRAMPOLINE_SIZE 32 +#else #define FFI_TRAMPOLINE_SIZE 12 +#endif + +#define FFI_TYPE_SMALL_STRUCT1 -1 +#define FFI_TYPE_SMALL_STRUCT2 -2 +#define FFI_TYPE_SMALL_STRUCT3 -3 +#define FFI_TYPE_SMALL_STRUCT4 -4 +#define FFI_TYPE_SMALL_STRUCT5 -5 +#define FFI_TYPE_SMALL_STRUCT6 -6 +#define FFI_TYPE_SMALL_STRUCT7 -7 +#define FFI_TYPE_SMALL_STRUCT8 -8 + +/* linux.S and hpux32.S expect FFI_TYPE_COMPLEX is the last generic type. */ +#define FFI_PA_TYPE_LAST FFI_TYPE_COMPLEX + +/* If new generic types are added, the jump tables in linux.S and hpux32.S + likely need updating. */ +#if FFI_TYPE_LAST != FFI_PA_TYPE_LAST +# error "You likely have broken jump tables" +#endif -#define FFI_TYPE_SMALL_STRUCT2 -1 -#define FFI_TYPE_SMALL_STRUCT3 -2 -#define FFI_TYPE_SMALL_STRUCT4 -3 -#define FFI_TYPE_SMALL_STRUCT5 -4 -#define FFI_TYPE_SMALL_STRUCT6 -5 -#define FFI_TYPE_SMALL_STRUCT7 -6 -#define FFI_TYPE_SMALL_STRUCT8 -7 #endif diff --git a/src/pa/hpux32.S b/src/pa/hpux32.S index d0e5f695..1629c035 100644 --- a/src/pa/hpux32.S +++ b/src/pa/hpux32.S @@ -109,52 +109,104 @@ L$CFI13 /* Prepare to store the result; we need to recover flags and rvalue. */ ldw -48(%r3), %r21 ; r21 <- flags - ldw -52(%r3), %r20 ; r20 <- rvalue - /* Store the result according to the return type. The most - likely types should come first. */ + /* Adjust flags range from [-8, 15] to [0, 23]. */ + addi 8, %r21, %r21 -L$checkint - comib,<>,n FFI_TYPE_INT, %r21, L$checkint8 - b L$done - stw %ret0, 0(%r20) + blr %r21, %r0 + ldw -52(%r3), %r20 ; r20 <- rvalue -L$checkint8 - comib,<>,n FFI_TYPE_UINT8, %r21, L$checkint16 + /* Giant jump table */ + /* 8-byte small struct */ + b,n L$smst8 + nop + /* 7-byte small struct */ + b,n L$smst7 + nop + /* 6-byte small struct */ + b,n L$smst6 + nop + /* 5-byte small struct */ + b,n L$smst5 + nop + /* 4-byte small struct */ + b,n L$smst4 + nop + /* 3-byte small struct */ + b,n L$smst3 + nop + /* 2-byte small struct */ + b,n L$smst2 + nop + /* 1-byte small struct */ b L$done stb %ret0, 0(%r20) - -L$checkint16 - comib,<>,n FFI_TYPE_UINT16, %r21, L$checkdbl + /* void */ + b,n L$done + nop + /* int */ b L$done - sth %ret0, 0(%r20) - -L$checkdbl - comib,<>,n FFI_TYPE_DOUBLE, %r21, L$checkfloat + stw %ret0, 0(%r20) + /* float */ + b L$done + fstw %fr4L,0(%r20) + /* double */ b L$done fstd %fr4,0(%r20) - -L$checkfloat - comib,<>,n FFI_TYPE_FLOAT, %r21, L$checkll + /* long double */ + b,n L$done + nop + /* unsigned int8 */ b L$done - fstw %fr4L,0(%r20) + stw %ret0, 0(%r20) + /* signed int8 */ + b L$done + stw %ret0, 0(%r20) + /* unsigned int16 */ + b L$done + stw %ret0, 0(%r20) + /* signed int16 */ + b L$done + stw %ret0, 0(%r20) + /* unsigned int32 */ + b L$done + stw %ret0, 0(%r20) + /* signed int32 */ + b L$done + stw %ret0, 0(%r20) + /* unsigned int64 */ + b,n L$uint64 + nop + /* signed int64 */ + b,n L$sint64 + nop + /* large struct */ + b,n L$done + nop + /* pointer */ + b L$done + stw %ret0, 0(%r20) + /* complex */ + b,n L$done + nop + + /* Store the result according to the return type. The most + likely types should come first. */ -L$checkll - comib,<>,n FFI_TYPE_UINT64, %r21, L$checksmst2 +L$uint64 +L$sint64 stw %ret0, 0(%r20) b L$done stw %ret1, 4(%r20) -L$checksmst2 - comib,<>,n FFI_TYPE_SMALL_STRUCT2, %r21, L$checksmst3 +L$smst2 /* 2-byte structs are returned in ret0 as ????xxyy. */ extru %ret0, 23, 8, %r22 stbs,ma %r22, 1(%r20) b L$done stb %ret0, 0(%r20) -L$checksmst3 - comib,<>,n FFI_TYPE_SMALL_STRUCT3, %r21, L$checksmst4 +L$smst3 /* 3-byte structs are returned in ret0 as ??xxyyzz. */ extru %ret0, 15, 8, %r22 stbs,ma %r22, 1(%r20) @@ -163,8 +215,7 @@ L$checksmst3 b L$done stb %ret0, 0(%r20) -L$checksmst4 - comib,<>,n FFI_TYPE_SMALL_STRUCT4, %r21, L$checksmst5 +L$smst4 /* 4-byte structs are returned in ret0 as wwxxyyzz. */ extru %ret0, 7, 8, %r22 stbs,ma %r22, 1(%r20) @@ -175,8 +226,7 @@ L$checksmst4 b L$done stb %ret0, 0(%r20) -L$checksmst5 - comib,<>,n FFI_TYPE_SMALL_STRUCT5, %r21, L$checksmst6 +L$smst5 /* 5 byte values are returned right justified: ret0 ret1 5: ??????aa bbccddee */ @@ -190,8 +240,7 @@ L$checksmst5 b L$done stb %ret1, 0(%r20) -L$checksmst6 - comib,<>,n FFI_TYPE_SMALL_STRUCT6, %r21, L$checksmst7 +L$smst6 /* 6 byte values are returned right justified: ret0 ret1 6: ????aabb ccddeeff */ @@ -207,8 +256,7 @@ L$checksmst6 b L$done stb %ret1, 0(%r20) -L$checksmst7 - comib,<>,n FFI_TYPE_SMALL_STRUCT7, %r21, L$checksmst8 +L$smst7 /* 7 byte values are returned right justified: ret0 ret1 7: ??aabbcc ddeeffgg */ @@ -226,8 +274,7 @@ L$checksmst7 b L$done stb %ret1, 0(%r20) -L$checksmst8 - comib,<>,n FFI_TYPE_SMALL_STRUCT8, %r21, L$done +L$smst8 /* 8 byte values are returned right justified: ret0 ret1 8: aabbccdd eeffgghh */ diff --git a/src/pa/hpux64.S b/src/pa/hpux64.S new file mode 100644 index 00000000..6a82b57e --- /dev/null +++ b/src/pa/hpux64.S @@ -0,0 +1,681 @@ +/* ----------------------------------------------------------------------- + hpux64.S - (c) 2005-2022 John David Anglin + + HPUX PA 64-Bit 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 AUTHOR 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 +#include + + .LEVEL 2.0w + .text + .align 4 + + /* void ffi_call_pa64(void (*)(char *, extended_cif *), + extended_cif *ecif, + unsigned bytes, + unsigned flags, + unsigned *rvalue, + void (*fn)()); + */ + + .export ffi_call_pa64,code + .import ffi_prep_args_pa64,code + + .align 4 + +L$FB1 +ffi_call_pa64 + .proc + .callinfo FRAME=48,CALLS,SAVE_RP,ENTRY_GR=4 + .entry + std %rp, -16(%sp) + copy %r3, %r1 +L$CFI11 + copy %sp, %r3 +L$CFI12 + std,ma %r1, 48(%sp) + + /* Setup the stack for calling prep_args... + We want the stack to look like this: + + [ Previous stack ] <- %r3 + + [ 48-byte register save area ] + + [ Stack space for call arguments ] <- %r4 + + [ 16-byte rame marker ] + + [ 128-byte stack for calling prep_args ] <- %sp + */ + + std %r4, 8(%r3) ; save r4 +L$CFI13 + std %r23, 16(%r3) ; save flags we need it later + std %r22, 24(%r3) ; save rvalue + std %r21, 32(%r3) ; save fn pointer + + copy %sp, %r4 + copy %r4, %r26 ; argument stack pointer + addl %r24, %sp, %sp ; allocate argument space + + ldo 112(%sp), %r29 ; arg pointer for prep args + + /* Call prep_args: + %arg0(stack) -- set up above to point to call arguments + %arg1(ecif) -- same as incoming param + %arg2(bytes) -- same as incoming param */ + bl ffi_prep_args_pa64,%r2 + ldo 128(%sp), %sp + ldo -128(%sp), %sp + + /* Load the arguments that should be passed in registers + The fp args were loaded by the prep_args function. */ + ldd 0(%r4), %r26 + ldd 8(%r4), %r25 + ldd 16(%r4), %r24 + ldd 24(%r4), %r23 + ldd 32(%r4), %r22 + ldd 40(%r4), %r21 + ldd 48(%r4), %r20 + ldd 56(%r4), %r19 + + ldd 24(%r3), %ret0 ; %ret0 <- rvalue + + ldd 32(%r3), %r1 ; %r1 <- function pointer + ldd 16(%r1), %rp ; fn address + ldd 24(%r1), %dp ; New gp + bve,l (%rp), %r2 ; Call the user function + ldo 64(%r4), %r29 ; Argument pointer + + /* Prepare to store the result; recover flags and rvalue. */ + ldd 16(%r3), %r21 ; r21 <- flags + extrd,s %r21, 63, 32, %r21 ; sign extend flags for blr + + /* Adjust flags range from [-16, 15] to [0, 31]. */ + addi 16, %r21, %r21 + + blr %r21, %r0 + ldd 24(%r3), %r20 ; r20 <- rvalue + + /* Giant jump table */ + /* 16-byte small struct */ + b,n L$smst16 + nop + /* 15-byte small struct */ + b,n L$smst15 + nop + /* 14-byte small struct */ + b,n L$smst14 + nop + /* 13-byte small struct */ + b,n L$smst13 + nop + /* 12-byte small struct */ + b,n L$smst12 + nop + /* 11-byte small struct */ + b,n L$smst11 + nop + /* 10-byte small struct */ + b,n L$smst10 + nop + /* 9-byte small struct */ + b,n L$smst9 + nop + /* 8-byte small struct */ + b,n L$smst8 + nop + /* 7-byte small struct */ + b,n L$smst7 + nop + /* 6-byte small struct */ + b,n L$smst6 + nop + /* 5-byte small struct */ + b,n L$smst5 + nop + /* 4-byte small struct */ + b,n L$smst4 + nop + /* 3-byte small struct */ + b,n L$smst3 + nop + /* 2-byte small struct */ + b,n L$smst2 + nop + /* 1-byte small struct */ + b,n L$smst1 + nop + /* void */ + b,n L$done + nop + /* int */ + b L$done + std %ret0, 0(%r20) + /* float */ + b L$done + fstw %fr4R, 0(%r20) + /* double */ + b L$done + fstd %fr4, 0(%r20) + /* long double */ + b,n L$longdouble + nop + /* unsigned int8 */ + b L$done + std %ret0, 0(%r20) + /* signed int8 */ + b L$done + std %ret0, 0(%r20) + /* unsigned int16 */ + b L$done + std %ret0, 0(%r20) + /* signed int16 */ + b L$done + std %ret0, 0(%r20) + /* unsigned int32 */ + b L$done + std %ret0, 0(%r20) + /* signed int32 */ + b L$done + std %ret0, 0(%r20) + /* unsigned int64 */ + b L$done + std %ret0, 0(%r20) + /* signed int64 */ + b L$done + std %ret0, 0(%r20) + /* large struct */ + b,n L$done + nop + /* pointer */ + b L$done + std %ret0, 0(%r20) + /* complex */ + b,n L$done + nop + +L$longdouble + std %ret0, 0(%r20) + b L$done + std %ret1, 8(%r20) + + /* We need to copy byte-by-byte the exact number bytes + in the struct to avoid clobbering other data. */ +L$smst1 + extrd,u %ret0, 7, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst2 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst3 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst4 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst5 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst6 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst7 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst8 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + b L$done + stb %ret0, 0(%r20) + +L$smst9 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst10 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 15, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst11 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 23, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst12 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 31, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst13 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 39, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst14 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 47, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst15 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 55, 8, %r22 + b L$done + stb %r22, 0(%r20) + +L$smst16 + extrd,u %ret0, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret0, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb,ma %ret0, 1(%r20) + extrd,u %ret1, 7, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 15, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 23, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 31, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 39, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 47, 8, %r22 + stb,ma %r22, 1(%r20) + extrd,u %ret1, 55, 8, %r22 + stb,ma %r22, 1(%r20) + stb %ret1, 0(%r20) + +L$done + /* all done, restore registers and return */ + copy %r4, %sp + ldd 8(%r3), %r4 + ldd -16(%r3), %rp + bve (%rp) + ldd,mb -48(%sp), %r3 + .exit + .procend +L$FE1 + .size ffi_call_pa64, .-ffi_call_pa64 + + /* void ffi_closure_pa64(void); + Called with closure argument in %r21 */ + + .export ffi_closure_pa64,code + .import ffi_closure_inner_pa64,code + .align 4 +L$FB2 +ffi_closure_pa64 + .proc + .callinfo FRAME=128,CALLS,SAVE_RP,ENTRY_GR=3 + .entry + + std %rp, -16(%sp) + copy %r3, %r1 +L$CFI21 + copy %sp, %r3 +L$CFI22 + std,ma %r1, 128(%sp) +L$CFI23 + + /* Put arguments onto the stack and call ffi_closure_inner. */ + std %r26, -64(%r29) + std %r25, -56(%r29) + std %r24, -48(%r29) + std %r23, -40(%r29) + std %r22, -32(%r29) + std %r21, -24(%r29) + std %r20, -16(%r29) + std %r19, -8(%r29) + + /* Load and save start of argument stack. */ + ldo -64(%r29), %r25 + std %r25, 8(%r3) + + /* Setup arg pointer. */ + ldo -16(%sp), %ret1 + + /* Retrieve closure pointer and real gp. */ + copy %dp, %r26 + bl ffi_closure_inner_pa64, %r2 + ldd 0(%dp), %dp + + /* Retrieve start of argument stack. */ + ldd 8(%r3), %r1 + + /* Restore r3 and op stack. */ + ldd,mb -128(%sp), %r3 + + /* Load return address. */ + ldd -16(%sp), %rp + + /* Load return values from first and second stack slots. */ + ldd 0(%r1), %ret0 + bve (%rp) + ldd 8(%r1), %ret1 + + .exit + .procend + .end +L$FE2: + .size ffi_closure_pa64, .-ffi_closure_pa64 + + .section .eh_frame,"aw",@progbits +L$frame1: + .word L$ECIE1-L$SCIE1 ;# Length of Common Information Entry +L$SCIE1: + .word 0x0 ;# CIE Identifier Tag + .byte 0x3 ;# CIE Version + .stringz "" ;# CIE Augmentation + .uleb128 0x1 ;# CIE Code Alignment Factor + .sleb128 8 ;# CIE Data Alignment Factor + .byte 0x2 ;# CIE RA Column + .byte 0xc ;# DW_CFA_def_cfa + .uleb128 0x1e + .uleb128 0x0 + .align 8 +L$ECIE1: +L$SFDE1: + .word L$EFDE1-L$ASFDE1 ;# FDE Length +L$ASFDE1: + .word L$ASFDE1-L$frame1 ;# FDE CIE offset + .dword L$FB1 ;# FDE initial location + .dword L$FE1-L$FB1 ;# FDE address range + + .byte 0x4 ;# DW_CFA_advance_loc4 + .word L$CFI11-L$FB1 + .byte 0x9 ;# DW_CFA_register: r3 in r1 + .uleb128 0x3 + .uleb128 0x1 + .byte 0x11 ;# DW_CFA_offset_extended_sf: r2 at cfa-16 + .uleb128 0x2 + .sleb128 -2 + .byte 0x4 ;# DW_CFA_advance_loc4 + .word L$CFI12-L$CFI11 + .byte 0xd ;# DW_CFA_def_cfa_register: r3 + .uleb128 0x3 + + .byte 0x4 ;# DW_CFA_advance_loc4 + .word L$CFI13-L$CFI12 + .byte 0x83 ;# DW_CFA_offset: r3 at cfa+0 + .uleb128 0 + .byte 0x84 ;# DW_CFA_offset: r4 at cfa+8 + .uleb128 1 + + .align 8 +L$EFDE1: + +L$SFDE2: + .word L$EFDE2-L$ASFDE2 ;# FDE Length +L$ASFDE2: + .word L$ASFDE2-L$frame1 ;# FDE CIE offset + .dword L$FB2 ;# FDE initial location + .dword L$FE2-L$FB2 ;# FDE address range + .byte 0x4 ;# DW_CFA_advance_loc4 + .word L$CFI21-L$FB2 + .byte 0x9 ;# DW_CFA_register: r3 in r1 + .uleb128 0x3 + .uleb128 0x1 + .byte 0x11 ;# DW_CFA_offset_extended_sf: r2 at cfa-16 + .uleb128 0x2 + .sleb128 -2 + + .byte 0x4 ;# DW_CFA_advance_loc4 + .word L$CFI22-L$CFI21 + .byte 0xd ;# DW_CFA_def_cfa_register: r3 + .uleb128 0x3 + + .byte 0x4 ;# DW_CFA_advance_loc4 + .word L$CFI23-L$CFI22 + .byte 0x83 ;# DW_CFA_offset: r3 at cfa+0 + .uleb128 0 + + .align 8 +L$EFDE2: diff --git a/src/pa/linux.S b/src/pa/linux.S index 33ef0b13..2d3b0360 100644 --- a/src/pa/linux.S +++ b/src/pa/linux.S @@ -103,51 +103,103 @@ ffi_call_pa32: /* Prepare to store the result; we need to recover flags and rvalue. */ ldw -48(%r3), %r21 /* r21 <- flags */ - ldw -52(%r3), %r20 /* r20 <- rvalue */ - /* Store the result according to the return type. */ + /* Adjust flags range from [-8, 15] to [0, 23]. */ + addi 8, %r21, %r21 -.Lcheckint: - comib,<>,n FFI_TYPE_INT, %r21, .Lcheckint8 - b .Ldone - stw %ret0, 0(%r20) + blr %r21, %r0 + ldw -52(%r3), %r20 /* r20 <- rvalue */ -.Lcheckint8: - comib,<>,n FFI_TYPE_UINT8, %r21, .Lcheckint16 + /* Giant jump table */ + /* 8-byte small struct */ + b,n .Lsmst8 + nop + /* 7-byte small struct */ + b,n .Lsmst7 + nop + /* 6-byte small struct */ + b,n .Lsmst6 + nop + /* 5-byte small struct */ + b,n .Lsmst5 + nop + /* 4-byte small struct */ + b,n .Lsmst4 + nop + /* 3-byte small struct */ + b,n .Lsmst3 + nop + /* 2-byte small struct */ + b,n .Lsmst2 + nop + /* 1-byte small struct */ b .Ldone stb %ret0, 0(%r20) - -.Lcheckint16: - comib,<>,n FFI_TYPE_UINT16, %r21, .Lcheckdbl + /* void */ + b,n .Ldone + nop + /* int */ b .Ldone - sth %ret0, 0(%r20) - -.Lcheckdbl: - comib,<>,n FFI_TYPE_DOUBLE, %r21, .Lcheckfloat + stw %ret0, 0(%r20) + /* float */ + b .Ldone + fstw %fr4L,0(%r20) + /* double */ b .Ldone fstd %fr4,0(%r20) - -.Lcheckfloat: - comib,<>,n FFI_TYPE_FLOAT, %r21, .Lcheckll + /* long double */ b .Ldone - fstw %fr4L,0(%r20) + fstd %fr4,0(%r20) + /* unsigned int8 */ + b .Ldone + stw %ret0, 0(%r20) + /* sint8 */ + b .Ldone + stw %ret0, 0(%r20) + /* unsigned int16 */ + b .Ldone + stw %ret0, 0(%r20) + /* sint16 */ + b .Ldone + stw %ret0, 0(%r20) + /* unsigned int32 */ + b .Ldone + stw %ret0, 0(%r20) + /* sint32 */ + b .Ldone + stw %ret0, 0(%r20) + /* unsigned int64 */ + b,n .Luint64 + nop + /* signed int64 */ + b,n .Lsint64 + nop + /* large struct */ + b,n .Ldone + nop + /* pointer */ + b .Ldone + stw %ret0, 0(%r20) + /* complex */ + b,n .Ldone + nop + + /* Store the result according to the return type. */ -.Lcheckll: - comib,<>,n FFI_TYPE_UINT64, %r21, .Lchecksmst2 +.Luint64: +.Lsint64: stw %ret0, 0(%r20) b .Ldone stw %ret1, 4(%r20) -.Lchecksmst2: - comib,<>,n FFI_TYPE_SMALL_STRUCT2, %r21, .Lchecksmst3 +.Lsmst2: /* 2-byte structs are returned in ret0 as ????xxyy. */ extru %ret0, 23, 8, %r22 stbs,ma %r22, 1(%r20) b .Ldone stb %ret0, 0(%r20) -.Lchecksmst3: - comib,<>,n FFI_TYPE_SMALL_STRUCT3, %r21, .Lchecksmst4 +.Lsmst3: /* 3-byte structs are returned in ret0 as ??xxyyzz. */ extru %ret0, 15, 8, %r22 stbs,ma %r22, 1(%r20) @@ -156,8 +208,7 @@ ffi_call_pa32: b .Ldone stb %ret0, 0(%r20) -.Lchecksmst4: - comib,<>,n FFI_TYPE_SMALL_STRUCT4, %r21, .Lchecksmst5 +.Lsmst4: /* 4-byte structs are returned in ret0 as wwxxyyzz. */ extru %ret0, 7, 8, %r22 stbs,ma %r22, 1(%r20) @@ -168,8 +219,7 @@ ffi_call_pa32: b .Ldone stb %ret0, 0(%r20) -.Lchecksmst5: - comib,<>,n FFI_TYPE_SMALL_STRUCT5, %r21, .Lchecksmst6 +.Lsmst5: /* 5 byte values are returned right justified: ret0 ret1 5: ??????aa bbccddee */ @@ -183,8 +233,7 @@ ffi_call_pa32: b .Ldone stb %ret1, 0(%r20) -.Lchecksmst6: - comib,<>,n FFI_TYPE_SMALL_STRUCT6, %r21, .Lchecksmst7 +.Lsmst6: /* 6 byte values are returned right justified: ret0 ret1 6: ????aabb ccddeeff */ @@ -200,8 +249,7 @@ ffi_call_pa32: b .Ldone stb %ret1, 0(%r20) -.Lchecksmst7: - comib,<>,n FFI_TYPE_SMALL_STRUCT7, %r21, .Lchecksmst8 +.Lsmst7: /* 7 byte values are returned right justified: ret0 ret1 7: ??aabbcc ddeeffgg */ @@ -219,8 +267,7 @@ ffi_call_pa32: b .Ldone stb %ret1, 0(%r20) -.Lchecksmst8: - comib,<>,n FFI_TYPE_SMALL_STRUCT8, %r21, .Ldone +.Lsmst8: /* 8 byte values are returned right justified: ret0 ret1 8: aabbccdd eeffgghh */ diff --git a/src/prep_cif.c b/src/prep_cif.c index 2d0f2521..0e2d58e5 100644 --- a/src/prep_cif.c +++ b/src/prep_cif.c @@ -234,7 +234,7 @@ ffi_status ffi_prep_cif_var(ffi_cif *cif, { ffi_status rc; size_t int_size = ffi_type_sint.size; - int i; + unsigned int i; rc = ffi_prep_cif_core(cif, abi, 1, nfixedargs, ntotalargs, rtype, atypes); diff --git a/src/tramp.c b/src/tramp.c index b9d273a1..5f19b557 100644 --- a/src/tramp.c +++ b/src/tramp.c @@ -39,6 +39,10 @@ #ifdef __linux__ #define _GNU_SOURCE 1 #endif + +#include +#include + #include #include #include @@ -266,7 +270,7 @@ ffi_tramp_get_temp_file (void) * trampoline table to make sure that the temporary file can be mapped. */ count = write(tramp_globals.fd, tramp_globals.text, tramp_globals.map_size); - if (count == tramp_globals.map_size && tramp_table_alloc ()) + if (count >=0 && (size_t)count == tramp_globals.map_size && tramp_table_alloc ()) return 1; close (tramp_globals.fd); @@ -374,6 +378,8 @@ tramp_table_unmap (struct tramp_table *table) static int ffi_tramp_init (void) { + long page_size; + if (tramp_globals.status == TRAMP_GLOBALS_PASSED) return 1; @@ -396,7 +402,8 @@ ffi_tramp_init (void) &tramp_globals.map_size); tramp_globals.ntramp = tramp_globals.map_size / tramp_globals.size; - if (sysconf (_SC_PAGESIZE) > tramp_globals.map_size) + page_size = sysconf (_SC_PAGESIZE); + if (page_size >= 0 && (size_t)page_size > tramp_globals.map_size) return 0; if (ffi_tramp_init_os ()) diff --git a/src/wasm32/ffi.c b/src/wasm32/ffi.c new file mode 100644 index 00000000..399a06e3 --- /dev/null +++ b/src/wasm32/ffi.c @@ -0,0 +1,934 @@ +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) 2018-2023 Hood Chatham, Brion Vibber, Kleis Auke Wolthuizen, and others. + + wasm32/emscripten 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. + ----------------------------------------------------------------------- */ + +#include +#include + +#include +#include + +#include + +#ifdef DEBUG_F +#define LOG_DEBUG(args...) \ + console.warn(`====LIBFFI(line __LINE__)`, args) +#else +#define LOG_DEBUG(args...) 0 +#endif + +#define EM_JS_MACROS(ret, name, args, body...) EM_JS(ret, name, args, body) + +#if WASM_BIGINT +EM_JS_DEPS(libffi, "$getWasmTableEntry,$setWasmTableEntry,$getEmptyTableSlot,$convertJsFunctionToWasm"); +#define CALL_FUNCTION_POINTER(ptr, args...) \ + (LOG_DEBUG("CALL_FUNC_PTR", ptr, args), \ + getWasmTableEntry(ptr).apply(null, args)) + +#define JS_FUNCTION_TO_WASM convertJsFunctionToWasm +#else +EM_JS_DEPS(libffi, "$getWasmTableEntry,$setWasmTableEntry,$getEmptyTableSlot,$convertJsFunctionToWasm,$dynCall,$generateFuncType,$uleb128Encode"); +#define CALL_FUNCTION_POINTER(ptr, args...) \ + (LOG_DEBUG("CALL_FUNC_PTR", sig, ptr, args), \ + dynCall(sig, ptr, args)) + +#define JS_FUNCTION_TO_WASM createLegalizerWrapper +#endif + +// Signature calculations are not needed if WASM_BIGINT is present. +#if WASM_BIGINT +#define SIG(sig) +#else +#define SIG(sig) sig +#endif + +#define DEREF_U8(addr, offset) HEAPU8[addr + offset] +#define DEREF_S8(addr, offset) HEAP8[addr + offset] +#define DEREF_U16(addr, offset) HEAPU16[(addr >> 1) + offset] +#define DEREF_S16(addr, offset) HEAP16[(addr >> 1) + offset] +#define DEREF_U32(addr, offset) HEAPU32[(addr >> 2) + offset] +#define DEREF_S32(addr, offset) HEAP32[(addr >> 2) + offset] + +#define DEREF_F32(addr, offset) HEAPF32[(addr >> 2) + offset] +#define DEREF_F64(addr, offset) HEAPF64[(addr >> 3) + offset] + +#if WASM_BIGINT +// We have HEAPU64 in this case. +#define DEREF_U64(addr, offset) HEAPU64[(addr >> 3) + offset] +#endif + + +#define CHECK_FIELD_OFFSET(struct, field, offset) \ + _Static_assert( \ + offsetof(struct, field) == offset, \ + "Memory layout of '" #struct "' has changed: '" #field "' is in an unexpected location"); + +CHECK_FIELD_OFFSET(ffi_cif, abi, 4*0); +CHECK_FIELD_OFFSET(ffi_cif, nargs, 4*1); +CHECK_FIELD_OFFSET(ffi_cif, arg_types, 4*2); +CHECK_FIELD_OFFSET(ffi_cif, rtype, 4*3); +CHECK_FIELD_OFFSET(ffi_cif, nfixedargs, 4*6); + +#define CIF__ABI(addr) DEREF_U32(addr, 0) +#define CIF__NARGS(addr) DEREF_U32(addr, 1) +#define CIF__ARGTYPES(addr) DEREF_U32(addr, 2) +#define CIF__RTYPE(addr) DEREF_U32(addr, 3) +#define CIF__NFIXEDARGS(addr) DEREF_U32(addr, 6) + +CHECK_FIELD_OFFSET(ffi_type, size, 0); +CHECK_FIELD_OFFSET(ffi_type, alignment, 4); +CHECK_FIELD_OFFSET(ffi_type, type, 6); +CHECK_FIELD_OFFSET(ffi_type, elements, 8); + +#define FFI_TYPE__SIZE(addr) DEREF_U32(addr, 0) +#define FFI_TYPE__ALIGN(addr) DEREF_U16(addr + 4, 0) +#define FFI_TYPE__TYPEID(addr) DEREF_U16(addr + 6, 0) +#define FFI_TYPE__ELEMENTS(addr) DEREF_U32(addr + 8, 0) + +#define ALIGN_ADDRESS(addr, align) (addr &= (~((align) - 1))) +#define STACK_ALLOC(stack, size, align) ((stack -= (size)), ALIGN_ADDRESS(stack, align)) + +// Most wasm runtimes support at most 1000 Js trampoline args. +#define MAX_ARGS 1000 + +#include + +#define VARARGS_FLAG 1 + +#define FFI_OK_MACRO 0 +_Static_assert(FFI_OK_MACRO == FFI_OK, "FFI_OK must be 0"); + +#define FFI_BAD_TYPEDEF_MACRO 1 +_Static_assert(FFI_BAD_TYPEDEF_MACRO == FFI_BAD_TYPEDEF, "FFI_BAD_TYPEDEF must be 1"); + +ffi_status FFI_HIDDEN +ffi_prep_cif_machdep(ffi_cif *cif) +{ + if (cif->abi != FFI_WASM32_EMSCRIPTEN) + return FFI_BAD_ABI; + // This is called after ffi_prep_cif_machdep_var so we need to avoid + // overwriting cif->nfixedargs. + if (!(cif->flags & VARARGS_FLAG)) + cif->nfixedargs = cif->nargs; + if (cif->nargs > MAX_ARGS) + return FFI_BAD_TYPEDEF; + if (cif->rtype->type == FFI_TYPE_COMPLEX) + return FFI_BAD_TYPEDEF; + // If they put the COMPLEX type into a struct we won't notice, but whatever. + for (int i = 0; i < cif->nargs; i++) + if (cif->arg_types[i]->type == FFI_TYPE_COMPLEX) + return FFI_BAD_TYPEDEF; + return FFI_OK; +} + +ffi_status FFI_HIDDEN +ffi_prep_cif_machdep_var(ffi_cif *cif, unsigned nfixedargs, unsigned ntotalargs) +{ + cif->flags |= VARARGS_FLAG; + cif->nfixedargs = nfixedargs; + // The varargs takes up one extra argument + if (cif->nfixedargs + 1 > MAX_ARGS) + return FFI_BAD_TYPEDEF; + return FFI_OK; +} + +/** + * A Javascript helper function. This takes an argument typ which is a wasm + * pointer to an ffi_type object. It returns a pair a type and a type id. + * + * - If it is not a struct, return its type and its typeid field. + * - If it is a struct of size >= 2, return the type and its typeid (which + * will be FFI_TYPE_STRUCT) + * - If it is a struct of size 0, return FFI_TYPE_VOID (????? this is broken) + * - If it is a struct of size 1, replace it with the single field and apply + * the same logic again to that. + * + * By always unboxing structs up front, we can avoid messy casework later. + */ +EM_JS_MACROS( +void, +unbox_small_structs, (ffi_type type_ptr), { + var type_id = FFI_TYPE__TYPEID(type_ptr); + while (type_id === FFI_TYPE_STRUCT) { + var elements = FFI_TYPE__ELEMENTS(type_ptr); + var first_element = DEREF_U32(elements, 0); + if (first_element === 0) { + type_id = FFI_TYPE_VOID; + break; + } else if (DEREF_U32(elements, 1) === 0) { + type_ptr = first_element; + type_id = FFI_TYPE__TYPEID(first_element); + } else { + break; + } + } + return [type_ptr, type_id]; +}) + +EM_JS_MACROS( +void, +ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), +{ + var abi = CIF__ABI(cif); + var nargs = CIF__NARGS(cif); + var nfixedargs = CIF__NFIXEDARGS(cif); + var arg_types_ptr = CIF__ARGTYPES(cif); + var rtype_unboxed = unbox_small_structs(CIF__RTYPE(cif)); + var rtype_ptr = rtype_unboxed[0]; + var rtype_id = rtype_unboxed[1]; + var orig_stack_ptr = stackSave(); + var cur_stack_ptr = orig_stack_ptr; + + var args = []; + // Does our onwards call return by argument or normally? We return by argument + // no matter what. + var ret_by_arg = false; + + if (rtype_id === FFI_TYPE_COMPLEX) { + throw new Error('complex ret marshalling nyi'); + } + if (rtype_id < 0 || rtype_id > FFI_TYPE_LAST) { + throw new Error('Unexpected rtype ' + rtype_id); + } + // If the return type is a struct with multiple entries or a long double, the + // function takes an extra first argument which is a pointer to return value. + // Conveniently, we've already received a pointer to return value, so we can + // just use this. We also mark a flag that we don't need to convert the return + // value of the dynamic call back to C. + if (rtype_id === FFI_TYPE_LONGDOUBLE || rtype_id === FFI_TYPE_STRUCT) { + args.push(rvalue); + ret_by_arg = true; + } + + SIG(var sig = ""); + +#if !WASM_BIGINT + switch(rtype_id) { + case FFI_TYPE_VOID: + SIG(sig = 'v'); + break; + case FFI_TYPE_STRUCT: + case FFI_TYPE_LONGDOUBLE: + SIG(sig = 'vi'); + break; + case FFI_TYPE_INT: + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: + case FFI_TYPE_POINTER: + SIG(sig = 'i'); + break; + case FFI_TYPE_FLOAT: + SIG(sig = 'f'); + break; + case FFI_TYPE_DOUBLE: + SIG(sig = 'd'); + break; + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + SIG(sig = 'j'); + break; + } +#endif + + // Accumulate a Javascript list of arguments for the Javascript wrapper for + // the wasm function. The Javascript wrapper does a type conversion from + // Javascript to C automatically, here we manually do the inverse conversion + // from C to Javascript. + for (var i = 0; i < nfixedargs; i++) { + var arg_ptr = DEREF_U32(avalue, i); + var arg_unboxed = unbox_small_structs(DEREF_U32(arg_types_ptr, i)); + var arg_type_ptr = arg_unboxed[0]; + var arg_type_id = arg_unboxed[1]; + + // It's okay here to always use unsigned integers as long as the size is 32 + // or 64 bits. Smaller sizes get extended to 32 bits differently according + // to whether they are signed or unsigned. + switch (arg_type_id) { + case FFI_TYPE_INT: + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: + case FFI_TYPE_POINTER: + args.push(DEREF_U32(arg_ptr, 0)); + SIG(sig += 'i'); + break; + case FFI_TYPE_FLOAT: + args.push(DEREF_F32(arg_ptr, 0)); + SIG(sig += 'f'); + break; + case FFI_TYPE_DOUBLE: + args.push(DEREF_F64(arg_ptr, 0)); + SIG(sig += 'd'); + break; + case FFI_TYPE_UINT8: + args.push(DEREF_U8(arg_ptr, 0)); + SIG(sig += 'i'); + break; + case FFI_TYPE_SINT8: + args.push(DEREF_S8(arg_ptr, 0)); + SIG(sig += 'i'); + break; + case FFI_TYPE_UINT16: + args.push(DEREF_U16(arg_ptr, 0)); + SIG(sig += 'i'); + break; + case FFI_TYPE_SINT16: + args.push(DEREF_S16(arg_ptr, 0)); + SIG(sig += 'i'); + break; + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + #if WASM_BIGINT + args.push(DEREF_U64(arg_ptr, 0)); + #else + args.push(DEREF_U32(arg_ptr, 0)); + args.push(DEREF_U32(arg_ptr, 1)); + #endif + SIG(sig += 'j'); + break; + case FFI_TYPE_LONGDOUBLE: + // long double is passed as a pair of BigInts. + #if WASM_BIGINT + args.push(DEREF_U64(arg_ptr, 0)); + args.push(DEREF_U64(arg_ptr, 1)); + #else + args.push(DEREF_U32(arg_ptr, 0)); + args.push(DEREF_U32(arg_ptr, 1)); + args.push(DEREF_U32(arg_ptr, 2)); + args.push(DEREF_U32(arg_ptr, 3)); + #endif + SIG(sig += "jj"); + break; + case FFI_TYPE_STRUCT: + // Nontrivial structs are passed by pointer. + // Have to copy the struct onto the stack though because C ABI says it's + // call by value. + var size = FFI_TYPE__SIZE(arg_type_ptr); + var align = FFI_TYPE__ALIGN(arg_type_ptr); + STACK_ALLOC(cur_stack_ptr, size, align); + HEAP8.subarray(cur_stack_ptr, cur_stack_ptr+size).set(HEAP8.subarray(arg_ptr, arg_ptr + size)); + args.push(cur_stack_ptr); + SIG(sig += 'i'); + break; + case FFI_TYPE_COMPLEX: + throw new Error('complex marshalling nyi'); + default: + throw new Error('Unexpected type ' + arg_type_id); + } + } + + // Wasm functions can't directly manipulate the callstack, so varargs + // arguments have to go on a separate stack. A varags function takes one extra + // argument which is a pointer to where on the separate stack the args are + // located. Because stacks are allocated backwards, we have to loop over the + // varargs backwards. + // + // We don't have any way of knowing how many args were actually passed, so we + // just always copy extra nonsense past the end. The ownwards call will know + // not to look at it. + if (nfixedargs != nargs) { + SIG(sig += 'i'); + var struct_arg_info = []; + for (var i = nargs - 1; i >= nfixedargs; i--) { + var arg_ptr = DEREF_U32(avalue, i); + var arg_unboxed = unbox_small_structs(DEREF_U32(arg_types_ptr, i)); + var arg_type_ptr = arg_unboxed[0]; + var arg_type_id = arg_unboxed[1]; + switch (arg_type_id) { + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT8: + STACK_ALLOC(cur_stack_ptr, 1, 1); + DEREF_U8(cur_stack_ptr, 0) = DEREF_U8(arg_ptr, 0); + break; + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT16: + STACK_ALLOC(cur_stack_ptr, 2, 2); + DEREF_U16(cur_stack_ptr, 0) = DEREF_U16(arg_ptr, 0); + break; + case FFI_TYPE_INT: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: + case FFI_TYPE_POINTER: + case FFI_TYPE_FLOAT: + STACK_ALLOC(cur_stack_ptr, 4, 4); + DEREF_U32(cur_stack_ptr, 0) = DEREF_U32(arg_ptr, 0); + break; + case FFI_TYPE_DOUBLE: + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + STACK_ALLOC(cur_stack_ptr, 8, 8); + DEREF_U32(cur_stack_ptr, 0) = DEREF_U32(arg_ptr, 0); + DEREF_U32(cur_stack_ptr, 1) = DEREF_U32(arg_ptr, 1); + break; + case FFI_TYPE_LONGDOUBLE: + STACK_ALLOC(cur_stack_ptr, 16, 8); + DEREF_U32(cur_stack_ptr, 0) = DEREF_U32(arg_ptr, 0); + DEREF_U32(cur_stack_ptr, 1) = DEREF_U32(arg_ptr, 1); + DEREF_U32(cur_stack_ptr, 2) = DEREF_U32(arg_ptr, 2); + DEREF_U32(cur_stack_ptr, 3) = DEREF_U32(arg_ptr, 3); + break; + case FFI_TYPE_STRUCT: + // Again, struct must be passed by pointer. + // But ABI is by value, so have to copy struct onto stack. + // Currently arguments are going onto stack so we can't put it there now. Come back for this. + STACK_ALLOC(cur_stack_ptr, 4, 4); + struct_arg_info.push([cur_stack_ptr, arg_ptr, FFI_TYPE__SIZE(arg_type_ptr), FFI_TYPE__ALIGN(arg_type_ptr)]); + break; + case FFI_TYPE_COMPLEX: + throw new Error('complex arg marshalling nyi'); + default: + throw new Error('Unexpected argtype ' + arg_type_id); + } + } + // extra normal argument which is the pointer to the varargs. + args.push(cur_stack_ptr); + // Now allocate variable struct args on stack too. + for (var i = 0; i < struct_arg_info.length; i++) { + var struct_info = struct_arg_info[i]; + var arg_target = struct_info[0]; + var arg_ptr = struct_info[1]; + var size = struct_info[2]; + var align = struct_info[3]; + STACK_ALLOC(cur_stack_ptr, size, align); + HEAP8.subarray(cur_stack_ptr, cur_stack_ptr+size).set(HEAP8.subarray(arg_ptr, arg_ptr + size)); + DEREF_U32(arg_target, 0) = cur_stack_ptr; + } + } + stackRestore(cur_stack_ptr); + stackAlloc(0); // stackAlloc enforces alignment invariants on the stack pointer + var result = CALL_FUNCTION_POINTER(fn, args); + // Put the stack pointer back (we moved it if there were any struct args or we + // made a varargs call) + stackRestore(orig_stack_ptr); + + // We need to return by argument. If return value was a nontrivial struct or + // long double, the onwards call already put the return value in rvalue + if (ret_by_arg) { + return; + } + + // Otherwise the result was automatically converted from C into Javascript and + // we need to manually convert it back to C. + switch (rtype_id) { + case FFI_TYPE_VOID: + break; + case FFI_TYPE_INT: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: + case FFI_TYPE_POINTER: + DEREF_U32(rvalue, 0) = result; + break; + case FFI_TYPE_FLOAT: + DEREF_F32(rvalue, 0) = result; + break; + case FFI_TYPE_DOUBLE: + DEREF_F64(rvalue, 0) = result; + break; + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT8: + DEREF_U8(rvalue, 0) = result; + break; + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT16: + DEREF_U16(rvalue, 0) = result; + break; + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + #if WASM_BIGINT + DEREF_U64(rvalue, 0) = result; + #else + DEREF_U32(rvalue, 0) = result; + DEREF_U32(rvalue, 1) = getTempRet0(); + #endif + break; + case FFI_TYPE_COMPLEX: + throw new Error('complex ret marshalling nyi'); + default: + throw new Error('Unexpected rtype ' + rtype_id); + } +}); + +void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) { + ffi_call_js(cif, fn, rvalue, avalue); +} + +CHECK_FIELD_OFFSET(ffi_closure, ftramp, 4*0); +CHECK_FIELD_OFFSET(ffi_closure, cif, 4*1); +CHECK_FIELD_OFFSET(ffi_closure, fun, 4*2); +CHECK_FIELD_OFFSET(ffi_closure, user_data, 4*3); + +#define CLOSURE__wrapper(addr) DEREF_U32(addr, 0) +#define CLOSURE__cif(addr) DEREF_U32(addr, 1) +#define CLOSURE__fun(addr) DEREF_U32(addr, 2) +#define CLOSURE__user_data(addr) DEREF_U32(addr, 3) + +EM_JS_MACROS(void *, ffi_closure_alloc_js, (size_t size, void **code), { + var closure = _malloc(size); + var index = getEmptyTableSlot(); + DEREF_U32(code, 0) = index; + CLOSURE__wrapper(closure) = index; + return closure; +}) + +void * __attribute__ ((visibility ("default"))) +ffi_closure_alloc(size_t size, void **code) { + return ffi_closure_alloc_js(size, code); +} + +EM_JS_MACROS(void, ffi_closure_free_js, (void *closure), { + var index = CLOSURE__wrapper(closure); + freeTableIndexes.push(index); + _free(closure); +}) + +void __attribute__ ((visibility ("default"))) +ffi_closure_free(void *closure) { + return ffi_closure_free_js(closure); +} + +#if !WASM_BIGINT + +// When !WASM_BIGINT, we assume there is no JS bigint integration, so JavaScript +// functions cannot take 64 bit integer arguments. +// +// We need to make our own wasm legalizer adaptor that splits 64 bit integer +// arguments and then calls the JavaScript trampoline, then the JavaScript +// trampoline reassembles them, calls the closure, then splits the result (if +// it's a 64 bit integer) and the adaptor puts it back together. +// +// This is basically the reverse of the Emscripten function +// createDyncallWrapper. +EM_JS(void, createLegalizerWrapper, (int trampoline, int sig), { + if(!sig.includes("j")) { + return convertJsFunctionToWasm(trampoline, sig); + } + var sections = []; + var prelude = [ + 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") + 0x01, 0x00, 0x00, 0x00, // version: 1 + ]; + sections.push(prelude); + var wrappersig = [ + // if return type is j, we will put the upper 32 bits into tempRet0. + sig[0].replace("j", "i"), + // in the rest of the argument list, one 64 bit integer is legalized into + // two 32 bit integers. + sig.slice(1).replace(/j/g, "ii"), + ].join(""); + + var typeSectionBody = [ + 0x03, // number of types = 3 + ]; + generateFuncType(wrappersig, typeSectionBody); // The signature of the wrapper we are generating + generateFuncType(sig, typeSectionBody); // the signature of the function pointer we will call + generateFuncType("i", typeSectionBody); // the signature of getTempRet0 + + var typeSection = [0x01 /* Type section code */]; + uleb128Encode(typeSectionBody.length, typeSection); // length of section in bytes + typeSection.push.apply(typeSection, typeSectionBody); + sections.push(typeSection); + + var importSection = [ + 0x02, // import section code + 0x0d, // length of section in bytes + 0x02, // number of imports = 2 + // Import the getTempRet0 function, which we will call "r" + 0x01, 0x65, // name "e" + 0x01, 0x72, // name "r" + 0x00, // importing a function + 0x02, // type 2 = () -> i32 + // Import the wrapped function, which we will call "f" + 0x01, 0x65, // name "e" + 0x01, 0x66, // name "f" + 0x00, // importing a function + 0x00, // type 0 = wrappersig + ]; + sections.push(importSection); + + var functionSection = [ + 0x03, // function section code + 0x02, // length of section in bytes + 0x01, // number of functions = 1 + 0x01, // type 1 = sig + ]; + sections.push(functionSection); + + var exportSection = [ + 0x07, // export section code + 0x05, // length of section in bytes + 0x01, // One export + 0x01, 0x66, // name "f" + 0x00, // type: function + 0x02, // function index 2 = the wrapper function + ]; + sections.push(exportSection); + + var convert_code = []; + convert_code.push(0x00); // no local variables (except the arguments) + + function localGet(j) { + convert_code.push(0x20); // local.get + uleb128Encode(j, convert_code); + } + + for (var i = 1; i < sig.length; i++) { + if (sig[i] == "j") { + localGet(i - 1); + convert_code.push( + 0xa7 // i32.wrap_i64 + ); + localGet(i - 1); + convert_code.push( + 0x42, 0x20, // i64.const 32 + 0x88, // i64.shr_u + 0xa7 // i32.wrap_i64 + ); + } else { + localGet(i - 1); + } + } + convert_code.push( + 0x10, 0x01 // call f + ); + if (sig[0] === "j") { + // Need to reassemble a 64 bit integer. Lower 32 bits is on stack. Upper 32 + // bits we get from getTempRet0 + convert_code.push( + 0xad, // i64.extend_i32_unsigned + 0x10, 0x00, // Call function 0 (r = getTempRet0) + // join lower 32 bits and upper 32 bits + 0xac, // i64.extend_i32_signed + 0x42, 0x20, // i64.const 32 + 0x86, // i64.shl, + 0x84 // i64.or + ); + } + convert_code.push(0x0b); // end + + var codeBody = [0x01]; // one code + uleb128Encode(convert_code.length, codeBody); + codeBody.push.apply(codeBody, convert_code); + var codeSection = [0x0a /* Code section code */]; + uleb128Encode(codeBody.length, codeSection); + codeSection.push.apply(codeSection, codeBody); + sections.push(codeSection); + + var bytes = new Uint8Array([].concat.apply([], sections)); + // We can compile this wasm module synchronously because it is small. + var module = new WebAssembly.Module(bytes); + var instance = new WebAssembly.Instance(module, { + e: { + r: getTempRet0, + f: trampoline, + }, + }); + return instance.exports.f; +}); +#endif + +EM_JS_MACROS( +ffi_status, +ffi_prep_closure_loc_js, +(ffi_closure *closure, ffi_cif *cif, void *fun, void *user_data, void *codeloc), +{ + var abi = CIF__ABI(cif); + var nargs = CIF__NARGS(cif); + var nfixedargs = CIF__NFIXEDARGS(cif); + var arg_types_ptr = CIF__ARGTYPES(cif); + var rtype_unboxed = unbox_small_structs(CIF__RTYPE(cif)); + var rtype_ptr = rtype_unboxed[0]; + var rtype_id = rtype_unboxed[1]; + + // First construct the signature of the javascript trampoline we are going to create. + // Important: this is the signature for calling us, the onward call always has sig viiii. + var sig; + var ret_by_arg = false; + switch (rtype_id) { + case FFI_TYPE_VOID: + sig = 'v'; + break; + case FFI_TYPE_STRUCT: + case FFI_TYPE_LONGDOUBLE: + // Return via a first pointer argument. + sig = 'vi'; + ret_by_arg = true; + break; + case FFI_TYPE_INT: + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: + case FFI_TYPE_POINTER: + sig = 'i'; + break; + case FFI_TYPE_FLOAT: + sig = 'f'; + break; + case FFI_TYPE_DOUBLE: + sig = 'd'; + break; + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + sig = 'j'; + break; + case FFI_TYPE_COMPLEX: + throw new Error('complex ret marshalling nyi'); + default: + throw new Error('Unexpected rtype ' + rtype_id); + } + var unboxed_arg_type_id_list = []; + var unboxed_arg_type_info_list = []; + for (var i = 0; i < nargs; i++) { + var arg_unboxed = unbox_small_structs(DEREF_U32(arg_types_ptr, i)); + var arg_type_ptr = arg_unboxed[0]; + var arg_type_id = arg_unboxed[1]; + unboxed_arg_type_id_list.push(arg_type_id); + unboxed_arg_type_info_list.push([FFI_TYPE__SIZE(arg_type_ptr), FFI_TYPE__ALIGN(arg_type_ptr)]); + } + for (var i = 0; i < nfixedargs; i++) { + switch (unboxed_arg_type_id_list[i]) { + case FFI_TYPE_INT: + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: + case FFI_TYPE_POINTER: + case FFI_TYPE_STRUCT: + sig += 'i'; + break; + case FFI_TYPE_FLOAT: + sig += 'f'; + break; + case FFI_TYPE_DOUBLE: + sig += 'd'; + break; + case FFI_TYPE_LONGDOUBLE: + sig += 'jj'; + break; + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + sig += 'j'; + break; + case FFI_TYPE_COMPLEX: + throw new Error('complex marshalling nyi'); + default: + throw new Error('Unexpected argtype ' + arg_type_id); + } + } + if (nfixedargs < nargs) { + // extra pointer to varargs stack + sig += "i"; + } + LOG_DEBUG("CREATE_CLOSURE", "sig:", sig); + function trampoline() { + var args = Array.prototype.slice.call(arguments); + var size = 0; + var orig_stack_ptr = stackSave(); + var cur_ptr = orig_stack_ptr; + var ret_ptr; + var jsarg_idx = 0; + // Should we return by argument or not? The onwards call returns by argument + // no matter what. (Warning: ret_by_arg means the opposite in ffi_call) + if (ret_by_arg) { + ret_ptr = args[jsarg_idx++]; + } else { + // We might return 4 bytes or 8 bytes, allocate 8 just in case. + STACK_ALLOC(cur_ptr, 8, 8); + ret_ptr = cur_ptr; + } + cur_ptr -= 4 * nargs; + var args_ptr = cur_ptr; + var carg_idx = 0; + // Here we either have the actual argument, or a pair of BigInts for long + // double, or a pointer to struct. We have to store into args_ptr[i] a + // pointer to the ith argument. If the argument is a struct, just store the + // pointer. Otherwise allocate stack space and copy the js argument onto the + // stack. + for (; carg_idx < nfixedargs; carg_idx++) { + // jsarg_idx might start out as 0 or 1 depending on ret_by_arg + // it advances an extra time for long double + var cur_arg = args[jsarg_idx++]; + var arg_type_info = unboxed_arg_type_info_list[carg_idx]; + var arg_size = arg_type_info[0]; + var arg_align = arg_type_info[1]; + var arg_type_id = unboxed_arg_type_id_list[carg_idx]; + switch (arg_type_id) { + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT8: + // Bad things happen if we don't align to 4 here + STACK_ALLOC(cur_ptr, 1, 4); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_U8(cur_ptr, 0) = cur_arg; + break; + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT16: + // Bad things happen if we don't align to 4 here + STACK_ALLOC(cur_ptr, 2, 4); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_U16(cur_ptr, 0) = cur_arg; + break; + case FFI_TYPE_INT: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT32: + case FFI_TYPE_POINTER: + STACK_ALLOC(cur_ptr, 4, 4); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_U32(cur_ptr, 0) = cur_arg; + break; + case FFI_TYPE_STRUCT: + // cur_arg is already a pointer to struct + // copy it onto stack to pass by value + STACK_ALLOC(cur_ptr, arg_size, arg_align); + HEAP8.subarray(cur_ptr, cur_ptr + arg_size).set(HEAP8.subarray(cur_arg, cur_arg + arg_size)); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + break; + case FFI_TYPE_FLOAT: + STACK_ALLOC(cur_ptr, 4, 4); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_F32(cur_ptr, 0) = cur_arg; + break; + case FFI_TYPE_DOUBLE: + STACK_ALLOC(cur_ptr, 8, 8); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + DEREF_F64(cur_ptr, 0) = cur_arg; + break; + case FFI_TYPE_UINT64: + case FFI_TYPE_SINT64: + STACK_ALLOC(cur_ptr, 8, 8); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + #if WASM_BIGINT + DEREF_U64(cur_ptr, 0) = cur_arg; + #else + // Bigint arg was split by legalizer adaptor + DEREF_U32(cur_ptr, 0) = cur_arg; + cur_arg = args[jsarg_idx++]; + DEREF_U32(cur_ptr, 1) = cur_arg; + #endif + break; + case FFI_TYPE_LONGDOUBLE: + STACK_ALLOC(cur_ptr, 16, 8); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + #if WASM_BIGINT + DEREF_U64(cur_ptr, 0) = cur_arg; + cur_arg = args[jsarg_idx++]; + DEREF_U64(cur_ptr, 1) = cur_arg; + #else + // Was split by legalizer adaptor + DEREF_U32(cur_ptr, 0) = cur_arg; + cur_arg = args[jsarg_idx++]; + DEREF_U32(cur_ptr, 1) = cur_arg; + cur_arg = args[jsarg_idx++]; + DEREF_U32(cur_ptr, 2) = cur_arg; + cur_arg = args[jsarg_idx++]; + DEREF_U32(cur_ptr, 3) = cur_arg; + #endif + break; + } + } + // If its a varargs call, last js argument is a pointer to the varargs. + var varargs = args[args.length - 1]; + // We have no way of knowing how many varargs were actually provided, this + // fills the rest of the stack space allocated with nonsense. The onward + // call will know to ignore the nonsense. + + // We either have a pointer to the argument if the argument is not a struct + // or a pointer to pointer to struct. We need to store a pointer to the + // argument into args_ptr[i] + for (; carg_idx < nargs; carg_idx++) { + var arg_type_id = unboxed_arg_type_id_list[carg_idx]; + var arg_type_info = unboxed_arg_type_info_list[carg_idx]; + var arg_size = arg_type_info[0]; + var arg_align = arg_type_info[1]; + if (arg_type_id === FFI_TYPE_STRUCT) { + // In this case varargs is a pointer to pointer to struct so we need to + // deref once + var struct_ptr = DEREF_U32(varargs, 0); + STACK_ALLOC(cur_ptr, arg_size, arg_align); + HEAP8.subarray(cur_ptr, cur_ptr + arg_size).set(HEAP8.subarray(struct_ptr, struct_ptr + arg_size)); + DEREF_U32(args_ptr, carg_idx) = cur_ptr; + } else { + DEREF_U32(args_ptr, carg_idx) = varargs; + } + varargs += 4; + } + stackRestore(cur_ptr); + stackAlloc(0); // stackAlloc enforces alignment invariants on the stack pointer + LOG_DEBUG("CALL_CLOSURE", "closure:", closure, "fptr", CLOSURE__fun(closure), "cif", CLOSURE__cif(closure)); + getWasmTableEntry(CLOSURE__fun(closure))( + CLOSURE__cif(closure), ret_ptr, args_ptr, + CLOSURE__user_data(closure) + ); + stackRestore(orig_stack_ptr); + + // If we aren't supposed to return by argument, figure out what to return. + if (!ret_by_arg) { + switch (sig[0]) { + case "i": + return DEREF_U32(ret_ptr, 0); + case "j": + #if WASM_BIGINT + return DEREF_U64(ret_ptr, 0); + #else + // Split the return i64, set the upper 32 bits into tempRet0 and return + // the lower 32 bits. + setTempRet0(DEREF_U32(ret_ptr, 1)); + return DEREF_U32(ret_ptr, 0); + #endif + case "d": + return DEREF_F64(ret_ptr, 0); + case "f": + return DEREF_F32(ret_ptr, 0); + } + } + } + try { + var wasm_trampoline = JS_FUNCTION_TO_WASM(trampoline, sig); + } catch(e) { + return FFI_BAD_TYPEDEF_MACRO; + } + setWasmTableEntry(codeloc, wasm_trampoline); + CLOSURE__cif(closure) = cif; + CLOSURE__fun(closure) = fun; + CLOSURE__user_data(closure) = user_data; + return FFI_OK_MACRO; +}) + +// EM_JS does not correctly handle function pointer arguments, so we need a +// helper +ffi_status ffi_prep_closure_loc(ffi_closure *closure, ffi_cif *cif, + void (*fun)(ffi_cif *, void *, void **, void *), + void *user_data, void *codeloc) { + if (cif->abi != FFI_WASM32_EMSCRIPTEN) + return FFI_BAD_ABI; + return ffi_prep_closure_loc_js(closure, cif, (void *)fun, user_data, + codeloc); +} diff --git a/src/wasm32/ffitarget.h b/src/wasm32/ffitarget.h new file mode 100644 index 00000000..ac78b743 --- /dev/null +++ b/src/wasm32/ffitarget.h @@ -0,0 +1,62 @@ +/* -----------------------------------------------------------------*-C-*- + ffitarget.h - Copyright (c) 2018-2023 Hood Chatham, Brion Vibber, Kleis Auke Wolthuizen, and others. + + Target configuration macros for wasm32. + + 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. + + ----------------------------------------------------------------------- */ + +#ifndef LIBFFI_TARGET_H +#define LIBFFI_TARGET_H + +#ifndef LIBFFI_H +#error "Please do not include ffitarget.h directly into your source. Use ffi.h instead." +#endif + +/* ---- Generic type definitions ----------------------------------------- */ + +typedef unsigned long ffi_arg; +typedef signed long ffi_sarg; + +// TODO: https://github.com/emscripten-core/emscripten/issues/9868 +typedef void (*ffi_fp)(void); + +typedef enum ffi_abi { + FFI_FIRST_ABI = 0, + FFI_WASM32, // "raw", no structures, varargs, or closures (not implemented!) + FFI_WASM32_EMSCRIPTEN, // structures, varargs, and split 64-bit params + FFI_LAST_ABI, +#ifdef __EMSCRIPTEN__ + FFI_DEFAULT_ABI = FFI_WASM32_EMSCRIPTEN +#else + FFI_DEFAULT_ABI = FFI_WASM32 +#endif +} ffi_abi; + +#define FFI_CLOSURES 1 +// #define FFI_GO_CLOSURES 0 +#define FFI_TRAMPOLINE_SIZE 4 +// #define FFI_NATIVE_RAW_API 0 +#define FFI_TARGET_SPECIFIC_VARIADIC 1 +#define FFI_EXTRA_CIF_FIELDS unsigned int nfixedargs + +#endif diff --git a/src/x86/sysv.S b/src/x86/sysv.S index 7110f02f..c7a0fb51 100644 --- a/src/x86/sysv.S +++ b/src/x86/sysv.S @@ -888,10 +888,27 @@ ENDF(C(ffi_closure_raw_THISCALL)) #endif /* !FFI_NO_RAW_API */ #ifdef X86_DARWIN -# define COMDAT(X) \ - .section __TEXT,__text,coalesced,pure_instructions; \ +/* The linker in use on earlier Darwin needs weak definitions to be + placed in a coalesced section. That section should not be called + __TEXT,__text since that would be re-defining the attributes of the + .text section (which is an error for earlier tools). Here we use + '__textcoal_nt' which is what GCC emits for this. + Later linker versions are happy to use a normal section and, after + Darwin12 / OSX 10.8, the tools warn that using coalesced sections + for this is deprecated so we must switch to avoid build fails and/or + deprecation warnings. */ +# if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1080 +# define COMDAT(X) \ + .section __TEXT,__textcoal_nt,coalesced,pure_instructions; \ + .weak_definition X; \ + FFI_HIDDEN(X) +# else +# define COMDAT(X) \ + .text; \ .weak_definition X; \ FFI_HIDDEN(X) +# endif #elif defined __ELF__ && !(defined(__sun__) && defined(__svr4__)) # define COMDAT(X) \ .section .text.X,"axG",@progbits,X,comdat; \ @@ -916,7 +933,37 @@ ENDF(C(__x86.get_pc_thunk.dx)) #endif /* DARWIN || HIDDEN */ #endif /* __PIC__ */ -/* Sadly, OSX cctools-as doesn't understand .cfi directives at all. */ +/* Sadly, OSX cctools-as does not understand .cfi directives at all so + we build an eh frame by hand. */ + +#ifdef __APPLE__ +/* The cctools assembler will try to make a difference between two local + symbols into a relocation against, which will not work in the eh (produces + link-time fails). + To avoid this, we compute the symbol difference with a .set directive and + then substitute this value. */ +# define LEN(N, P) .set Llen$N$P,L(N)-L(P); .long Llen$N$P +/* Note, this assume DW_CFA_advance_loc1 fits into 7 bits. */ +# define ADV(N, P) .set Ladv$N$P,L(N)-L(P); .byte 2, Ladv$N$P +/* For historical reasons, the EH reg numbers for SP and FP are swapped from + the DWARF ones for 32b Darwin. */ +# define SP 5 +# define FP 4 +# define ENC 0x10 +#else +# define LEN(N, P) .long L(N)-L(P) +/* Assume DW_CFA_advance_loc1 fits. */ +# define ADV(N, P) .byte 2, L(N)-L(P) +# define SP 4 +# define FP 5 +# define ENC 0x1b +#endif + +#ifdef HAVE_AS_X86_PCREL +# define PCREL(X) X-. +#else +# define PCREL(X) X@rel +#endif #ifdef __APPLE__ .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support @@ -928,17 +975,11 @@ EHFrame0: #else .section .eh_frame,EH_FRAME_FLAGS,@progbits #endif - -#ifdef HAVE_AS_X86_PCREL -# define PCREL(X) X - . -#else -# define PCREL(X) X@rel +#ifndef __APPLE__ +/* EH sections are already suitably aligned on Darwin. */ + .balign 4 #endif -/* Simplify advancing between labels. Assume DW_CFA_advance_loc1 fits. */ -#define ADV(N, P) .byte 2, L(N)-L(P) - - .balign 4 L(CIE): .set L(set0),L(ECIE)-L(SCIE) .long L(set0) /* CIE Length */ @@ -950,8 +991,8 @@ L(SCIE): .byte 0x7c /* CIE Data Alignment Factor */ .byte 0x8 /* CIE RA Column */ .byte 1 /* Augmentation size */ - .byte 0x1b /* FDE Encoding (pcrel sdata4) */ - .byte 0xc, 4, 4 /* DW_CFA_def_cfa, %esp offset 4 */ + .byte ENC /* FDE Encoding (pcrel abs/4byte) */ + .byte 0xc, SP, 4 /* DW_CFA_def_cfa, %esp offset 4 */ .byte 0x80+8, 1 /* DW_CFA_offset, %eip offset 1*-4 */ .balign 4 L(ECIE): @@ -959,20 +1000,20 @@ L(ECIE): .set L(set1),L(EFDE1)-L(SFDE1) .long L(set1) /* FDE Length */ L(SFDE1): - .long L(SFDE1)-L(CIE) /* FDE CIE offset */ + LEN(SFDE1, CIE) /* FDE CIE offset */ .long PCREL(L(UW0)) /* Initial location */ - .long L(UW5)-L(UW0) /* Address range */ + LEN(UW5, UW0) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW1, UW0) - .byte 0xc, 5, 8 /* DW_CFA_def_cfa, %ebp 8 */ - .byte 0x80+5, 2 /* DW_CFA_offset, %ebp 2*-4 */ + .byte 0xc, FP, 8 /* DW_CFA_def_cfa, %ebp 8 */ + .byte 0x80+FP, 2 /* DW_CFA_offset, %ebp 2*-4 */ ADV(UW2, UW1) .byte 0x80+3, 0 /* DW_CFA_offset, %ebx 0*-4 */ ADV(UW3, UW2) .byte 0xa /* DW_CFA_remember_state */ - .byte 0xc, 4, 4 /* DW_CFA_def_cfa, %esp 4 */ + .byte 0xc, SP, 4 /* DW_CFA_def_cfa, %esp 4 */ .byte 0xc0+3 /* DW_CFA_restore, %ebx */ - .byte 0xc0+5 /* DW_CFA_restore, %ebp */ + .byte 0xc0+FP /* DW_CFA_restore, %ebp */ ADV(UW4, UW3) .byte 0xb /* DW_CFA_restore_state */ .balign 4 @@ -981,9 +1022,9 @@ L(EFDE1): .set L(set2),L(EFDE2)-L(SFDE2) .long L(set2) /* FDE Length */ L(SFDE2): - .long L(SFDE2)-L(CIE) /* FDE CIE offset */ + LEN(SFDE2, CIE) /* FDE CIE offset */ .long PCREL(L(UW6)) /* Initial location */ - .long L(UW8)-L(UW6) /* Address range */ + LEN(UW8,UW6) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW7, UW6) .byte 0xe, closure_FS+4 /* DW_CFA_def_cfa_offset */ @@ -993,9 +1034,9 @@ L(EFDE2): .set L(set3),L(EFDE3)-L(SFDE3) .long L(set3) /* FDE Length */ L(SFDE3): - .long L(SFDE3)-L(CIE) /* FDE CIE offset */ + LEN(SFDE3, CIE) /* FDE CIE offset */ .long PCREL(L(UW9)) /* Initial location */ - .long L(UW11)-L(UW9) /* Address range */ + LEN(UW11, UW9) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW10, UW9) .byte 0xe, closure_FS+4 /* DW_CFA_def_cfa_offset */ @@ -1005,9 +1046,9 @@ L(EFDE3): .set L(set4),L(EFDE4)-L(SFDE4) .long L(set4) /* FDE Length */ L(SFDE4): - .long L(SFDE4)-L(CIE) /* FDE CIE offset */ + LEN(SFDE4, CIE) /* FDE CIE offset */ .long PCREL(L(UW12)) /* Initial location */ - .long L(UW20)-L(UW12) /* Address range */ + LEN(UW20, UW12) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW13, UW12) .byte 0xe, closure_FS+4 /* DW_CFA_def_cfa_offset */ @@ -1033,9 +1074,9 @@ L(EFDE4): .set L(set5),L(EFDE5)-L(SFDE5) .long L(set5) /* FDE Length */ L(SFDE5): - .long L(SFDE5)-L(CIE) /* FDE CIE offset */ + LEN(SFDE5, CIE) /* FDE CIE offset */ .long PCREL(L(UW21)) /* Initial location */ - .long L(UW23)-L(UW21) /* Address range */ + LEN(UW23, UW21) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW22, UW21) .byte 0xe, closure_FS+4 /* DW_CFA_def_cfa_offset */ @@ -1045,9 +1086,9 @@ L(EFDE5): .set L(set6),L(EFDE6)-L(SFDE6) .long L(set6) /* FDE Length */ L(SFDE6): - .long L(SFDE6)-L(CIE) /* FDE CIE offset */ + LEN(SFDE6, CIE) /* FDE CIE offset */ .long PCREL(L(UW24)) /* Initial location */ - .long L(UW26)-L(UW24) /* Address range */ + LEN(UW26, UW24) /* Address range */ .byte 0 /* Augmentation size */ .byte 0xe, 8 /* DW_CFA_def_cfa_offset */ .byte 0x80+8, 2 /* DW_CFA_offset %eip, 2*-4 */ @@ -1059,9 +1100,9 @@ L(EFDE6): .set L(set7),L(EFDE7)-L(SFDE7) .long L(set7) /* FDE Length */ L(SFDE7): - .long L(SFDE7)-L(CIE) /* FDE CIE offset */ + LEN(SFDE7, CIE) /* FDE CIE offset */ .long PCREL(L(UW27)) /* Initial location */ - .long L(UW31)-L(UW27) /* Address range */ + LEN(UW31, UW27) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW28, UW27) .byte 0xe, closure_FS+4 /* DW_CFA_def_cfa_offset */ @@ -1073,14 +1114,13 @@ L(SFDE7): #endif .balign 4 L(EFDE7): - #if !FFI_NO_RAW_API .set L(set8),L(EFDE8)-L(SFDE8) .long L(set8) /* FDE Length */ L(SFDE8): - .long L(SFDE8)-L(CIE) /* FDE CIE offset */ + LEN(SFDE8, CIE) /* FDE CIE offset */ .long PCREL(L(UW32)) /* Initial location */ - .long L(UW40)-L(UW32) /* Address range */ + LEN(UW40, UW32) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW33, UW32) .byte 0xe, raw_closure_S_FS+4 /* DW_CFA_def_cfa_offset */ @@ -1102,9 +1142,9 @@ L(EFDE8): .set L(set9),L(EFDE9)-L(SFDE9) .long L(set9) /* FDE Length */ L(SFDE9): - .long L(SFDE9)-L(CIE) /* FDE CIE offset */ + LEN(SFDE9, CIE) /* FDE CIE offset */ .long PCREL(L(UW41)) /* Initial location */ - .long L(UW52)-L(UW41) /* Address range */ + LEN(UW52, UW41) /* Address range */ .byte 0 /* Augmentation size */ ADV(UW42, UW41) .byte 0xe, 0 /* DW_CFA_def_cfa_offset */ @@ -1141,8 +1181,12 @@ L(EFDE9): @feat.00 = 1 #endif -#ifdef __APPLE__ +#if defined(__APPLE__) .subsections_via_symbols +# if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1070 && __clang__ +/* compact unwind is not used with GCC at present, was not present before 10.6 + but has some bugs there, so do not emit until 10.7. */ .section __LD,__compact_unwind,regular,debug /* compact unwind for ffi_call_i386 */ @@ -1216,6 +1260,7 @@ L(EFDE9): .long 0x04000000 /* use dwarf unwind info */ .long 0 .long 0 +#endif /* use compact unwind */ #endif /* __APPLE__ */ #endif /* ifndef _MSC_VER */ diff --git a/testsuite/emscripten/build-tests.sh b/testsuite/emscripten/build-tests.sh new file mode 100755 index 00000000..ea514ecc --- /dev/null +++ b/testsuite/emscripten/build-tests.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +emcc_exists="$(command -v emcc)" +if [ ! "${emcc_exists}" ]; then + echo "Emscripten not on path" + exit 1 +fi + +set -e + +cd "$1" +shift + +# Parse arguments +while [ $# -gt 0 ]; do + case $1 in + --wasm-bigint) WASM_BIGINT=true ;; + *) + echo "ERROR: Unknown parameter: $1" >&2 + exit 1 + ;; + esac + shift +done + + +export CFLAGS="-fPIC -O2 -I../../target/include $EXTRA_CFLAGS" +export CXXFLAGS="$CFLAGS -sNO_DISABLE_EXCEPTION_CATCHING $EXTRA_CXXFLAGS" +export LDFLAGS=" \ + -L../../target/lib/ -lffi \ + -sEXPORT_ALL \ + -sMODULARIZE \ + -sMAIN_MODULE \ + -sSTRICT_JS \ + -sNO_DISABLE_EXCEPTION_CATCHING \ + $EXTRA_LD_FLAGS \ +" + +# This needs to test false if there exists an environment variable called +# WASM_BIGINT whose contents are empty. Don't use +x. +if [ -n "${WASM_BIGINT}" ] ; then + export LDFLAGS+=" -sWASM_BIGINT" +else + export LDFLAGS+=" -sEXPORTED_RUNTIME_METHODS='getTempRet0,setTempRet0'" +fi + +# Rename main functions to test__filename so we can link them together +ls *c | sed 's!\(.*\)\.c!sed -i "s/main/test__\1/g" \0!g' | bash + +# Compile +ls *.c | sed 's/\(.*\)\.c/emcc $CFLAGS -c \1.c -o \1.o /g' | bash +ls *.cc | sed 's/\(.*\)\.cc/em++ $CXXFLAGS -c \1.cc -o \1.o /g' | bash + +# Link +em++ $LDFLAGS *.o -o test.js +cp ../emscripten/test.html . diff --git a/testsuite/emscripten/build.sh b/testsuite/emscripten/build.sh new file mode 100755 index 00000000..c77c041b --- /dev/null +++ b/testsuite/emscripten/build.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +command -v emcc >/dev/null 2>&1 || { + echo >&2 "emsdk could not be found. Aborting." + exit 1 +} + +set -e + +SOURCE_DIR=$PWD + +# Working directories +TARGET=$SOURCE_DIR/target +mkdir -p "$TARGET" + +# Define default arguments + +# JS BigInt to Wasm i64 integration, disabled by default +# This needs to test false if there exists an environment variable called +# WASM_BIGINT whose contents are empty. Don't use +x. +if [ -n "${WASM_BIGINT}" ]; then + WASM_BIGINT=true +else + WASM_BIGINT=false +fi + +# Parse arguments +while [ $# -gt 0 ]; do + case $1 in + --wasm-bigint) WASM_BIGINT=true ;; + --debug) DEBUG=true ;; + *) + echo "ERROR: Unknown parameter: $1" >&2 + exit 1 + ;; + esac + shift +done + +# Common compiler flags +export CFLAGS="-O3 -fPIC" +if [ "$WASM_BIGINT" = "true" ]; then + # We need to detect WASM_BIGINT support at compile time + export CFLAGS+=" -DWASM_BIGINT" +fi +if [ "$DEBUG" = "true" ]; then + export CFLAGS+=" -DDEBUG_F" +fi +export CXXFLAGS="$CFLAGS" + +# Build paths +export CPATH="$TARGET/include" +export PKG_CONFIG_PATH="$TARGET/lib/pkgconfig" +export EM_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" + +# Specific variables for cross-compilation +export CHOST="wasm32-unknown-linux" # wasm32-unknown-emscripten + +autoreconf -fiv +emconfigure ./configure --host=$CHOST --prefix="$TARGET" --enable-static --disable-shared --disable-dependency-tracking \ + --disable-builddir --disable-multi-os-directory --disable-raw-api --disable-docs +make install +cp fficonfig.h target/include/ +cp include/ffi_common.h target/include/ diff --git a/testsuite/emscripten/conftest.py b/testsuite/emscripten/conftest.py new file mode 100644 index 00000000..57fac809 --- /dev/null +++ b/testsuite/emscripten/conftest.py @@ -0,0 +1,80 @@ +from pathlib import Path +from pytest import fixture +from pytest_pyodide.server import spawn_web_server +from pytest_pyodide import runner + +TEST_PATH = Path(__file__).parents[1].resolve() + + +class BaseRunner(runner._BrowserBaseRunner): + def __init__( + self, + *args, + test_dir, + **kwargs, + ): + self.test_dir = test_dir + super().__init__( + *args, + **kwargs, + load_pyodide=False, + ) + + def prepare_driver(self): + self.base_url = ( + f"http://{self.server_hostname}:{self.server_port}/{self.test_dir}/" + ) + self.goto(f"{self.base_url}/test.html") + + def javascript_setup(self): + self.run_js("globalThis.TestModule = await globalThis.Module();") + + +class FirefoxRunner(BaseRunner, runner.SeleniumFirefoxRunner): + pass + + +class ChromeRunner(BaseRunner, runner.SeleniumChromeRunner): + pass + + +# TODO: Figure out how to get NodeRunner to work. + +RUNNER_DICT = {x.browser: x for x in [FirefoxRunner, ChromeRunner]} + + +@fixture(params=list(RUNNER_DICT), scope="class") +def selenium_class_scope(request, web_server_main): + server_hostname, server_port, server_log = web_server_main + assert request.param in RUNNER_DICT + cls = RUNNER_DICT[request.param] + selenium = cls( + test_dir=request.cls.TEST_BUILD_DIR, + server_port=server_port, + server_hostname=server_hostname, + server_log=server_log, + ) + request.cls.call_number = 0 + try: + yield selenium + finally: + print(selenium.logs) + selenium.driver.quit() + + +@fixture(scope="function") +def selenium(selenium_class_scope, request): + selenium = selenium_class_scope + request.cls.call_number += 1 + # Refresh page every 50 calls to prevent firefox out of memory errors + if request.cls.call_number % 50 == 0: + selenium.driver.refresh() + selenium.javascript_setup() + selenium.clean_logs() + yield selenium + + +@fixture(scope="session") +def web_server_main(request): + with spawn_web_server(TEST_PATH) as output: + yield output diff --git a/testsuite/emscripten/node-tests.sh b/testsuite/emscripten/node-tests.sh new file mode 100755 index 00000000..88dd74b3 --- /dev/null +++ b/testsuite/emscripten/node-tests.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# JS BigInt to Wasm i64 integration, disabled by default +WASM_BIGINT=false + +emcc_exists="$(command -v emcc)" +if [ ! "${emcc_exists}" ]; then + echo "Emscripten not on path" + exit 1 +fi + +# Parse arguments +while [ $# -gt 0 ]; do + case $1 in + --wasm-bigint) WASM_BIGINT=true ;; + *) + echo "ERROR: Unknown parameter: $1" >&2 + exit 1 + ;; + esac + shift +done + +# Common compiler flags +export CFLAGS="-fPIC $EXTRA_CFLAGS" +if [ "$WASM_BIGINT" = "true" ]; then + # We need to detect WASM_BIGINT support at compile time + export CFLAGS+=" -DWASM_BIGINT" +fi +export CXXFLAGS="$CFLAGS -sNO_DISABLE_EXCEPTION_CATCHING $EXTRA_CXXFLAGS" +export LDFLAGS="-sEXPORTED_FUNCTIONS=_main,_malloc,_free -sALLOW_TABLE_GROWTH -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING" +if [ "$WASM_BIGINT" = "true" ]; then + export LDFLAGS+=" -sWASM_BIGINT" +else + export LDFLAGS+=" -sEXPORTED_RUNTIME_METHODS='getTempRet0,setTempRet0'" +fi + +# Specific variables for cross-compilation +export CHOST="wasm32-unknown-linux" # wasm32-unknown-emscripten + +autoreconf -fiv +emconfigure ./configure --prefix="$(pwd)/target" --host=$CHOST --enable-static --disable-shared \ + --disable-builddir --disable-multi-os-directory --disable-raw-api --disable-docs || + (cat config.log && exit 1) +make + +EMMAKEN_JUST_CONFIGURE=1 emmake make check \ + RUNTESTFLAGS="LDFLAGS_FOR_TARGET='$LDFLAGS'" || (cat testsuite/libffi.log && exit 1) diff --git a/testsuite/emscripten/quiet_emcc_info.py b/testsuite/emscripten/quiet_emcc_info.py new file mode 100644 index 00000000..b2b3f06d --- /dev/null +++ b/testsuite/emscripten/quiet_emcc_info.py @@ -0,0 +1,36 @@ +""" +Prevent emcc from printing info level logging + +dejagnu will fail tests because of these log statements and the messages depend +on emscripten cache state so xfailing the right tests is very hard. + +See emscripten issue: +https://github.com/emscripten-core/emscripten/issues/18607 +""" + +from pathlib import Path +from shutil import which +from sys import exit +from textwrap import dedent + +EMCC_PATH = Path(which("emcc") + ".py") + +emcc_text = EMCC_PATH.read_text() +if "# quiet_emcc_info patch" in emcc_text: + exit(0) + +emcc_lines = emcc_text.splitlines() +emcc_lines.insert( + 1, + dedent( + """ + # quiet_emcc_info patch + import logging + for name in ["cache", "system_libs", "shared"]: + logger = logging.getLogger(name) + logger.setLevel(logging.WARN) + """ + ), +) + +EMCC_PATH.write_text("\n".join(emcc_lines)) diff --git a/testsuite/emscripten/test.html b/testsuite/emscripten/test.html new file mode 100644 index 00000000..77015038 --- /dev/null +++ b/testsuite/emscripten/test.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testsuite/emscripten/test_libffi.py b/testsuite/emscripten/test_libffi.py new file mode 100644 index 00000000..059d5356 --- /dev/null +++ b/testsuite/emscripten/test_libffi.py @@ -0,0 +1,51 @@ +import subprocess +import pathlib +import pytest + +TEST_PATH = pathlib.Path(__file__).parents[1].resolve() + +xfails = {} + + +def libffi_tests(self, selenium, libffi_test): + if libffi_test in xfails: + pytest.xfail(f'known failure with code "{xfails[libffi_test]}"') + res = selenium.run_js( + """ + window.TestModule = await Module(); + """ + ) + selenium.run_js( + f""" + try {{ + TestModule._test__{libffi_test}(); + }} catch(e){{ + if(e.name !== "ExitStatus"){{ + throw e; + }} + if(e.status !== 0){{ + throw new Error(`Terminated with nonzero status code ${{e.status}}: ` + e.message); + }} + }} + """ + ) + + +class TestCall: + TEST_BUILD_DIR = "libffi.call.test" + test_call = libffi_tests + + +class TestClosures: + TEST_BUILD_DIR = "libffi.closures.test" + test_closures = libffi_tests + + +def pytest_generate_tests(metafunc): + test_build_dir = metafunc.cls.TEST_BUILD_DIR + test_names = [x.stem for x in (TEST_PATH / test_build_dir).glob("*.o")] + metafunc.parametrize("libffi_test", test_names) + + +if __name__ == "__main__": + subprocess.call(["build-tests.sh", "libffi.call"]) diff --git a/testsuite/lib/libffi.exp b/testsuite/lib/libffi.exp index 7457af41..a84e5331 100644 --- a/testsuite/lib/libffi.exp +++ b/testsuite/lib/libffi.exp @@ -221,6 +221,13 @@ proc libffi-dg-test-1 { target_compile prog do_what extra_tool_flags } { set output_match [lreplace $output_match 1 1 $x] } + if { [ istarget "wasm32-*-*" ] } { + # emscripten will get confused if told to build as .exe + set exec_suffix "" + } else { + set exec_suffix ".exe" + } + # Set up the compiler flags, based on what we're going to do. set options [list] @@ -231,7 +238,7 @@ proc libffi-dg-test-1 { target_compile prog do_what extra_tool_flags } { } "link" { set compile_type "executable" - set output_file "[file rootname [file tail $prog]].exe" + set output_file "[file rootname [file tail $prog]]$exec_suffix" # The following line is needed for targets like the i960 where # the default output file is b.out. Sigh. } @@ -240,7 +247,7 @@ proc libffi-dg-test-1 { target_compile prog do_what extra_tool_flags } { # FIXME: "./" is to cope with "." not being in $PATH. # Should this be handled elsewhere? # YES. - set output_file "./[file rootname [file tail $prog]].exe" + set output_file "./[file rootname [file tail $prog]]$exec_suffix" # This is the only place where we care if an executable was # created or not. If it was, dg.exp will try to run it. remote_file build delete $output_file; @@ -380,6 +387,7 @@ proc libffi_target_compile { source dest type options } { || [istarget "*-*-darwin2*"] } { # lappend options "additional_flags=-Wl,-allow_stack_execute" lappend options "additional_flags=-Wno-unused-command-line-argument" + lappend options "additional_flags=-Wl,-search_paths_first" } # If you're building the compiler with --prefix set to a place @@ -416,6 +424,12 @@ proc libffi_target_compile { source dest type options } { } } + # emscripten emits this warning while building the feature test + # which causes it to be seen as unsupported. + if { [string match "wasm32-*" $target_triplet] } { + lappend options "additional_flags=-Wno-unused-command-line-argument" + } + verbose "options: $options" return [target_compile $source $dest $type $options] } diff --git a/testsuite/libffi.call/bpo-38748.c b/testsuite/libffi.call/bpo_38748.c similarity index 100% rename from testsuite/libffi.call/bpo-38748.c rename to testsuite/libffi.call/bpo_38748.c diff --git a/testsuite/libffi.closures/closure_loc_fn0.c b/testsuite/libffi.closures/closure_loc_fn0.c index 4f2f4f8c..f344a607 100644 --- a/testsuite/libffi.closures/closure_loc_fn0.c +++ b/testsuite/libffi.closures/closure_loc_fn0.c @@ -7,9 +7,6 @@ Originator: 20030828 */ - - -/* { dg-do run { xfail wasm32*-*-* } } */ #include "ffitest.h" static void @@ -83,9 +80,9 @@ int main (void) CHECK(ffi_prep_closure_loc(pcl, &cif, closure_loc_test_fn0, (void *) 3 /* userdata */, codeloc) == FFI_OK); -#ifndef FFI_EXEC_STATIC_TRAMP +#if !defined(FFI_EXEC_STATIC_TRAMP) && !defined(__EMSCRIPTEN__) /* With static trampolines, the codeloc does not point to closure */ - CHECK(memcmp(pcl, codeloc, sizeof(*pcl)) == 0); + CHECK(memcmp(pcl, FFI_CL(codeloc), sizeof(*pcl)) == 0); #endif res = (*((closure_loc_test_type0)codeloc)) diff --git a/testsuite/libffi.closures/cls_longdouble_va.c b/testsuite/libffi.closures/cls_longdouble_va.c index 493f0f6f..ba32f5c8 100644 --- a/testsuite/libffi.closures/cls_longdouble_va.c +++ b/testsuite/libffi.closures/cls_longdouble_va.c @@ -9,10 +9,21 @@ /* { dg-output "" { xfail mips-sgi-irix6* } } PR libffi/46660 */ #include "ffitest.h" +#include #define BUF_SIZE 50 static char buffer[BUF_SIZE]; +static int +wrap_printf(char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + long double ldArg = va_arg(ap, long double); + va_end(ap); + CHECK((int)ldArg == 7); + return printf(fmt, ldArg); +} + static void cls_longdouble_va_fn(ffi_cif* cif __UNUSED__, void* resp, void** args, void* userdata __UNUSED__) @@ -50,7 +61,7 @@ int main (void) args[1] = &ldArg; args[2] = NULL; - ffi_call(&cif, FFI_FN(printf), &res, args); + ffi_call(&cif, FFI_FN(wrap_printf), &res, args); /* { dg-output "7.0" } */ printf("res: %d\n", (int) res); /* { dg-output "\nres: 4" } */ diff --git a/testsuite/libffi.closures/cls_multi_schar.c b/testsuite/libffi.closures/cls_multi_schar.c index 921777a0..9eb02f9e 100644 --- a/testsuite/libffi.closures/cls_multi_schar.c +++ b/testsuite/libffi.closures/cls_multi_schar.c @@ -64,7 +64,7 @@ int main (void) /* { dg-output "2 125: 127" } */ printf("res: %d\n", (signed char)res_call); /* { dg-output "\nres: 127" } */ - CHECK(res_call == 127); + CHECK((signed char)res_call == 127); CHECK(ffi_prep_closure_loc(pcl, &cif, test_func_gn, NULL, code) == FFI_OK); diff --git a/testsuite/libffi.closures/cls_multi_sshort.c b/testsuite/libffi.closures/cls_multi_sshort.c index d78c62d2..9d4db5b6 100644 --- a/testsuite/libffi.closures/cls_multi_sshort.c +++ b/testsuite/libffi.closures/cls_multi_sshort.c @@ -65,7 +65,7 @@ int main (void) /* { dg-output "2 32765: 32767" } */ printf("res: %d\n", (unsigned short)res_call); /* { dg-output "\nres: 32767" } */ - CHECK(res_call == 32767); + CHECK((unsigned short)res_call == 32767); CHECK(ffi_prep_closure_loc(pcl, &cif, test_func_gn, NULL, code) == FFI_OK); diff --git a/testsuite/libffi.closures/huge_struct.c b/testsuite/libffi.closures/huge_struct.c index 71cae268..572a0c8f 100644 --- a/testsuite/libffi.closures/huge_struct.c +++ b/testsuite/libffi.closures/huge_struct.c @@ -5,7 +5,7 @@ Originator: Blake Chaffin 6/18/2007 */ -/* { dg-do run { xfail strongarm*-*-* xscale*-*-* wasm32*-*-* } } */ +/* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */ /* { dg-options -mlong-double-128 { target powerpc64*-*-linux-gnu* } } */ /* { dg-options -Wformat=0 { target moxie*-*-elf or1k-*-* } } */ diff --git a/testsuite/libffi.closures/unwindtest.cc b/testsuite/libffi.closures/unwindtest.cc index 6c97c61e..b643c48e 100644 --- a/testsuite/libffi.closures/unwindtest.cc +++ b/testsuite/libffi.closures/unwindtest.cc @@ -48,6 +48,7 @@ typedef int (*closure_test_type1)(float, float, float, float, signed short, float, float, int, double, int, int, float, int, int, int, int); +extern "C" int main (void) { ffi_cif cif; diff --git a/testsuite/libffi.closures/unwindtest_ffi_call.cc b/testsuite/libffi.closures/unwindtest_ffi_call.cc index 153d2409..6feaa575 100644 --- a/testsuite/libffi.closures/unwindtest_ffi_call.cc +++ b/testsuite/libffi.closures/unwindtest_ffi_call.cc @@ -14,6 +14,7 @@ static int checking(int a __UNUSED__, short b __UNUSED__, throw 9; } +extern "C" int main (void) { ffi_cif cif;