-libffi - Copyright (c) 1996-2021 Anthony Green, Red Hat, Inc and others.
+libffi - Copyright (c) 1996-2025 Anthony Green, Red Hat, Inc and others.
See source files for details.
Permission is hereby granted, free of charge, to any person obtaining
-f9ea41683444ebe11cfa45b05223899764df28fb
+e2eda0cf72a0598b44278cc91860ea402273fa29
The first line of this file holds the git revision number of the
last merge done from the master library sources.
## Process this with automake to create Makefile.in
-AUTOMAKE_OPTIONS = foreign subdir-objects info-in-builddir
+AUTOMAKE_OPTIONS = foreign subdir-objects
-ACLOCAL_AMFLAGS = -I .. -I ../config
+ACLOCAL_AMFLAGS = -I m4
+
+# Alias required by AX_ENABLE_BUILDDIR / config-ml
+.PHONY: all-configured
+all-configured: all
SUBDIRS = include testsuite man
+if BUILD_DOCS
+## This hack is needed because it doesn't seem possible to make a
+## conditional info_TEXINFOS in Automake. At least Automake 1.14
+## either gives errors -- if this attempted in the most
+## straightforward way -- or simply unconditionally tries to build the
+## info file.
+SUBDIRS += doc
+endif
-EXTRA_DIST = LICENSE ChangeLog.old \
- m4/libtool.m4 m4/lt~obsolete.m4 \
- m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 \
- m4/ltversion.m4 src/debug.c msvcc.sh \
- generate-darwin-source-and-headers.py \
- libffi.xcodeproj/project.pbxproj \
- libtool-ldflags libtool-version configure.host README.md \
- libffi.map.in LICENSE-BUILDTOOLS msvc_build make_sunver.pl
+EXTRA_DIST = LICENSE ChangeLog.old \
+ m4/libtool.m4 m4/lt~obsolete.m4 \
+ m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 \
+ m4/ltversion.m4 src/debug.c msvcc.sh \
+ generate-darwin-source-and-headers.py \
+ libffi.xcodeproj/project.pbxproj \
+ src/powerpc/t-aix \
+ libtool-ldflags libtool-version configure.host README.md \
+ libffi.map.in LICENSE-BUILDTOOLS msvc_build make_sunver.pl
# local.exp is generated by configure
DISTCLEANFILES = local.exp
-# Automake Documentation:
-# If your package has Texinfo files in many directories, you can use the
-# variable TEXINFO_TEX to tell Automake where to find the canonical
-# `texinfo.tex' for your package. The value of this variable should be
-# the relative path from the current `Makefile.am' to `texinfo.tex'.
-TEXINFO_TEX = ../gcc/doc/include/texinfo.tex
-
-# Defines info, dvi, pdf and html targets
-MAKEINFOFLAGS = -I $(srcdir)/../gcc/doc/include
-info_TEXINFOS = doc/libffi.texi
-
-# AM_CONDITIONAL on configure option --generated-files-in-srcdir
-if GENINSRC
-STAMP_GENINSRC = stamp-geninsrc
-else
-STAMP_GENINSRC =
-endif
-
-# AM_CONDITIONAL on configure check ACX_CHECK_PROG_VER([MAKEINFO])
-if BUILD_DOCS
-STAMP_BUILD_INFO = stamp-build-info
-else
-STAMP_BUILD_INFO =
-endif
-
-all-local: $(STAMP_GENINSRC)
-
-stamp-geninsrc: doc/libffi.info
- cp -p $(top_builddir)/doc/libffi.info $(srcdir)/doc/libffi.info
- @touch $@
-
-doc/libffi.info: $(STAMP_BUILD_INFO)
-
-stamp-build-info: doc/libffi.texi $(srcdir)/doc/version.texi doc/$(am__dirstamp)
- $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)/doc -o doc/libffi.info $(srcdir)/doc/libffi.texi
- @touch $@
-
-CLEANFILES = $(STAMP_GENINSRC) $(STAMP_BUILD_INFO)
-MAINTAINERCLEANFILES = $(srcdir)/doc/libffi.info
-
-## ################################################################
-
-##
-## This section is for make and multilib madness.
-##
-
-# Work around what appears to be a GNU make bug handling MAKEFLAGS
-# values defined in terms of make variables, as is the case for CC and
-# friends when we are called from the top level Makefile.
-AM_MAKEFLAGS = \
- "AR_FLAGS=$(AR_FLAGS)" \
- "CC_FOR_BUILD=$(CC_FOR_BUILD)" \
- "CFLAGS=$(CFLAGS)" \
- "CXXFLAGS=$(CXXFLAGS)" \
- "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
- "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
- "INSTALL=$(INSTALL)" \
- "INSTALL_DATA=$(INSTALL_DATA)" \
- "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
- "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
- "JC1FLAGS=$(JC1FLAGS)" \
- "LDFLAGS=$(LDFLAGS)" \
- "LIBCFLAGS=$(LIBCFLAGS)" \
- "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
- "MAKE=$(MAKE)" \
- "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
- "PICFLAG=$(PICFLAG)" \
- "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
- "RUNTESTFLAGS=$(RUNTESTFLAGS)" \
- "SHELL=$(SHELL)" \
- "exec_prefix=$(exec_prefix)" \
- "infodir=$(infodir)" \
- "libdir=$(libdir)" \
- "mandir=$(mandir)" \
- "prefix=$(prefix)" \
- "AR=$(AR)" \
- "AS=$(AS)" \
- "CC=$(CC)" \
- "CXX=$(CXX)" \
- "LD=$(LD)" \
- "NM=$(NM)" \
- "RANLIB=$(RANLIB)" \
- "DESTDIR=$(DESTDIR)"
-
# Subdir rules rely on $(FLAGS_TO_PASS)
FLAGS_TO_PASS = $(AM_MAKEFLAGS)
src/m32r/ffitarget.h src/m68k/ffitarget.h \
src/m88k/ffitarget.h src/metag/ffitarget.h \
src/microblaze/ffitarget.h src/mips/ffitarget.h \
- src/moxie/ffitarget.h src/nios2/ffitarget.h \
+ src/moxie/ffitarget.h \
src/or1k/ffitarget.h src/pa/ffitarget.h \
src/powerpc/ffitarget.h src/powerpc/asm.h \
- src/powerpc/ffi_powerpc.h src/riscv/ffitarget.h \
+ src/powerpc/ffi_powerpc.h src/powerpc/internal.h \
+ src/riscv/ffitarget.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/wasm/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/loongarch64/ffitarget.h
+ src/kvx/ffitarget.h src/kvx/asm.h \
+ src/loongarch64/ffitarget.h
EXTRA_libffi_la_SOURCES = src/aarch64/ffi.c src/aarch64/sysv.S \
src/aarch64/win64_armasm.S src/alpha/ffi.c src/alpha/osf.S \
src/metag/ffi.c src/metag/sysv.S src/microblaze/ffi.c \
src/microblaze/sysv.S src/mips/ffi.c src/mips/o32.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/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 \
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/wasm/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 \
libffi_convenience_la_DEPENDENCIES = $(libffi_la_DEPENDENCIES)
nodist_libffi_convenience_la_SOURCES = $(nodist_libffi_la_SOURCES)
-LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/../libtool-ldflags $(LDFLAGS))
+LTLDFLAGS = $(shell $(SHELL) $(top_srcdir)/libtool-ldflags $(LDFLAGS))
-AM_CFLAGS = -Wall -g -fexceptions $(CET_FLAGS)
+AM_CFLAGS =
if FFI_DEBUG
# Build debug. Define FFI_DEBUG on the commandline so that, when building with
# MSVC, it can link against the debug CRT.
if LIBFFI_BUILD_VERSIONED_SHLIB_SUN
libffi_version_script = -Wl,-M,libffi.map-sun
libffi_version_dep = libffi.map-sun
-libffi.map-sun : libffi.map $(top_srcdir)/../contrib/make_sunver.pl \
+libffi.map-sun : libffi.map $(top_srcdir)/make_sunver.pl \
$(libffi_la_OBJECTS) $(libffi_la_LIBADD)
- perl $(top_srcdir)/../contrib/make_sunver.pl libffi.map \
- $(libffi_la_OBJECTS) $(libffi_la_LIBADD) \
+ perl $(top_srcdir)/make_sunver.pl libffi.map \
+ `echo $(libffi_la_OBJECTS) $(libffi_la_LIBADD) | \
+ sed 's,\([^/ ]*\)\.l\([ao]\),.libs/\1.\2,g'` \
> $@ || (rm -f $@ ; exit 1)
endif
else
$(COMPILE) -D$(TARGET) -DGENERATE_LIBFFI_MAP \
-E -x assembler-with-cpp -o $@ $(top_srcdir)/libffi.map.in
-if ENABLE_DARWIN_AT_RPATH
-libffi_darwin_rpath = -Wl,-rpath,@loader_path
-endif
-libffi_la_LDFLAGS = -no-undefined $(libffi_version_info) \
- $(libffi_version_script) $(LTLDFLAGS) $(AM_LTLDFLAGS) \
- $(libffi_darwin_rpath)
+libffi_la_LDFLAGS = -no-undefined $(libffi_version_info) $(libffi_version_script) $(LTLDFLAGS) $(AM_LTLDFLAGS)
libffi_la_DEPENDENCIES = $(libffi_la_LIBADD) $(libffi_version_dep)
AM_CPPFLAGS = -I. -I$(top_srcdir)/include -Iinclude -I$(top_srcdir)/src
-AM_CCASFLAGS = $(AM_CPPFLAGS) $(CET_FLAGS)
+AM_CCASFLAGS = '$(AM_CPPFLAGS)'
-# Multilib support. Automake should provide these on its own.
-all-recursive: all-multi
-install-recursive: install-multi
-mostlyclean-recursive: mostlyclean-multi
-clean-recursive: clean-multi
-distclean-recursive: distclean-multi
-maintainer-clean-recursive: maintainer-clean-multi
+dist-hook:
+ d=`(cd $(distdir); pwd)`; (cd doc; make pdf; cp *.pdf $$d/doc)
+ if [ -d $(top_srcdir)/.git ] ; then (cd $(top_srcdir); git log --no-decorate) ; else echo 'See git log for history.' ; fi > $(distdir)/ChangeLog
+ s=`awk '/was released on/{ print NR; exit}' $(top_srcdir)/README.md`; tail -n +$$(($$s-1)) $(top_srcdir)/README.md > $(distdir)/README.md
-include $(top_srcdir)/../multilib.am
+# target overrides
+-include $(tmake_file)
Status
======
-libffi-3.4.1 was released on June 28, 2021. Check the libffi web page
-for updates: <URL:http://sourceware.org/libffi/>.
+libffi-3.5.2 was released on August 2, 2025.
What is libffi?
| Alpha | Linux | GCC |
| Alpha | Tru64 | GCC |
| ARC | Linux | GCC |
+| ARC32 | Linux | GCC |
+| ARC64 | Linux | GCC |
| ARM | Linux | GCC |
| ARM | iOS | GCC |
| ARM | Windows | MSVC |
| Blackfin | uClinux | GCC |
| CSKY | Linux | GCC |
| HPPA | HPUX | GCC |
+| HPPA64 | HPUX | GCC |
| KVX | Linux | GCC |
| IA-64 | Linux | GCC |
+| LoongArch64 | Linux | GCC |
| M68K | FreeMiNT | GCC |
| M68K | Linux | GCC |
| M68K | RTEMS | GCC |
| MIPS | RTEMS | GCC |
| MIPS64 | Linux | GCC |
| Moxie | Bare metal | GCC |
-| Nios II | Linux | GCC |
| OpenRISC | Linux | GCC |
+| PowerPC 32-bit | AIX | GCC |
| PowerPC 32-bit | AIX | IBM XL C |
| PowerPC 64-bit | AIX | IBM XL C |
| PowerPC | AMIGA | GCC |
| RISC-V 64-bit | Linux | GCC |
| S390 | Linux | GCC |
| S390X | Linux | GCC |
+| SH3 | Linux | GCC |
+| SH4 | Linux | GCC |
+| SH5/SH64 | Linux | GCC |
| SPARC | Linux | GCC |
| SPARC | Solaris | GCC |
| SPARC | Solaris | Oracle Solaris Studio C |
| SPARC64 | Solaris | Oracle Solaris Studio C |
| TILE-Gx/TILEPro | Linux | GCC |
| VAX | OpenBSD/vax | GCC |
+| WASM32 | Emscripten | EMCC |
+| WASM64 | Emscripten | EMCC |
| X86 | FreeBSD | GCC |
| X86 | GNU HURD | GCC |
| X86 | Interix | GCC |
| X86 | Solaris | GCC |
| X86 | Solaris | Oracle Solaris Studio C |
| X86 | Windows/Cygwin | GCC |
-| X86 | Windows/MingW | GCC |
+| X86 | Windows/MinGW | GCC |
+| X86-64 | DragonFly BSD | GCC |
| X86-64 | FreeBSD | GCC |
| X86-64 | Linux | GCC |
| X86-64 | Linux/x32 | GCC |
| X86-64 | OpenBSD | GCC |
| X86-64 | Solaris | Oracle Solaris Studio C |
| X86-64 | Windows/Cygwin | GCC |
-| X86-64 | Windows/MingW | GCC |
+| X86-64 | Windows/MinGW | GCC |
| X86-64 | Mac OSX | GCC |
| Xtensa | Linux | GCC |
If you're building libffi directly from git hosted sources, configure
won't exist yet; run ./autogen.sh first. This will require that you
-install autoconf, automake and libtool.
+install autoconf, automake, libtool and texinfo.
You may want to tell configure where to install the libffi library and
header files. To do that, use the ``--prefix`` configure switch. Libffi
Microsoft's Visual C++ compiler. In this case, use the msvcc.sh
wrapper script during configuration like so:
- path/to/configure CC=path/to/msvcc.sh CXX=path/to/msvcc.sh LD=link CPP="cl -nologo -EP" CPPFLAGS="-DFFI_BUILDING_DLL"
+ path/to/configure CC=path/to/msvcc.sh CXX=path/to/msvcc.sh LD=link CPP="cl -nologo -EP" CXXCPP="cl -nologo -EP" CPPFLAGS="-DFFI_BUILDING_DLL"
For 64-bit Windows builds, use ``CC="path/to/msvcc.sh -m64"`` and
``CXX="path/to/msvcc.sh -m64"``. You may also need to specify
See the git log for details at http://github.com/libffi/libffi.
- 3.4.2 Jun-28-21
+ 3.5.2 Aug-2-2025
+ Add wasm64 support.
+ Add DragonFly BSD support.
+ Ensure trampoline file descriptors are closed on exec.
+
+ 3.5.1 Jun-10-2025
+ Fix symbol versioning error.
+
+ 3.5.0 Jun-8-2025
+ Add FFI_VERSION_STRING and FFI_VERSION_NUMBER macros, as well
+ as ffi_get_version() and ffi_get_version_number() functions.
+ Add ffi_get_default_abi() and ffi_get_closure_size() functions.
+ Fix closures on powerpc64-linux when statically linking.
+ Mark the PA stack as non-executable.
+
+ 3.4.8 Apr-9-2025
+ Add static trampoline support for powerpc-linux (32-bit SYSV BE),
+ powerpc64-linux (64-bit ELFv1 BE) and
+ powerpc64le-linux (64-bit ELFv2 LE)
+ Various x86-64 bug fixes (x32 ABI and improper memory access for
+ small argument calls).
+ Fix to enable pointer authentication for aarch64.
+
+ 3.4.7 Feb-8-2025
+ Add static trampoline support for Linux on s390x.
+ Fix BTI support for ARM64.
+ Support pointer authentication for ARM64.
+ Fix ASAN compatibility.
+ Fix x86-64 calls with 6 GP registers and some SSE registers.
+ Miscellaneous fixes for ARC and Darwin ARM64.
+ Fix OpenRISC or1k and Solaris 10 builds.
+ Remove nios2 port.
+
+ 3.4.6 Feb-18-2024
+ Fix long double regression on mips64 and alpha.
+
+ 3.4.5 Feb-15-2024
+ Add support for wasm32.
+ Add support for aarch64 branch target identification (bti).
+ Add support for ARCv3: ARC32 & ARC64.
+ Add support for HPPA64, and many HPPA fixes.
+ Add support for Haikuos on PowerPC.
+ Fixes for AIX, loongson, MIPS, power, sparc64, and x86 Darwin.
+
+ 3.4.4 Oct-23-2022
+ Important aarch64 fixes, including support for linux builds
+ with Link Time Optimization (-flto).
+ Fix x86 stdcall stack alignment.
+ Fix x86 Windows msvc assembler compatibility.
+ Fix moxie and or1k small structure args.
+
+ 3.4.3 Sep-19-2022
+ All struct args are passed by value, regardless of size, as per ABIs.
+ Enable static trampolines for Cygwin.
+ Add support for Loongson's LoongArch64 architecture.
+ Fix x32 static trampolines.
+ Fix 32-bit x86 stdcall stack corruption.
+ Fix ILP32 aarch64 support.
+
+ 3.4.2 Jun-28-2021
Add static trampoline support for Linux on x86_64 and ARM64.
Add support for Alibaba's CSKY architecture.
Add support for Kalray's KVX architecture.
Reject float and small integer argument in ffi_prep_cif_var().
Callers must promote these types themselves.
- 3.3 Nov-23-19
+ 3.3 Nov-23-2019
Add RISC-V support.
New API in support of GO closures.
Add IEEE754 binary128 long double support for 64-bit Power
Add pre-built PDF documentation to source distribution.
Many new test cases and bug fixes.
- 3.2.1 Nov-12-14
+ 3.2.1 Nov-12-2014
Build fix for non-iOS AArch64 targets.
- 3.2 Nov-11-14
+ 3.2 Nov-11-2014
Add C99 Complex Type support (currently only supported on
s390).
Add support for PASCAL and REGISTER calling conventions on x86
Add OpenRISC and Cygwin-64 support.
Bug fixes.
- 3.1 May-19-14
+ 3.1 May-19-2014
Add AArch64 (ARM64) iOS support.
Add Nios II support.
Add m88k and DEC VAX support.
Archive off the manually maintained ChangeLog in favor of git
log.
- 3.0.13 Mar-17-13
+ 3.0.13 Mar-17-2013
Add Meta support.
Add missing Moxie bits.
Fix stack alignment bug on 32-bit x86.
with GCC (OS X, Solaris).
Fix Cygwin regression.
- 3.0.12 Feb-11-13
+ 3.0.12 Feb-11-2013
Add Moxie support.
Add AArch64 support.
Add Blackfin support.
Solaris and AIX.
Work around LLVM/GCC interoperability issue on x86_64.
- 3.0.11 Apr-11-12
+ 3.0.11 Apr-11-2012
Lots of build fixes.
Add support for variadic functions (ffi_prep_cif_var).
Add Linux/x32 support.
Fix Octeon and MC68881 support.
Fix code pessimizations.
- 3.0.10 Aug-23-11
+ 3.0.10 Aug-23-2011
Add support for Apple's iOS.
Add support for ARM VFP ABI.
Add RTEMS support for MIPS and M68K.
Testsuite fixes for Tru64 Unix.
Additional platform support.
- 3.0.9 Dec-31-09
+ 3.0.9 Dec-31-2009
Add AVR32 and win64 ports. Add ARM softfp support.
Many fixes for AIX, Solaris, HP-UX, *BSD.
Several PowerPC and x86-64 bug fixes.
Build DLL for windows.
- 3.0.8 Dec-19-08
+ 3.0.8 Dec-19-2008
Add *BSD, BeOS, and PA-Linux support.
- 3.0.7 Nov-11-08
+ 3.0.7 Nov-11-2008
Fix for ppc FreeBSD.
(thanks to Andreas Tobler)
- 3.0.6 Jul-17-08
+ 3.0.6 Jul-17-2008
Fix for closures on sh.
Mark the sh/sh64 stack as non-executable.
(both thanks to Kaz Kojima)
- 3.0.5 Apr-3-08
+ 3.0.5 Apr-3-2008
Fix libffi.pc file.
Fix #define ARM for IcedTea users.
Fix x86 closure bug.
- 3.0.4 Feb-24-08
+ 3.0.4 Feb-24-2008
Fix x86 OpenBSD configury.
- 3.0.3 Feb-22-08
+ 3.0.3 Feb-22-2008
Enable x86 OpenBSD thanks to Thomas Heller, and
x86-64 FreeBSD thanks to Björn König and Andreas Tobler.
Clean up test instruction in README.
- 3.0.2 Feb-21-08
+ 3.0.2 Feb-21-2008
Improved x86 FreeBSD support.
Thanks to Björn König.
- 3.0.1 Feb-15-08
+ 3.0.1 Feb-15-2008
Fix instruction cache flushing bug on MIPS.
Thanks to David Daney.
- 3.0.0 Feb-15-08
+ 3.0.0 Feb-15-2008
Many changes, mostly thanks to the GCC project.
Cygnus Solutions is now Red Hat.
[10 years go by...]
- 1.20 Oct-5-98
+ 1.20 Oct-5-1998
Raffaele Sena produces ARM port.
- 1.19 Oct-5-98
+ 1.19 Oct-5-1998
Fixed x86 long double and long long return support.
m68k bug fixes from Andreas Schwab.
Patch for DU assembler compatibility for the Alpha from Richard
Henderson.
- 1.18 Apr-17-98
+ 1.18 Apr-17-1998
Bug fixes and MIPS configuration changes.
- 1.17 Feb-24-98
+ 1.17 Feb-24-1998
Bug fixes and m68k port from Andreas Schwab. PowerPC port from
Geoffrey Keating. Various bug x86, Sparc and MIPS bug fixes.
- 1.16 Feb-11-98
+ 1.16 Feb-11-1998
Richard Henderson produces Alpha port.
- 1.15 Dec-4-97
+ 1.15 Dec-4-1997
Fixed an n32 ABI bug. New libtool, auto* support.
1.14 May-13-97
Fixed a minor portability problem reported by Russ McManus
<mcmanr@eq.gs.com>.
- 1.13 Dec-2-96
+ 1.13 Dec-2-1996
Added --enable-purify-safety to keep Purify from complaining
about certain low level code.
Sparc fix for calling functions with < 6 args.
Linux x86 a.out fix.
- 1.12 Nov-22-96
+ 1.12 Nov-22-1996
Added missing ffi_type_void, needed for supporting void return
types. Fixed test case for non MIPS machines. Cygnus Support
is now Cygnus Solutions.
- 1.11 Oct-30-96
+ 1.11 Oct-30-1996
Added notes about GNU make.
- 1.10 Oct-29-96
+ 1.10 Oct-29-1996
Added configuration fix for non GNU compilers.
- 1.09 Oct-29-96
+ 1.09 Oct-29-1996
Added --enable-debug configure switch. Clean-ups based on LCLint
feedback. ffi_mips.h is always installed. Many configuration
fixes. Fixed ffitest.c for sparc builds.
- 1.08 Oct-15-96
+ 1.08 Oct-15-1996
Fixed n32 problem. Many clean-ups.
- 1.07 Oct-14-96
+ 1.07 Oct-14-1996
Gordon Irlam rewrites v8.S again. Bug fixes.
- 1.06 Oct-14-96
+ 1.06 Oct-14-1996
Gordon Irlam improved the sparc port.
- 1.05 Oct-14-96
+ 1.05 Oct-14-1996
Interface changes based on feedback.
- 1.04 Oct-11-96
+ 1.04 Oct-11-1996
Sparc port complete (modulo struct passing bug).
- 1.03 Oct-10-96
+ 1.03 Oct-10-1996
Passing struct args, and returning struct values works for
all architectures/calling conventions. Expanded tests.
- 1.02 Oct-9-96
+ 1.02 Oct-9-1996
Added SGI n32 support. Fixed bugs in both o32 and Linux support.
Added "make test".
- 1.01 Oct-8-96
+ 1.01 Oct-8-1996
Fixed float passing bug in mips version. Restructured some
of the code. Builds cleanly with SGI tools.
- 1.00 Oct-7-96
+ 1.00 Oct-7-1996
First release. No public announcement.
Authors & Credits
frv Anthony Green
ia64 Hans Boehm
kvx Yann Sionneau
+ loongarch64 Cheng Lulu, Xi Ruoyao, Xu Hao,
+ Zhang Wenlong, Pan Xuefeng
m32r Kazuhiro Inaoka
m68k Andreas Schwab
m88k Miod Vallat
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
sparc Anthony Green, Gordon Irlam
tile-gx/tilepro Walter Lee
vax Miod Vallat
+ wasm32 Hood Chatham, Brion Vibber, Kleis Auke Wolthuizen
x86 Anthony Green, Jon Beniston
x86-64 Bo Thorsen
xtensa Chris Zankel
# Unlike /dev/zero, the MAP_ANON(YMOUS) defines can be probed for.
AC_CACHE_CHECK([for MAP_ANON(YMOUS)], ac_cv_decl_map_anon,
- [AC_TRY_COMPILE(
-[#include <sys/types.h>
+ [AC_COMPILE_IFELSE(
+[AC_LANG_PROGRAM([[#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
-],
-[int n = MAP_ANONYMOUS;],
+]],
+[[int n = MAP_ANONYMOUS;]])],
ac_cv_decl_map_anon=yes,
ac_cv_decl_map_anon=no)])
dnl
m4_define([LIBFFI_ENABLE],[dnl
m4_define([_g_switch],[--enable-$1])dnl
-m4_define([_g_help],[AC_HELP_STRING(_g_switch$3,[$4 @<:@default=$2@:>@])])dnl
+m4_define([_g_help],[AS_HELP_STRING([_g_switch$3],[$4 @<:@default=$2@:>@])])dnl
AC_ARG_ENABLE($1,_g_help,
m4_bmatch([$5],
[^permit ],
# by now (in libtool), but require it now just to be safe...
test -z "$SECTION_LDFLAGS" && SECTION_LDFLAGS=''
test -z "$OPT_LDFLAGS" && OPT_LDFLAGS=''
- AC_REQUIRE([AC_PROG_LD])
- AC_REQUIRE([AC_PROG_AWK])
-
- # The name set by libtool depends on the version of libtool. Shame on us
- # for depending on an impl detail, but c'est la vie. Older versions used
- # ac_cv_prog_gnu_ld, but now it's lt_cv_prog_gnu_ld, and is copied back on
- # top of with_gnu_ld (which is also set by --with-gnu-ld, so that actually
- # makes sense). We'll test with_gnu_ld everywhere else, so if that isn't
- # set (hence we're using an older libtool), then set it.
- if test x${with_gnu_ld+set} != xset; then
- if test x${ac_cv_prog_gnu_ld+set} != xset; then
- # We got through "ac_require(ac_prog_ld)" and still not set? Huh?
- with_gnu_ld=no
- else
- with_gnu_ld=$ac_cv_prog_gnu_ld
- fi
- fi
-
- # Start by getting the version number. I think the libtool test already
- # does some of this, but throws away the result.
- libat_ld_is_gold=no
- if $LD --version 2>/dev/null | grep 'GNU gold'> /dev/null 2>&1; then
- libat_ld_is_gold=yes
- fi
- changequote(,)
- ldver=`$LD --version 2>/dev/null |
- sed -e 's/GNU gold /GNU ld /;s/GNU ld version /GNU ld /;s/GNU ld ([^)]*) /GNU ld /;s/GNU ld \([0-9.][0-9.]*\).*/\1/; q'`
- changequote([,])
- libat_gnu_ld_version=`echo $ldver | \
- $AWK -F. '{ if (NF<3) [$]3=0; print ([$]1*100+[$]2)*100+[$]3 }'`
-
- # Set --gc-sections.
- if test "$with_gnu_ld" = "notbroken"; then
- # GNU ld it is! Joy and bunny rabbits!
-
- # All these tests are for C++; save the language and the compiler flags.
- # Need to do this so that g++ won't try to link in libstdc++
- ac_test_CFLAGS="${CFLAGS+set}"
- ac_save_CFLAGS="$CFLAGS"
- CFLAGS='-x c++ -Wl,--gc-sections'
-
- # Check for -Wl,--gc-sections
- # XXX This test is broken at the moment, as symbols required for linking
- # are now in libsupc++ (not built yet). In addition, this test has
- # cored on solaris in the past. In addition, --gc-sections doesn't
- # really work at the moment (keeps on discarding used sections, first
- # .eh_frame and now some of the glibc sections for iconv).
- # Bzzzzt. Thanks for playing, maybe next time.
- AC_MSG_CHECKING([for ld that supports -Wl,--gc-sections])
- AC_TRY_RUN([
- int main(void)
- {
- try { throw 1; }
- catch (...) { };
- return 0;
- }
- ], [ac_sectionLDflags=yes],[ac_sectionLDflags=no], [ac_sectionLDflags=yes])
- if test "$ac_test_CFLAGS" = set; then
- CFLAGS="$ac_save_CFLAGS"
- else
- # this is the suspicious part
- CFLAGS=''
- fi
- if test "$ac_sectionLDflags" = "yes"; then
- SECTION_LDFLAGS="-Wl,--gc-sections $SECTION_LDFLAGS"
- fi
- AC_MSG_RESULT($ac_sectionLDflags)
- fi
-
- # Set linker optimization flags.
- if test x"$with_gnu_ld" = x"yes"; then
- OPT_LDFLAGS="-Wl,-O1 $OPT_LDFLAGS"
- fi
-
- AC_SUBST(SECTION_LDFLAGS)
- AC_SUBST(OPT_LDFLAGS)
-])
-
-
-dnl
-dnl If GNU ld is in use, check to see if tricky linker opts can be used. If
-dnl the native linker is in use, all variables will be defined to something
-dnl safe (like an empty string).
-dnl
-dnl Defines:
-dnl SECTION_LDFLAGS='-Wl,--gc-sections' if possible
-dnl OPT_LDFLAGS='-Wl,-O1' if possible
-dnl LD (as a side effect of testing)
-dnl Sets:
-dnl with_gnu_ld
-dnl libat_ld_is_gold (possibly)
-dnl libat_gnu_ld_version (possibly)
-dnl
-dnl The last will be a single integer, e.g., version 1.23.45.0.67.89 will
-dnl set libat_gnu_ld_version to 12345. Zeros cause problems.
-dnl
-AC_DEFUN([LIBFFI_CHECK_LINKER_FEATURES], [
- # If we're not using GNU ld, then there's no point in even trying these
- # tests. Check for that first. We should have already tested for gld
- # by now (in libtool), but require it now just to be safe...
- test -z "$SECTION_LDFLAGS" && SECTION_LDFLAGS=''
- test -z "$OPT_LDFLAGS" && OPT_LDFLAGS=''
- AC_REQUIRE([AC_PROG_LD])
+ AC_REQUIRE([LT_PATH_LD])
AC_REQUIRE([AC_PROG_AWK])
# The name set by libtool depends on the version of libtool. Shame on us
if $LD --version 2>/dev/null | grep 'LLD '> /dev/null 2>&1; then
libat_ld_is_lld=yes
fi
+ libat_ld_is_mold=no
+ if $LD --version 2>/dev/null | grep 'mold '> /dev/null 2>&1; then
+ libat_ld_is_mold=yes
+ fi
changequote(,)
ldver=`$LD --version 2>/dev/null |
sed -e 's/GNU gold /GNU ld /;s/GNU ld version /GNU ld /;s/GNU ld ([^)]*) /GNU ld /;s/GNU ld \([0-9.][0-9.]*\).*/\1/; q'`
# .eh_frame and now some of the glibc sections for iconv).
# Bzzzzt. Thanks for playing, maybe next time.
AC_MSG_CHECKING([for ld that supports -Wl,--gc-sections])
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
int main(void)
{
try { throw 1; }
catch (...) { };
return 0;
}
- ], [ac_sectionLDflags=yes],[ac_sectionLDflags=no], [ac_sectionLDflags=yes])
+ ]])],[ac_sectionLDflags=yes],[ac_sectionLDflags=no],[ac_sectionLDflags=yes])
if test "$ac_test_CFLAGS" = set; then
CFLAGS="$ac_save_CFLAGS"
else
AC_MSG_CHECKING([for shared libgcc])
ac_save_CFLAGS="$CFLAGS"
CFLAGS=' -lgcc_s'
- AC_TRY_LINK(, [return 0;], libat_shared_libgcc=yes, libat_shared_libgcc=no)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[libat_shared_libgcc=yes],[libat_shared_libgcc=no])
CFLAGS="$ac_save_CFLAGS"
if test $libat_shared_libgcc = no; then
cat > conftest.c <<EOF
rm -f conftest.c conftest.so
if test x${libat_libgcc_s_suffix+set} = xset; then
CFLAGS=" -lgcc_s$libat_libgcc_s_suffix"
- AC_TRY_LINK(, [return 0;], libat_shared_libgcc=yes)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[return 0;]])],[libat_shared_libgcc=yes],[])
CFLAGS="$ac_save_CFLAGS"
fi
fi
enable_symvers=gnu
elif test $libat_ld_is_lld = yes ; then
enable_symvers=gnu
+ elif test $libat_ld_is_mold = yes ; then
+ enable_symvers=gnu
else
# The right tools, the right setup, but too old. Fallbacks?
AC_MSG_WARN(=== Linker version $libat_gnu_ld_version is too old for)
dnl Process this with autoconf to create configure
-AC_PREREQ(2.68)
+AC_PREREQ([2.68])
-AC_INIT([libffi], [3.4.2], [http://github.com/libffi/libffi/issues])
+AC_INIT([libffi],[3.5.2],[http://github.com/libffi/libffi/issues])
AC_CONFIG_HEADERS([fficonfig.h])
-AM_ENABLE_MULTILIB(, ..)
+FFI_VERSION_STRING="3.5.2"
+FFI_VERSION_NUMBER=30502
+AC_SUBST(FFI_VERSION_STRING)
+AC_SUBST(FFI_VERSION_NUMBER)
-AC_CANONICAL_SYSTEM
+AC_CANONICAL_TARGET
target_alias=${target_alias-$host_alias}
-AM_INIT_AUTOMAKE([no-dist])
-
-# We would like our source tree to be readonly. However when releases or
-# pre-releases are generated, the flex/bison generated files as well as the
-# various formats of manuals need to be included along with the rest of the
-# sources. Therefore we have --enable-generated-files-in-srcdir to do
-# just that.
-AC_MSG_CHECKING(generated-files-in-srcdir)
-AC_ARG_ENABLE(generated-files-in-srcdir,
-AS_HELP_STRING([--enable-generated-files-in-srcdir],
- [put copies of generated files in source dir intended for creating source tarballs for users without texinfo bison or flex]),
-[case "$enableval" in
- yes) enable_generated_files_in_srcdir=yes ;;
- no) enable_generated_files_in_srcdir=no ;;
- *) AC_MSG_ERROR([Unknown argument to enable/disable version-specific libs]);;
- esac],
-[enable_generated_files_in_srcdir=no])
-AC_MSG_RESULT($enable_generated_files_in_srcdir)
-AM_CONDITIONAL(GENINSRC, test "$enable_generated_files_in_srcdir" = yes)
+case "${host}" in
+ frv*-elf)
+ LDFLAGS=`echo $LDFLAGS | sed "s/\-B[^ ]*libgloss\/frv\///"`\ -B`pwd`/../libgloss/frv/
+ ;;
+esac
+
+AX_ENABLE_BUILDDIR
+
+AM_INIT_AUTOMAKE
# The same as in boehm-gc and libstdc++. Have to borrow it from there.
# We must force CC to /not/ be precious variables; otherwise
AC_SUBST(CFLAGS)
-# Add CET specific flags if CET is enabled
-GCC_CET_FLAGS(CET_FLAGS)
-AC_SUBST(CET_FLAGS)
-
AM_PROG_AS
AM_PROG_CC_C_O
-AC_PROG_LIBTOOL
-AM_CONDITIONAL([ENABLE_DARWIN_AT_RPATH], [test x$enable_darwin_at_rpath = xyes])
+AC_PROG_EGREP
+LT_INIT
+AC_CONFIG_MACRO_DIR([m4])
-AC_CHECK_TOOL(READELF, readelf)
+AC_CHECK_TOOLS([READELF], [readelf greadelf])
# Test for 64-bit build.
AC_CHECK_SIZEOF([size_t])
-AC_SUBST(SYSROOT_CFLAGS_FOR_TARGET)
+AX_COMPILER_VENDOR
+AX_CC_MAXOPT
+# The AX_CFLAGS_WARN_ALL macro doesn't currently work for sunpro
+# compiler.
+if test "$ax_cv_c_compiler_vendor" != "sun"; then
+ AX_CFLAGS_WARN_ALL
+fi
+
+if test "x$GCC" = "xyes"; then
+ CFLAGS="$CFLAGS -fexceptions"
+fi
cat > local.exp <<EOF
-set SYSROOT_CFLAGS_FOR_TARGET "$SYSROOT_CFLAGS_FOR_TARGET"
+set CC_FOR_TARGET "$CC"
+set CXX_FOR_TARGET "$CXX"
set compiler_vendor "$ax_cv_c_compiler_vendor"
EOF
AC_CHECK_HEADERS(sys/memfd.h)
AC_CHECK_FUNCS([memfd_create])
-AC_CHECK_HEADERS(sys/mman.h)
-AC_CHECK_FUNCS([mmap mkostemp mkstemp])
-AC_FUNC_MMAP_BLACKLIST
-
dnl The -no-testsuite modules omit the test subdir.
AM_CONDITIONAL(TESTSUBDIR, test -d $srcdir/testsuite)
AC_SUBST(AM_RUNTESTFLAGS)
AC_SUBST(AM_LTLDFLAGS)
-AC_HEADER_STDC
+m4_warn([obsolete],
+[The preprocessor macro `STDC_HEADERS' is obsolete.
+ Except in unusual embedded environments, you can safely include all
+ ISO C90 headers unconditionally.])dnl
+
AC_CHECK_FUNCS(memcpy)
-AC_FUNC_ALLOCA
+AC_CHECK_HEADERS(alloca.h)
AC_CHECK_SIZEOF(double)
AC_CHECK_SIZEOF(long double)
GCC_AS_CFI_PSEUDO_OP
+AC_ARG_VAR([WASM64_MEMORY64], [Used only for the wasm64 target. Set to 1 (default) or 2 for Emscripten's -sMEMORY64 mode])
+
case "$TARGET" in
SPARC)
AC_CACHE_CHECK([assembler and linker support unaligned pc related relocs],
save_LDFLAGS="$LDFLAGS"
CFLAGS="$CFLAGS -fpic"
LDFLAGS="$LDFLAGS -shared"
- AC_TRY_LINK([asm (".text; foo: nop; .data; .align 4; .byte 0; .uaword %r_disp32(foo); .text");],,
- [libffi_cv_as_sparc_ua_pcrel=yes],
- [libffi_cv_as_sparc_ua_pcrel=no])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[asm (".text; foo: nop; .data; .align 4; .byte 0; .uaword %r_disp32(foo); .text");]], [[]])],[libffi_cv_as_sparc_ua_pcrel=yes],[libffi_cv_as_sparc_ua_pcrel=no])
CFLAGS="$save_CFLAGS"
LDFLAGS="$save_LDFLAGS"])
if test "x$libffi_cv_as_sparc_ua_pcrel" = xyes; then
libffi_cv_as_register_pseudo_op, [
libffi_cv_as_register_pseudo_op=unknown
# Check if we have .register
- AC_TRY_COMPILE(,[asm (".register %g2, #scratch");],
- [libffi_cv_as_register_pseudo_op=yes],
- [libffi_cv_as_register_pseudo_op=no])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[asm (".register %g2, #scratch");]])],[libffi_cv_as_register_pseudo_op=yes],[libffi_cv_as_register_pseudo_op=no])
])
if test "x$libffi_cv_as_register_pseudo_op" = xyes; then
AC_DEFINE(HAVE_AS_REGISTER_PSEUDO_OP, 1,
libffi_cv_as_s390_zarch=no
echo 'void foo(void) { bar(); bar(); }' > conftest.c
if $CC $CFLAGS -S conftest.c > /dev/null 2>&1; then
- if grep -q brasl conftest.s; then
+ if $GREP -q brasl conftest.s; then
libffi_cv_as_s390_zarch=yes
fi
fi
[Define if the compiler uses zarch features.])
fi
;;
+ wasm64)
+ if test -z "$WASM64_MEMORY64"; then
+ WASM64_MEMORY64=1
+ fi
+ CFLAGS="$CFLAGS -sMEMORY64=$WASM64_MEMORY64"
+ ;;
esac
AC_CACHE_CHECK([whether compiler supports pointer authentication],
libffi_cv_as_ptrauth, [
libffi_cv_as_ptrauth=unknown
- AC_TRY_COMPILE(,[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
#ifdef __clang__
# if __has_feature(ptrauth_calls)
-# define HAVE_PTRAUTH 1
+# define HAVE_ARM64E_PTRAUTH 1
# endif
#endif
-#ifndef HAVE_PTRAUTH
+#ifndef HAVE_ARM64E_PTRAUTH
# error Pointer authentication not supported
#endif
- ],
- [libffi_cv_as_ptrauth=yes],
- [libffi_cv_as_ptrauth=no])
+ ]])],[libffi_cv_as_ptrauth=yes],[libffi_cv_as_ptrauth=no])
])
if test "x$libffi_cv_as_ptrauth" = xyes; then
- AC_DEFINE(HAVE_PTRAUTH, 1,
+ AC_DEFINE(HAVE_ARM64E_PTRAUTH, 1,
[Define if your compiler supports pointer authentication.])
fi
-# On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC.
+# On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC;
+# if EMUTRAMP is active too ffi could try mapping without PROT_EXEC,
+# but the kernel needs to recognize the trampoline generated by ffi.
+# Otherwise fallback to double mmap trick.
AC_ARG_ENABLE(pax_emutramp,
- [ --enable-pax_emutramp enable pax emulated trampolines, for we can't use PROT_EXEC],
+ [ --enable-pax_emutramp enable pax emulated trampolines (experimental)],
if test "$enable_pax_emutramp" = "yes"; then
+ AC_MSG_WARN([EMUTRAMP is experimental only. Use --enable-pax_emutramp=experimental to enforce.])
+ elif test "$enable_pax_emutramp" = "experimental"; then
AC_DEFINE(FFI_MMAP_EXEC_EMUTRAMP_PAX, 1,
- [Define this if you want to enable pax emulated trampolines])
+ [Define this if you want to enable pax emulated trampolines (experimental)])
fi)
+LT_SYS_SYMBOL_USCORE
+if test "x$sys_symbol_underscore" = xyes; then
+ AC_DEFINE(SYMBOL_UNDERSCORE, 1, [Define if symbols are underscored.])
+fi
+
FFI_EXEC_TRAMPOLINE_TABLE=0
case "$target" in
*arm*-apple-* | aarch64-apple-*)
[Cannot use PROT_EXEC on this target, so, we revert to
alternative means])
;;
- *-apple-* | *-*-freebsd* | *-*-kfreebsd* | *-*-openbsd* | *-pc-solaris* | *-linux-android*)
+ *-apple-* | *-*-dragonfly* | *-*-freebsd* | *-*-kfreebsd* | *-*-openbsd* | *-pc-solaris* | *-linux-android*)
AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1,
[Cannot use malloc on this target, so, we revert to
alternative means])
fi
if test "x$GCC" = "xyes"; then
+ AX_CHECK_COMPILE_FLAG(-fno-lto, libffi_cv_no_lto=-fno-lto)
+
AC_CACHE_CHECK([whether .eh_frame section should be read-only],
libffi_cv_ro_eh_frame, [
libffi_cv_ro_eh_frame=yes
echo 'extern void foo (void); void bar (void) { foo (); foo (); }' > conftest.c
- if $CC $CFLAGS -c -fpic -fexceptions -fno-lto -o conftest.o conftest.c > /dev/null 2>&1; then
- if $READELF -WS conftest.o | grep -q -n 'eh_frame .* WA'; then
+ if $CC $CFLAGS -c -fpic -fexceptions $libffi_cv_no_lto -o conftest.o conftest.c > /dev/null 2>&1; then
+ if $READELF -WS conftest.o 2>/dev/null | $GREP -q -n 'eh_frame .* WA'; then
libffi_cv_ro_eh_frame=no
fi
fi
echo 'int __attribute__ ((visibility ("hidden"))) foo (void) { return 1 ; }' > conftest.c
libffi_cv_hidden_visibility_attribute=no
if AC_TRY_COMMAND(${CC-cc} -Werror -S conftest.c -o conftest.s 1>&AS_MESSAGE_LOG_FD); then
- if egrep '(\.hidden|\.private_extern).*foo' conftest.s >/dev/null; then
+ if $EGREP '(\.hidden|\.private_extern).*foo|foo.*,hidden' conftest.s >/dev/null; then
libffi_cv_hidden_visibility_attribute=yes
fi
fi
fi
fi
-# See if makeinfo has been installed and is modern enough
-# that we can use it.
-ACX_CHECK_PROG_VER([MAKEINFO], [makeinfo], [--version],
- [GNU texinfo.* \([0-9][0-9.]*\)],
- [4.[4-9]*|4.[1-9][0-9]*|[5-9]*|[1-9][0-9]*])
-AM_CONDITIONAL(BUILD_DOCS, test $gcc_cv_prog_makeinfo_modern = "yes")
+AC_ARG_ENABLE(docs,
+ AS_HELP_STRING([--disable-docs],[Disable building of docs (default: no)]),
+ [enable_docs=no],
+ [enable_docs=yes])
+AM_CONDITIONAL(BUILD_DOCS, [test x$enable_docs = xyes])
AH_BOTTOM([
#ifdef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE
fi)
AC_ARG_ENABLE(exec-static-tramp,
-[ --enable-exec-static-tramp enable use of static exec trampolines])
+[ --disable-exec-static-tramp disable use of static exec trampolines (enabled by default)])
-if test "$enable_exec_static_tramp" = yes; then
+if test "$enable_exec_static_tramp" != no; then
case "$target" in
- *-cygwin*)
+ *-cygwin* | *-msys*)
+ # Only define static trampolines if we are using the cygwin runtime.
+ # Will this need to be changed for mingw?
+ if test "x$GCC" = "xyes"; then
+ AC_DEFINE(FFI_EXEC_STATIC_TRAMP, 1,
+ [Define this if you want statically defined trampolines])
+ fi
;;
- *arm*-*-linux-* | aarch64*-*-linux-* | i*86-*-linux-* | x86_64-*-linux-*)
+ *arm*-*-linux-* | aarch64*-*-linux-* | i*86-*-linux-* | x86_64-*-linux-* \
+ | loongarch*-*-linux-* | s390x*-linux-* | powerpc*-linux-*)
AC_DEFINE(FFI_EXEC_STATIC_TRAMP, 1,
[Define this if you want statically defined trampolines])
;;
AC_DEFINE(USING_PURIFY, 1, [Define this if you are using Purify and want to suppress spurious messages.])
fi)
-GCC_WITH_TOOLEXECLIBDIR
-
-if test -n "$with_cross_host" &&
- test x"$with_cross_host" != x"no"; then
- toolexecdir='$(exec_prefix)/$(target_alias)'
- case ${with_toolexeclibdir} in
- no)
- toolexeclibdir='$(toolexecdir)/lib'
- ;;
- *)
- toolexeclibdir=${with_toolexeclibdir}
- ;;
- esac
+AC_ARG_ENABLE(multi-os-directory,
+[ --disable-multi-os-directory
+ disable use of gcc --print-multi-os-directory to change the library installation directory])
+
+# These variables are only ever used when we cross-build to X86_WIN32.
+# And we only support this with GCC, so...
+if test "x$GCC" = "xyes"; then
+ if test -n "$with_cross_host" &&
+ test x"$with_cross_host" != x"no"; then
+ toolexecdir='${exec_prefix}'/'$(target_alias)'
+ toolexeclibdir='${toolexecdir}'/lib
+ else
+ toolexecdir='${libdir}'/gcc-lib/'$(target_alias)'
+ toolexeclibdir='${libdir}'
+ fi
+ if test x"$enable_multi_os_directory" != x"no"; then
+ multi_os_directory=`$CC $CFLAGS -print-multi-os-directory`
+ case $multi_os_directory in
+ .) ;; # Avoid trailing /.
+ ../*) toolexeclibdir=$toolexeclibdir/$multi_os_directory ;;
+ esac
+ fi
+ AC_SUBST(toolexecdir)
else
- toolexecdir='$(libdir)/gcc-lib/$(target_alias)'
- toolexeclibdir='$(libdir)'
+ toolexeclibdir='${libdir}'
fi
-multi_os_directory=`$CC -print-multi-os-directory`
-case $multi_os_directory in
- .) ;; # Avoid trailing /.
- *) toolexeclibdir=$toolexeclibdir/$multi_os_directory ;;
-esac
-AC_SUBST(toolexecdir)
AC_SUBST(toolexeclibdir)
-if test "${multilib}" = "yes"; then
- multilib_arg="--enable-multilib"
-else
- multilib_arg=
-fi
+# Conditionalize the makefile for this target machine.
+tmake_file_=
+for f in ${tmake_file}; do
+ if test -f ${srcdir}/src/$TARGETDIR/$f; then
+ tmake_file_="${tmake_file_} \$(srcdir)/src/$TARGETDIR/$f"
+ fi
+done
+tmake_file="${tmake_file_}"
+AC_SUBST(tmake_file)
# Check linker support.
LIBFFI_ENABLE_SYMVERS
-# Determine what GCC version number to use in filesystem paths.
-GCC_BASE_VER
-
AC_CONFIG_COMMANDS(include, [test -d include || mkdir include])
AC_CONFIG_COMMANDS(src, [
test -d src || mkdir src
test -d src/$TARGETDIR || mkdir src/$TARGETDIR
], [TARGETDIR="$TARGETDIR"])
-AC_CONFIG_FILES(include/Makefile include/ffi.h Makefile testsuite/Makefile man/Makefile libffi.pc)
-
-AC_CONFIG_LINKS(include/ffitarget.h:src/$TARGETDIR/ffitarget.h)
+AC_CONFIG_FILES(include/Makefile include/ffi.h Makefile testsuite/Makefile man/Makefile doc/Makefile libffi.pc)
AC_OUTPUT
+
+# Copy this file instead of using AC_CONFIG_LINK in order to support
+# compiling with MSVC, which won't understand cygwin style symlinks.
+cp ${srcdir}/src/$TARGETDIR/ffitarget.h include/ffitarget.h
# THIS TABLE IS SORTED. KEEP IT THAT WAY.
# Most of the time we can define all the variables all at once...
case "${host}" in
- aarch64*-*-cygwin* | aarch64*-*-mingw* | aarch64*-*-win* )
+ aarch64*-*-cygwin* | aarch64*-*-msys* | aarch64*-*-mingw* | aarch64*-*-win* )
TARGET=ARM_WIN64; TARGETDIR=aarch64
if test "${ax_cv_c_compiler_vendor}" = "microsoft"; then
MSVC=1
SOURCES="ffi.c arcompact.S"
;;
- arm*-*-cygwin* | arm*-*-mingw* | arm*-*-win* )
+ arm*-*-cygwin* | arm*-*-msys* | arm*-*-mingw* | arm*-*-win* )
TARGET=ARM_WIN32; TARGETDIR=arm
if test "${ax_cv_c_compiler_vendor}" = "microsoft"; then
MSVC=1
;;
hppa*64-*-hpux*)
TARGET=PA64_HPUX; TARGETDIR=pa
+ SOURCES="ffi64.c hpux64.S"
;;
hppa*-*-hpux*)
TARGET=PA_HPUX; TARGETDIR=pa
TARGET=X86_FREEBSD; TARGETDIR=x86
;;
- i?86-*-cygwin* | i?86-*-mingw* | i?86-*-win* | i?86-*-os2* | i?86-*-interix* \
- | x86_64-*-cygwin* | x86_64-*-mingw* | x86_64-*-win* )
+ i?86-*-cygwin* | i?86-*-msys* | i?86-*-mingw* | i?86-*-win* | i?86-*-os2* | i?86-*-interix* \
+ | x86_64-*-cygwin* | x86_64-*-msys* | x86_64-*-mingw* | x86_64-*-win* )
TARGETDIR=x86
if test $ac_cv_sizeof_size_t = 4; then
TARGET=X86_WIN32
TARGET=MIPS; TARGETDIR=mips
;;
- nios2*-linux*)
- TARGET=NIOS2; TARGETDIR=nios2
- SOURCES="ffi.c sysv.S"
- ;;
-
or1k*-*-*)
TARGET=OR1K; TARGETDIR=or1k
SOURCES="ffi.c sysv.S"
powerpc-*-eabi*)
TARGET=POWERPC; TARGETDIR=powerpc
;;
- powerpc-*-beos*)
+ powerpc-*-beos* | powerpc-*-haiku*)
TARGET=POWERPC; TARGETDIR=powerpc
;;
powerpc-*-darwin* | powerpc64-*-darwin*)
;;
powerpc-*-aix* | rs6000-*-aix*)
TARGET=POWERPC_AIX; TARGETDIR=powerpc
+ # Create AIX-style "FAT" libraries.
+ tmake_file="t-aix"
;;
powerpc-*-freebsd* | powerpc-*-openbsd* | powerpc-*-netbsd*)
TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc
SOURCES="ffi.c elfbsd.S"
;;
+ wasm32-*-*)
+ TARGET=wasm32; TARGETDIR=wasm
+ SOURCES="ffi.c"
+ ;;
+
+ wasm64-*-*)
+ TARGET=wasm64; TARGETDIR=wasm
+ SOURCES="ffi.c"
+ ;;
+
xtensa*-*)
TARGET=XTENSA; TARGETDIR=xtensa
SOURCES="ffi.c sysv.S"
This manual is for libffi, a portable foreign function interface
library.
-Copyright @copyright{} 2008--2019, 2021 Anthony Green and Red Hat, Inc.
+Copyright @copyright{} 2008--2025 Anthony Green and Red Hat, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Some programs may not know at the time of compilation what arguments
are to be passed to a function. For instance, an interpreter may be
told at run-time about the number and types of arguments used to call
-a given function. @samp{Libffi} can be used in such programs to
+a given function. @code{libffi} can be used in such programs to
provide a bridge from the interpreter program to compiled code.
-The @samp{libffi} library provides a portable, high level programming
+The @code{libffi} library provides a portable, high level programming
interface to various calling conventions. This allows a programmer to
call any function specified by a call interface description at run
time.
@acronym{FFI} stands for Foreign Function Interface. A foreign
function interface is the popular name for the interface that allows
code written in one language to call code written in another language.
-The @samp{libffi} library really only provides the lowest, machine
+The @code{libffi} library really only provides the lowest, machine
dependent layer of a fully featured foreign function interface. A
-layer must exist above @samp{libffi} that handles type conversions for
+layer must exist above @code{libffi} that handles type conversions for
values passed between the two languages.
@cindex FFI
@cindex Foreign Function Interface
@node The Basics
@section The Basics
-@samp{Libffi} assumes that you have a pointer to the function you wish
+@code{libffi} assumes that you have a pointer to the function you wish
to call and that you know the number and types of arguments to pass
it, as well as the return type of the function.
@code{void} (using @code{ffi_type_void}), then @var{rvalue} is
ignored.
-In most situations, @samp{libffi} will handle promotion according to
+In most situations, @code{libffi} will handle promotion according to
the ABI. However, for historical reasons, there is a special case
with return values that must be handled by your code. In particular,
for integral (not @code{struct}) types that are narrower than the
system register size, the return value will be widened by
-@samp{libffi}. @samp{libffi} provides a type, @code{ffi_arg}, that
+@code{libffi}. @code{libffi} provides a type, @code{ffi_arg}, that
can be used as the return type. For example, if the CIF was defined
-with a return type of @code{char}, @samp{libffi} will try to store a
+with a return type of @code{char}, @code{libffi} will try to store a
full @code{ffi_arg} into the return value.
@var{avalues} is a vector of @code{void *} pointers that point to the
memory locations holding the argument values for a call. If @var{cif}
declares that the function has no arguments (i.e., @var{nargs} was 0),
-then @var{avalues} is ignored. Note that argument values may be
-modified by the callee (for instance, structs passed by value); the
-burden of copying pass-by-value arguments is placed on the caller.
+then @var{avalues} is ignored.
Note that while the return value must be register-sized, arguments
should exactly match their declared type. For example, if an argument
a larger type -- usually @code{ffi_arg}.
@end defun
+@findex ffi_get_version
+@defun {const char *} ffi_get_version (void)
+Returns the library version as a string. This string is also
+available at build time as the macro @code{FFI_VERSION_STRING}.
+@end defun
+
+@findex ffi_get_version_number
+@defun {unsigned long} ffi_get_version_number (void)
+Returns the library version as an unsigned long value where
+version ``x.y.z'' is represented as the number x*10000+y*100+z.
+This number is also available at build time as the macro
+@code{FFI_VERSION_NUMBER}.
+@end defun
+
+@findex ffi_get_default_abi
+@defun {unsigned int} ffi_get_default_abi (void)
+Return the value of @code{FFI_DEFAULT_ABI}.
+@end defun
+
+@findex ffi_get_closure_size
+@defun {size_t} ffi_get_closure_size (void)
+Return @code{sizeof(ffi_closure)}.
+@end defun
@node Simple Example
@section Simple Example
&ffi_type_sint, args) == FFI_OK)
@{
s = "Hello World!";
- ffi_call(&cif, puts, &rc, values);
+ ffi_call(&cif, (void(*)())puts, &rc, values);
/* rc now holds the result of the call to puts */
/* values holds a pointer to the function's arg, so to
call puts() again all we need to do is change the
value of s */
s = "This is cool!";
- ffi_call(&cif, puts, &rc, values);
+ ffi_call(&cif, (void(*)())puts, &rc, values);
@}
return 0;
@node Structures
@subsection Structures
-@samp{libffi} is perfectly happy passing structures back and forth.
-You must first describe the structure to @samp{libffi} by creating a
+@code{libffi} is perfectly happy passing structures back and forth.
+You must first describe the structure to @code{libffi} by creating a
new @code{ffi_type} object for it.
@tindex ffi_type
This is a @samp{NULL}-terminated array of pointers to @code{ffi_type}
objects. There is one element per field of the struct.
-Note that @samp{libffi} has no special support for bit-fields. You
+Note that @code{libffi} has no special support for bit-fields. You
must manage these manually.
@end table
@end deftp
@subsubsection Arrays
-@samp{libffi} does not have direct support for arrays or unions.
+@code{libffi} does not have direct support for arrays or unions.
However, they can be emulated using structures.
To emulate an array, simply create an @code{ffi_type} using
members of real @code{FFI_TYPE_STRUCT} objects.
However, a phony array type like this will not cause any errors from
-@samp{libffi} if you use it as an argument or return type. This may
+@code{libffi} if you use it as an argument or return type. This may
be confusing.
@subsubsection Unions
tm_type.size = tm_type.alignment = 0;
tm_type.type = FFI_TYPE_STRUCT;
- tm_type.elements = &tm_type_elements;
+ tm_type.elements = tm_type_elements;
for (i = 0; i < 9; i++)
tm_type_elements[i] = &ffi_type_sint;
@node Complex
@subsection Complex Types
-@samp{libffi} supports the complex types defined by the C99
+@code{libffi} supports the complex types defined by the C99
standard (@code{_Complex float}, @code{_Complex double} and
@code{_Complex long double} with the built-in type descriptors
@code{ffi_type_complex_float}, @code{ffi_type_complex_double} and
Custom complex types like @code{_Complex int} can also be used.
An @code{ffi_type} object has to be defined to describe the
-complex type to @samp{libffi}.
+complex type to @code{libffi}.
@tindex ffi_type
@deftp {Data type} ffi_type
the writable address that was returned.
@end defun
-
Once you have allocated the memory for a closure, you must construct a
@code{ffi_cif} describing the function call. Finally you can prepare
the closure function:
Note that memory allocated by @code{ffi_closure_alloc} and freed by
@code{ffi_closure_free} does not come from the same general pool of
memory that @code{malloc} and @code{free} use. To accomodate security
-settings, @samp{libffi} may aquire memory, for example, by mapping
+settings, @code{libffi} may aquire memory, for example, by mapping
temporary files into multiple places in the address space (once to
write out the closure, a second to execute it). The search follows
this list, using the first that works:
-@set UPDATED 31 August 2022
-@set UPDATED-MONTH August 2022
-@set EDITION 3.4.2
-@set VERSION 3.4.2
+@set UPDATED 2 August 2025
+@set UPDATED-MONTH August 2025
+@set EDITION 3.5.2
+@set VERSION 3.5.2
pass
-class simulator_platform(Platform):
- arch = 'i386'
- triple = 'i386-apple-darwin11'
-
- prefix = "#ifdef __i386__\n\n"
- suffix = "\n\n#endif"
- src_dir = 'x86'
- src_files = ['sysv.S', 'ffi.c', 'internal.h']
-
-
-class simulator64_platform(Platform):
+class x86_64_platform(Platform):
arch = 'x86_64'
- triple = 'x86_64-apple-darwin13'
prefix = "#ifdef __x86_64__\n\n"
suffix = "\n\n#endif"
src_files = ['unix64.S', 'ffi64.c', 'ffiw64.c', 'win64.S', 'internal64.h', 'asmnames.h']
-class device_platform(Platform):
- arch = 'armv7'
- triple = 'arm-apple-darwin11'
+class arm64_platform(Platform):
+ arch = 'arm64'
- prefix = "#ifdef __arm__\n\n"
+ prefix = "#ifdef __arm64__\n\n"
suffix = "\n\n#endif"
- src_dir = 'arm'
+ src_dir = 'aarch64'
src_files = ['sysv.S', 'ffi.c', 'internal.h']
-class device64_platform(Platform):
- arch = 'arm64'
- triple = 'aarch64-apple-darwin13'
+class armv7_platform(Platform):
+ arch = 'armv7'
- prefix = "#ifdef __arm64__\n\n"
+ prefix = "#ifdef __arm__\n\n"
suffix = "\n\n#endif"
- src_dir = 'aarch64'
+ src_dir = 'arm'
src_files = ['sysv.S', 'ffi.c', 'internal.h']
-class ios_simulator_platform(simulator_platform):
+class ios_simulator_x86_64_platform(x86_64_platform):
+ target = 'x86_64-apple-ios-simulator'
directory = 'darwin_ios'
sdk = 'iphonesimulator'
version_min = '-miphoneos-version-min=7.0'
-class ios_simulator64_platform(simulator64_platform):
+class ios_simulator_arm64_platform(arm64_platform):
+ target = 'arm64-apple-ios-simulator'
directory = 'darwin_ios'
sdk = 'iphonesimulator'
version_min = '-miphoneos-version-min=7.0'
-class ios_device_platform(device_platform):
+class ios_device_armv7_platform(armv7_platform):
+ target = 'armv7-apple-ios'
directory = 'darwin_ios'
sdk = 'iphoneos'
version_min = '-miphoneos-version-min=7.0'
-class ios_device64_platform(device64_platform):
+class ios_device_arm64_platform(arm64_platform):
+ target = 'arm64-apple-ios'
directory = 'darwin_ios'
sdk = 'iphoneos'
version_min = '-miphoneos-version-min=7.0'
-class desktop32_platform(Platform):
+class desktop_x86_64_platform(x86_64_platform):
+ target = 'x86_64-apple-macos'
directory = 'darwin_osx'
sdk = 'macosx'
- arch = 'i386'
- triple = 'i386-apple-darwin10'
version_min = '-mmacosx-version-min=10.6'
- src_dir = 'x86'
- src_files = ['sysv.S', 'ffi.c', 'internal.h']
-
- prefix = "#ifdef __i386__\n\n"
- suffix = "\n\n#endif"
-class desktop64_platform(Platform):
+class desktop_arm64_platform(arm64_platform):
+ target = 'arm64-apple-macos'
directory = 'darwin_osx'
sdk = 'macosx'
- arch = 'x86_64'
- triple = 'x86_64-apple-darwin10'
- version_min = '-mmacosx-version-min=10.6'
+ version_min = '-mmacosx-version-min=11.0'
- prefix = "#ifdef __x86_64__\n\n"
- suffix = "\n\n#endif"
- src_dir = 'x86'
- src_files = ['unix64.S', 'ffi64.c', 'ffiw64.c', 'win64.S', 'internal64.h', 'asmnames.h']
+
+class tvos_simulator_x86_64_platform(x86_64_platform):
+ target = 'x86_64-apple-tvos-simulator'
+ directory = 'darwin_tvos'
+ sdk = 'appletvsimulator'
+ version_min = '-mtvos-version-min=9.0'
-class tvos_simulator64_platform(simulator64_platform):
+class tvos_simulator_arm64_platform(arm64_platform):
+ target = 'arm64-apple-tvos-simulator'
directory = 'darwin_tvos'
sdk = 'appletvsimulator'
version_min = '-mtvos-version-min=9.0'
-class tvos_device64_platform(device64_platform):
+class tvos_device_arm64_platform(arm64_platform):
+ target = 'arm64-apple-tvos'
directory = 'darwin_tvos'
sdk = 'appletvos'
version_min = '-mtvos-version-min=9.0'
-class watchos_simulator_platform(simulator_platform):
+class watchos_simulator_x86_64_platform(x86_64_platform):
+ target = 'x86_64-apple-watchos-simulator'
directory = 'darwin_watchos'
sdk = 'watchsimulator'
version_min = '-mwatchos-version-min=4.0'
-class watchos_device_platform(device_platform):
+class watchos_simulator_arm64_platform(arm64_platform):
+ target = 'arm64-apple-watchos-simulator'
+ directory = 'darwin_watchos'
+ sdk = 'watchsimulator'
+ version_min = '-mwatchos-version-min=4.0'
+
+
+class watchos_device_armv7k_platform(armv7_platform):
+ target = 'armv7k-apple-watchos'
directory = 'darwin_watchos'
sdk = 'watchos'
arch = 'armv7k'
version_min = '-mwatchos-version-min=4.0'
+class watchos_device_arm64_32_platform(arm64_platform):
+ target = 'arm64_32-apple-watchos'
+ directory = 'darwin_watchos'
+ sdk = 'watchos'
+ arch = 'arm64_32'
+ version_min = '-mwatchos-version-min=4.0'
+
+
def mkdir_p(path):
try:
os.makedirs(path)
def build_target(platform, platform_headers):
def xcrun_cmd(cmd):
- return 'xcrun -sdk %s %s -arch %s' % (platform.sdk, cmd, platform.arch)
+ return 'xcrun -sdk %s %s -target %s' % (platform.sdk, cmd, platform.target)
tag='%s-%s' % (platform.sdk, platform.arch)
build_dir = 'build_%s' % tag
mkdir_p(build_dir)
env = dict(CC=xcrun_cmd('clang'),
LD=xcrun_cmd('ld'),
- CFLAGS='%s -fembed-bitcode' % (platform.version_min))
+ CFLAGS='%s' % (platform.version_min))
working_dir = os.getcwd()
try:
os.chdir(build_dir)
- subprocess.check_call(['../configure', '-host', platform.triple], env=env)
+ subprocess.check_call(
+ [
+ "../configure",
+ f"--host={platform.target}",
+ ] + (
+ [] if platform.sdk == "macosx" else [f"--build={os.uname().machine}-apple-darwin"]
+ ),
+ env=env
+ )
finally:
os.chdir(working_dir)
copy_files('include', 'darwin_common/include', pattern='*.h')
if generate_ios:
- copy_src_platform_files(ios_simulator_platform)
- copy_src_platform_files(ios_simulator64_platform)
- copy_src_platform_files(ios_device_platform)
- copy_src_platform_files(ios_device64_platform)
+ copy_src_platform_files(ios_simulator_x86_64_platform)
+ copy_src_platform_files(ios_simulator_arm64_platform)
+ copy_src_platform_files(ios_device_armv7_platform)
+ copy_src_platform_files(ios_device_arm64_platform)
if generate_osx:
- copy_src_platform_files(desktop64_platform)
+ copy_src_platform_files(desktop_x86_64_platform)
+ copy_src_platform_files(desktop_arm64_platform)
if generate_tvos:
- copy_src_platform_files(tvos_simulator64_platform)
- copy_src_platform_files(tvos_device64_platform)
+ copy_src_platform_files(tvos_simulator_x86_64_platform)
+ copy_src_platform_files(tvos_simulator_arm64_platform)
+ copy_src_platform_files(tvos_device_arm64_platform)
if generate_watchos:
- copy_src_platform_files(watchos_simulator_platform)
- copy_src_platform_files(watchos_device_platform)
+ copy_src_platform_files(watchos_simulator_x86_64_platform)
+ copy_src_platform_files(watchos_simulator_arm64_platform)
+ copy_src_platform_files(watchos_device_armv7k_platform)
+ copy_src_platform_files(watchos_device_arm64_32_platform)
platform_headers = collections.defaultdict(set)
if generate_ios:
- build_target(ios_simulator_platform, platform_headers)
- build_target(ios_simulator64_platform, platform_headers)
- build_target(ios_device_platform, platform_headers)
- build_target(ios_device64_platform, platform_headers)
+ build_target(ios_simulator_x86_64_platform, platform_headers)
+ build_target(ios_simulator_arm64_platform, platform_headers)
+ build_target(ios_device_armv7_platform, platform_headers)
+ build_target(ios_device_arm64_platform, platform_headers)
if generate_osx:
- build_target(desktop64_platform, platform_headers)
+ build_target(desktop_x86_64_platform, platform_headers)
+ build_target(desktop_arm64_platform, platform_headers)
if generate_tvos:
- build_target(tvos_simulator64_platform, platform_headers)
- build_target(tvos_device64_platform, platform_headers)
+ build_target(tvos_simulator_x86_64_platform, platform_headers)
+ build_target(tvos_simulator_arm64_platform, platform_headers)
+ build_target(tvos_device_arm64_platform, platform_headers)
if generate_watchos:
- build_target(watchos_simulator_platform, platform_headers)
- build_target(watchos_device_platform, platform_headers)
+ build_target(watchos_simulator_x86_64_platform, platform_headers)
+ build_target(watchos_simulator_arm64_platform, platform_headers)
+ build_target(watchos_device_armv7k_platform, platform_headers)
+ build_target(watchos_device_arm64_32_platform, platform_headers)
mkdir_p('darwin_common/include')
for header_name, tag_tuples in platform_headers.items():
noinst_HEADERS=ffi_common.h ffi_cfi.h tramp.h
EXTRA_DIST=ffi.h.in
-# Where generated headers like ffitarget.h get installed.
-gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER)
-toollibffidir := $(libdir)/gcc/$(target_alias)/$(gcc_version)/include
-
-toollibffi_HEADERS = ffi.h ffitarget.h
+nodist_include_HEADERS = ffi.h ffitarget.h
/* -----------------------------------------------------------------*-C-*-
libffi @VERSION@
- - Copyright (c) 2011, 2014, 2019, 2021 Anthony Green
+ - Copyright (c) 2011, 2014, 2019, 2021, 2022, 2024, 2025 Anthony Green
- Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc.
Permission is hereby granted, free of charge, to any person
/* ---- System configuration information --------------------------------- */
+/* If these change, update src/mips/ffitarget.h. */
+#define FFI_TYPE_VOID 0
+#define FFI_TYPE_INT 1
+#define FFI_TYPE_FLOAT 2
+#define FFI_TYPE_DOUBLE 3
+#if @HAVE_LONG_DOUBLE@
+#define FFI_TYPE_LONGDOUBLE 4
+#else
+#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE
+#endif
+#define FFI_TYPE_UINT8 5
+#define FFI_TYPE_SINT8 6
+#define FFI_TYPE_UINT16 7
+#define FFI_TYPE_SINT16 8
+#define FFI_TYPE_UINT32 9
+#define FFI_TYPE_SINT32 10
+#define FFI_TYPE_UINT64 11
+#define FFI_TYPE_SINT64 12
+#define FFI_TYPE_STRUCT 13
+#define FFI_TYPE_POINTER 14
+#define FFI_TYPE_COMPLEX 15
+
+/* This should always refer to the last type code (for sanity checks). */
+#define FFI_TYPE_LAST FFI_TYPE_COMPLEX
+
#include <ffitarget.h>
#ifndef LIBFFI_ASM
when using the static version of the library.
Besides, as a workaround, they can define FFI_BUILDING if they
*know* they are going to link with the static library. */
-#if defined _MSC_VER
+#if defined _MSC_VER && !defined(FFI_STATIC_BUILD)
# if defined FFI_BUILDING_DLL /* Building libffi.DLL with msvcc.sh */
# define FFI_API __declspec(dllexport)
-# elif !defined FFI_BUILDING /* Importing libffi.DLL */
+# else /* Importing libffi.DLL */
# define FFI_API __declspec(dllimport)
-# else /* Building/linking static library */
-# define FFI_API
# endif
#else
# define FFI_API
FFI_EXTERN ffi_type ffi_type_float;
FFI_EXTERN ffi_type ffi_type_double;
FFI_EXTERN ffi_type ffi_type_pointer;
-
-#if @HAVE_LONG_DOUBLE@
FFI_EXTERN ffi_type ffi_type_longdouble;
-#else
-#define ffi_type_longdouble ffi_type_double
-#endif
#ifdef FFI_TARGET_HAS_COMPLEX_TYPE
FFI_EXTERN ffi_type ffi_type_complex_float;
FFI_EXTERN ffi_type ffi_type_complex_double;
-#if @HAVE_LONG_DOUBLE@
FFI_EXTERN ffi_type ffi_type_complex_longdouble;
-#else
-#define ffi_type_complex_longdouble ffi_type_complex_double
-#endif
#endif
#endif /* LIBFFI_HIDE_BASIC_TYPES */
FFI_API
size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated));
+/* ---- Version API ------------------------------------------------------ */
+
+#define FFI_VERSION_STRING "@FFI_VERSION_STRING@"
+#define FFI_VERSION_NUMBER @FFI_VERSION_NUMBER@
+
+#ifndef LIBFFI_ASM
+/* Return a version string. */
+FFI_API const char *ffi_get_version (void);
+
+/* Return the version as an unsigned long value: (x * 10000 + y * 100 + z) */
+FFI_API unsigned long ffi_get_version_number (void);
+#endif
+
+/* ---- Internals API ---------------------------------------------------- */
+
+FFI_API unsigned int ffi_get_default_abi (void);
+FFI_API size_t ffi_get_closure_size (void);
+
/* ---- Definitions for closures ----------------------------------------- */
#if FFI_CLOSURES
ffi_cif *cif;
void (*fun)(ffi_cif*,void*,void**,void*);
void *user_data;
+#if defined(_MSC_VER) && defined(_M_IX86)
+ void *padding;
+#endif
} ffi_closure
#ifdef __GNUC__
__attribute__((aligned (8)))
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 *,
ffi_cif *,
void (*fun)(ffi_cif*,void*,void**,void*),
void *user_data,
- void*codeloc);
+ void *codeloc);
#ifdef __sgi
# pragma pack 8
/* If this is enabled, then a raw closure has the same layout
as a regular closure. We use this to install an intermediate
- handler to do the transaltion, void** -> ffi_raw*. */
+ handler to do the translation, void** -> ffi_raw*. */
void (*translate_args)(ffi_cif*,void*,void**,void*);
void *this_closure;
#endif /* FFI_CLOSURES */
-#if FFI_GO_CLOSURES
+#ifdef FFI_GO_CLOSURES
typedef struct {
void *tramp;
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 ---------------------------- */
#endif
-/* If these change, update src/mips/ffitarget.h. */
-#define FFI_TYPE_VOID 0
-#define FFI_TYPE_INT 1
-#define FFI_TYPE_FLOAT 2
-#define FFI_TYPE_DOUBLE 3
-#if @HAVE_LONG_DOUBLE@
-#define FFI_TYPE_LONGDOUBLE 4
-#else
-#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE
-#endif
-#define FFI_TYPE_UINT8 5
-#define FFI_TYPE_SINT8 6
-#define FFI_TYPE_UINT16 7
-#define FFI_TYPE_SINT16 8
-#define FFI_TYPE_UINT32 9
-#define FFI_TYPE_SINT32 10
-#define FFI_TYPE_UINT64 11
-#define FFI_TYPE_SINT64 12
-#define FFI_TYPE_STRUCT 13
-#define FFI_TYPE_POINTER 14
-#define FFI_TYPE_COMPLEX 15
-
-/* This should always refer to the last type code (for sanity checks). */
-#define FFI_TYPE_LAST FFI_TYPE_COMPLEX
-
#ifdef __cplusplus
}
#endif
# define cfi_remember_state .cfi_remember_state
# define cfi_restore_state .cfi_restore_state
# define cfi_window_save .cfi_window_save
-# define cfi_negate_ra_state .cfi_negate_ra_state
# define cfi_personality(enc, exp) .cfi_personality enc, exp
# define cfi_lsda(enc, exp) .cfi_lsda enc, exp
# define cfi_escape(...) .cfi_escape __VA_ARGS__
+# define cfi_window_save .cfi_window_save
#else
# define cfi_remember_state
# define cfi_restore_state
# define cfi_window_save
-# define cfi_negate_ra_state
# define cfi_personality(enc, exp)
# define cfi_lsda(enc, exp)
# define cfi_escape(...)
+# define cfi_window_save
#endif /* HAVE_AS_CFI_PSEUDO_OP */
#endif /* FFI_CFI_H */
# endif
# endif
# define MAYBE_UNUSED __attribute__((__unused__))
+# define NORETURN __attribute__((__noreturn__))
#else
# define MAYBE_UNUSED
+# define NORETURN
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
#include <stdio.h>
#endif
+#ifndef __SANITIZE_ADDRESS__
+# ifdef __clang__
+# if __has_feature(address_sanitizer)
+# define FFI_ASAN
+# endif
+# endif
+#endif
+#ifdef __SANITIZE_ADDRESS__
+#define FFI_ASAN
+#endif
+
+#ifdef FFI_ASAN
+#define FFI_ASAN_NO_SANITIZE __attribute__((no_sanitize_address))
+#else
+#define FFI_ASAN_NO_SANITIZE
+#endif
+
#ifdef FFI_DEBUG
-void ffi_assert(char *expr, char *file, int line);
+NORETURN void ffi_assert(const char *expr, const char *file, int line);
void ffi_stop_here(void);
-void ffi_type_test(ffi_type *a, char *file, int line);
+void ffi_type_test(ffi_type *a, const char *file, int line);
#define FFI_ASSERT(x) ((x) ? (void)0 : ffi_assert(#x, __FILE__,__LINE__))
#define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l)))
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
{
ffi_type_longdouble;
ffi_type_pointer;
- /* Exported functions. */
ffi_call;
ffi_prep_cif;
ffi_prep_cif_var;
ffi_raw_to_ptrarray;
ffi_raw_size;
+#if !FFI_NATIVE_RAW_API
ffi_java_raw_call;
+#endif
+
ffi_java_ptrarray_to_raw;
ffi_java_raw_to_ptrarray;
ffi_java_raw_size;
*;
};
+/* ----------------------------------------------------------------------
+ Symbols **added in libffi 3.5.0**.
+ Give them a fresh namespace so that package managers notice when
+ code requires a newer libffi than 3.4.x.
+ -------------------------------------------------------------------- */
+LIBFFI_BASE_8.1 {
+ global:
+ ffi_get_version;
+ ffi_get_version_number;
+ ffi_get_default_abi;
+ ffi_get_closure_size;
+} LIBFFI_BASE_8.0;
+
#ifdef FFI_TARGET_HAS_COMPLEX_TYPE
LIBFFI_COMPLEX_8.0 {
global:
ffi_prep_closure_loc;
ffi_prep_raw_closure;
ffi_prep_raw_closure_loc;
+#if !FFI_NATIVE_RAW_API
ffi_prep_java_raw_closure;
ffi_prep_java_raw_closure_loc;
+#endif
} LIBFFI_BASE_8.0;
#endif
#
# Here are a set of rules to help you update your library version
# information:
-#
+#
# 1. Start with version information of `0:0:0' for each libtool library.
#
# 2. Update the version information only immediately before a public
# release, then set age to 0.
#
# CURRENT:REVISION:AGE
-9:0:1
+10:0:2
#include <ffi_common.h>
#include "internal.h"
#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
#include <windows.h> /* FlushInstructionCache */
#endif
#include <tramp.h>
#if FFI_EXEC_TRAMPOLINE_TABLE
#ifdef __MACH__
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
#include <ptrauth.h>
#endif
#include <mach/vm_param.h>
state.
The terse state variable names match the names used in the AARCH64
- PCS. */
+ PCS.
+
+ The struct area is allocated downwards from the top of the argument
+ area. It is used to hold copies of structures passed by value that are
+ bigger than 16 bytes. */
struct arg_state
{
unsigned ngrn; /* Next general-purpose register number. */
unsigned nsrn; /* Next vector register number. */
size_t nsaa; /* Next stack offset. */
+ size_t next_struct_area; /* Place to allocate big structs. */
#if defined (__APPLE__)
unsigned allocating_variadic;
/* Initialize a procedure call argument marshalling state. */
static void
-arg_init (struct arg_state *state)
+arg_init (struct arg_state *state, size_t size)
{
state->ngrn = 0;
state->nsrn = 0;
state->nsaa = 0;
+ state->next_struct_area = size;
#if defined (__APPLE__)
state->allocating_variadic = 0;
#endif
if (alignment < 8)
alignment = 8;
#endif
-
+
nsaa = FFI_ALIGN (nsaa, alignment);
state->nsaa = nsaa + size;
return (char *)stack + nsaa;
}
+/* Allocate and copy a structure that is passed by value on the stack and
+ return a pointer to it. */
+static void *
+allocate_and_copy_struct_to_stack (struct arg_state *state, void *stack,
+ size_t alignment, size_t size, void *value)
+{
+ size_t dest = state->next_struct_area - size;
+
+ /* Round down to the natural alignment of the value. */
+ dest = FFI_ALIGN_DOWN (dest, alignment);
+ state->next_struct_area = dest;
+
+ return memcpy ((char *) stack + dest, value, size);
+}
+
static ffi_arg
extend_integer_type (void *source, int type)
{
switch (type)
{
case FFI_TYPE_UINT8:
- return *(UINT8 *) source;
+ {
+ UINT8 u8;
+ memcpy (&u8, source, sizeof (u8));
+ return u8;
+ }
case FFI_TYPE_SINT8:
- return *(SINT8 *) source;
+ {
+ SINT8 s8;
+ memcpy (&s8, source, sizeof (s8));
+ return s8;
+ }
case FFI_TYPE_UINT16:
- return *(UINT16 *) source;
+ {
+ UINT16 u16;
+ memcpy (&u16, source, sizeof (u16));
+ return u16;
+ }
case FFI_TYPE_SINT16:
- return *(SINT16 *) source;
+ {
+ SINT16 s16;
+ memcpy (&s16, source, sizeof (s16));
+ return s16;
+ }
case FFI_TYPE_UINT32:
- return *(UINT32 *) source;
+ {
+ UINT32 u32;
+ memcpy (&u32, source, sizeof (u32));
+ return u32;
+ }
case FFI_TYPE_INT:
case FFI_TYPE_SINT32:
- return *(SINT32 *) source;
+ {
+ SINT32 s32;
+ memcpy (&s32, source, sizeof (s32));
+ return s32;
+ }
case FFI_TYPE_UINT64:
case FFI_TYPE_SINT64:
- return *(UINT64 *) source;
- break;
+ {
+ UINT64 u64;
+ memcpy (&u64, source, sizeof (u64));
+ return u64;
+ }
case FFI_TYPE_POINTER:
- return *(uintptr_t *) source;
+ {
+ uintptr_t uptr;
+ memcpy (&uptr, source, sizeof (uptr));
+ return uptr;
+ }
default:
abort();
}
ssize_t f = h - AARCH64_RET_S4;
void *x0;
+#define BTI_J "hint #36"
asm volatile (
"adr %0, 0f\n"
" add %0, %0, %1\n"
" br %0\n"
-"0: ldp s16, s17, [%3]\n" /* S4 */
+"0: "BTI_J"\n" /* S4 */
+" ldp s16, s17, [%3]\n"
" ldp s18, s19, [%3, #8]\n"
" b 4f\n"
-" ldp s16, s17, [%3]\n" /* S3 */
+" "BTI_J"\n" /* S3 */
+" ldp s16, s17, [%3]\n"
" ldr s18, [%3, #8]\n"
" b 3f\n"
-" ldp s16, s17, [%3]\n" /* S2 */
+" "BTI_J"\n" /* S2 */
+" ldp s16, s17, [%3]\n"
" b 2f\n"
" nop\n"
-" ldr s16, [%3]\n" /* S1 */
+" "BTI_J"\n" /* S1 */
+" ldr s16, [%3]\n"
" b 1f\n"
" nop\n"
-" ldp d16, d17, [%3]\n" /* D4 */
+" "BTI_J"\n" /* D4 */
+" ldp d16, d17, [%3]\n"
" ldp d18, d19, [%3, #16]\n"
" b 4f\n"
-" ldp d16, d17, [%3]\n" /* D3 */
+" "BTI_J"\n" /* D3 */
+" ldp d16, d17, [%3]\n"
" ldr d18, [%3, #16]\n"
" b 3f\n"
-" ldp d16, d17, [%3]\n" /* D2 */
+" "BTI_J"\n" /* D2 */
+" ldp d16, d17, [%3]\n"
" b 2f\n"
" nop\n"
-" ldr d16, [%3]\n" /* D1 */
+" "BTI_J"\n" /* D1 */
+" ldr d16, [%3]\n"
" b 1f\n"
" nop\n"
-" ldp q16, q17, [%3]\n" /* Q4 */
+" "BTI_J"\n" /* Q4 */
+" ldp q16, q17, [%3]\n"
" ldp q18, q19, [%3, #32]\n"
" b 4f\n"
-" ldp q16, q17, [%3]\n" /* Q3 */
+" "BTI_J"\n" /* Q3 */
+" ldp q16, q17, [%3]\n"
" ldr q18, [%3, #32]\n"
" b 3f\n"
-" ldp q16, q17, [%3]\n" /* Q2 */
+" "BTI_J"\n" /* Q2 */
+" ldp q16, q17, [%3]\n"
" b 2f\n"
" nop\n"
-" ldr q16, [%3]\n" /* Q1 */
+" "BTI_J"\n" /* Q1 */
+" ldr q16, [%3]\n"
" b 1f\n"
"4: str q19, [%2, #48]\n"
"3: str q18, [%2, #32]\n"
"2: str q17, [%2, #16]\n"
"1: str q16, [%2]"
: "=&r"(x0)
- : "r"(f * 12), "r"(dest), "r"(src)
+ : "r"(f * 16), "r"(dest), "r"(src)
: "memory", "v16", "v17", "v18", "v19");
}
#endif
void *closure) FFI_HIDDEN;
/* Call a function with the provided arguments and capture the return
- value. */
+ value.
+ n.b. ffi_call_SYSV will steal the alloca'd `stack` variable here for use
+ _as its own stack_ - so we need to compile this function without ASAN */
+FFI_ASAN_NO_SANITIZE
static void
ffi_call_int (ffi_cif *cif, void (*fn)(void), void *orig_rvalue,
void **avalue, void *closure)
else if (flags & AARCH64_RET_NEED_COPY)
rsize = 16;
- /* Allocate consectutive stack for everything we'll need.
+ /* Allocate consecutive stack for everything we'll need.
The frame uses 40 bytes for: lr, fp, rvalue, flags, sp */
context = alloca (sizeof(struct call_context) + stack_bytes + 40 + rsize);
stack = context + 1;
frame = (void*)((uintptr_t)stack + (uintptr_t)stack_bytes);
rvalue = (rsize ? (void*)((uintptr_t)frame + 40) : orig_rvalue);
- arg_init (&state);
+ arg_init (&state, stack_bytes);
for (i = 0, nargs = cif->nargs; i < nargs; i++)
{
ffi_type *ty = cif->arg_types[i];
size_t s = ty->size;
void *a = avalue[i];
int h, t;
+ void *dest;
t = ty->type;
switch (t)
case FFI_TYPE_STRUCT:
case FFI_TYPE_COMPLEX:
{
- void *dest;
-
h = is_vfp_type (ty);
if (h)
{
else if (s > 16)
{
/* If the argument is a composite type that is larger than 16
- bytes, then the argument has been copied to memory, and
+ bytes, then the argument is copied to memory, and
the argument is replaced by a pointer to the copy. */
- a = &avalue[i];
+ dest = allocate_and_copy_struct_to_stack (&state, stack,
+ ty->alignment, s,
+ avalue[i]);
+ a = &dest;
t = FFI_TYPE_POINTER;
s = sizeof (void *);
goto do_pointer;
return FFI_BAD_ABI;
void (*start)(void);
-
+
if (cif->flags & AARCH64_FLAG_ARG_V)
start = ffi_closure_SYSV_V;
else
start = ffi_closure_SYSV;
#if FFI_EXEC_TRAMPOLINE_TABLE
-#ifdef __MACH__
-#ifdef HAVE_PTRAUTH
+# ifdef __MACH__
+# ifdef HAVE_ARM64E_PTRAUTH
codeloc = ptrauth_auth_data(codeloc, ptrauth_key_function_pointer, 0);
-#endif
+# endif
void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE);
config[0] = closure;
config[1] = start;
-#endif
+# endif
#else
static const unsigned char trampoline[16] = {
0x90, 0x00, 0x00, 0x58, /* ldr x16, tramp+16 */
};
char *tramp = closure->tramp;
-#if defined(FFI_EXEC_STATIC_TRAMP)
+# if defined(FFI_EXEC_STATIC_TRAMP)
if (ffi_tramp_is_present(closure))
{
/* Initialize the static trampoline's parameters. */
ffi_tramp_set_parms (closure->ftramp, start, closure);
goto out;
}
-#endif
+# endif
/* Initialize the dynamic trampoline. */
memcpy (tramp, trampoline, sizeof(trampoline));
-
+
*(UINT64 *)(tramp + 16) = (uintptr_t)start;
ffi_clear_cache(tramp, tramp + FFI_TRAMPOLINE_SIZE);
/* Also flush the cache for code mapping. */
-#ifdef _WIN32
+# ifdef _WIN32
// Not using dlmalloc.c for Windows ARM64 builds
// so calling ffi_data_to_code_pointer() isn't necessary
unsigned char *tramp_code = tramp;
- #else
+# else
unsigned char *tramp_code = ffi_data_to_code_pointer (tramp);
- #endif
+# endif
ffi_clear_cache (tramp_code, tramp_code + FFI_TRAMPOLINE_SIZE);
+# if defined(FFI_EXEC_STATIC_TRAMP)
out:
+# endif
#endif
closure->cif = cif;
int i, h, nargs, flags, isvariadic = 0;
struct arg_state state;
- arg_init (&state);
+ arg_init (&state, cif->bytes);
flags = cif->flags;
if (flags & AARCH64_FLAG_VARARG)
{
void *reg = &context->x[state.ngrn];
state.ngrn += (unsigned int)n;
-
+
/* Eeek! We need a pointer to the structure, however the
homogeneous float elements are being passed in individual
registers, therefore for float and double the structure
{
/* Replace Composite type of size greater than 16 with a
pointer. */
+#ifdef __ILP32__
+ UINT64 avalue_tmp;
+ memcpy (&avalue_tmp,
+ allocate_int_to_reg_or_stack (context, &state,
+ stack, sizeof (void *)),
+ sizeof (UINT64));
+ avalue[i] = (void *)(UINT32)avalue_tmp;
+#else
avalue[i] = *(void **)
allocate_int_to_reg_or_stack (context, &state, stack,
sizeof (void *));
+#endif
}
else
{
#if defined (__APPLE__)
#define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs
-#elif !defined(_WIN32)
-/* iOS and Windows reserve x18 for the system. Disable Go closures until
+#elif !defined(_WIN32) && !defined(__ANDROID__)
+/* iOS, Windows and Android reserve x18 for the system. Disable Go closures until
a new static chain is chosen. */
#define FFI_GO_CLOSURES 1
#endif
/* Helpers for writing assembly compatible with arm ptr auth */
#ifdef LIBFFI_ASM
-#ifdef HAVE_PTRAUTH
-#define SIGN_LR pacibsp
-#define SIGN_LR_WITH_REG(x) pacib lr, x
-#define AUTH_LR_AND_RET retab
-#define AUTH_LR_WITH_REG(x) autib lr, x
-#define BRANCH_AND_LINK_TO_REG blraaz
-#define BRANCH_TO_REG braaz
-#else
-#define SIGN_LR
-#define SIGN_LR_WITH_REG(x)
-#define AUTH_LR_AND_RET ret
-#define AUTH_LR_WITH_REG(x)
-#define BRANCH_AND_LINK_TO_REG blr
-#define BRANCH_TO_REG br
-#endif
-
-#endif
+ #if defined(HAVE_ARM64E_PTRAUTH)
+ /* ARM64E ABI For Darwin */
+ #define SIGN_LR pacibsp
+ #define SIGN_LR_WITH_REG(x) pacib lr, x
+ #define AUTH_LR_AND_RET retab
+ #define AUTH_LR_WITH_REG(x) autib lr, x
+ #define BRANCH_AND_LINK_TO_REG blraaz
+ #define SIGN_LR_LINUX_ONLY
+ #define BRANCH_TO_REG braaz
+ #define PAC_CFI_WINDOW_SAVE
+ #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0
+ /* Linux PAC Support */
+ #elif defined(__ARM_FEATURE_PAC_DEFAULT)
+ #define GNU_PROPERTY_AARCH64_POINTER_AUTH (1 << 1)
+ #define PAC_CFI_WINDOW_SAVE cfi_window_save
+ #define TMP_REG x9
+ #define BRANCH_TO_REG br
+ #define BRANCH_AND_LINK_TO_REG blr
+ #define SIGN_LR_LINUX_ONLY SIGN_LR
+ /* Which key to sign with? */
+ #if (__ARM_FEATURE_PAC_DEFAULT & 1) == 1
+ /* Signed with A-key */
+ #define SIGN_LR hint #25 /* paciasp */
+ #define AUTH_LR hint #29 /* autiasp */
+ #else
+ /* Signed with B-key */
+ #define SIGN_LR hint #27 /* pacibsp */
+ #define AUTH_LR hint #31 /* autibsp */
+ #endif /* __ARM_FEATURE_PAC_DEFAULT */
+ #define AUTH_LR_WITH_REG(x) _auth_lr_with_reg x
+.macro _auth_lr_with_reg modifier
+ mov TMP_REG, sp
+ mov sp, \modifier
+ AUTH_LR
+ mov sp, TMP_REG
+.endm
+ #define SIGN_LR_WITH_REG(x) _sign_lr_with_reg x
+.macro _sign_lr_with_reg modifier
+ mov TMP_REG, sp
+ mov sp, \modifier
+ SIGN_LR
+ mov sp, TMP_REG
+.endm
+ #define AUTH_LR_AND_RET _auth_lr_and_ret modifier
+.macro _auth_lr_and_ret modifier
+ AUTH_LR
+ ret
+.endm
+ #undef TMP_REG
+
+ /* No Pointer Auth */
+ #else
+ #define SIGN_LR
+ #define SIGN_LR_WITH_REG(x)
+ #define AUTH_LR_AND_RET ret
+ #define AUTH_LR_WITH_REG(x)
+ #define BRANCH_AND_LINK_TO_REG blr
+ #define SIGN_LR_LINUX_ONLY
+ #define BRANCH_TO_REG br
+ #define PAC_CFI_WINDOW_SAVE
+ #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0
+ #endif /* HAVE_ARM64E_PTRAUTH */
+#endif /* LIBFFI_ASM */
#endif
#endif
+#ifdef __APPLE__
+# define L(X) CONCAT1(L, X)
+#else
+# define L(X) CONCAT1(.L, X)
+#endif
+
#ifdef __AARCH64EB__
# define BE(X) X
#else
#define PTR_SIZE 8
#endif
+#define BTI_C hint #34
+#define BTI_J hint #36
+/*
+ * The ELF Notes section needs to indicate if BTI is supported, as the first ELF loaded that doesn't
+ * declare this support disables it for memory region containing the loaded library.
+ */
+# define GNU_PROPERTY_AARCH64_BTI (1 << 0) /* Has Branch Target Identification */
.text
.align 4
x5 closure
*/
- cfi_startproc
CNAME(ffi_call_SYSV):
- /* Sign the lr with x1 since that is where it will be stored */
+ cfi_startproc
+ BTI_C
+ PAC_CFI_WINDOW_SAVE
+ /* Sign the lr with x1 since that is the CFA which is the modifer used in auth instructions */
SIGN_LR_WITH_REG(x1)
- /* Use a stack frame allocated by our caller. */
-#if defined(HAVE_PTRAUTH) && defined(__APPLE__)
+#if defined(HAVE_ARM64E_PTRAUTH) && defined(__APPLE__)
/* darwin's libunwind assumes that the cfa is the sp and that's the data
* used to sign the lr. In order to allow unwinding through this
* function it is necessary to point the cfa at the signing register.
*/
cfi_def_cfa(x1, 0);
-#else
- cfi_def_cfa(x1, 40);
#endif
+ /* Use a stack frame allocated by our caller. */
stp x29, x30, [x1]
+ cfi_def_cfa_register(x1)
+ cfi_rel_offset (x29, 0)
+ cfi_rel_offset (x30, 8)
mov x9, sp
str x9, [x1, #32]
mov x29, x1
- mov sp, x0
cfi_def_cfa_register(x29)
- cfi_rel_offset (x29, 0)
- cfi_rel_offset (x30, 8)
+ mov sp, x0
mov x9, x2 /* save fn */
mov x8, x3 /* install structure return */
/* Save the return value as directed. */
adr x5, 0f
and w4, w4, #AARCH64_RET_MASK
- add x5, x5, x4, lsl #3
+ add x5, x5, x4, lsl #4
br x5
- /* Note that each table entry is 2 insns, and thus 8 bytes.
+ /* Note that each table entry is 4 insns, and thus 16 bytes.
For integer data, note that we're storing into ffi_arg
and therefore we want to extend to 64 bits; these types
have two consecutive entries allocated for them. */
.align 4
-0: b 99f /* VOID */
+0: BTI_J /* VOID */
+ b 99f
+ nop
nop
-1: str x0, [x3] /* INT64 */
+1: BTI_J /* INT64 */
+ str x0, [x3]
b 99f
-2: stp x0, x1, [x3] /* INT128 */
+ nop
+2: BTI_J /* INT128 */
+ stp x0, x1, [x3]
b 99f
+ nop
3: brk #1000 /* UNUSED */
b 99f
+ nop
+ nop
4: brk #1000 /* UNUSED */
b 99f
+ nop
+ nop
5: brk #1000 /* UNUSED */
b 99f
+ nop
+ nop
6: brk #1000 /* UNUSED */
b 99f
+ nop
+ nop
7: brk #1000 /* UNUSED */
b 99f
-8: st4 { v0.s, v1.s, v2.s, v3.s }[0], [x3] /* S4 */
+ nop
+ nop
+8: BTI_J /* S4 */
+ st4 { v0.s, v1.s, v2.s, v3.s }[0], [x3]
b 99f
-9: st3 { v0.s, v1.s, v2.s }[0], [x3] /* S3 */
+ nop
+9: BTI_J /* S3 */
+ st3 { v0.s, v1.s, v2.s }[0], [x3]
b 99f
-10: stp s0, s1, [x3] /* S2 */
+ nop
+10: BTI_J /* S2 */
+ stp s0, s1, [x3]
b 99f
-11: str s0, [x3] /* S1 */
+ nop
+11: BTI_J
+ str s0, [x3] /* S1 */
b 99f
-12: st4 { v0.d, v1.d, v2.d, v3.d }[0], [x3] /* D4 */
+ nop
+12: BTI_J /* D4 */
+ st4 { v0.d, v1.d, v2.d, v3.d }[0], [x3]
b 99f
-13: st3 { v0.d, v1.d, v2.d }[0], [x3] /* D3 */
+ nop
+13: BTI_J /* D3 */
+ st3 { v0.d, v1.d, v2.d }[0], [x3]
b 99f
-14: stp d0, d1, [x3] /* D2 */
+ nop
+14: BTI_J /* D2 */
+ stp d0, d1, [x3]
b 99f
-15: str d0, [x3] /* D1 */
+ nop
+15: BTI_J /* D1 */
+ str d0, [x3]
b 99f
-16: str q3, [x3, #48] /* Q4 */
nop
-17: str q2, [x3, #32] /* Q3 */
+16: BTI_J /* Q4 */
+ str q3, [x3, #48]
+ nop
nop
-18: stp q0, q1, [x3] /* Q2 */
+17: BTI_J /* Q3 */
+ str q2, [x3, #32]
+ nop
+ nop
+18: BTI_J /* Q2 */
+ stp q0, q1, [x3]
b 99f
-19: str q0, [x3] /* Q1 */
+ nop
+19: BTI_J /* Q1 */
+ str q0, [x3]
b 99f
-20: uxtb w0, w0 /* UINT8 */
+ nop
+20: BTI_J /* UINT8 */
+ uxtb w0, w0
str x0, [x3]
+ nop
21: b 99f /* reserved */
nop
-22: uxth w0, w0 /* UINT16 */
+ nop
+ nop
+22: BTI_J /* UINT16 */
+ uxth w0, w0
str x0, [x3]
+ nop
23: b 99f /* reserved */
nop
-24: mov w0, w0 /* UINT32 */
+ nop
+ nop
+24: BTI_J /* UINT32 */
+ mov w0, w0
str x0, [x3]
+ nop
25: b 99f /* reserved */
nop
-26: sxtb x0, w0 /* SINT8 */
+ nop
+ nop
+26: BTI_J /* SINT8 */
+ sxtb x0, w0
str x0, [x3]
+ nop
27: b 99f /* reserved */
nop
-28: sxth x0, w0 /* SINT16 */
+ nop
+ nop
+28: BTI_J /* SINT16 */
+ sxth x0, w0
str x0, [x3]
+ nop
29: b 99f /* reserved */
nop
-30: sxtw x0, w0 /* SINT32 */
+ nop
+ nop
+30: BTI_J /* SINT32 */
+ sxtw x0, w0
str x0, [x3]
+ nop
31: b 99f /* reserved */
nop
+ nop
+ nop
/* Return now that result has been populated. */
99:
.align 4
CNAME(ffi_closure_SYSV_V):
cfi_startproc
+ BTI_C
SIGN_LR
+ PAC_CFI_WINDOW_SAVE
stp x29, x30, [sp, #-ffi_closure_SYSV_FS]!
cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
cfi_rel_offset (x29, 0)
#endif
.align 4
- cfi_startproc
CNAME(ffi_closure_SYSV):
+ cfi_startproc
+ BTI_C
SIGN_LR
+ PAC_CFI_WINDOW_SAVE
stp x29, x30, [sp, #-ffi_closure_SYSV_FS]!
cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
cfi_rel_offset (x29, 0)
ldp PTR_REG(0), PTR_REG(1), [x17, #FFI_TRAMPOLINE_CLOSURE_OFFSET] /* load cif, fn */
ldr PTR_REG(2), [x17, #FFI_TRAMPOLINE_CLOSURE_OFFSET+PTR_SIZE*2] /* load user_data */
#ifdef FFI_GO_CLOSURES
-.Ldo_closure:
+L(do_closure):
#endif
add x3, sp, #16 /* load context */
add x4, sp, #ffi_closure_SYSV_FS /* load stack */
/* Load the return value as directed. */
adr x1, 0f
and w0, w0, #AARCH64_RET_MASK
- add x1, x1, x0, lsl #3
+ add x1, x1, x0, lsl #4
add x3, sp, #16+CALL_CONTEXT_SIZE
br x1
- /* Note that each table entry is 2 insns, and thus 8 bytes. */
+ /* Note that each table entry is 4 insns, and thus 16 bytes. */
.align 4
-0: b 99f /* VOID */
+0: BTI_J /* VOID */
+ b 99f
nop
-1: ldr x0, [x3] /* INT64 */
+ nop
+1: BTI_J /* INT64 */
+ ldr x0, [x3]
b 99f
-2: ldp x0, x1, [x3] /* INT128 */
+ nop
+2: BTI_J /* INT128 */
+ ldp x0, x1, [x3]
b 99f
+ nop
3: brk #1000 /* UNUSED */
nop
+ nop
+ nop
4: brk #1000 /* UNUSED */
nop
+ nop
+ nop
5: brk #1000 /* UNUSED */
nop
+ nop
+ nop
6: brk #1000 /* UNUSED */
nop
+ nop
+ nop
7: brk #1000 /* UNUSED */
nop
-8: ldr s3, [x3, #12] /* S4 */
nop
-9: ldr s2, [x3, #8] /* S3 */
nop
-10: ldp s0, s1, [x3] /* S2 */
+8: BTI_J /* S4 */
+ ldr s3, [x3, #12]
+ nop
+ nop
+9: BTI_J /* S3 */
+ ldr s2, [x3, #8]
+ nop
+ nop
+10: BTI_J /* S2 */
+ ldp s0, s1, [x3]
b 99f
-11: ldr s0, [x3] /* S1 */
+ nop
+11: BTI_J /* S1 */
+ ldr s0, [x3]
b 99f
-12: ldr d3, [x3, #24] /* D4 */
nop
-13: ldr d2, [x3, #16] /* D3 */
+12: BTI_J /* D4 */
+ ldr d3, [x3, #24]
+ nop
+ nop
+13: BTI_J /* D3 */
+ ldr d2, [x3, #16]
nop
-14: ldp d0, d1, [x3] /* D2 */
+ nop
+14: BTI_J /* D2 */
+ ldp d0, d1, [x3]
b 99f
-15: ldr d0, [x3] /* D1 */
+ nop
+15: BTI_J /* D1 */
+ ldr d0, [x3]
b 99f
-16: ldr q3, [x3, #48] /* Q4 */
nop
-17: ldr q2, [x3, #32] /* Q3 */
+16: BTI_J /* Q4 */
+ ldr q3, [x3, #48]
+ nop
+ nop
+17: BTI_J /* Q3 */
+ ldr q2, [x3, #32]
nop
-18: ldp q0, q1, [x3] /* Q2 */
+ nop
+18: BTI_J /* Q2 */
+ ldp q0, q1, [x3]
b 99f
-19: ldr q0, [x3] /* Q1 */
+ nop
+19: BTI_J /* Q1 */
+ ldr q0, [x3]
b 99f
-20: ldrb w0, [x3, #BE(7)] /* UINT8 */
+ nop
+20: BTI_J /* UINT8 */
+ ldrb w0, [x3, #BE(7)]
b 99f
+ nop
21: brk #1000 /* reserved */
nop
-22: ldrh w0, [x3, #BE(6)] /* UINT16 */
+ nop
+ nop
+22: BTI_J /* UINT16 */
+ ldrh w0, [x3, #BE(6)]
b 99f
+ nop
23: brk #1000 /* reserved */
nop
-24: ldr w0, [x3, #BE(4)] /* UINT32 */
+ nop
+ nop
+24: BTI_J /* UINT32 */
+ ldr w0, [x3, #BE(4)]
b 99f
+ nop
25: brk #1000 /* reserved */
nop
-26: ldrsb x0, [x3, #BE(7)] /* SINT8 */
+ nop
+ nop
+26: BTI_J /* SINT8 */
+ ldrsb x0, [x3, #BE(7)]
b 99f
+ nop
27: brk #1000 /* reserved */
nop
-28: ldrsh x0, [x3, #BE(6)] /* SINT16 */
+ nop
+ nop
+28: BTI_J /* SINT16 */
+ ldrsh x0, [x3, #BE(6)]
b 99f
+ nop
29: brk #1000 /* reserved */
nop
-30: ldrsw x0, [x3, #BE(4)] /* SINT32 */
+ nop
+ nop
+30: BTI_J /* SINT32 */
+ ldrsw x0, [x3, #BE(4)]
+ nop
nop
31: /* reserved */
99: ldp x29, x30, [sp], #ffi_closure_SYSV_FS
#if defined(FFI_EXEC_STATIC_TRAMP)
.align 4
CNAME(ffi_closure_SYSV_V_alt):
+ BTI_C
/* See the comments above trampoline_code_table. */
ldr x17, [sp, #8] /* Load closure in x17 */
add sp, sp, #16 /* Restore the stack */
.align 4
CNAME(ffi_closure_SYSV_alt):
+ BTI_C
/* See the comments above trampoline_code_table. */
ldr x17, [sp, #8] /* Load closure in x17 */
add sp, sp, #16 /* Restore the stack */
.align 4
CNAME(ffi_go_closure_SYSV_V):
cfi_startproc
+ BTI_C
stp x29, x30, [sp, #-ffi_closure_SYSV_FS]!
cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
cfi_rel_offset (x29, 0)
#endif
.align 4
- cfi_startproc
CNAME(ffi_go_closure_SYSV):
+ cfi_startproc
+ BTI_C
+ SIGN_LR_LINUX_ONLY
+ PAC_CFI_WINDOW_SAVE
stp x29, x30, [sp, #-ffi_closure_SYSV_FS]!
cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
cfi_rel_offset (x29, 0)
/* Load ffi_closure_inner arguments. */
ldp PTR_REG(0), PTR_REG(1), [x18, #PTR_SIZE]/* load cif, fn */
mov x2, x18 /* load user_data */
- b .Ldo_closure
+ b L(do_closure)
cfi_endproc
.globl CNAME(ffi_go_closure_SYSV)
#if defined __ELF__ && defined __linux__
.section .note.GNU-stack,"",%progbits
+
+ .pushsection .note.gnu.property, "a";
+ .balign 8;
+ .long 4;
+ .long 0x10;
+ .long 0x5;
+ .asciz "GNU";
+ .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */
+ .long 4;
+ .long GNU_PROPERTY_AARCH64_BTI | GNU_PROPERTY_AARCH64_POINTER_AUTH;
+ .long 0;
+ .popsection;
#endif
#define ENTRY(x) .globl CNAME(x)` .type CNAME(x),%function` CNAME(x):
#endif
+#if __SIZEOF_POINTER__ == 8
+#define PTRS 8
+#define FLTS 8
+#define LARG ldl
+#define SARG stl
+#define ADDPTR addl
+#define MOVPTR movl
+#else
+#define PTRS 4
+#define FLTS 4
+#define LARG ld
+#define SARG st
+#define ADDPTR add
+#define MOVPTR mov
+#endif
+
+#define FRAME_LEN (8 * PTRS + 16)
+
.text
- /* R0: ffi_prep_args */
- /* R1: &ecif */
- /* R2: cif->bytes */
- /* R3: fig->flags */
- /* R4: ecif.rvalue */
- /* R5: fn */
-ENTRY(ffi_call_ARCompact)
+ENTRY(ffi_call_asm)
+ .cfi_startproc
+
/* Save registers. */
- st.a fp, [sp, -4] /* fp + 20, fp */
- push_s blink /* fp + 16, blink */
- st.a r4, [sp, -4] /* fp + 12, ecif.rvalue */
- push_s r3 /* fp + 8, fig->flags */
- st.a r5, [sp, -4] /* fp + 4, fn */
- push_s r2 /* fp + 0, cif->bytes */
- mov fp, sp
-
- /* Make room for all of the new args. */
- sub sp, sp, r2
-
- /* Place all of the ffi_prep_args in position. */
- /* ffi_prep_args(char *stack, extended_cif *ecif) */
- /* R1 already set. */
-
- /* And call. */
- jl_s.d [r0]
- mov_s r0, sp
-
- ld.ab r12, [fp, 4] /* cif->bytes */
- ld.ab r11, [fp, 4] /* fn */
-
- /* Move first 8 parameters in registers... */
- ld_s r0, [sp]
- ld_s r1, [sp, 4]
- ld_s r2, [sp, 8]
- ld_s r3, [sp, 12]
- ld r4, [sp, 16]
- ld r5, [sp, 20]
- ld r6, [sp, 24]
- ld r7, [sp, 28]
-
- /* ...and adjust the stack. */
- min r12, r12, 32
+ .cfi_def_cfa r1, FRAME_LEN
+ SARG fp, [r1, FRAME_LEN - 2*PTRS]
+ .cfi_offset fp, -2*PTRS
+ SARG blink, [r1, FRAME_LEN - 1*PTRS]
+ .cfi_offset blink, -1*PTRS
+ ADDPTR fp, r1, FRAME_LEN
+ MOVPTR sp, r0
+ .cfi_def_cfa fp, 0
+
+ /* Load arguments. */
+ MOVPTR r11, r2 /* fn */
+ MOVPTR r12, r3 /* closure */
+
+ /* Save arguments. */
+ LARG r0, [fp, -FRAME_LEN+0*PTRS]
+ LARG r1, [fp, -FRAME_LEN+1*PTRS]
+ LARG r2, [fp, -FRAME_LEN+2*PTRS]
+ LARG r3, [fp, -FRAME_LEN+3*PTRS]
+ LARG r4, [fp, -FRAME_LEN+4*PTRS]
+ LARG r5, [fp, -FRAME_LEN+5*PTRS]
+ LARG r6, [fp, -FRAME_LEN+6*PTRS]
+ LARG r7, [fp, -FRAME_LEN+7*PTRS]
/* Call the function. */
- jl.d [r11]
- add sp, sp, r12
-
- mov sp, fp
- pop_s r3 /* fig->flags, return type */
- pop_s r2 /* ecif.rvalue, pointer for return value */
-
- /* If the return value pointer is NULL, assume no return value. */
- breq.d r2, 0, epilogue
- pop_s blink
-
- /* Return INT. */
- brne r3, FFI_TYPE_INT, return_double
- b.d epilogue
- st_s r0, [r2]
-
-return_double:
- brne r3, FFI_TYPE_DOUBLE, epilogue
- st_s r0, [r2]
- st_s r1, [r2,4]
-
-epilogue:
- j_s.d [blink]
- ld.ab fp, [sp, 4]
-
-ENTRY(ffi_closure_ARCompact)
- st.a r0, [sp, -32]
- st_s r1, [sp, 4]
- st_s r2, [sp, 8]
- st_s r3, [sp, 12]
- st r4, [sp, 16]
- st r5, [sp, 20]
- st r6, [sp, 24]
- st r7, [sp, 28]
-
- /* pointer to arguments */
- mov_s r2, sp
-
- /* return value goes here */
- sub sp, sp, 8
- mov_s r1, sp
-
- push_s blink
+ jl [r11]
+
+ /* Save return value (r0/r1) */
+ SARG r0, [fp, -FRAME_LEN+0*PTRS]
+ SARG r1, [fp, -FRAME_LEN+1*PTRS]
+
+ /* Restore and return. */
+ add sp, fp, -FRAME_LEN
+ .cfi_def_cfa sp, FRAME_LEN
+ LARG blink, [fp, -1*PTRS]
+ .cfi_restore blink
+ LARG fp, [fp, -2*PTRS]
+ .cfi_restore fp
+ j_s [blink]
+ .cfi_endproc
+ .size ffi_call_asm, .-ffi_call_asm
+
+/*
+ ffi_closure_asm. Expects address of the passed-in ffi_closure in r8.
+ void ffi_closure_inner (ffi_cif *cif,
+ void (*fun) (ffi_cif *, void *, void **, void *),
+ void *user_data,
+ size_t *stackargs, struct call_context *regargs)
+*/
+
+ENTRY(ffi_closure_asm)
+ .cfi_startproc
+
+ ADDPTR sp, sp, -FRAME_LEN
+ .cfi_def_cfa_offset FRAME_LEN
+
+ /* Make a frame. */
+ SARG fp, [sp, FRAME_LEN-2*PTRS]
+ .cfi_offset fp, -2*PTRS
+ SARG blink, [sp, FRAME_LEN-1*PTRS]
+ .cfi_offset blink, -1*PTRS
+ ADDPTR fp, sp, FRAME_LEN
+
+ /* Save arguments. */
+ SARG r0, [sp, 0*PTRS]
+ SARG r1, [sp, 1*PTRS]
+ SARG r2, [sp, 2*PTRS]
+ SARG r3, [sp, 3*PTRS]
+ SARG r4, [sp, 4*PTRS]
+ SARG r5, [sp, 5*PTRS]
+ SARG r6, [sp, 6*PTRS]
+ SARG r7, [sp, 7*PTRS]
+
+ /* Enter C. */
+ LARG r0, [r8, FFI_TRAMPOLINE_SIZE+0*PTRS]
+ LARG r1, [r8, FFI_TRAMPOLINE_SIZE+1*PTRS]
+ LARG r2, [r8, FFI_TRAMPOLINE_SIZE+2*PTRS]
+ ADDPTR r3, sp, FRAME_LEN
+ MOVPTR r4, sp
+
+ /* Call the C code. */
+ bl ffi_closure_inner
- bl.d ffi_closure_inner_ARCompact
- mov_s r0, r8 /* codeloc, set by trampoline */
-
- pop_s blink
-
- /* set return value to r1:r0 */
- pop_s r0
- pop_s r1
- j_s.d [blink]
- add_s sp, sp, 32
+ /* Return values. */
+ LARG r0, [sp, 0*PTRS]
+ LARG r1, [sp, 1*PTRS]
+
+ /* Restore and return. */
+ LARG blink, [sp, FRAME_LEN-1*PTRS]
+ .cfi_restore blink
+ LARG fp, [sp, FRAME_LEN-2*PTRS]
+ .cfi_restore fp
+ ADDPTR sp, sp, FRAME_LEN
+ .cfi_def_cfa_offset 0
+ j_s [blink]
+ .cfi_endproc
+ .size ffi_closure_asm, .-ffi_closure_asm
+
+/*
+ ffi_go_closure_asm. Expects address of the passed-in ffi_go_closure in r12.
+ void ffi_closure_inner (ffi_cif *cif,
+ void (*fun) (ffi_cif *, void *, void **, void *),
+ void *user_data,
+ size_t *stackargs, struct call_context *regargs)
+*/
+
+ENTRY(ffi_go_closure_asm)
+ .cfi_startproc
+
+ ADDPTR sp, sp, -FRAME_LEN
+ .cfi_def_cfa_offset FRAME_LEN
+
+ /* make a frame */
+ SARG fp, [sp, FRAME_LEN-2*PTRS]
+ .cfi_offset fp, -2*PTRS
+ SARG blink, [sp, FRAME_LEN-1*PTRS]
+ .cfi_offset blink, -1*PTRS
+ ADDPTR fp, sp, FRAME_LEN
+
+ /* save arguments */
+ SARG r0, [sp, 0*PTRS]
+ SARG r1, [sp, 1*PTRS]
+ SARG r2, [sp, 2*PTRS]
+ SARG r3, [sp, 3*PTRS]
+ SARG r4, [sp, 4*PTRS]
+ SARG r5, [sp, 5*PTRS]
+ SARG r6, [sp, 6*PTRS]
+ SARG r7, [sp, 7*PTRS]
+
+ /* enter C */
+ LARG r0, [r12, 1*PTRS]
+ LARG r1, [r12, 2*PTRS]
+ MOVPTR r2, r12
+ ADDPTR r3, sp, FRAME_LEN
+ MOVPTR r4, sp
+
+ bl ffi_closure_inner
+
+ /* Return values. */
+ LARG r0, [sp, 0*PTRS]
+ LARG r1, [sp, 1*PTRS]
+
+
+ LARG blink, [sp, FRAME_LEN-1*PTRS]
+ .cfi_restore blink
+ LARG fp, [sp, FRAME_LEN-2*PTRS]
+ .cfi_restore fp
+ ADDPTR sp, sp, FRAME_LEN
+ .cfi_def_cfa_offset 0
+ j_s [blink]
+ .cfi_endproc
+ .size ffi_go_closure_asm, .-ffi_go_closure_asm
#include <sys/cachectl.h>
+#define NARGREG 8
+#define STKALIGN 4
+#define MAXCOPYARG (2 * sizeof(double))
+
+typedef struct call_context
+{
+ size_t r[8];
+ /* used by the assembly code to in-place construct its own stack frame */
+ char frame[16];
+} call_context;
+
+typedef struct call_builder
+{
+ call_context *aregs;
+ int used_integer;
+ //int used_float;
+ size_t *used_stack;
+ void *struct_stack;
+} call_builder;
+
+/* integer (not pointer) less than ABI XLEN */
+/* FFI_TYPE_INT does not appear to be used */
+#if defined(__ARC64_ARCH64__)
+#define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT64)
+#else
+#define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT32)
+#endif
+
/* for little endian ARC, the code is in fact stored as mixed endian for
performance reasons */
#if __BIG_ENDIAN__
#define CODE_ENDIAN(x) ( (((uint32_t) (x)) << 16) | (((uint32_t) (x)) >> 16))
#endif
-/* ffi_prep_args is called by the assembly routine once stack
- space has been allocated for the function's arguments. */
-
-void
-ffi_prep_args (char *stack, extended_cif * ecif)
-{
- unsigned int i;
- void **p_argv;
- char *argp;
- ffi_type **p_arg;
-
- argp = stack;
-
- if (ecif->cif->rtype->type == FFI_TYPE_STRUCT)
- {
- *(void **) argp = ecif->rvalue;
- argp += 4;
- }
-
- p_argv = ecif->avalue;
-
- for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
- (i != 0); i--, p_arg++)
- {
- size_t z;
- int alignment;
-
- /* align alignment to 4 */
- alignment = (((*p_arg)->alignment - 1) | 3) + 1;
-
- /* Align if necessary. */
- if ((alignment - 1) & (unsigned) argp)
- argp = (char *) FFI_ALIGN (argp, alignment);
-
- z = (*p_arg)->size;
- if (z < sizeof (int))
- {
- z = sizeof (int);
-
- switch ((*p_arg)->type)
- {
- case FFI_TYPE_SINT8:
- *(signed int *) argp = (signed int) *(SINT8 *) (*p_argv);
- break;
-
- case FFI_TYPE_UINT8:
- *(unsigned int *) argp = (unsigned int) *(UINT8 *) (*p_argv);
- break;
-
- case FFI_TYPE_SINT16:
- *(signed int *) argp = (signed int) *(SINT16 *) (*p_argv);
- break;
-
- case FFI_TYPE_UINT16:
- *(unsigned int *) argp = (unsigned int) *(UINT16 *) (*p_argv);
- break;
-
- case FFI_TYPE_STRUCT:
- memcpy (argp, *p_argv, (*p_arg)->size);
- break;
-
- default:
- FFI_ASSERT (0);
- }
- }
- else if (z == sizeof (int))
- {
- *(unsigned int *) argp = (unsigned int) *(UINT32 *) (*p_argv);
- }
- else
- {
- if ((*p_arg)->type == FFI_TYPE_STRUCT)
- {
- memcpy (argp, *p_argv, z);
- }
- else
- {
- /* Double or long long 64bit. */
- memcpy (argp, *p_argv, z);
- }
- }
- p_argv++;
- argp += z;
- }
-
- return;
-}
-
/* Perform machine dependent cif processing. */
ffi_status
ffi_prep_cif_machdep (ffi_cif * cif)
return FFI_OK;
}
-extern void ffi_call_ARCompact (void (*)(char *, extended_cif *),
- extended_cif *, unsigned, unsigned,
- unsigned *, void (*fn) (void));
+/* allocates a single register, float register, or XLEN-sized stack slot to a datum */
+static void marshal_atom(call_builder *cb, int type, void *data) {
+ size_t value = 0;
+ switch (type) {
+ case FFI_TYPE_UINT8: value = *(uint8_t *)data; break;
+ case FFI_TYPE_SINT8: value = *(int8_t *)data; break;
+ case FFI_TYPE_UINT16: value = *(uint16_t *)data; break;
+ case FFI_TYPE_SINT16: value = *(int16_t *)data; break;
+ /* 32-bit quantities are always sign-extended in the ABI */
+ case FFI_TYPE_UINT32: value = *(int32_t *)data; break;
+ case FFI_TYPE_SINT32: value = *(int32_t *)data; break;
+#if defined(__ARC64_ARCH64__)
+ case FFI_TYPE_UINT64: value = *(uint64_t *)data; break;
+ case FFI_TYPE_SINT64: value = *(int64_t *)data; break;
+#endif
+ case FFI_TYPE_POINTER: value = *(size_t *)data; break;
+ default: FFI_ASSERT(0); break;
+ }
+
+ if (cb->used_integer == NARGREG) {
+ *cb->used_stack++ = value;
+ } else {
+ cb->aregs->r[cb->used_integer++] = value;
+ }
+}
-void
-ffi_call (ffi_cif * cif, void (*fn) (void), void *rvalue, void **avalue)
-{
- extended_cif ecif;
+/* adds an argument to a call, or a not by reference return value */
+static void marshal(call_builder *cb, ffi_type *type, int var, void *data) {
+ size_t realign[2];
+
+#if (defined(__ARC64_ARCH64__) || defined(__ARC64_ARCH32__))
+ if (type->size > 2 * __SIZEOF_POINTER__) {
+ if (var) {
+ marshal_atom(cb, FFI_TYPE_POINTER, &data);
+ } else {
+ /* copy to stack and pass by reference */
+ data = memcpy (cb->struct_stack, data, type->size);
+ cb->struct_stack = (size_t *) FFI_ALIGN ((char *) cb->struct_stack + type->size, __SIZEOF_POINTER__);
+ marshal_atom(cb, FFI_TYPE_POINTER, &data);
+ }
+ }
+#else
+ if (type->type == FFI_TYPE_STRUCT) {
+ if (var) {
+ if (type->size > 0)
+ marshal_atom(cb, FFI_TYPE_POINTER, data);
+ } else {
+ int i;
+
+ for (i = 0; i < type->size; i += sizeof(size_t)) {
+ marshal_atom(cb, FFI_TYPE_POINTER, data);
+ data += sizeof(size_t);
+ }
+ }
+ }
+#endif
+ else if (IS_INT(type->type) || type->type == FFI_TYPE_POINTER) {
+ marshal_atom(cb, type->type, data);
+ } else {
+ memcpy(realign, data, type->size);
+ if (type->size > 0)
+ marshal_atom(cb, FFI_TYPE_POINTER, realign);
+ if (type->size > __SIZEOF_POINTER__)
+ marshal_atom(cb, FFI_TYPE_POINTER, realign + 1);
+ }
+}
- ecif.cif = cif;
- ecif.avalue = avalue;
+static void unmarshal_atom(call_builder *cb, int type, void *data) {
+ size_t value;
+
+ if (cb->used_integer == NARGREG) {
+ value = *cb->used_stack++;
+ } else {
+ value = cb->aregs->r[cb->used_integer++];
+ }
+
+ switch (type) {
+ case FFI_TYPE_UINT8: *(uint8_t *)data = value; break;
+ case FFI_TYPE_SINT8: *(uint8_t *)data = value; break;
+ case FFI_TYPE_UINT16: *(uint16_t *)data = value; break;
+ case FFI_TYPE_SINT16: *(uint16_t *)data = value; break;
+ case FFI_TYPE_UINT32: *(uint32_t *)data = value; break;
+ case FFI_TYPE_SINT32: *(uint32_t *)data = value; break;
+#if defined(__ARC64_ARCH64__)
+ case FFI_TYPE_UINT64: *(uint64_t *)data = value; break;
+ case FFI_TYPE_SINT64: *(uint64_t *)data = value; break;
+#endif
+ case FFI_TYPE_POINTER: *(size_t *)data = value; break;
+ default: FFI_ASSERT(0); break;
+ }
+}
- /* 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))
- {
- ecif.rvalue = alloca (cif->rtype->size);
- }
- else
- ecif.rvalue = rvalue;
+/* for arguments passed by reference returns the pointer, otherwise the arg is copied (up to MAXCOPYARG bytes) */
+static void *unmarshal(call_builder *cb, ffi_type *type, int var, void *data) {
+ size_t realign[2];
- switch (cif->abi)
- {
- case FFI_ARCOMPACT:
- ffi_call_ARCompact (ffi_prep_args, &ecif, cif->bytes,
- cif->flags, ecif.rvalue, fn);
- break;
+#if defined(__ARC64_ARCH64__)
+ void *pointer;
- default:
- FFI_ASSERT (0);
- break;
+ if (type->size > 2 * __SIZEOF_POINTER__) {
+ /* pass by reference */
+ unmarshal_atom(cb, FFI_TYPE_POINTER, (char*)&pointer);
+ return pointer;
+ }
+#elif defined(__ARC64_ARCH32__)
+ if (type->type == FFI_TYPE_STRUCT) {
+ if (type->size > 2 * __SIZEOF_POINTER__) {
+ unmarshal_atom(cb, FFI_TYPE_POINTER, &realign[0]);
+ memcpy(data, (const void*)realign[0], type->size);
+ return data;
+ } else {
+ int i;
+ void *pdata = data;
+
+ for (i = 0; i < type->size; i += sizeof(size_t)) {
+ unmarshal_atom(cb, FFI_TYPE_POINTER, pdata);
+ pdata += sizeof(size_t);
+ }
+ return data;
}
+ }
+#else
+ if (type->type == FFI_TYPE_STRUCT) {
+
+ if (var) {
+ int i;
+ void *pdata = data;
+
+ for (i = 0; i < type->size; i += sizeof(size_t)) {
+ unmarshal_atom(cb, FFI_TYPE_POINTER, pdata);
+ pdata += sizeof(size_t);
+ }
+ return data;
+ } else {
+ if (type->size > 0)
+ unmarshal_atom(cb, FFI_TYPE_POINTER, &realign[0]);
+ memcpy(data, (const void*)realign[0], type->size);
+ return data;
+ }
+ }
+#endif
+ else if (IS_INT(type->type) || type->type == FFI_TYPE_POINTER) {
+ unmarshal_atom(cb, type->type, data);
+ return data;
+ } else {
+ if (type->size > 0)
+ unmarshal_atom(cb, FFI_TYPE_POINTER, realign);
+ if (type->size > __SIZEOF_POINTER__)
+ unmarshal_atom(cb, FFI_TYPE_POINTER, realign + 1);
+ memcpy(data, realign, type->size);
+ return data;
+ }
+}
+
+static int passed_by_ref(ffi_type *type, int var) {
+ if (type->type == FFI_TYPE_STRUCT)
+ return 1;
+
+ return type->size > 2 * __SIZEOF_POINTER__;
}
-int
-ffi_closure_inner_ARCompact (ffi_closure * closure, void *rvalue,
- ffi_arg * args)
+/* Low level routine for calling functions */
+extern void ffi_call_asm (void *stack, struct call_context *regs,
+ void (*fn) (void), void *closure) FFI_HIDDEN;
+
+static void
+ffi_call_int (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue,
+ void *closure)
{
- void **arg_area, **p_argv;
- ffi_cif *cif = closure->cif;
- char *argp = (char *) args;
- ffi_type **p_argt;
- int i;
+ int return_by_ref = passed_by_ref(cif->rtype, 0);
- arg_area = (void **) alloca (cif->nargs * sizeof (void *));
+ /* Allocate space for stack arg parameters. */
+ size_t arg_bytes = FFI_ALIGN(2 * sizeof(size_t) * cif->nargs, STKALIGN);
+ /* Allocate space for copies of big structures. */
+ size_t struct_bytes = FFI_ALIGN(cif->bytes, STKALIGN);
+ // size_t rval_bytes = 0;
+ // if (rvalue == NULL && cif->rtype->size > 2*__SIZEOF_POINTER__)
+ // rval_bytes = FFI_ALIGN(cif->rtype->size, STKALIGN);
+ size_t alloc_size = arg_bytes + /*rval_bytes +*/ struct_bytes + sizeof(call_context);
+ size_t alloc_base = (size_t)alloca(alloc_size);
- /* handle hidden argument */
- if (cif->flags == FFI_TYPE_STRUCT)
- {
- rvalue = *(void **) argp;
- argp += 4;
- }
+ // if (rval_bytes)
+ // rvalue = (void*)(alloc_base + arg_bytes);
- p_argv = arg_area;
+ call_builder cb;
+ cb.used_integer = 0;
+ cb.aregs = (call_context*)(alloc_base + arg_bytes /*+ rval_bytes*/ + struct_bytes);
+ cb.used_stack = (void*)alloc_base;
+ cb.struct_stack = (void *)(alloc_base + arg_bytes /*+ rval_bytes*/);
- for (i = 0, p_argt = cif->arg_types; i < cif->nargs;
- i++, p_argt++, p_argv++)
- {
- size_t z;
- int alignment;
+ // if (cif->rtype->type == FFI_TYPE_STRUCT)
+ // marshal(&cb, &ffi_type_pointer, 0, &rvalue);
+
+ if (return_by_ref)
+ marshal(&cb, &ffi_type_pointer, 0, &rvalue);
- /* align alignment to 4 */
- alignment = (((*p_argt)->alignment - 1) | 3) + 1;
+ int i;
+ for (i = 0; i < cif->nargs; i++)
+ marshal(&cb, cif->arg_types[i], 0, avalue[i]);
- /* Align if necessary. */
- if ((alignment - 1) & (unsigned) argp)
- argp = (char *) FFI_ALIGN (argp, alignment);
+ ffi_call_asm ((void *) alloc_base, cb.aregs, fn, closure);
- z = (*p_argt)->size;
- *p_argv = (void *) argp;
- argp += z;
+ cb.used_integer = 0;
+ if (!return_by_ref && rvalue)
+ {
+ if (IS_INT(cif->rtype->type)
+ && cif->rtype->size < sizeof (ffi_arg))
+ {
+ /* Integer types smaller than ffi_arg need to be extended. */
+ switch (cif->rtype->type) {
+ case FFI_TYPE_SINT8:
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_SINT32:
+ unmarshal_atom (&cb, (sizeof (ffi_arg) > 4
+ ? FFI_TYPE_SINT64 : FFI_TYPE_SINT32),
+ rvalue);
+ break;
+ case FFI_TYPE_UINT8:
+ case FFI_TYPE_UINT16:
+ case FFI_TYPE_UINT32:
+ unmarshal_atom (&cb, (sizeof (ffi_arg) > 4
+ ? FFI_TYPE_UINT64 : FFI_TYPE_UINT32),
+ rvalue);
+ break;
+ }
+ }
+ else
+ unmarshal(&cb, cif->rtype, 0, rvalue);
}
+}
- (closure->fun) (cif, rvalue, arg_area, closure->user_data);
+void
+ffi_call (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue)
+{
+ ffi_call_int(cif, fn, rvalue, avalue, NULL);
+}
- return cif->flags;
+void
+ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue,
+ void **avalue, void *closure)
+{
+ ffi_call_int(cif, fn, rvalue, avalue, closure);
}
-extern void ffi_closure_ARCompact (void);
+extern void ffi_closure_asm(void) FFI_HIDDEN;
ffi_status
ffi_prep_closure_loc (ffi_closure * closure, ffi_cif * cif,
{
uint32_t *tramp = (uint32_t *) & (closure->tramp[0]);
+#if defined(__ARC64_ARCH64__)
+ size_t address_ffi_closure = (size_t) ffi_closure_asm;
+#endif
+
switch (cif->abi)
{
+#if defined(__ARC64_ARCH64__)
+ case FFI_ARC64:
+ FFI_ASSERT (tramp == codeloc);
+ tramp[0] = CODE_ENDIAN (0x580a1fc0); /* movl r8, pcl */
+ tramp[1] = CODE_ENDIAN (0x5c0b1f80); /* movhl r12, limm */
+ tramp[2] = CODE_ENDIAN ((uint32_t)(address_ffi_closure >> 32));
+ tramp[3] = CODE_ENDIAN (0x5c051f8c); /* orl r12, r12, limm */
+ tramp[4] = CODE_ENDIAN ((uint32_t)(address_ffi_closure & 0xffffffff));
+ tramp[5] = CODE_ENDIAN (0x20200300); /* j [r12] */
+ break;
+#else
case FFI_ARCOMPACT:
FFI_ASSERT (tramp == codeloc);
tramp[0] = CODE_ENDIAN (0x200a1fc0); /* mov r8, pcl */
tramp[1] = CODE_ENDIAN (0x20200f80); /* j [long imm] */
- tramp[2] = CODE_ENDIAN (ffi_closure_ARCompact);
+ tramp[2] = CODE_ENDIAN ((uint32_t) ffi_closure_asm);
break;
+#endif
default:
return FFI_BAD_ABI;
return FFI_OK;
}
+
+extern void ffi_go_closure_asm (void) FFI_HIDDEN;
+
+ffi_status
+ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif,
+ void (*fun) (ffi_cif *, void *, void **, void *))
+{
+ if (cif->abi <= FFI_FIRST_ABI || cif->abi >= FFI_LAST_ABI)
+ return FFI_BAD_ABI;
+
+ closure->tramp = (void *) ffi_go_closure_asm;
+ closure->cif = cif;
+ closure->fun = fun;
+
+ return FFI_OK;
+}
+
+/* Called by the assembly code with aregs pointing to saved argument registers
+ and stack pointing to the stacked arguments. Return values passed in
+ registers will be reloaded from aregs. */
+void FFI_HIDDEN
+ffi_closure_inner (ffi_cif *cif,
+ void (*fun) (ffi_cif *, void *, void **, void *),
+ void *user_data,
+ size_t *stack, call_context *aregs)
+{
+ void **avalue = alloca(cif->nargs * sizeof(void*));
+ /* storage for arguments which will be copied by unmarshal(). We could
+ theoretically avoid the copies in many cases and use at most 128 bytes
+ of memory, but allocating disjoint storage for each argument is
+ simpler. */
+ char *astorage = alloca(cif->bytes);
+ char *ptr = astorage;
+ void *rvalue;
+ call_builder cb;
+ int i;
+
+ cb.aregs = aregs;
+ cb.used_integer = 0;
+ cb.used_stack = stack;
+
+ /* handle hidden argument */
+ if (cif->flags == FFI_TYPE_STRUCT)
+ unmarshal(&cb, &ffi_type_pointer, 0, &rvalue);
+ else
+ rvalue = alloca(cif->rtype->size);
+
+ for (i = 0; i < cif->nargs; i++) {
+ avalue[i] = unmarshal(&cb, cif->arg_types[i], 1, ptr);
+ ptr += cif->arg_types[i]->size;
+ }
+
+ fun (cif, rvalue, avalue, user_data);
+
+ if (cif->rtype->type != FFI_TYPE_VOID) {
+ cb.used_integer = 0;
+ marshal(&cb, cif->rtype, 1, rvalue);
+ }
+}
typedef enum ffi_abi
{
FFI_FIRST_ABI = 0,
+#if __SIZEOF_POINTER__ == 8
+ FFI_ARC64,
+#else
FFI_ARCOMPACT,
+#endif
FFI_LAST_ABI,
+#if __SIZEOF_POINTER__ == 8
+ FFI_DEFAULT_ABI = FFI_ARC64
+#else
FFI_DEFAULT_ABI = FFI_ARCOMPACT
+#endif
} ffi_abi;
#endif
#define FFI_CLOSURES 1
+#define FFI_GO_CLOSURES 1
+#if __SIZEOF_POINTER__ == 8
+#define FFI_TRAMPOLINE_SIZE 24
+#else
#define FFI_TRAMPOLINE_SIZE 12
+#endif
+
#define FFI_NATIVE_RAW_API 0
#endif
#include <machine/sysarch.h>
#endif
+#if defined(__QNX__)
+#include <sys/mman.h>
+#endif
+
/* Forward declares. */
static int vfp_type_p (const ffi_type *);
static void layout_vfp_args (ffi_cif *);
config[1] = closure_func;
#else
-#if defined(FFI_EXEC_STATIC_TRAMP)
+# if defined(FFI_EXEC_STATIC_TRAMP)
if (ffi_tramp_is_present(closure))
{
/* Initialize the static trampoline's parameters. */
ffi_tramp_set_parms (closure->ftramp, closure_func, closure);
goto out;
}
-#endif
+# endif
/* Initialize the dynamic trampoline. */
-#ifndef _WIN32
+# ifndef _WIN32
memcpy(closure->tramp, ffi_arm_trampoline, 8);
-#else
+# else
// cast away function type so MSVC doesn't set the lower bit of the function pointer
memcpy(closure->tramp, (void*)((uintptr_t)ffi_arm_trampoline & 0xFFFFFFFE), FFI_TRAMPOLINE_CLOSURE_OFFSET);
-#endif
+# endif
-#if defined (__QNX__)
- msync(closure->tramp, 8, 0x1000000); /* clear data map */
- msync(codeloc, 8, 0x1000000); /* clear insn map */
-#elif defined(_WIN32)
+# if defined(__QNX__)
+ msync (closure->tramp, 8, MS_INVALIDATE_ICACHE); /* clear data map */
+ msync (codeloc, 8, MS_INVALIDATE_ICACHE); /* clear insn map */
+# elif defined(_WIN32)
FlushInstructionCache(GetCurrentProcess(), closure->tramp, FFI_TRAMPOLINE_SIZE);
-#else
+# else
__clear_cache(closure->tramp, closure->tramp + 8); /* clear data map */
__clear_cache(codeloc, codeloc + 8); /* clear insn map */
-#endif
-#ifdef _WIN32
+# endif
+# ifdef _WIN32
*(void(**)(void))(closure->tramp + FFI_TRAMPOLINE_CLOSURE_FUNCTION) = closure_func;
-#else
+# else
*(void (**)(void))(closure->tramp + 8) = closure_func;
-#endif
+# endif
+# if defined(FFI_EXEC_STATIC_TRAMP)
out:
+# endif
#endif
closure->cif = cif;
cmp r3, #3 @ load only d0 if possible
ite le
-#ifdef __clang__
- vldrle d0, [r0]
- vldmgt r0, {d0-d7}
-#else
ldcle p11, cr0, [r0] @ vldrle d0, [r0]
ldcgt p11, cr0, [r0], {16} @ vldmgt r0, {d0-d7}
-#endif
add r0, r0, #64 @ discard the vfp register args
/* FALLTHRU */
ARM_FUNC_END(ffi_call_VFP)
#endif
0:
E(ARM_TYPE_VFP_S)
-#ifdef __clang__
- vstr s0, [r2]
-#else
stc p10, cr0, [r2] @ vstr s0, [r2]
-#endif
pop {fp,pc}
E(ARM_TYPE_VFP_D)
-#ifdef __clang__
- vstr d0, [r2]
-#else
stc p11, cr0, [r2] @ vstr d0, [r2]
-#endif
pop {fp,pc}
E(ARM_TYPE_VFP_N)
-#ifdef __clang__
- vstm r2, {d0-d3}
-#else
stc p11, cr0, [r2], {8} @ vstm r2, {d0-d3}
-#endif
pop {fp,pc}
E(ARM_TYPE_INT64)
str r1, [r2, #4]
add ip, sp, #16
sub sp, sp, #64+32 @ allocate frame
cfi_adjust_cfa_offset(64+32)
-#ifdef __clang__
- vstm sp, {d0-d7}
-#else
stc p11, cr0, [sp], {16} @ vstm sp, {d0-d7}
-#endif
stmdb sp!, {ip,lr}
/* See above. */
cfi_rel_offset(lr, 4)
0:
E(ARM_TYPE_VFP_S)
-#ifdef __clang__
- vldr s0, [r2]
-#else
ldc p10, cr0, [r2] @ vldr s0, [r2]
-#endif
b call_epilogue
E(ARM_TYPE_VFP_D)
-#ifdef __clang__
- vldr d0, [r2]
-#else
ldc p11, cr0, [r2] @ vldr d0, [r2]
-#endif
b call_epilogue
E(ARM_TYPE_VFP_N)
-#ifdef __clang__
- vldm r2, {d0-d3}
-#else
ldc p11, cr0, [r2], {8} @ vldm r2, {d0-d3}
-#endif
b call_epilogue
E(ARM_TYPE_INT64)
ldr r1, [r2, #4]
/* -----------------------------------------------------------------------
- closures.c - Copyright (c) 2019 Anthony Green
+ closures.c - Copyright (c) 2019, 2022 Anthony Green
Copyright (c) 2007, 2009, 2010 Red Hat, Inc.
Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc
Copyright (c) 2011 Plausible Labs Cooperative, Inc.
DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------- */
-#if defined __linux__ && !defined _GNU_SOURCE
+#if (defined __linux__ || defined __CYGWIN__) && !defined _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
+#ifndef __EMSCRIPTEN__
+
#include <fficonfig.h>
#include <ffi.h>
#include <ffi_common.h>
# define FFI_MMAP_EXEC_WRIT 1
# define HAVE_MNTENT 1
# endif
-# if defined(_WIN32) || defined(__OS2__)
-/* Windows systems may have Data Execution Protection (DEP) enabled,
+# if defined(__CYGWIN__) || defined(_WIN32) || defined(__OS2__)
+/* Windows systems may have Data Execution Protection (DEP) enabled,
which requires the use of VirtualMalloc/VirtualFree to alloc/free
executable memory. */
# define FFI_MMAP_EXEC_WRIT 1
# endif
#endif
-#if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
-# if defined(__linux__) && !defined(__ANDROID__)
+#if FFI_MMAP_EXEC_WRIT && defined(__linux__) && !defined(__ANDROID__)
+# if !defined FFI_MMAP_EXEC_SELINUX
/* When defined to 1 check for SELinux and if SELinux is active,
don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
might cause audit messages. */
# define FFI_MMAP_EXEC_SELINUX 1
-# endif
-#endif
+# endif /* !defined FFI_MMAP_EXEC_SELINUX */
+# if !defined FFI_MMAP_PAX
+/* Also check for PaX MPROTECT */
+# define FFI_MMAP_PAX 1
+# endif /* !defined FFI_MMAP_PAX */
+#endif /* FFI_MMAP_EXEC_WRIT && defined(__linux__) && !defined(__ANDROID__) */
#if FFI_CLOSURES
#include <mach/mach.h>
#include <pthread.h>
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
#include <ptrauth.h>
#endif
#include <stdio.h>
/* Remap the trampoline table on top of the placeholder page */
trampoline_page = config_page + PAGE_MAX_SIZE;
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
trampoline_page_template = (vm_address_t)(uintptr_t)ptrauth_auth_data((void *)&ffi_closure_trampoline_table_page, ptrauth_key_function_pointer, 0);
#else
trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_MAX_SIZE, 0x0,
VM_FLAGS_OVERWRITE, mach_task_self (), trampoline_page_template,
FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
- if (kt != KERN_SUCCESS || !(cur_prot & VM_PROT_EXECUTE))
+ if (kt != KERN_SUCCESS)
{
vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
return NULL;
}
+ if (!(cur_prot & VM_PROT_EXECUTE))
+ {
+ /* If VM_PROT_EXECUTE isn't set on the remapped trampoline page, set it */
+ kt = vm_protect (mach_task_self (), trampoline_page, PAGE_MAX_SIZE,
+ FALSE, cur_prot | VM_PROT_EXECUTE);
+ if (kt != KERN_SUCCESS)
+ {
+ vm_deallocate (mach_task_self (), config_page, PAGE_MAX_SIZE * 2);
+ return NULL;
+ }
+ }
+
/* We have valid trampoline and config pages */
table = calloc (1, sizeof (ffi_trampoline_table));
table->free_count = FFI_TRAMPOLINE_COUNT;
ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
entry->trampoline =
(void *) (trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
-#ifdef HAVE_PTRAUTH
+#ifdef HAVE_ARM64E_PTRAUTH
entry->trampoline = ptrauth_sign_unauthenticated(entry->trampoline, ptrauth_key_function_pointer, 0);
#endif
#endif /* !FFI_MMAP_EXEC_SELINUX */
-/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
-#ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
+/* On PaX enable kernels that have MPROTECT enabled we can't use PROT_EXEC. */
+#if defined FFI_MMAP_PAX
#include <stdlib.h>
-static int emutramp_enabled = -1;
+enum {
+ PAX_MPROTECT = (1 << 0),
+ PAX_EMUTRAMP = (1 << 1),
+};
+static int cached_pax_flags = -1;
static int
-emutramp_enabled_check (void)
+pax_flags_check (void)
{
char *buf = NULL;
size_t len = 0;
while (getline (&buf, &len, f) != -1)
if (!strncmp (buf, "PaX:", 4))
{
- char emutramp;
- if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
- ret = (emutramp == 'E');
+ if (NULL != strchr (buf + 4, 'M'))
+ ret |= PAX_MPROTECT;
+ if (NULL != strchr (buf + 4, 'E'))
+ ret |= PAX_EMUTRAMP;
break;
}
free (buf);
return ret;
}
-#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
- : (emutramp_enabled = emutramp_enabled_check ()))
-#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
+#define get_pax_flags() (cached_pax_flags >= 0 ? cached_pax_flags \
+ : (cached_pax_flags = pax_flags_check ()))
+#define has_pax_flags(flags) ((flags) == ((flags) & get_pax_flags ()))
+#define is_mprotect_enabled() (has_pax_flags (PAX_MPROTECT))
+#define is_emutramp_enabled() (has_pax_flags (PAX_EMUTRAMP))
+
+#endif /* defined FFI_MMAP_PAX */
#elif defined (__CYGWIN__) || defined(__INTERIX)
#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
-#ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
-#define is_emutramp_enabled() 0
-#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
+#if !defined FFI_MMAP_PAX
+# define is_mprotect_enabled() 0
+# define is_emutramp_enabled() 0
+#endif /* !defined FFI_MMAP_PAX */
/* Declare all functions defined in dlmalloc.c as static. */
static void *dlmalloc(size_t);
/* Open a temporary file name, and immediately unlink it. */
static int
-open_temp_exec_file_name (char *name, int flags)
+open_temp_exec_file_name (char *name, int flags MAYBE_UNUSED)
{
int fd;
/* Return a file descriptor of a temporary zero-sized file in a
writable and executable filesystem. */
-static int
+int
open_temp_exec_file (void)
{
int fd;
Failure to allocate the space will cause SIGBUS to be thrown when
the mapping is subsequently written to. */
static int
-allocate_space (int fd, off_t offset, off_t len)
+allocate_space (int fd, off_t len)
{
- static size_t page_size;
+ static long page_size;
/* Obtain system page size. */
if (!page_size)
offset = execsize;
- if (allocate_space (execfd, offset, length))
+ if (allocate_space (execfd, length))
return MFAIL;
flags &= ~(MAP_PRIVATE | MAP_ANONYMOUS);
return ptr;
}
- if (execfd == -1 && is_emutramp_enabled ())
+ /* -1 != execfd hints that we already decided to use dlmmap_locked
+ last time. */
+ if (execfd == -1 && is_mprotect_enabled ())
{
- ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
- return ptr;
+#ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
+ if (is_emutramp_enabled ())
+ {
+ /* emutramp requires the kernel recognizing the trampoline pattern
+ generated by ffi_prep_closure_loc; there is no way to test
+ in advance whether this will work, so this is experimental. */
+ ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
+ return ptr;
+ }
+#endif
+ /* fallback to dlmmap_locked. */
}
-
- if (execfd == -1 && !is_selinux_enabled ())
+ else if (execfd == -1 && !is_selinux_enabled ())
{
ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
MREMAP_DUP and prot at this point. */
}
- if (execsize == 0 || execfd == -1)
- {
- pthread_mutex_lock (&open_temp_exec_file_mutex);
- ptr = dlmmap_locked (start, length, prot, flags, offset);
- pthread_mutex_unlock (&open_temp_exec_file_mutex);
+ pthread_mutex_lock (&open_temp_exec_file_mutex);
+ ptr = dlmmap_locked (start, length, prot, flags, offset);
+ pthread_mutex_unlock (&open_temp_exec_file_mutex);
- return ptr;
- }
-
- return dlmmap_locked (start, length, prot, flags, offset);
+ return ptr;
}
/* Release memory at the given address, as well as the corresponding
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;
}
if (ffi_tramp_is_supported ())
ffi_tramp_free (((ffi_closure *) ptr)->ftramp);
- dlfree (FFI_RESTORE_PTR (ptr));
+ dlfree (ptr);
}
int
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 *
#endif /* FFI_CLOSURES */
#endif /* NetBSD with PROT_MPROTECT */
+#endif /* __EMSCRIPTEN__ */
/* This function should only be called via the FFI_ASSERT() macro */
-void ffi_assert(char *expr, char *file, int line)
+NORETURN void ffi_assert(const char *expr, const char *file, int line)
{
fprintf(stderr, "ASSERTION FAILURE: %s at %s:%d\n", expr, file, line);
ffi_stop_here();
/* Perform a sanity check on an ffi_type structure */
-void ffi_type_test(ffi_type *a, char *file, int line)
+void ffi_type_test(ffi_type *a, const char *file, int line)
{
FFI_ASSERT_AT(a != NULL, file, line);
malloc does support the following options.
*/
+/* The system's malloc.h may have conflicting defines. */
+#undef M_TRIM_THRESHOLD
+#undef M_GRANULARITY
+#undef M_MMAP_THRESHOLD
+
#define M_TRIM_THRESHOLD (-1)
#define M_GRANULARITY (-2)
#define M_MMAP_THRESHOLD (-3)
mchunkptr tnext = chunk_plus_offset(sp, ssize);
mchunkptr p = tnext;
int nfences = 0;
+ (void)nfences; // Suppress unused variable warning
/* reset top to new space */
init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
}
#endif /* NO_MALLINFO */
-void dlmalloc_stats() {
+void dlmalloc_stats(void) {
internal_malloc_stats(gm);
}
/* A 64-bit pointer value. In LP64 mode, this is effectively a plain
pointer. In ILP32 mode, it's a pointer that's been extended to
64 bits by "addp4". */
+#ifdef __hpux
+typedef void *PTR64;
+#else // some other unix
typedef void *PTR64 __attribute__((mode(DI)));
+#endif
/* Memory image of fp register contents. This is the implementation
specific format used by ldf.fill/stf.spill. All we care about is
point types without type conversions. Type conversion to long double breaks
the denorm support. */
+#ifdef __hpux
+#define stf_spill(addr, value)
+#else
#define stf_spill(addr, value) \
asm ("stf.spill %0 = %1%P0" : "=m" (*addr) : "f"(value));
+#endif
/* Load a value from ADDR, which is in the current cpu implementation's
fp spill format. As above, this must also be a macro. */
+#ifdef __hpux
+#define ldf_fill(result, addr)
+#else
#define ldf_fill(result, addr) \
asm ("ldf.fill %0 = %1%P1" : "=f"(result) : "m"(*addr));
+#endif
/* Return the size of the C type associated with with TYPE. Which will
be one of the FFI_IA64_TYPE_HFA_* values. */
.pred.safe_across_calls p1-p5,p16-p63
.text
+/* HPUX assembler needs to see these symbols, otherwise compilation
+ fails */
+#ifdef __hpux
+ .global memcpy
+ .global ffi_closure_unix_inner
+#endif
/* int ffi_call_unix (struct ia64_args *stack, PTR64 rvalue,
void (*fn)(void), int flags);
mov ar.pfs = loc0
addl r18 = @ltoffx(.Lst_table), gp
;;
+ /* default assembler on HP-UX does not support LDXMOV */
+#ifdef __hpux
+ ld8 r18 = [r18]
+#else
ld8.mov r18 = [r18], .Lst_table
+#endif
mov b0 = loc1
;;
shladd r18 = r16, 3, r18
addl r18 = @ltoffx(.Lld_table), gp
mov ar.pfs = loc0
;;
- ld8.mov r18 = [r18], .Lld_table
+#ifdef __hpux
+ ld8 r18 = [r18]
+#else
+ ld8.mov r18 = [r18], .Lst_table
+#endif
mov b0 = loc1
;;
shladd r18 = r16, 3, r18
;;
.endp ffi_closure_unix
-
+#ifdef __hpux
+ .rodata
+#else
.section .rodata
+#endif
.align 8
.Lst_table:
data8 @pcrel(.Lst_void) // FFI_TYPE_VOID
#include <ffi.h>
#include <ffi_common.h>
+#include <tramp.h>
#include <stdlib.h>
#include <stdint.h>
/* -----------------------------------------------------------------------
ffi.c - Copyright (c) 2004 Renesas Technology
Copyright (c) 2008 Red Hat, Inc.
-
- M32R Foreign Function Interface
+ Copyright (c) 2022 Anthony Green
+
+ M32R Foreign Function Interface
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
if (((*p_arg)->alignment - 1) & (unsigned) argp)
argp = (char *) FFI_ALIGN (argp, (*p_arg)->alignment);
- if (avn != 0)
+ if (avn != 0)
{
avn--;
z = (*p_arg)->size;
case FFI_TYPE_SINT8:
*(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
break;
-
+
case FFI_TYPE_UINT8:
*(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
break;
-
+
case FFI_TYPE_SINT16:
*(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
break;
-
+
case FFI_TYPE_UINT16:
*(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
break;
-
+
case FFI_TYPE_STRUCT:
z = (*p_arg)->size;
if ((*p_arg)->alignment != 1)
argp += z;
}
}
-
+
return;
}
void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
{
extended_cif ecif;
+ ffi_type **arg_types = cif->arg_types;
+ int i, nargs = cif->nargs;
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) &&
+ if ((rvalue == NULL) &&
(cif->rtype->type == FFI_TYPE_STRUCT))
{
ecif.rvalue = alloca (cif->rtype->size);
}
else
- ecif.rvalue = rvalue;
-
- switch (cif->abi)
+ ecif.rvalue = rvalue;
+
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ for (i = 0; i < nargs; i++)
+ {
+ ffi_type *at = arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT && size > 4)
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
+
+ switch (cif->abi)
{
case FFI_SYSV:
- ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes,
+ ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes,
cif->flags, ecif.rvalue, fn);
if (cif->rtype->type == FFI_TYPE_STRUCT)
{
#include <stdint.h>
#include <stdlib.h>
+#include <stdio.h>
#ifdef __GNUC__
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))
{
int i;
void **p_argv;
- char *argp;
+ char *argp, *argp_f;
ffi_type **p_arg;
+ memset(stack, 0, bytes);
+
#ifdef FFI_MIPS_N32
+ int soft_float = (ecif->cif->abi == FFI_N32_SOFT_FLOAT
+ || ecif->cif->abi == FFI_N64_SOFT_FLOAT);
/* If more than 8 double words are used, the remainder go
on the stack. We reorder stuff on the stack here to
support this easily. */
- if (bytes > 8 * sizeof(ffi_arg))
- argp = &stack[bytes - (8 * sizeof(ffi_arg))];
+ /* if ret is _Complex long double, args reg shift2, and a0 should holds pointer to rvalue */
+ if (ecif->cif->rtype->type == FFI_TYPE_COMPLEX && ecif->cif->rtype->elements[0]->type == FFI_TYPE_LONGDOUBLE)
+ {
+ if (bytes + 16 > 8 * sizeof(ffi_arg))
+ argp = &stack[bytes - (8 * sizeof(ffi_arg))];
+ else
+ argp = stack;
+ * (unsigned long *) argp = (unsigned long) ecif->rvalue;
+ argp += 16;
+ }
else
- argp = stack;
+ {
+ if (bytes > 8 * sizeof(ffi_arg))
+ argp = &stack[bytes - (8 * sizeof(ffi_arg))];
+ else
+ argp = stack;
+ }
#else
argp = stack;
#endif
- memset(stack, 0, bytes);
+ argp_f = argp;
#ifdef FFI_MIPS_N32
if ( ecif->cif->rstruct_flag != 0 )
if (type == FFI_TYPE_POINTER)
type = (ecif->cif->abi == FFI_N64
|| ecif->cif->abi == FFI_N64_SOFT_FLOAT)
- ? FFI_TYPE_SINT64 : FFI_TYPE_SINT32;
+ ? FFI_TYPE_SINT64 : FFI_TYPE_UINT32;
if (i < 8 && (ecif->cif->abi == FFI_N32_SOFT_FLOAT
|| ecif->cif->abi == FFI_N64_SOFT_FLOAT))
#endif
break;
+#ifdef FFI_MIPS_N32
+ case FFI_TYPE_COMPLEX:
+ /* expand from 4+4 to 8+8 if pass with fpr reg */
+ /* argp will wind back to stack when we process all of reg args */
+ /* all var_args passed with gpr, should be expand */
+ if(!soft_float
+ && (*p_arg)->elements[0]->type == FFI_TYPE_FLOAT
+ && argp>=argp_f
+ && i < ecif->cif->mips_nfixedargs)
+ {
+ *(float *) argp = *(float *)(* p_argv);
+ argp += z;
+ char *tmp = (void *) (*p_argv);
+ *(float *) argp = *(float *)(tmp+4);
+ }
+ else
+ memcpy(argp, *p_argv, (*p_arg)->size);
+ break;
+#endif
/* This can only happen with 64bit slots. */
case FFI_TYPE_FLOAT:
*(float *) argp = *(float *)(* p_argv);
passed in an integer register". This code traverses structure
definitions and generates the appropriate flags. */
+static int
+calc_n32_struct_flags_element(unsigned *flags, ffi_type *e,
+ unsigned *loc, unsigned *arg_reg)
+{
+ /* Align this object. */
+ *loc = FFI_ALIGN(*loc, e->alignment);
+ if (e->type == FFI_TYPE_DOUBLE)
+ {
+ /* Already aligned to FFI_SIZEOF_ARG. */
+ *arg_reg = *loc / FFI_SIZEOF_ARG;
+ if (*arg_reg > 7)
+ return 1;
+ *flags += (FFI_TYPE_DOUBLE << (*arg_reg * FFI_FLAG_BITS));
+ }
+ *loc += e->size;
+ return 0;
+}
+
static unsigned
calc_n32_struct_flags(int soft_float, ffi_type *arg,
unsigned *loc, unsigned *arg_reg)
while ((e = arg->elements[index]))
{
- /* Align this object. */
- *loc = FFI_ALIGN(*loc, e->alignment);
- if (e->type == FFI_TYPE_DOUBLE)
+ if (e->type == FFI_TYPE_COMPLEX)
{
- /* Already aligned to FFI_SIZEOF_ARG. */
- *arg_reg = *loc / FFI_SIZEOF_ARG;
- if (*arg_reg > 7)
- break;
- flags += (FFI_TYPE_DOUBLE << (*arg_reg * FFI_FLAG_BITS));
- *loc += e->size;
+ if (calc_n32_struct_flags_element(&flags, e->elements[0], loc, arg_reg))
+ break;
+ if (calc_n32_struct_flags_element(&flags, e->elements[0], loc, arg_reg))
+ break;
}
else
- *loc += e->size;
+ if (calc_n32_struct_flags_element(&flags, e, loc, arg_reg))
+ break;
index++;
}
/* Next Argument register at alignment of FFI_SIZEOF_ARG. */
static unsigned
calc_n32_return_struct_flags(int soft_float, ffi_type *arg)
{
- unsigned flags = 0;
+ unsigned flags;
unsigned small = FFI_TYPE_SMALLSTRUCT;
ffi_type *e;
e = arg->elements[0];
- if (e->type == FFI_TYPE_DOUBLE)
- flags = FFI_TYPE_DOUBLE;
- else if (e->type == FFI_TYPE_FLOAT)
- flags = FFI_TYPE_FLOAT;
-
- if (flags && (e = arg->elements[1]))
+ if (e->type == FFI_TYPE_COMPLEX)
{
- if (e->type == FFI_TYPE_DOUBLE)
- flags += FFI_TYPE_DOUBLE << FFI_FLAG_BITS;
- else if (e->type == FFI_TYPE_FLOAT)
- flags += FFI_TYPE_FLOAT << FFI_FLAG_BITS;
- else
+ int type = e->elements[0]->type;
+
+ if (type != FFI_TYPE_DOUBLE && type != FFI_TYPE_FLOAT)
return small;
- if (flags && (arg->elements[2]))
+ if (arg->elements[1])
{
- /* There are three arguments and the first two are
- floats! This must be passed the old way. */
+ /* Two floating point fields with more fields!
+ This must be passed the old way. */
return small;
}
- if (soft_float)
- flags += FFI_TYPE_STRUCT_SOFT;
+
+ flags = (type << FFI_FLAG_BITS) + type;
}
else
- if (!flags)
- return small;
+ {
+ if (e->type != FFI_TYPE_DOUBLE && e->type != FFI_TYPE_FLOAT)
+ return small;
+
+ flags = e->type;
+ if (arg->elements[1])
+ {
+ e = arg->elements[1];
+ if (e->type != FFI_TYPE_DOUBLE && e->type != FFI_TYPE_FLOAT)
+ return small;
+
+ if (arg->elements[2])
+ {
+ /* There are three arguments and the first two are
+ floats! This must be passed the old way. */
+ return small;
+ }
+
+ flags += e->type << FFI_FLAG_BITS;
+ }
+ }
+
+ if (soft_float)
+ flags += FFI_TYPE_STRUCT_SOFT;
return flags;
}
* does not have special handling for floating point args.
*/
- if (cif->rtype->type != FFI_TYPE_STRUCT && cif->abi == FFI_O32)
+ if (cif->rtype->type != FFI_TYPE_STRUCT && cif->rtype->type != FFI_TYPE_COMPLEX && cif->abi == FFI_O32)
{
if (cif->nargs > 0 && cif->nargs == nfixedargs)
{
case FFI_TYPE_STRUCT:
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
+ case FFI_TYPE_COMPLEX:
cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 2);
+ if (cif->rtype->type == FFI_TYPE_COMPLEX)
+ cif->flags += ((*cif->rtype->elements[0]).type) << (FFI_FLAG_BITS * 4);
break;
case FFI_TYPE_SINT64:
#ifdef FFI_MIPS_N32
/* Set the flags necessary for N32 processing */
{
- int type;
unsigned arg_reg = 0;
unsigned loc = 0;
unsigned count = (cif->nargs < 8) ? cif->nargs : 8;
while (count-- > 0 && arg_reg < 8)
{
- type = (cif->arg_types)[index]->type;
+ ffi_type *t = cif->arg_types[index];
- // Pass variadic arguments in integer registers even if they're floats
- if (soft_float || index >= nfixedargs)
- {
- switch (type)
- {
- case FFI_TYPE_FLOAT:
- type = FFI_TYPE_UINT32;
- break;
- case FFI_TYPE_DOUBLE:
- type = FFI_TYPE_UINT64;
- break;
- default:
- break;
- }
- }
- switch (type)
+ switch (t->type)
{
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
- cif->flags +=
- ((cif->arg_types)[index]->type << (arg_reg * FFI_FLAG_BITS));
+ if (!soft_float && index < nfixedargs)
+ cif->flags += t->type << (arg_reg * FFI_FLAG_BITS);
arg_reg++;
break;
case FFI_TYPE_LONGDOUBLE:
cif->flags +=
(FFI_TYPE_DOUBLE << (arg_reg * FFI_FLAG_BITS));
arg_reg++;
+ if (arg_reg >= 8)
+ continue;
cif->flags +=
(FFI_TYPE_DOUBLE << (arg_reg * FFI_FLAG_BITS));
arg_reg++;
}
break;
+ case FFI_TYPE_COMPLEX:
+ switch (t->elements[0]->type)
+ {
+ case FFI_TYPE_LONGDOUBLE:
+ arg_reg = FFI_ALIGN(arg_reg, 2);
+ if (soft_float || index >= nfixedargs)
+ {
+ arg_reg += 2;
+ }
+ else
+ {
+ cif->flags +=
+ (FFI_TYPE_DOUBLE << (arg_reg * FFI_FLAG_BITS));
+ arg_reg++;
+ if (arg_reg >= 8)
+ continue;
+ cif->flags +=
+ (FFI_TYPE_DOUBLE << (arg_reg * FFI_FLAG_BITS));
+ arg_reg++;
+ if (arg_reg >= 8)
+ continue;
+ }
+ /* passthrough */
+ case FFI_TYPE_FLOAT:
+ // one fpr can only holds one arg even it is single
+ cif->bytes += 16;
+ /* passthrough */
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_DOUBLE:
+ if (soft_float || index >= nfixedargs)
+ {
+ arg_reg += 2;
+ }
+ else
+ {
+ uint32_t type = t->elements[0]->type != FFI_TYPE_LONGDOUBLE? t->elements[0]->type: FFI_TYPE_DOUBLE;
+ cif->flags +=
+ (type << (arg_reg * FFI_FLAG_BITS));
+ arg_reg++;
+ if (arg_reg >= 8)
+ continue;
+ cif->flags +=
+ (type << (arg_reg * FFI_FLAG_BITS));
+ arg_reg++;
+ }
+ break;
+ default:
+ arg_reg += 2;
+ break;
+ }
+ break;
+
case FFI_TYPE_STRUCT:
loc = arg_reg * FFI_SIZEOF_ARG;
cif->flags += calc_n32_struct_flags(soft_float || index >= nfixedargs,
- (cif->arg_types)[index],
- &loc, &arg_reg);
+ t, &loc, &arg_reg);
break;
default:
if (cif->abi == FFI_N32_SOFT_FLOAT || cif->abi == FFI_N32)
cif->flags += FFI_TYPE_SINT32 << (FFI_FLAG_BITS * 8);
else
- cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+ cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 8);
break;
case FFI_TYPE_FLOAT:
/* else fall through */
case FFI_TYPE_DOUBLE:
if (soft_float)
- cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+ cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 8);
else
cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 8);
break;
two doubles. */
if (soft_float)
{
- cif->flags += FFI_TYPE_STRUCT << (FFI_FLAG_BITS * 8);
- cif->flags += FFI_TYPE_SMALLSTRUCT2 << (4 + (FFI_FLAG_BITS * 8));
+ /* if ret is long double, the ret is given by v0 and a0, no idea why
+ * Let's us VOID | VOID | LONGDOUBLE for it*/
+ cif->flags += FFI_TYPE_LONGDOUBLE << (FFI_FLAG_BITS * 8);
}
else
{
<< (4 + (FFI_FLAG_BITS * 8));
}
break;
+ case FFI_TYPE_COMPLEX:
+ {
+ int type = cif->rtype->elements[0]->type;
+
+ cif->flags += (FFI_TYPE_COMPLEX << (FFI_FLAG_BITS * 8));
+ if (soft_float || (type != FFI_TYPE_FLOAT && type != FFI_TYPE_DOUBLE && type != FFI_TYPE_LONGDOUBLE))
+ {
+ switch (type)
+ {
+ case FFI_TYPE_DOUBLE:
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_INT:
+ type = FFI_TYPE_SMALLSTRUCT2;
+ break;
+ case FFI_TYPE_LONGDOUBLE:
+ type = FFI_TYPE_LONGDOUBLE;
+ break;
+ case FFI_TYPE_FLOAT:
+ default:
+ type = FFI_TYPE_SMALLSTRUCT;
+ }
+ cif->flags += type << (4 + (FFI_FLAG_BITS * 8));
+ }
+ else
+ {
+ //cif->flags += (type + (type << FFI_FLAG_BITS))
+ // << (4 + (FFI_FLAG_BITS * 8));
+ cif->flags += type << (4 + (FFI_FLAG_BITS * 8));
+ }
+ break;
+ }
+ case FFI_TYPE_UINT32:
+ /* In the N32 or N64 ABI unsigned 32-bit integer should be
+ *sign*-extended. */
+ cif->flags += FFI_TYPE_SINT32 << (FFI_FLAG_BITS * 8);
+ break;
+ case FFI_TYPE_SINT64:
+ cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 8);
+ break;
default:
- cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+ cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 8);
break;
}
}
#endif
-
return FFI_OK;
}
/* value address then we need to make one */
if ((rvalue == NULL) &&
- (cif->rtype->type == FFI_TYPE_STRUCT))
+ (cif->rtype->type == FFI_TYPE_STRUCT || cif->rtype->type == FFI_TYPE_COMPLEX))
ecif.rvalue = alloca(cif->rtype->size);
else
ecif.rvalue = rvalue;
argn = 1;
seen_int = 1;
}
+ if ((cif->flags >> (FFI_FLAG_BITS * 2)) == FFI_TYPE_COMPLEX)
+ {
+ rvalue = fpr;
+ argn = 1;
+ }
i = 0;
avn = cif->nargs;
}
else
{
+ if (cif->rtype->type == FFI_TYPE_COMPLEX) {
+ __asm__ volatile ("move $v1, %0" : : "r"(cif->rtype->size));
+ }
return cif->rtype->type;
}
}
#endif
argn = 1;
}
+ if (cif->rtype->type == FFI_TYPE_COMPLEX && cif->rtype->elements[0]->type == FFI_TYPE_LONGDOUBLE)
+ argn = 2;
i = 0;
avn = cif->nargs;
#endif
avaluep[i] = (char *) argp;
}
+ else if (arg_types[i]->type == FFI_TYPE_COMPLEX && arg_types[i]->elements[0]->type == FFI_TYPE_DOUBLE)
+ {
+ argp = (argn >= 8 || i >= cif->mips_nfixedargs || soft_float) ? ar + argn : fpr + argn;
+ avaluep[i] = (char *) argp;
+ }
+ else if (arg_types[i]->type == FFI_TYPE_COMPLEX && arg_types[i]->elements[0]->type == FFI_TYPE_LONGDOUBLE)
+ {
+ /* align long double */
+ argn += ((argn & 0x1)? 1 : 0);
+ argp = (argn >= 8 || i >= cif->mips_nfixedargs || soft_float) ? ar + argn : fpr + argn;
+ avaluep[i] = (char *) argp;
+ }
+ else if (arg_types[i]->type == FFI_TYPE_COMPLEX && arg_types[i]->elements[0]->type == FFI_TYPE_FLOAT)
+ {
+ if (argn >= 8 || i >= cif->mips_nfixedargs || soft_float)
+ argp = ar + argn;
+ else
+ {
+ argp = fpr + argn;
+ /* the normal args for function holds 8bytes, while here we convert it to ptr */
+ uint32_t *tmp = (uint32_t *)argp;
+ tmp[1] = tmp[2];
+ }
+ avaluep[i] = (char *) argp;
+ }
else
{
unsigned type = arg_types[i]->type;
/* The size of a pointer depends on the ABI */
if (type == FFI_TYPE_POINTER)
type = (cif->abi == FFI_N64 || cif->abi == FFI_N64_SOFT_FLOAT)
- ? FFI_TYPE_SINT64 : FFI_TYPE_SINT32;
+ ? FFI_TYPE_SINT64 : FFI_TYPE_UINT32;
if (soft_float && type == FFI_TYPE_FLOAT)
- type = FFI_TYPE_UINT32;
+ type = FFI_TYPE_SINT32;
switch (type)
{
#error "Please do not include ffitarget.h directly into your source. Use ffi.h instead."
#endif
-#ifdef __linux__
-# include <asm/sgidefs.h>
-#elif defined(__rtems__)
+#ifdef __rtems__
/*
* Subprogram calling convention - copied from sgidefs.h
*/
#define _MIPS_SIM_ABI32 1
#define _MIPS_SIM_NABI32 2
#define _MIPS_SIM_ABI64 3
-#elif !defined(__OpenBSD__) && !defined(__FreeBSD__)
+#elif !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__linux__)
# include <sgidefs.h>
#endif
# endif
#endif
+#define FFI_TARGET_HAS_COMPLEX_TYPE 1
#define FFI_FLAG_BITS 2
/* SGI's strange assembler requires that we multiply by 4 rather
#define FFI_TYPE_STRUCT_SMALL 93
#define FFI_TYPE_STRUCT_SMALL2 109
+#define FFI_TYPE_COMPLEX_SMALL 95
+#define FFI_TYPE_COMPLEX_SMALL2 111
+#define FFI_TYPE_COMPLEX_FF 47
+#define FFI_TYPE_COMPLEX_DD 63
+#define FFI_TYPE_COMPLEX_LDLD 79
+
/* and for n32 soft float, add 16 * 2^4 */
#define FFI_TYPE_STRUCT_D_SOFT 317
#define FFI_TYPE_STRUCT_F_SOFT 301
REG_L t6, 3*FFI_SIZEOF_ARG($fp) # load the flags word into t6.
+ # when retval is _Complex long double, $f12/$a0, $f13/$a1 will be skipped
+ # no idea why, but gcc does it.
+ SRL t4, t6, 8*FFI_FLAG_BITS
+ move t8, t6
+ bne t4, FFI_TYPE_COMPLEX_LDLD, loadregs1
+
+ SLL t8, t6, 2*FFI_FLAG_BITS
+
+
+loadregs1:
#ifdef __mips_soft_float
REG_L a0, 0*FFI_SIZEOF_ARG(t9)
REG_L a1, 1*FFI_SIZEOF_ARG(t9)
REG_L a6, 6*FFI_SIZEOF_ARG(t9)
REG_L a7, 7*FFI_SIZEOF_ARG(t9)
#else
- and t4, t6, ((1<<FFI_FLAG_BITS)-1)
+ and t4, t8, ((1<<FFI_FLAG_BITS)-1)
REG_L a0, 0*FFI_SIZEOF_ARG(t9)
beqz t4, arg1_next
bne t4, FFI_TYPE_FLOAT, arg1_doublep
l.d $f12, 0*FFI_SIZEOF_ARG(t9)
arg1_next:
- SRL t4, t6, 1*FFI_FLAG_BITS
+ SRL t4, t8, 1*FFI_FLAG_BITS
and t4, ((1<<FFI_FLAG_BITS)-1)
REG_L a1, 1*FFI_SIZEOF_ARG(t9)
beqz t4, arg2_next
l.d $f13, 1*FFI_SIZEOF_ARG(t9)
arg2_next:
- SRL t4, t6, 2*FFI_FLAG_BITS
+ SRL t4, t8, 2*FFI_FLAG_BITS
and t4, ((1<<FFI_FLAG_BITS)-1)
REG_L a2, 2*FFI_SIZEOF_ARG(t9)
beqz t4, arg3_next
l.d $f14, 2*FFI_SIZEOF_ARG(t9)
arg3_next:
- SRL t4, t6, 3*FFI_FLAG_BITS
+ SRL t4, t8, 3*FFI_FLAG_BITS
and t4, ((1<<FFI_FLAG_BITS)-1)
REG_L a3, 3*FFI_SIZEOF_ARG(t9)
beqz t4, arg4_next
l.d $f15, 3*FFI_SIZEOF_ARG(t9)
arg4_next:
- SRL t4, t6, 4*FFI_FLAG_BITS
+ SRL t4, t8, 4*FFI_FLAG_BITS
and t4, ((1<<FFI_FLAG_BITS)-1)
REG_L a4, 4*FFI_SIZEOF_ARG(t9)
beqz t4, arg5_next
l.d $f16, 4*FFI_SIZEOF_ARG(t9)
arg5_next:
- SRL t4, t6, 5*FFI_FLAG_BITS
+ SRL t4, t8, 5*FFI_FLAG_BITS
and t4, ((1<<FFI_FLAG_BITS)-1)
REG_L a5, 5*FFI_SIZEOF_ARG(t9)
beqz t4, arg6_next
l.d $f17, 5*FFI_SIZEOF_ARG(t9)
arg6_next:
- SRL t4, t6, 6*FFI_FLAG_BITS
+ SRL t4, t8, 6*FFI_FLAG_BITS
and t4, ((1<<FFI_FLAG_BITS)-1)
REG_L a6, 6*FFI_SIZEOF_ARG(t9)
beqz t4, arg7_next
l.d $f18, 6*FFI_SIZEOF_ARG(t9)
arg7_next:
- SRL t4, t6, 7*FFI_FLAG_BITS
+ SRL t4, t8, 7*FFI_FLAG_BITS
and t4, ((1<<FFI_FLAG_BITS)-1)
REG_L a7, 7*FFI_SIZEOF_ARG(t9)
beqz t4, arg8_next
arg8_next:
#endif
-callit:
+callit:
# Load the function pointer
REG_L t9, 5*FFI_SIZEOF_ARG($fp)
# Shift the return type flag over
SRL t6, 8*FFI_FLAG_BITS
- beq t6, FFI_TYPE_SINT32, retint
- bne t6, FFI_TYPE_INT, retfloat
-retint:
+ bne t6, FFI_TYPE_UINT64, retsint32
+
+retuint64:
jal t9
REG_L t4, 4*FFI_SIZEOF_ARG($fp)
- REG_S v0, 0(t4)
+ sd v0, 0(t4)
+ b epilogue
+
+retsint32:
+ bne t6, FFI_TYPE_SINT32, retuint16
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sll v0, v0, 0
+ sd v0, 0(t4)
+ b epilogue
+
+retuint16:
+ bne t6, FFI_TYPE_UINT16, retsint16
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ andi v0, v0, 0xffff
+ sd v0, 0(t4)
+ b epilogue
+
+retsint16:
+ bne t6, FFI_TYPE_SINT16, retuint8
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ dsll v0, v0, 48
+ dsra v0, v0, 48
+ sd v0, 0(t4)
+ b epilogue
+
+retuint8:
+ bne t6, FFI_TYPE_UINT8, retsint8
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ andi v0, v0, 0xff
+ sd v0, 0(t4)
+ b epilogue
+
+retsint8:
+ bne t6, FFI_TYPE_SINT8, retfloat
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sd v0, 0(t4)
+ dsll v0, v0, 56
+ dsra v0, v0, 56
b epilogue
retfloat:
s.d $f0, 0(t4)
b epilogue
-retstruct_d:
+retstruct_d:
bne t6, FFI_TYPE_STRUCT_D, retstruct_f
jal t9
REG_L t4, 4*FFI_SIZEOF_ARG($fp)
s.s $f0, 0(t4)
b epilogue
-retstruct_d_d:
- bne t6, FFI_TYPE_STRUCT_DD, retstruct_f_f
+retstruct_d_d:
+ bne t6, FFI_TYPE_STRUCT_DD, retcomplex_d_d
jal t9
REG_L t4, 4*FFI_SIZEOF_ARG($fp)
s.d $f0, 0(t4)
s.d $f2, 8(t4)
b epilogue
+
+retcomplex_d_d:
+ bne t6, FFI_TYPE_COMPLEX_DD, retcomplex_ld_ld
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.d $f0, 0(t4)
+ s.d $f2, 8(t4)
+ b epilogue
+
+retcomplex_ld_ld:
+ bne t6, FFI_TYPE_COMPLEX_LDLD, retstruct_f_f
+ jal t9
+ b epilogue
-retstruct_f_f:
- bne t6, FFI_TYPE_STRUCT_FF, retstruct_d_f
+retstruct_f_f:
+ bne t6, FFI_TYPE_STRUCT_FF, retcomplex_f_f
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.s $f0, 0(t4)
+ s.s $f2, 4(t4)
+ b epilogue
+
+retcomplex_f_f:
+ bne t6, FFI_TYPE_COMPLEX_FF, retstruct_d_f
jal t9
REG_L t4, 4*FFI_SIZEOF_ARG($fp)
s.s $f0, 0(t4)
jal t9
REG_L t4, 4*FFI_SIZEOF_ARG($fp)
sd v0, 0(t4)
- sd v1, 8(t4)
+ sd a0, 8(t4) # not typo, it is a0, I have no idea, gcc does do it
b epilogue
retstruct_f_f_soft:
jal t9
REG_L t4, 4*FFI_SIZEOF_ARG($fp)
sw v0, 0(t4)
- sd v1, 8(t4)
+ sd a0, 8(t4) # not typo, it is a0, I have no idea, gcc does do it
b epilogue
retstruct_small:
b epilogue
retstruct_small2:
- bne t6, FFI_TYPE_STRUCT_SMALL2, retstruct
+ bne t6, FFI_TYPE_STRUCT_SMALL2, retlongdouble_soft
jal t9
REG_L t4, 4*FFI_SIZEOF_ARG($fp)
REG_S v0, 0(t4)
REG_S v1, 8(t4)
b epilogue
-retstruct:
+retlongdouble_soft:
+ bne t6, FFI_TYPE_LONGDOUBLE, retcomplex_small
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ REG_S v0, 0(t4)
+ REG_S a0, 8(t4) # not typo, it is a0, I have no idea, gcc does do it
+ b epilogue
+
+retcomplex_small:
+ bne t6, FFI_TYPE_COMPLEX_SMALL, retcomplex_small2
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ REG_S v0, 0(t4)
+ b epilogue
+
+retcomplex_small2:
+ bne t6, FFI_TYPE_COMPLEX_SMALL2, retstruct
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ REG_S v0, 0(t4)
+ REG_S v1, 8(t4)
+ b epilogue
+
+retstruct:
noretval:
jal t9
*/
#define SIZEOF_FRAME2 (20 * FFI_SIZEOF_ARG)
-
+
#define A7_OFF2 (19 * FFI_SIZEOF_ARG)
#define A6_OFF2 (18 * FFI_SIZEOF_ARG)
#define A5_OFF2 (17 * FFI_SIZEOF_ARG)
# Call ffi_closure_mips_inner_N32 to do the real work.
LA t9, ffi_closure_mips_inner_N32
+#if _MIPS_SIM==_ABIN32
+ lw a0, 4($15) # cif
+ lw a1, 8($15) # fun
+#else
REG_L a0, 8($15) # cif
REG_L a1, 16($15) # fun
+#endif
move a2, t7 # userdata=closure
ADDU a3, $sp, V0_OFF2 # rvalue
ADDU a4, $sp, A0_OFF2 # ar
# Call ffi_closure_mips_inner_N32 to do the real work.
LA t9, ffi_closure_mips_inner_N32
+#if _MIPS_SIM==_ABIN32
+ lw a0, 20($12) # cif
+ lw a1, 24($12) # fun
+ lw a2, 28($12) # user_data
+#else
REG_L a0, 56($12) # cif
REG_L a1, 64($12) # fun
REG_L a2, 72($12) # user_data
+#endif
ADDU a3, $sp, V0_OFF2
+ # FIXME: a4 does work, while if ret is _Complex long double, it will overwrite Fn_OFF2
ADDU a4, $sp, A0_OFF2
ADDU a5, $sp, F12_OFF2
jalr t9
+cls_retuint64:
# Return flags are in v0
- bne v0, FFI_TYPE_SINT32, cls_retint
+ bne v0, FFI_TYPE_UINT64, cls_retsint32
+ ld v0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retsint32:
+ bne v0, FFI_TYPE_SINT32, cls_retsint16
lw v0, V0_OFF2($sp)
b cls_epilogue
-cls_retint:
- bne v0, FFI_TYPE_INT, cls_retfloat
- REG_L v0, V0_OFF2($sp)
+cls_retsint16:
+ bne v0, FFI_TYPE_SINT16, cls_retuint16
+ lh v0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retuint16:
+ bne v0, FFI_TYPE_UINT16, cls_retsint8
+ lhu v0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retsint8:
+ bne v0, FFI_TYPE_SINT8, cls_retuint8
+ lb v0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retuint8:
+ bne v0, FFI_TYPE_UINT8, cls_retfloat
+ lbu v0, V0_OFF2($sp)
b cls_epilogue
cls_retfloat:
b cls_epilogue
cls_retstruct_d_d:
- bne v0, FFI_TYPE_STRUCT_DD, cls_retstruct_f_f
+ bne v0, FFI_TYPE_STRUCT_DD, cls_retcomplex_d_d
+ l.d $f0, V0_OFF2($sp)
+ l.d $f2, V1_OFF2($sp)
+ b cls_epilogue
+
+cls_retcomplex_d_d:
+ bne v0, FFI_TYPE_COMPLEX_DD, cls_retcomplex_f_f
l.d $f0, V0_OFF2($sp)
l.d $f2, V1_OFF2($sp)
b cls_epilogue
-cls_retstruct_f_f:
- bne v0, FFI_TYPE_STRUCT_FF, cls_retstruct_d_f
+cls_retstruct_f_f:
+ bne v0, FFI_TYPE_STRUCT_FF, cls_retcomplex_f_f
l.s $f0, V0_OFF2($sp)
l.s $f2, V1_OFF2($sp)
b cls_epilogue
+cls_retcomplex_f_f:
+ bne v0, FFI_TYPE_COMPLEX_FF, cls_retstruct_d_f
+ l.s $f0, V0_OFF2($sp)
+ l.s $f2, (V0_OFF2+4)($sp)
+ b cls_epilogue
+
cls_retstruct_d_f:
bne v0, FFI_TYPE_STRUCT_DF, cls_retstruct_f_d
l.d $f0, V0_OFF2($sp)
b cls_epilogue
cls_retstruct_f_d:
- bne v0, FFI_TYPE_STRUCT_FD, cls_retstruct_small2
+ bne v0, FFI_TYPE_STRUCT_FD, cls_retcomplex_ld_ld
l.s $f0, V0_OFF2($sp)
l.d $f2, V1_OFF2($sp)
b cls_epilogue
+#else
+cls_longdouble_soft:
+ bne v0, FFI_TYPE_LONGDOUBLE, cls_retcomplex_ld_ld
+ REG_L v0, V0_OFF2($sp)
+ REG_L a0, V1_OFF2($sp) # not typo, it is a0, I have no idea, gcc does do it
+ b cls_epilogue
#endif
-
+
+cls_retcomplex_ld_ld:
+ bne v0, FFI_TYPE_COMPLEX_LDLD, cls_retstruct_small2
+ REG_L t8, A0_OFF2($sp)
+ REG_L t9, 16($sp)
+ REG_S t9, 0(t8)
+ REG_L t9, 24($sp)
+ REG_S t9, 8(t8)
+ REG_L t9, 32($sp)
+ REG_S t9, 16(t8)
+ REG_L t9, 40($sp)
+ REG_S t9, 24(t8)
+ b cls_epilogue
+
cls_retstruct_small2:
REG_L v0, V0_OFF2($sp)
REG_L v1, V1_OFF2($sp)
#endif /* __GNUC__ */
#endif
+
+#if defined __ELF__ && defined __linux__
+ .section .note.GNU-stack,"",%progbits
+#endif
# bne t0, FFI_ARGS_F_D, call_it
l.s $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args
l.d $f14, 2*FFI_SIZEOF_ARG($sp) # passing double and float
+
#endif
call_it:
REG_L t1, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
beqz t1, noretval
- bne t2, FFI_TYPE_INT, retlonglong
+ and t1, t2, ((1<<4)-1)
+ bne t1, FFI_TYPE_INT, retlonglong
jalr t9
REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
REG_S v0, 0(t0)
retlonglong:
# Really any 64-bit int, signed or not.
- bne t2, FFI_TYPE_UINT64, retfloat
+ bne t1, FFI_TYPE_UINT64, retfloat
jalr t9
REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
REG_S v1, 4(t0)
b epilogue
retfloat:
- bne t2, FFI_TYPE_FLOAT, retdouble
+ bne t1, FFI_TYPE_FLOAT, retdouble
jalr t9
REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
#ifndef __mips_soft_float
b epilogue
retdouble:
- bne t2, FFI_TYPE_DOUBLE, noretval
+ bne t1, FFI_TYPE_DOUBLE, retcomplex
jalr t9
REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
#ifndef __mips_soft_float
#endif
b epilogue
+retcomplex:
+ # mask out the complex elements type.
+ # the struct of flags (bits):
+ # 0-1: arg0
+ # 2-3: arg1
+ # 4-7: return type
+ # 8-11: rtype elements type: for complex
+ # Note here: t2 is flags>>4
+ bne t1, FFI_TYPE_COMPLEX, noretval
+ jalr t9
+ REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
+ REG_L t1, A3_OFF($fp) # load the flags word
+ SRL t1, t1, 8
+ li t3, 3
+ beq t1, t3, 3f # double
+ li t3, 2
+ beq t1, t3, 2f # float
+ # FIXME: long double
+ slti t3, t1, 5
+ beqz t3, 5f # (u)int8/16/32/64
+2:
+#ifndef __mips_soft_float
+ s.s $f0, 0(t0)
+ s.s $f2, 4(t0)
+#else
+ # FIXME: do nothing can pass all of the testsuite
+#endif
+ b epilogue
+3:
+#ifndef __mips_soft_float
+ s.d $f0, 0(t0)
+ s.d $f2, 8(t0)
+#else
+ # FIXME: do nothing can pass all of the testsuite
+#endif
+ b epilogue
+
+5:
+ REG_S v1, 4(t0)
+ REG_S v0, 0(t0)
+ b epilogue
+
noretval:
jalr t9
li $9, FFI_TYPE_DOUBLE
l.d $f0, V0_OFF2($fp)
beq $8, $9, closure_done
+
+ li $9, FFI_TYPE_COMPLEX
+ bne $8, $9, 1f
+
+ li $9, 8
+ l.s $f0, V0_OFF2($fp)
+ l.s $f2, V1_OFF2($fp)
+ beq $3, $9, closure_done
+
+ li $9, 16
+ l.d $f0, V0_OFF2($fp)
+ l.d $f2, (V0_OFF2+8)($fp)
+ beq $3, $9, closure_done
#endif
1:
REG_L $3, V1_OFF2($fp)
$LEFDE2:
#endif
+
+#if defined __ELF__ && defined __linux__
+ .section .note.GNU-stack,"",%progbits
+#endif
/* -----------------------------------------------------------------------
- ffi.c - Copyright (C) 2012, 2013, 2018 Anthony Green
-
- Moxie Foreign Function Interface
+ ffi.c - Copyright (C) 2012, 2013, 2018, 2021, 2022 Anthony Green
+
+ Moxie Foreign Function Interface
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
i--, p_arg++)
{
size_t z;
-
+
z = (*p_arg)->size;
if ((*p_arg)->type == FFI_TYPE_STRUCT)
{
z = sizeof(void*);
*(void **) argp = *p_argv;
- }
+ }
else if (z < sizeof(int))
{
z = sizeof(int);
case FFI_TYPE_SINT8:
*(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
break;
-
+
case FFI_TYPE_UINT8:
*(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
break;
-
+
case FFI_TYPE_SINT16:
*(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
break;
-
+
case FFI_TYPE_UINT16:
*(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
break;
-
+
default:
FFI_ASSERT(0);
}
return FFI_OK;
}
-extern void ffi_call_EABI(void *(*)(char *, extended_cif *),
- extended_cif *,
- unsigned, unsigned,
- unsigned *,
+extern void ffi_call_EABI(void *(*)(char *, extended_cif *),
+ extended_cif *,
+ unsigned, unsigned,
+ unsigned *,
void (*fn)(void));
-void ffi_call(ffi_cif *cif,
- void (*fn)(void),
- void *rvalue,
+void ffi_call(ffi_cif *cif,
+ void (*fn)(void),
+ void *rvalue,
void **avalue)
{
extended_cif ecif;
+ ffi_type **arg_types = cif->arg_types;
+ int i, nargs = cif->nargs;
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) &&
+ if ((rvalue == NULL) &&
(cif->rtype->type == FFI_TYPE_STRUCT))
{
ecif.rvalue = alloca(cif->rtype->size);
else
ecif.rvalue = rvalue;
- switch (cif->abi)
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ for (i = 0; i < nargs; i++)
+ {
+ ffi_type *at = arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT) /* && size > 4) All struct args?? */
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
+
+ switch (cif->abi)
{
case FFI_EABI:
- ffi_call_EABI(ffi_prep_args, &ecif, cif->bytes,
+ ffi_call_EABI(ffi_prep_args, &ecif, cif->bytes,
cif->flags, ecif.rvalue, fn);
break;
default:
void *struct_rvalue = (void *) arg1;
/* 6 words reserved for register args + 3 words from jsr */
- char *stack_args = frame_pointer + 9*4;
+ char *stack_args = frame_pointer + 9*4;
/* Lay the register arguments down in a continuous chunk of memory. */
unsigned register_args[6] =
avalue[i] = ptr;
break;
case FFI_TYPE_STRUCT:
- avalue[i] = *(void**)ptr;
+ {
+ if (arg_types[i]->size > 4)
+ {
+ void *copy = alloca(arg_types[i]->size);
+ memcpy(copy, *(void**)ptr, arg_types[i]->size);
+ avalue[i] = copy;
+ }
+ else
+ avalue[i] = *(void**)ptr;
+ }
break;
default:
/* This is an 8-byte value. */
ffi_type **arg;
int count = 0;
int nfixedargs;
-
+
nfixedargs = ecif->cif->nfixedargs;
arg = ecif->cif->arg_types;
void **argv = ecif->avalue;
*(void **) stack = ecif->rvalue;
stack += 4;
count = 4;
- }
+ }
for(i=0; i<ecif->cif->nargs; i++)
{
if ((nfixedargs == 0) && (count < 24))
{
count = 24;
- stack = stacktemp + 24;
+ stack = stacktemp + 24;
}
nfixedargs--;
s = 4;
- switch((*arg)->type)
+ switch((*arg)->type)
{
case FFI_TYPE_STRUCT:
*(void **)stack = *argv;
{
stack += 4;
count += 4;
- }
+ }
s = (*arg)->size;
memcpy(stack, *argv, s);
break;
extern void ffi_call_SYSV(unsigned,
extended_cif *,
- void *(*)(int *, extended_cif *),
+ void *(*)(char *, extended_cif *),
unsigned *,
void (*fn)(void),
unsigned);
size += 4;
else
size += 8;
+
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ {
+ ffi_type *at = cif->arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT) /* && size > 4) All struct args? */
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
}
/* for variadic functions more space is needed on the stack */
ecif.avalue = avalue;
ecif.rvalue = rvalue;
- switch (cif->abi)
+ switch (cif->abi)
{
case FFI_SYSV:
ffi_call_SYSV(size, &ecif, ffi_prep_args, rvalue, fn, cif->flags);
}
-void ffi_closure_SYSV(unsigned long r3, unsigned long r4, unsigned long r5,
+void ffi_closure_SYSV(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7, unsigned long r8)
{
register int *sp __asm__ ("r17");
register int *r13 __asm__ ("r13");
ffi_closure* closure = (ffi_closure*) r13;
- char *stack_args = sp;
+ char *stack_args = (char*) sp;
/* Lay the register arguments down in a continuous chunk of memory. */
unsigned register_args[6] =
/* preserve struct type return pointer passing */
- if ((cif->rtype != NULL) && (cif->rtype->type == FFI_TYPE_STRUCT))
+ if ((cif->rtype != NULL) && (cif->rtype->type == FFI_TYPE_STRUCT))
{
ptr += 4;
count = 4;
long long rvalue;
(closure->fun) (cif, &rvalue, avalue, closure->user_data);
if (cif->rtype)
- asm ("l.ori r12, %0, 0x0\n l.lwz r11, 0(r12)\n l.lwz r12, 4(r12)" : : "r" (&rvalue));
+ asm ("l.ori r12, %0, 0x0\n l.lwz r11, 0(r12)\n l.lwz r12, 4(r12)" : : "r" (&rvalue));
}
}
ffi_status ffi_prep_cif_machdep (ffi_cif *cif)
{
cif->flags = 0;
-
+
/* structures are returned as pointers */
if (cif->rtype->type == FFI_TYPE_STRUCT)
cif->flags = FFI_TYPE_STRUCT;
- else
+ else
if (cif->rtype->size > 4)
cif->flags = FFI_TYPE_UINT64;
status = ffi_prep_cif_machdep (cif);
cif->nfixedargs = nfixedargs;
return status;
-}
+}
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. */
}
void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
{
extended_cif ecif;
+ size_t i, nargs = cif->nargs;
+ ffi_type **arg_types = cif->arg_types;
ecif.cif = cif;
ecif.avalue = avalue;
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ for (i = 0; i < nargs; i++)
+ {
+ ffi_type *at = arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT && size > 8)
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
+
/* If the return value is a struct and we don't have a return
value address then we need to make one. */
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;
}
/* 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]);
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:
/* 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:
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
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;
}
--- /dev/null
+/* -----------------------------------------------------------------------
+ ffi64.c - (c) 2022 John David Anglin <dave.anglin@bell.net>
+
+ 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 <ffi.h>
+#include <ffi_common.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#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
#endif
#ifdef PA64_HPUX
-#error "PA64_HPUX FFI is not yet implemented"
FFI_PA64,
FFI_LAST_ABI,
FFI_DEFAULT_ABI = FFI_PA64
#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
/* 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)
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)
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 */
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 */
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 */
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 */
--- /dev/null
+/* -----------------------------------------------------------------------
+ hpux64.S - (c) 2005-2022 John David Anglin <dave.anglin@bell.net>
+
+ 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 <fficonfig.h>
+#include <ffi.h>
+
+ .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:
/* 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)
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)
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 */
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 */
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 */
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 */
.align 4
.LEFDE2:
+
+#if defined(__ELF__) && defined(__linux__)
+.section .note.GNU-stack,"",%progbits
+#endif
#include "ffi.h"
#include "ffi_common.h"
#include "ffi_powerpc.h"
+#include "internal.h"
+#include <tramp.h>
#if HAVE_LONG_DOUBLE_VARIANT
/* Adjust ffi_type_longdouble. */
closure->fun = fun;
return FFI_OK;
}
+
+#ifdef FFI_EXEC_STATIC_TRAMP
+void *
+ffi_tramp_arch (size_t *tramp_size, size_t *map_size)
+{
+ extern void *trampoline_code_table;
+ *tramp_size = PPC_TRAMP_SIZE;
+ *map_size = PPC_TRAMP_MAP_SIZE;
+#if defined (_CALL_AIX) || _CALL_ELF == 1
+ /* The caller wants the entry point address of the trampoline code,
+ not the address of the function descriptor. */
+ return *(void **)trampoline_code_table;
+#else
+ return &trampoline_code_table;
+#endif
+}
+#endif
}
/* Adjust the size of S to be correct for AIX.
- Word-align double unless it is the first member of a structure. */
+ Word-align double unless it is the first member of a structure recursively.
+ Return non-zero if we found a recursive first member aggregate of interest. */
-static void
-aix_adjust_aggregate_sizes (ffi_type *s)
+static int
+aix_adjust_aggregate_sizes (ffi_type *s, int outer_most_type_or_first_member)
{
- int i;
+ int i, nested_first_member=0, final_align, rc=0;
if (s->type != FFI_TYPE_STRUCT)
- return;
+ return 0;
s->size = 0;
for (i = 0; s->elements[i] != NULL; i++)
{
- ffi_type *p;
+ ffi_type p;
int align;
-
- p = s->elements[i];
- aix_adjust_aggregate_sizes (p);
- align = p->alignment;
- if (i != 0 && p->type == FFI_TYPE_DOUBLE)
- align = 4;
- s->size = FFI_ALIGN(s->size, align) + p->size;
+
+ /* nested aggregates layout differently on AIX, so take a copy of the type */
+ p = *(s->elements[i]);
+ if (i == 0)
+ nested_first_member = aix_adjust_aggregate_sizes(&p, outer_most_type_or_first_member);
+ else
+ aix_adjust_aggregate_sizes(&p, 0);
+ align = p.alignment;
+ if (i != 0 && p.type == FFI_TYPE_DOUBLE)
+ align = 4;
+ s->size = FFI_ALIGN(s->size, align) + p.size;
}
-
- s->size = FFI_ALIGN(s->size, s->alignment);
-
- if (s->elements[0]->type == FFI_TYPE_UINT64
- || s->elements[0]->type == FFI_TYPE_SINT64
- || s->elements[0]->type == FFI_TYPE_DOUBLE
- || s->elements[0]->alignment == 8)
- s->alignment = s->alignment > 8 ? s->alignment : 8;
- /* Do not add additional tail padding. */
+
+ final_align=s->alignment;
+ if ((s->elements[0]->type == FFI_TYPE_UINT64
+ || s->elements[0]->type == FFI_TYPE_SINT64
+ || s->elements[0]->type == FFI_TYPE_DOUBLE
+ || s->elements[0]->alignment == 8 || nested_first_member)) {
+ final_align = s->alignment > 8 ? s->alignment : 8;
+ rc=1;
+ /* still use the adjusted alignment to calculate tail padding, but don't adjust the types alignment if
+ we aren't in the recursive first position */
+ if (outer_most_type_or_first_member)
+ s->alignment=final_align;
+ }
+
+ s->size = FFI_ALIGN(s->size, final_align);
+ return rc;
}
/* Perform machine dependent cif processing. */
if (cif->abi == FFI_AIX)
{
- aix_adjust_aggregate_sizes (cif->rtype);
+ aix_adjust_aggregate_sizes (cif->rtype, 1);
for (i = 0; i < cif->nargs; i++)
- aix_adjust_aggregate_sizes (cif->arg_types[i]);
+ aix_adjust_aggregate_sizes (cif->arg_types[i], 1);
}
/* Space for the frame pointer, callee's LR, CR, etc, and for
----------------------------------------------------------------------- */
#include "ffi.h"
+#include <tramp.h>
#ifdef POWERPC64
#include "ffi_common.h"
void *user_data,
void *codeloc)
{
-#if _CALL_ELF == 2
- unsigned int *tramp = (unsigned int *) &closure->tramp[0];
-
if (cif->abi < FFI_LINUX || cif->abi >= FFI_LAST_ABI)
return FFI_BAD_ABI;
- tramp[0] = 0xe96c0018; /* 0: ld 11,2f-0b(12) */
- tramp[1] = 0xe98c0010; /* ld 12,1f-0b(12) */
- tramp[2] = 0x7d8903a6; /* mtctr 12 */
- tramp[3] = 0x4e800420; /* bctr */
+#ifdef FFI_EXEC_STATIC_TRAMP
+ if (ffi_tramp_is_present(closure))
+ {
+ /* Initialize the static trampoline's parameters. */
+ void (*dest)(void) = ffi_closure_LINUX64;
+ ffi_tramp_set_parms (closure->ftramp, dest, closure);
+ }
+ else
+#endif
+ {
+#if _CALL_ELF == 2
+ unsigned int *tramp = (unsigned int *) &closure->tramp[0];
+ tramp[0] = 0xe96c0018; /* 0: ld 11,2f-0b(12) */
+ tramp[1] = 0xe98c0010; /* ld 12,1f-0b(12) */
+ tramp[2] = 0x7d8903a6; /* mtctr 12 */
+ tramp[3] = 0x4e800420; /* bctr */
/* 1: .quad function_addr */
/* 2: .quad context */
- *(void **) &tramp[4] = (void *) ffi_closure_LINUX64;
- *(void **) &tramp[6] = codeloc;
- flush_icache ((char *) tramp, (char *) codeloc, 4 * 4);
+ *(void **) &tramp[4] = (void *) ffi_closure_LINUX64;
+ *(void **) &tramp[6] = codeloc;
+ flush_icache ((char *) tramp, (char *) codeloc, 4 * 4);
#else
- void **tramp = (void **) &closure->tramp[0];
-
- if (cif->abi < FFI_LINUX || cif->abi >= FFI_LAST_ABI)
- return FFI_BAD_ABI;
-
- /* Copy function address and TOC from ffi_closure_LINUX64 OPD. */
- memcpy (&tramp[0], (void **) ffi_closure_LINUX64, sizeof (void *));
- tramp[1] = codeloc;
- memcpy (&tramp[2], (void **) ffi_closure_LINUX64 + 1, sizeof (void *));
+ /* Copy function address and TOC from ffi_closure_LINUX64 OPD. */
+ void **tramp = (void **) &closure->tramp[0];
+ memcpy (&tramp[0], (void **) ffi_closure_LINUX64, sizeof (void *));
+ tramp[1] = codeloc;
+ memcpy (&tramp[2], (void **) ffi_closure_LINUX64 + 1, sizeof (void *));
#endif
+ }
closure->cif = cif;
closure->fun = fun;
----------------------------------------------------------------------- */
#include "ffi.h"
+#include <tramp.h>
#ifndef POWERPC64
#include "ffi_common.h"
void *user_data,
void *codeloc)
{
- unsigned int *tramp;
-
if (cif->abi < FFI_SYSV || cif->abi >= FFI_LAST_ABI)
return FFI_BAD_ABI;
- tramp = (unsigned int *) &closure->tramp[0];
- tramp[0] = 0x7c0802a6; /* mflr r0 */
- tramp[1] = 0x429f0005; /* bcl 20,31,.+4 */
- tramp[2] = 0x7d6802a6; /* mflr r11 */
- tramp[3] = 0x7c0803a6; /* mtlr r0 */
- tramp[4] = 0x800b0018; /* lwz r0,24(r11) */
- tramp[5] = 0x816b001c; /* lwz r11,28(r11) */
- tramp[6] = 0x7c0903a6; /* mtctr r0 */
- tramp[7] = 0x4e800420; /* bctr */
- *(void **) &tramp[8] = (void *) ffi_closure_SYSV; /* function */
- *(void **) &tramp[9] = codeloc; /* context */
-
- /* Flush the icache. */
- flush_icache ((char *)tramp, (char *)codeloc, 8 * 4);
+#ifdef FFI_EXEC_STATIC_TRAMP
+ if (ffi_tramp_is_present(closure))
+ {
+ /* Initialize the static trampoline's parameters. */
+ void (*dest)(void) = ffi_closure_SYSV;
+ ffi_tramp_set_parms (closure->ftramp, dest, closure);
+ }
+ else
+#endif
+ {
+ unsigned int *tramp = (unsigned int *) &closure->tramp[0];
+ tramp[0] = 0x7c0802a6; /* mflr r0 */
+ tramp[1] = 0x429f0005; /* bcl 20,31,.+4 */
+ tramp[2] = 0x7d6802a6; /* mflr r11 */
+ tramp[3] = 0x7c0803a6; /* mtlr r0 */
+ tramp[4] = 0x800b0018; /* lwz r0,24(r11) */
+ tramp[5] = 0x816b001c; /* lwz r11,28(r11) */
+ tramp[6] = 0x7c0903a6; /* mtctr r0 */
+ tramp[7] = 0x4e800420; /* bctr */
+ *(void **) &tramp[8] = (void *) ffi_closure_SYSV; /* function */
+ *(void **) &tramp[9] = codeloc; /* context */
+
+ /* Flush the icache. */
+ flush_icache ((char *)tramp, (char *)codeloc, 8 * 4);
+ }
closure->cif = cif;
closure->fun = fun;
--- /dev/null
+#ifdef FFI_EXEC_STATIC_TRAMP
+/* For the trampoline code table mapping, a mapping size of 64K is chosen. */
+#define PPC_TRAMP_MAP_SHIFT 16
+#define PPC_TRAMP_MAP_SIZE (1 << PPC_TRAMP_MAP_SHIFT)
+# ifdef __PCREL__
+# define PPC_TRAMP_SIZE 24
+# else
+# define PPC_TRAMP_SIZE 40
+# endif /* __PCREL__ */
+#endif /* FFI_EXEC_STATIC_TRAMP */
#define LIBFFI_ASM
#include <fficonfig.h>
#include <ffi.h>
+#include "internal.h"
.file "linux64_closure.S"
.size .ffi_go_closure_linux64,.-.ffi_go_closure_linux64
# endif
# endif
+
+#ifdef FFI_EXEC_STATIC_TRAMP
+ .text
+ .align PPC_TRAMP_MAP_SHIFT
+ FFI_HIDDEN (trampoline_code_table)
+ .globl trampoline_code_table
+# if _CALL_ELF == 2
+ .type trampoline_code_table,@function
+trampoline_code_table:
+ .localentry trampoline_code_table,.-trampoline_code_table
+# else
+ .section ".opd","aw"
+ .align 3
+trampoline_code_table:
+ .quad .L.trampoline_code_table,.TOC.@tocbase,0
+ .type trampoline_code_table,@function
+ .text
+.L.trampoline_code_table:
+# endif
+ .rept PPC_TRAMP_MAP_SIZE / PPC_TRAMP_SIZE
+#ifdef __PCREL__
+ pla %r2,PPC_TRAMP_MAP_SIZE
+ ld %r11,0(%r2)
+ ld %r12,8(%r2)
+ mtctr %r12
+ bctr
+#else
+ mflr %r0
+ bcl 20,31,$+4
+ mflr %r11
+ addis %r11,%r11,PPC_TRAMP_MAP_SIZE@ha
+ mtlr %r0
+ ld %r12,(PPC_TRAMP_MAP_SIZE+0)@l(%r11)
+ mtctr %r12
+ ld %r11,(PPC_TRAMP_MAP_SIZE-8)@l(%r11)
+ bctr
+ nop
+#endif
+ .endr
+ .align PPC_TRAMP_MAP_SHIFT
+#if _CALL_ELF == 2
+ .size trampoline_code_table,.-trampoline_code_table
+#else
+ .size trampoline_code_table,.-.L.trampoline_code_table
#endif
+#endif /* FFI_EXEC_STATIC_TRAMP */
+#endif /* POWERPC64 */
#if (defined __ELF__ && defined __linux__) || _CALL_ELF == 2
.section .note.GNU-stack,"",@progbits
#define LIBFFI_ASM
#include <fficonfig.h>
#include <ffi.h>
+#include "internal.h"
#include <powerpc/asm.h>
.file "ppc_closure.S"
.cfi_endproc
END(ffi_go_closure_sysv)
+#ifdef FFI_EXEC_STATIC_TRAMP
+ .text
+ .align PPC_TRAMP_MAP_SHIFT
+ FFI_HIDDEN (trampoline_code_table)
+ .globl trampoline_code_table
+ .type trampoline_code_table,@function
+trampoline_code_table:
+ .rept PPC_TRAMP_MAP_SIZE / PPC_TRAMP_SIZE
+ mflr %r0
+ bcl 20,31,$+4
+ mflr %r11
+ addis %r11,%r11,PPC_TRAMP_MAP_SIZE@ha
+ mtlr %r0
+ lwz %r0,(PPC_TRAMP_MAP_SIZE-4)@l(%r11)
+ mtctr %r0
+ lwz %r11,(PPC_TRAMP_MAP_SIZE-8)@l(%r11)
+ bctr
+ nop
+ .endr
+ .size trampoline_code_table,.-trampoline_code_table
+ .align PPC_TRAMP_MAP_SHIFT
+#endif /* FFI_EXEC_STATIC_TRAMP */
+
#if defined __ELF__ && defined __linux__
.section .note.GNU-stack,"",@progbits
#endif
--- /dev/null
+# This file is needed by GCC in order to correctly build AIX FAT
+# library for libffi.
+# However, it has no sense to include this code here, as it depends
+# on GCC multilib architecture.
+# Thus, this file is a simple stub replaced in GCC repository.
/* -----------------------------------------------------------------------
- prep_cif.c - Copyright (c) 2011, 2012, 2021 Anthony Green
+ prep_cif.c - Copyright (c) 2011, 2012, 2021, 2025 Anthony Green
Copyright (c) 1996, 1998, 2007 Red Hat, Inc.
+ Copyright (c) 2022 Oracle and/or its affiliates.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
#endif
#ifdef XTENSA
&& (cif->rtype->size > 16)
-#endif
-#ifdef NIOS2
- && (cif->rtype->size > 8)
#endif
)
bytes = STACK_ARG_SIZE(sizeof(void*));
{
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);
if (rc != FFI_OK)
return rc;
- for (i = 1; i < ntotalargs; i++)
+ for (i = nfixedargs; i < ntotalargs; i++)
{
ffi_type *arg_type = atypes[i];
if (arg_type == &ffi_type_float
int used_integer;
int used_float;
size_t *used_stack;
+ void *struct_stack;
} call_builder;
/* integer (not pointer) less than ABI XLEN */
#endif
if (type->size > 2 * __SIZEOF_POINTER__) {
- /* pass by reference */
+ /* copy to stack and pass by reference */
+ data = memcpy (cb->struct_stack, data, type->size);
+ cb->struct_stack = (size_t *) FFI_ALIGN ((char *) cb->struct_stack + type->size, __SIZEOF_POINTER__);
marshal_atom(cb, FFI_TYPE_POINTER, &data);
} else if (IS_INT(type->type) || type->type == FFI_TYPE_POINTER) {
marshal_atom(cb, type->type, data);
that all remaining arguments are long long / __int128 */
size_t arg_bytes = cif->nargs <= 3 ? 0 :
FFI_ALIGN(2 * sizeof(size_t) * (cif->nargs - 3), STKALIGN);
+ /* Allocate space for copies of big structures. */
+ size_t struct_bytes = FFI_ALIGN (cif->bytes, STKALIGN);
size_t rval_bytes = 0;
if (rvalue == NULL && cif->rtype->size > 2*__SIZEOF_POINTER__)
rval_bytes = FFI_ALIGN(cif->rtype->size, STKALIGN);
- size_t alloc_size = arg_bytes + rval_bytes + sizeof(call_context);
+ size_t alloc_size = arg_bytes + rval_bytes + struct_bytes + sizeof(call_context);
/* the assembly code will deallocate all stack data at lower addresses
than the argument region, so we need to allocate the frame and the
call_builder cb;
cb.used_float = cb.used_integer = 0;
- cb.aregs = (call_context*)(alloc_base + arg_bytes + rval_bytes);
+ cb.aregs = (call_context*)(alloc_base + arg_bytes + rval_bytes + struct_bytes);
cb.used_stack = (void*)alloc_base;
+ cb.struct_stack = (void *) (alloc_base + arg_bytes + rval_bytes);
int return_by_ref = passed_by_ref(&cb, cif->rtype, 0);
if (return_by_ref)
cb.used_float = cb.used_integer = 0;
if (!return_by_ref && rvalue)
- unmarshal(&cb, cif->rtype, 0, rvalue);
+ {
+ if (IS_INT(cif->rtype->type)
+ && cif->rtype->size < sizeof (ffi_arg))
+ {
+ /* Integer types smaller than ffi_arg need to be extended. */
+ switch (cif->rtype->type)
+ {
+ case FFI_TYPE_SINT8:
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_SINT32:
+ unmarshal_atom (&cb, (sizeof (ffi_arg) > 4
+ ? FFI_TYPE_SINT64 : FFI_TYPE_SINT32),
+ rvalue);
+ break;
+ case FFI_TYPE_UINT8:
+ case FFI_TYPE_UINT16:
+ case FFI_TYPE_UINT32:
+ unmarshal_atom (&cb, (sizeof (ffi_arg) > 4
+ ? FFI_TYPE_UINT64 : FFI_TYPE_UINT32),
+ rvalue);
+ break;
+ }
+ }
+ else
+ unmarshal(&cb, cif->rtype, 0, rvalue);
+ }
}
void
closure->fun = fun;
closure->user_data = user_data;
+#if !defined(__FreeBSD__)
__builtin___clear_cache(codeloc, codeloc + FFI_TRAMPOLINE_SIZE);
+#endif
return FFI_OK;
}
#include <ffi_common.h>
#include <stdint.h>
#include "internal.h"
+#include <tramp.h>
/*====================== End of Includes =============================*/
#endif
0x07f1 /* br %r1 */
};
-
+ void (*dest)(void);
unsigned long *tramp = (unsigned long *)&closure->tramp;
if (cif->abi != FFI_SYSV)
return FFI_BAD_ABI;
+
+#if defined(FFI_EXEC_STATIC_TRAMP)
+ if (ffi_tramp_is_present(closure))
+ {
+ /* Initialize the static trampoline's parameters. */
+ dest = ffi_closure_SYSV;
+ ffi_tramp_set_parms (closure->ftramp, dest, closure);
+ goto out;
+ }
+#endif
+
memcpy (tramp, template, sizeof(template));
tramp[2] = (unsigned long)codeloc;
tramp[3] = (unsigned long)&ffi_closure_SYSV;
+#if defined(FFI_EXEC_STATIC_TRAMP)
+out:
+#endif
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
return FFI_OK;
}
+
+#if defined(FFI_EXEC_STATIC_TRAMP)
+void *
+ffi_tramp_arch (size_t *tramp_size, size_t *map_size)
+{
+ extern void *trampoline_code_table;
+
+ *tramp_size = FFI390_TRAMP_SIZE;
+ *map_size = FFI390_TRAMP_MAP_SIZE;
+ return &trampoline_code_table;
+}
+#endif
\ No newline at end of file
#define FFI390_RET_IN_MEM 8
#define FFI390_RET_STRUCT (FFI390_RET_VOID | FFI390_RET_IN_MEM)
+
+
+#if defined(FFI_EXEC_STATIC_TRAMP)
+/*
+ * For the trampoline code table mapping, a mapping size of 4K is chosen.
+ */
+#define FFI390_TRAMP_MAP_SHIFT 12
+#define FFI390_TRAMP_MAP_SIZE (1 << FFI390_TRAMP_MAP_SHIFT)
+#define FFI390_TRAMP_SIZE 16
+
+#endif
#define LIBFFI_ASM
#include <fficonfig.h>
#include <ffi.h>
+#include "internal.h"
.text
br %r14
.cfi_endproc
.size ffi_closure_SYSV,.-ffi_closure_SYSV
+
+
+#if defined(FFI_EXEC_STATIC_TRAMP)
+/*
+ * Below is the definition of the trampoline code table. Each element in
+ * the code table is a trampoline.
+ */
+/*
+ * The trampoline uses the volatile register r0 and r1. As the registers are
+ * marked volatile in the ABI, the original values are not saved.
+ *
+ * The trampoline has two parameters - target code to jump to and data for
+ * the target code. The trampoline extracts the parameters from its parameter
+ * block (see tramp_table_map()). The trampoline saves the data address in r0.
+ * Finally, it jumps to the target code.
+ */
+
+ .align FFI390_TRAMP_MAP_SIZE
+trampoline_code_table:
+ .rept FFI390_TRAMP_MAP_SIZE / FFI390_TRAMP_SIZE
+ basr %r1,0 # load next instruction address to r1
+ lmg %r0,%r1,4094(%r1) # load parameter block
+ # r0 -> data
+ # r1 -> code
+ br %r1 # jump to r1/code
+ .balign 8
+ .endr
+
+ .globl trampoline_code_table
+ FFI_HIDDEN(trampoline_code_table)
+#ifdef __ELF__
+ .type trampoline_code_table, @function
+ .size trampoline_code_table,.- trampoline_code_table
+#endif
+ .align FFI390_TRAMP_MAP_SIZE
+#endif /* FFI_EXEC_STATIC_TRAMP */
+
#endif /* !s390x */
#if defined __ELF__ && defined __linux__
*argp++ = *(SINT32 *)a;
break;
case FFI_TYPE_UINT32:
- case FFI_TYPE_FLOAT:
*argp++ = *(UINT32 *)a;
break;
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_POINTER:
+ *argp++ = *(UINT64 *)a;
+ break;
+ case FFI_TYPE_FLOAT:
+ flags |= SPARC_FLAG_FP_ARGS;
+ *argp++ = *(UINT32 *)a;
+ break;
case FFI_TYPE_DOUBLE:
+ flags |= SPARC_FLAG_FP_ARGS;
*argp++ = *(UINT64 *)a;
break;
void **avalue, void *closure)
{
size_t bytes = cif->bytes;
+ size_t i, nargs = cif->nargs;
+ ffi_type **arg_types = cif->arg_types;
FFI_ASSERT (cif->abi == FFI_V9);
if (rvalue == NULL && (cif->flags & SPARC_FLAG_RET_IN_MEM))
bytes += FFI_ALIGN (cif->rtype->size, 16);
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ for (i = 0; i < nargs; i++)
+ {
+ ffi_type *at = arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT && size > 4)
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
+
ffi_call_v9(cif, fn, rvalue, avalue, -bytes, closure);
}
/* -----------------------------------------------------------------------
tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman
+ Copyright (c) 2022 Anthony Green
API and support functions for managing statically defined closure
trampolines.
/* -------------------------- Headers and Definitions ---------------------*/
/*
- * Add support for other OSes later. For now, it is just Linux.
+ * Add support for other OSes later. For now, it is just Linux and Cygwin.
*/
-#if defined __linux__
+#if defined (__linux__) || defined (__CYGWIN__)
#ifdef __linux__
#define _GNU_SOURCE 1
#endif
+
+#include <ffi.h>
+#include <ffi_common.h>
+
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/limits.h>
#include <linux/types.h>
#endif
-#endif /* __linux__ */
+#ifdef __CYGWIN__
+#include <limits.h>
+#endif
+#endif
/*
* Each architecture defines static code for a trampoline code table. The
*/
static int tramp_table_alloc (void);
-#if defined __linux__
+#if defined (__linux__) || defined (__CYGWIN__)
static int
ffi_tramp_get_libffi (void)
unsigned long start, end, offset, inode;
uintptr_t addr = (uintptr_t) tramp_globals.text;
int nfields, found;
+ int open_flags = O_RDONLY;
+
+#ifdef O_CLOEXEC
+ open_flags |= O_CLOEXEC;
+#endif
snprintf (file, PATH_MAX, "/proc/%d/maps", getpid());
fp = fopen (file, "r");
if (!found)
return 0;
- tramp_globals.fd = open (file, O_RDONLY);
+ tramp_globals.fd = open (file, open_flags);
if (tramp_globals.fd == -1)
return 0;
return 1;
}
-#endif /* __linux__ */
+#endif /* defined (__linux__) || defined (__CYGWIN__) */
-#if defined __linux__
-
-#if defined HAVE_MKSTEMP
+#if defined (__linux__) || defined (__CYGWIN__)
static int
ffi_tramp_get_temp_file (void)
{
- char template[12] = "/tmp/XXXXXX";
ssize_t count;
tramp_globals.offset = 0;
- tramp_globals.fd = mkstemp (template);
- if (tramp_globals.fd == -1)
- return 0;
+ tramp_globals.fd = open_temp_exec_file ();
- unlink (template);
/*
* Write the trampoline code table into the temporary file and allocate a
* 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);
return 0;
}
-#else /* !defined HAVE_MKSTEMP */
-
-/*
- * TODO:
- * src/closures.c contains code for finding temp file that has EXEC
- * permissions. May be, some of that code can be shared with static
- * trampolines.
- */
-static int
-ffi_tramp_get_temp_file (void)
-{
- tramp_globals.offset = 0;
- tramp_globals.fd = -1;
- return 0;
-}
-
-#endif /* defined HAVE_MKSTEMP */
-
-#endif /* __linux__ */
+#endif /* defined (__linux__) || defined (__CYGWIN__) */
/* ------------------------ OS-specific Initialization ----------------------*/
-#if defined __linux__
+#if defined (__linux__) || defined (__CYGWIN__)
static int
ffi_tramp_init_os (void)
return ffi_tramp_get_temp_file ();
}
-#endif /* __linux__ */
+#endif /* defined (__linux__) || defined (__CYGWIN__) */
/* --------------------------- OS-specific Locking -------------------------*/
-#if defined __linux__
+#if defined (__linux__) || defined (__CYGWIN__)
static pthread_mutex_t tramp_globals_mutex = PTHREAD_MUTEX_INITIALIZER;
}
static void
-ffi_tramp_unlock()
+ffi_tramp_unlock(void)
{
pthread_mutex_unlock (&tramp_globals_mutex);
}
-#endif /* __linux__ */
+#endif /* defined (__linux__) || defined (__CYGWIN__) */
/* ------------------------ OS-specific Memory Mapping ----------------------*/
* sizeof (struct tramp_parm) cannot exceed the size of a parameter block.
*/
-#if defined __linux__
+#if defined (__linux__) || defined (__CYGWIN__)
static int
tramp_table_map (struct tramp_table *table)
(void) munmap (table->parm_table, tramp_globals.map_size);
}
-#endif /* __linux__ */
+#endif /* defined (__linux__) || defined (__CYGWIN__) */
/* ------------------------ Trampoline Initialization ----------------------*/
static int
ffi_tramp_init (void)
{
+ long page_size;
+
if (tramp_globals.status == TRAMP_GLOBALS_PASSED)
return 1;
&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 ())
/* -----------------------------------------------------------------------
- types.c - Copyright (c) 1996, 1998 Red Hat, Inc.
-
+ types.c - Copyright (c) 1996, 1998, 2024, 2025 Red Hat, Inc.
+
Predefined ffi_types needed by libffi.
Permission is hereby granted, free of charge, to any person obtaining
#include <ffi.h>
#include <ffi_common.h>
+/* Return a version string. */
+const char *ffi_get_version (void)
+{
+ return FFI_VERSION_STRING;
+}
+
+/* Return the version as an unsigned long value: (x * 10000 + y * 100 + z) */
+unsigned long ffi_get_version_number (void)
+{
+ return FFI_VERSION_NUMBER;
+}
+
+unsigned int ffi_get_default_abi (void)
+{
+ return FFI_DEFAULT_ABI;
+}
+
+size_t ffi_get_closure_size (void)
+{
+ return sizeof(ffi_closure);
+}
+
/* Type definitions */
#define FFI_TYPEDEF(name, type, id, maybe_const)\
#endif
#ifdef __alpha__
-/* Even if we're not configured to default to 128-bit long double,
+/* Even if we're not configured to default to 128-bit long double,
maintain binary compatibility, as -mlong-double-128 can be used
at any time. */
/* Validate the hard-coded number below. */
# error FFI_TYPE_LONGDOUBLE out of date
# endif
const ffi_type ffi_type_longdouble = { 16, 16, 4, NULL };
-#elif FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+#else
FFI_TYPEDEF(longdouble, long double, FFI_TYPE_LONGDOUBLE, FFI_LDBL_CONST);
#endif
#ifdef FFI_TARGET_HAS_COMPLEX_TYPE
FFI_COMPLEX_TYPEDEF(float, float, const);
FFI_COMPLEX_TYPEDEF(double, double, const);
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
FFI_COMPLEX_TYPEDEF(longdouble, long double, FFI_LDBL_CONST);
#endif
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------
+ 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 <ffi.h>
+#include <ffi_common.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <emscripten/emscripten.h>
+
+#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)
+
+EM_JS_DEPS(libffi, "$getWasmTableEntry,$setWasmTableEntry,$getEmptyTableSlot,$convertJsFunctionToWasm");
+
+#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]
+#define DEREF_U64(addr, offset) HEAPU64[(addr >> 3) + offset]
+
+#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");
+
+#if __SIZEOF_POINTER__ == 4
+
+#define FFI_EMSCRIPTEN_ABI FFI_WASM32_EMSCRIPTEN
+#define PTR_SIG 'i'
+
+#define DEC_PTR(p) p
+#define ENC_PTR(p) p
+
+#define DEREF_PTR(addr, offset) DEREF_U32(addr, offset)
+#define DEREF_PTR_NUMBER(addr, offset) DEREF_PTR(addr, offset)
+
+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, flags, 4*5);
+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__FLAGS(addr) DEREF_U32(addr, 5)
+#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)
+
+#elif __SIZEOF_POINTER__ == 8
+
+#define FFI_EMSCRIPTEN_ABI FFI_WASM64_EMSCRIPTEN
+#define PTR_SIG 'j'
+
+// DEC_PTR casts a pointer value (comming from Wasm) represented as BigInt (i64) to Number (i53).
+// This should be used for a pointer that is expected to be within the i53 range. If the pointer
+// value is outside the Number's range, the value will become NaN.
+#define DEC_PTR(p) bigintToI53Checked(p)
+// ENC_PTR casts a pointer value represented as Number to BigInt (i64)
+#define ENC_PTR(p) BigInt(p)
+
+#define DEREF_PTR(addr, offset) DEREF_U64(addr, offset)
+#define DEREF_PTR_NUMBER(addr, offset) DEC_PTR(DEREF_PTR(addr, offset))
+
+CHECK_FIELD_OFFSET(ffi_cif, abi, 0);
+CHECK_FIELD_OFFSET(ffi_cif, nargs, 4);
+CHECK_FIELD_OFFSET(ffi_cif, arg_types, 8);
+CHECK_FIELD_OFFSET(ffi_cif, rtype, 16);
+CHECK_FIELD_OFFSET(ffi_cif, flags, 28);
+CHECK_FIELD_OFFSET(ffi_cif, nfixedargs, 32);
+
+#define CIF__ABI(addr) DEREF_U32(addr, 0)
+#define CIF__NARGS(addr) DEREF_U32(addr + 4, 0)
+#define CIF__ARGTYPES(addr) DEREF_U64(addr + 8, 0)
+#define CIF__RTYPE(addr) DEREF_U64(addr + 16, 0)
+#define CIF__FLAGS(addr) DEREF_U32(addr + 28, 0)
+#define CIF__NFIXEDARGS(addr) DEREF_U32(addr + 32, 0)
+
+CHECK_FIELD_OFFSET(ffi_type, size, 0);
+CHECK_FIELD_OFFSET(ffi_type, alignment, 8);
+CHECK_FIELD_OFFSET(ffi_type, type, 10);
+CHECK_FIELD_OFFSET(ffi_type, elements, 16);
+
+#define FFI_TYPE__SIZE(addr) DEREF_U64(addr, 0)
+#define FFI_TYPE__ALIGN(addr) DEREF_U16(addr + 8, 0)
+#define FFI_TYPE__TYPEID(addr) DEREF_U16(addr + 10, 0)
+#define FFI_TYPE__ELEMENTS(addr) DEREF_U64(addr + 16, 0)
+
+#else
+#error "Unknown pointer size"
+#endif
+
+#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 <stddef.h>
+
+#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_EMSCRIPTEN_ABI)
+ 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), {
+ type_ptr = DEC_PTR(type_ptr);
+ var type_id = FFI_TYPE__TYPEID(type_ptr);
+ while (type_id === FFI_TYPE_STRUCT) {
+ // Don't unbox single element structs if they are bigger than 16 bytes. This
+ // is a work around for the fact that Python will give incorrect values for
+ // the size of the field in these cases: it says that the struct has pointer
+ // size and alignment and are of type pointer, even though it is more
+ // accurately a struct and has a larger size. Keeping it as a struct here
+ // will let us get the ABI right (which is in fact that the true argument is
+ // a pointer to the stack... so maybe Python issn't so wrong??)
+ //
+ // See the Python comment here:
+ // https://github.com/python/cpython/blob/a16a9f978f42b8a09297c1efbf33877f6388c403/Modules/_ctypes/stgdict.c#L718-L779
+ if (DEC_PTR(FFI_TYPE__SIZE(type_ptr)) > 16) {
+ break;
+ }
+ var elements = DEC_PTR(FFI_TYPE__ELEMENTS(type_ptr));
+ var first_element = DEREF_PTR_NUMBER(elements, 0);
+ if (first_element === 0) {
+ type_id = FFI_TYPE_VOID;
+ break;
+ } else if (DEREF_PTR_NUMBER(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),
+{
+ cif = DEC_PTR(cif);
+ fn = DEC_PTR(fn);
+ rvalue = DEC_PTR(rvalue);
+ avalue = DEC_PTR(avalue);
+ var abi = CIF__ABI(cif);
+ var nargs = CIF__NARGS(cif);
+ var nfixedargs = CIF__NFIXEDARGS(cif);
+ var arg_types_ptr = DEC_PTR(CIF__ARGTYPES(cif));
+ var flags = CIF__FLAGS(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(ENC_PTR(rvalue));
+ ret_by_arg = true;
+ }
+
+ // 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_PTR_NUMBER(avalue, i);
+ var arg_unboxed = unbox_small_structs(DEREF_PTR(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:
+ args.push(DEREF_U32(arg_ptr, 0));
+ break;
+ case FFI_TYPE_FLOAT:
+ args.push(DEREF_F32(arg_ptr, 0));
+ break;
+ case FFI_TYPE_DOUBLE:
+ args.push(DEREF_F64(arg_ptr, 0));
+ break;
+ case FFI_TYPE_UINT8:
+ args.push(DEREF_U8(arg_ptr, 0));
+ break;
+ case FFI_TYPE_SINT8:
+ args.push(DEREF_S8(arg_ptr, 0));
+ break;
+ case FFI_TYPE_UINT16:
+ args.push(DEREF_U16(arg_ptr, 0));
+ break;
+ case FFI_TYPE_SINT16:
+ args.push(DEREF_S16(arg_ptr, 0));
+ break;
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_SINT64:
+ args.push(DEREF_U64(arg_ptr, 0));
+ break;
+ case FFI_TYPE_LONGDOUBLE:
+ // long double is passed as a pair of BigInts.
+ args.push(DEREF_U64(arg_ptr, 0));
+ args.push(DEREF_U64(arg_ptr, 1));
+ 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 = DEC_PTR(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(ENC_PTR(cur_stack_ptr));
+ break;
+ case FFI_TYPE_POINTER:
+ args.push(DEREF_PTR(arg_ptr, 0));
+ 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 (flags & VARARGS_FLAG) {
+ var struct_arg_info = [];
+ for (var i = nargs - 1; i >= nfixedargs; i--) {
+ var arg_ptr = DEREF_PTR_NUMBER(avalue, i);
+ var arg_unboxed = unbox_small_structs(DEREF_PTR(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_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, __SIZEOF_POINTER__, __SIZEOF_POINTER__);
+ struct_arg_info.push([cur_stack_ptr, arg_ptr, DEC_PTR(FFI_TYPE__SIZE(arg_type_ptr)), FFI_TYPE__ALIGN(arg_type_ptr)]);
+ break;
+ case FFI_TYPE_POINTER:
+ STACK_ALLOC(cur_stack_ptr, __SIZEOF_POINTER__, __SIZEOF_POINTER__);
+ DEREF_PTR(cur_stack_ptr, 0) = DEREF_PTR(arg_ptr, 0);
+ 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(ENC_PTR(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_PTR(arg_target, 0) = ENC_PTR(cur_stack_ptr);
+ }
+ }
+ stackRestore(cur_stack_ptr);
+ stackAlloc(0); // stackAlloc enforces alignment invariants on the stack pointer
+ LOG_DEBUG("CALL_FUNC_PTR", "fn:", fn, "args:", args);
+ var result = getWasmTableEntry(fn).apply(null, 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:
+ 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:
+ DEREF_U64(rvalue, 0) = result;
+ break;
+ case FFI_TYPE_POINTER:
+ DEREF_PTR(rvalue, 0) = result;
+ 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);
+}
+
+#if __SIZEOF_POINTER__ == 4
+
+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)
+
+#elif __SIZEOF_POINTER__ == 8
+
+CHECK_FIELD_OFFSET(ffi_closure, ftramp, 0);
+CHECK_FIELD_OFFSET(ffi_closure, cif, 8);
+CHECK_FIELD_OFFSET(ffi_closure, fun, 16);
+CHECK_FIELD_OFFSET(ffi_closure, user_data, 24);
+
+#define CLOSURE__wrapper(addr) DEREF_U64(addr, 0)
+#define CLOSURE__cif(addr) DEREF_U64(addr, 1)
+#define CLOSURE__fun(addr) DEREF_U64(addr, 2)
+#define CLOSURE__user_data(addr) DEREF_U64(addr, 3)
+
+#else
+#error "Unknown pointer size"
+#endif
+
+EM_JS_MACROS(void *, ffi_closure_alloc_js, (size_t size, void **code), {
+ size = DEC_PTR(size);
+ code = DEC_PTR(code);
+ var closure = _malloc(size);
+ var index = getEmptyTableSlot();
+ DEREF_PTR(code, 0) = ENC_PTR(index);
+ CLOSURE__wrapper(closure) = ENC_PTR(index);
+ return ENC_PTR(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), {
+ closure = DEC_PTR(closure);
+ var index = DEC_PTR(CLOSURE__wrapper(closure));
+ freeTableIndexes.push(index);
+ _free(closure);
+})
+
+void __attribute__ ((visibility ("default")))
+ffi_closure_free(void *closure) {
+ return ffi_closure_free_js(closure);
+}
+
+EM_JS_MACROS(
+ffi_status,
+ffi_prep_closure_loc_js,
+(ffi_closure *closure, ffi_cif *cif, void *fun, void *user_data, void *codeloc),
+{
+ closure = DEC_PTR(closure);
+ cif = DEC_PTR(cif);
+ fun = DEC_PTR(fun);
+ user_data = DEC_PTR(user_data);
+ codeloc = DEC_PTR(codeloc);
+ var abi = CIF__ABI(cif);
+ var nargs = CIF__NARGS(cif);
+ var nfixedargs = CIF__NFIXEDARGS(cif);
+ var arg_types_ptr = DEC_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 = 'v' + PTR_SIG;
+ 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:
+ 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_POINTER:
+ sig = PTR_SIG;
+ 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_PTR(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([DEC_PTR(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:
+ 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_STRUCT:
+ case FFI_TYPE_POINTER:
+ sig += PTR_SIG;
+ 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 += PTR_SIG;
+ }
+ 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 -= __SIZEOF_POINTER__ * 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_PTR(args_ptr, carg_idx) = ENC_PTR(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_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr);
+ DEREF_U16(cur_ptr, 0) = cur_arg;
+ break;
+ case FFI_TYPE_INT:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT32:
+ STACK_ALLOC(cur_ptr, 4, 4);
+ DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(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(DEC_PTR(cur_arg), DEC_PTR(cur_arg) + arg_size));
+ DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr);
+ break;
+ case FFI_TYPE_FLOAT:
+ STACK_ALLOC(cur_ptr, 4, 4);
+ DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr);
+ DEREF_F32(cur_ptr, 0) = cur_arg;
+ break;
+ case FFI_TYPE_DOUBLE:
+ STACK_ALLOC(cur_ptr, 8, 8);
+ DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(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_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr);
+ DEREF_U64(cur_ptr, 0) = cur_arg;
+ break;
+ case FFI_TYPE_LONGDOUBLE:
+ STACK_ALLOC(cur_ptr, 16, 8);
+ DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr);
+ DEREF_U64(cur_ptr, 0) = cur_arg;
+ cur_arg = args[jsarg_idx++];
+ DEREF_U64(cur_ptr, 1) = cur_arg;
+ break;
+ case FFI_TYPE_POINTER:
+ STACK_ALLOC(cur_ptr, __SIZEOF_POINTER__, __SIZEOF_POINTER__);
+ DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr);
+ DEREF_PTR(cur_ptr, 0) = cur_arg;
+ break;
+ }
+ }
+ // If its a varargs call, last js argument is a pointer to the varargs.
+ var varargs = DEC_PTR(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_PTR_NUMBER(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_PTR(args_ptr, carg_idx) = ENC_PTR(cur_ptr);
+ } else {
+ DEREF_PTR(args_ptr, carg_idx) = ENC_PTR(varargs);
+ }
+ varargs += __SIZEOF_POINTER__;
+ }
+ 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), ENC_PTR(ret_ptr), ENC_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':
+ return DEREF_U64(ret_ptr, 0);
+ case 'd':
+ return DEREF_F64(ret_ptr, 0);
+ case 'f':
+ return DEREF_F32(ret_ptr, 0);
+ }
+ }
+ }
+ try {
+ var wasm_trampoline = convertJsFunctionToWasm(trampoline, sig);
+ } catch(e) {
+ return FFI_BAD_TYPEDEF_MACRO;
+ }
+ setWasmTableEntry(codeloc, wasm_trampoline);
+ CLOSURE__cif(closure) = ENC_PTR(cif);
+ CLOSURE__fun(closure) = ENC_PTR(fun);
+ CLOSURE__user_data(closure) = ENC_PTR(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_EMSCRIPTEN_ABI)
+ return FFI_BAD_ABI;
+ return ffi_prep_closure_loc_js(closure, cif, (void *)fun, user_data,
+ codeloc);
+}
--- /dev/null
+/* -----------------------------------------------------------------*-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,
+#if __SIZEOF_POINTER__ == 4
+ FFI_WASM32, // "raw", no structures, varargs, or closures (not implemented!)
+ FFI_WASM32_EMSCRIPTEN, // structures, varargs, and split 64-bit params
+#elif __SIZEOF_POINTER__ == 8
+ FFI_WASM64,
+ FFI_WASM64_EMSCRIPTEN,
+#else
+#error "Unknown pointer size"
+#endif
+ FFI_LAST_ABI,
+#if __SIZEOF_POINTER__ == 4
+#ifdef __EMSCRIPTEN__
+ FFI_DEFAULT_ABI = FFI_WASM32_EMSCRIPTEN
+#else
+ FFI_DEFAULT_ABI = FFI_WASM32
+#endif
+#elif __SIZEOF_POINTER__ == 8
+#ifdef __EMSCRIPTEN__
+ FFI_DEFAULT_ABI = FFI_WASM64_EMSCRIPTEN
+#else
+ FFI_DEFAULT_ABI = FFI_WASM64
+#endif
+#else
+#error "Unknown pointer size"
+#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
/* -----------------------------------------------------------------------
- ffi.c - Copyright (c) 2017 Anthony Green
+ ffi.c - Copyright (c) 2017, 2022 Anthony Green
Copyright (c) 1996, 1998, 1999, 2001, 2007, 2008 Red Hat, Inc.
Copyright (c) 2002 Ranjit Mathew
Copyright (c) 2002 Bo Thorsen
flags = X86_RET_INT64;
break;
case FFI_TYPE_STRUCT:
-#ifndef X86
- /* ??? This should be a different ABI rather than an ifdef. */
- if (cif->rtype->size == 1)
- flags = X86_RET_STRUCT_1B;
- else if (cif->rtype->size == 2)
- flags = X86_RET_STRUCT_2B;
- else if (cif->rtype->size == 4)
- flags = X86_RET_INT32;
- else if (cif->rtype->size == 8)
- flags = X86_RET_INT64;
- else
+ {
+#if defined(X86_WIN32) || defined(X86_DARWIN)
+ size_t size = cif->rtype->size;
+ if (size == 1)
+ flags = X86_RET_STRUCT_1B;
+ else if (size == 2)
+ flags = X86_RET_STRUCT_2B;
+ else if (size == 4)
+ flags = X86_RET_INT32;
+ else if (size == 8)
+ flags = X86_RET_INT64;
+ else
#endif
- {
- do_struct:
- switch (cabi)
- {
- case FFI_THISCALL:
- case FFI_FASTCALL:
- case FFI_STDCALL:
- case FFI_MS_CDECL:
- flags = X86_RET_STRUCTARG;
- break;
- default:
- flags = X86_RET_STRUCTPOP;
- break;
- }
- /* Allocate space for return value pointer. */
- bytes += FFI_ALIGN (sizeof(void*), FFI_SIZEOF_ARG);
- }
+ {
+ do_struct:
+ switch (cabi)
+ {
+ case FFI_THISCALL:
+ case FFI_FASTCALL:
+ case FFI_STDCALL:
+ case FFI_MS_CDECL:
+ flags = X86_RET_STRUCTARG;
+ break;
+ default:
+ flags = X86_RET_STRUCTPOP;
+ break;
+ }
+ /* Allocate space for return value pointer. */
+ bytes += FFI_ALIGN (sizeof(void*), FFI_SIZEOF_ARG);
+ }
+ }
break;
case FFI_TYPE_COMPLEX:
switch (cif->rtype->elements[0]->type)
{
ffi_type *t = cif->arg_types[i];
- bytes = FFI_ALIGN (bytes, t->alignment);
+#if defined(X86_WIN32)
+ if (cabi == FFI_STDCALL)
+ bytes = FFI_ALIGN (bytes, FFI_SIZEOF_ARG);
+ else
+#endif
+ bytes = FFI_ALIGN (bytes, t->alignment);
bytes += FFI_ALIGN (t->size, FFI_SIZEOF_ARG);
}
cif->bytes = bytes;
#if defined(_MSC_VER)
#pragma runtime_checks("s", off)
#endif
+/* n.b. ffi_call_unix64 will steal the alloca'd `stack` variable here for use
+ _as its own stack_ - so we need to compile this function without ASAN */
+FFI_ASAN_NO_SANITIZE
static void
ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
void **avalue, void *closure)
cases. */
if (t == FFI_TYPE_STRUCT && ty->alignment >= 16)
align = 16;
-
+
if (dir < 0)
{
/* ??? These reverse argument ABIs are probably too old
tramp[9] = 0xe9;
*(unsigned *)(tramp + 10) = (unsigned)dest - ((unsigned)codeloc + 14);
+#if defined(FFI_EXEC_STATIC_TRAMP)
out:
+#endif
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
/* -----------------------------------------------------------------------
- ffi64.c - Copyright (c) 2011, 2018 Anthony Green
+ ffi64.c - Copyright (c) 2011, 2018, 2022 Anthony Green
Copyright (c) 2013 The Written Word, Inc.
Copyright (c) 2008, 2010 Red Hat, Inc.
Copyright (c) 2002, 2007 Bo Thorsen <bo@suse.de>
return FFI_OK;
}
+/* n.b. ffi_call_unix64 will steal the alloca'd `stack` variable here for use
+ _as its own stack_ - so we need to compile this function without ASAN */
+FFI_ASAN_NO_SANITIZE
static void
ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
void **avalue, void *closure)
flags = UNIX64_RET_VOID;
}
+ arg_types = cif->arg_types;
+ avn = cif->nargs;
+
/* Allocate the space for the arguments, plus 4 words of temp space. */
stack = alloca (sizeof (struct register_args) + cif->bytes + 4*8);
reg_args = (struct register_args *) stack;
if (flags & UNIX64_FLAG_RET_IN_MEM)
reg_args->gpr[gprcount++] = (unsigned long) rvalue;
- avn = cif->nargs;
- arg_types = cif->arg_types;
-
for (i = 0; i < avn; ++i)
{
size_t n, size = arg_types[i]->size;
if (align < 8)
align = 8;
- /* Pass this argument in memory. */
- argp = (void *) FFI_ALIGN (argp, align);
- memcpy (argp, avalue[i], size);
- argp += size;
- }
+ /* Pass this argument in memory. */
+ argp = (void *) FFI_ALIGN (argp, align);
+ memcpy (argp, avalue[i], size);
+
+ argp += size;
+ }
else
{
/* The argument is passed entirely in registers. */
break;
default:
reg_args->gpr[gprcount] = 0;
- memcpy (®_args->gpr[gprcount], a, size);
+ memcpy (®_args->gpr[gprcount], a, size <= 8 ? size : 8);
}
gprcount++;
break;
void
ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
{
+ ffi_type **arg_types = cif->arg_types;
+ int i, nargs = cif->nargs;
+ const int max_reg_struct_size = cif->abi == FFI_GNUW64 ? 8 : 16;
+
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ for (i = 0; i < nargs; i++)
+ {
+ ffi_type *at = arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT && size > max_reg_struct_size)
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
+
#ifndef __ILP32__
if (cif->abi == FFI_EFI64 || cif->abi == FFI_GNUW64)
{
memcpy (tramp, trampoline, sizeof(trampoline));
*(UINT64 *)(tramp + sizeof (trampoline)) = (uintptr_t)dest;
+#if defined(FFI_EXEC_STATIC_TRAMP)
out:
+#endif
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
#if defined (X86_64) && defined (__i386__)
#undef X86_64
+#warning ******************************************************
+#warning ********** X86 IS DEFINED ****************************
+#warning ******************************************************
#define X86
#endif
FFI_LAST_ABI,
#ifdef __GNUC__
FFI_DEFAULT_ABI = FFI_GNUW64
-#else
+#else
FFI_DEFAULT_ABI = FFI_WIN64
-#endif
+#endif
#elif defined(X86_64) || (defined (__x86_64__) && defined (X86_DARWIN))
FFI_FIRST_ABI = 1,
#endif
#endif
-
UINT64 *stack;
size_t rsize;
struct win64_call_frame *frame;
+ ffi_type **arg_types = cif->arg_types;
+ int nargs = cif->nargs;
FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64);
+ /* If we have any large structure arguments, make a copy so we are passing
+ by value. */
+ for (i = 0; i < nargs; i++)
+ {
+ ffi_type *at = arg_types[i];
+ int size = at->size;
+ if (at->type == FFI_TYPE_STRUCT && size > 8)
+ {
+ char *argcopy = alloca (size);
+ memcpy (argcopy, avalue[i], size);
+ avalue[i] = argcopy;
+ }
+ }
+
flags = cif->flags;
rsize = 0;
ffi_cif* cif,
void (*fun)(ffi_cif*, void*, void**, void*),
void *user_data,
- void *codeloc)
+ void *codeloc MAYBE_UNUSED)
{
static const unsigned char trampoline[FFI_TRAMPOLINE_SIZE - 8] = {
/* endbr64 */
memcpy (tramp, trampoline, sizeof(trampoline));
*(UINT64 *)(tramp + sizeof (trampoline)) = (uintptr_t)ffi_closure_win64;
+#if defined(FFI_EXEC_STATIC_TRAMP)
out:
+#endif
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
/* -----------------------------------------------------------------------
- sysv.S - Copyright (c) 2017 Anthony Green
+ sysv.S - Copyright (c) 2017, 2022 Anthony Green
- Copyright (c) 2013 The Written Word, Inc.
- Copyright (c) 1996,1998,2001-2003,2005,2008,2010 Red Hat, Inc.
-
- X86 Foreign Function Interface
+
+ X86 Foreign Function Interface
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
#ifndef __x86_64__
#ifdef _MSC_VER
-#define LIBFFI_ASM
+#define LIBFFI_ASM
#include <fficonfig.h>
#include <ffi.h>
#include <ffi_cfi.h>
-#include "internal.h"
+#include "internal.h"
#define C2(X, Y) X ## Y
#define C1(X, Y) C2(X, Y)
perspective of the unwind info, it hasn't moved. */
mov ebp, ecx
L(UW1):
- // cfi_def_cfa(%ebp, 8)
- // cfi_rel_offset(%ebp, 0)
+ /* cfi_def_cfa(%ebp, 8) */
+ /* cfi_rel_offset(%ebp, 0) */
mov esp, edx /* set outgoing argument stack */
mov eax, [20+R_EAX*4+ebp] /* set register arguments */
mov ecx, [12+ebp] /* load return type code */
mov [ebp+8], ebx /* preserve %ebx */
L(UW2):
- // cfi_rel_offset(%ebx, 8)
+ /* cfi_rel_offset(%ebx, 8) */
and ecx, X86_RET_TYPE_MASK
lea ebx, [L(store_table) + ecx * 8]
mov esp, ebp
pop ebp
L(UW3):
- // cfi_remember_state
- // cfi_def_cfa(%esp, 4)
- // cfi_restore(%ebx)
- // cfi_restore(%ebp)
+ /* cfi_remember_state */
+ /* cfi_def_cfa(%esp, 4) */
+ /* cfi_restore(%ebx) */
+ /* cfi_restore(%ebp) */
ret
L(UW4):
- // cfi_restore_state
+ /* cfi_restore_state */
E(L(store_table), X86_RET_STRUCTPOP)
jmp L(e1)
int 3
L(UW5):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(@ffi_call_i386@8)
/* The inner helper is declared as
PUBLIC ffi_go_closure_EAX
ffi_go_closure_EAX PROC C
L(UW6):
- // cfi_startproc
+ /* cfi_startproc */
sub esp, closure_FS
L(UW7):
- // cfi_def_cfa_offset(closure_FS + 4)
+ /* cfi_def_cfa_offset(closure_FS + 4) */
FFI_CLOSURE_SAVE_REGS
mov edx, [eax+4] /* copy cif */
mov ecx, [eax +8] /* copy fun */
mov [esp+closure_CF+36], eax /* closure is user_data */
jmp L(do_closure_i386)
L(UW8):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_go_closure_EAX)
ALIGN 16
PUBLIC ffi_go_closure_ECX
ffi_go_closure_ECX PROC C
L(UW9):
- // cfi_startproc
+ /* cfi_startproc */
sub esp, closure_FS
L(UW10):
- // cfi_def_cfa_offset(closure_FS + 4)
+ /* cfi_def_cfa_offset(closure_FS + 4) */
FFI_CLOSURE_SAVE_REGS
mov edx, [ecx+4] /* copy cif */
mov eax, [ecx+8] /* copy fun */
mov [esp+closure_CF+36], ecx /* closure is user_data */
jmp L(do_closure_i386)
L(UW11):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_go_closure_ECX)
/* The closure entry points are reached from the ffi_closure trampoline.
PUBLIC ffi_closure_i386
ffi_closure_i386 PROC C
L(UW12):
- // cfi_startproc
+ /* cfi_startproc */
sub esp, closure_FS
L(UW13):
- // cfi_def_cfa_offset(closure_FS + 4)
+ /* cfi_def_cfa_offset(closure_FS + 4) */
FFI_CLOSURE_SAVE_REGS
FFI_CLOSURE_COPY_TRAMP_DATA
L(e2):
add esp, closure_FS
L(UW16):
- // cfi_adjust_cfa_offset(-closure_FS)
+ /* cfi_adjust_cfa_offset(-closure_FS) */
ret
L(UW17):
- // cfi_adjust_cfa_offset(closure_FS)
+ /* cfi_adjust_cfa_offset(closure_FS) */
E(L(load_table2), X86_RET_STRUCTPOP)
add esp, closure_FS
L(UW18):
- // cfi_adjust_cfa_offset(-closure_FS)
+ /* cfi_adjust_cfa_offset(-closure_FS) */
ret 4
L(UW19):
- // cfi_adjust_cfa_offset(closure_FS)
+ /* cfi_adjust_cfa_offset(closure_FS) */
E(L(load_table2), X86_RET_STRUCTARG)
jmp L(e2)
E(L(load_table2), X86_RET_STRUCT_1B)
int 3
L(UW20):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_closure_i386)
ALIGN 16
PUBLIC ffi_go_closure_STDCALL
ffi_go_closure_STDCALL PROC C
L(UW21):
- // cfi_startproc
+ /* cfi_startproc */
sub esp, closure_FS
L(UW22):
- // cfi_def_cfa_offset(closure_FS + 4)
+ /* cfi_def_cfa_offset(closure_FS + 4) */
FFI_CLOSURE_SAVE_REGS
mov edx, [ecx+4] /* copy cif */
mov eax, [ecx+8] /* copy fun */
mov [esp+closure_CF+36], ecx /* closure is user_data */
jmp L(do_closure_STDCALL)
L(UW23):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_go_closure_STDCALL)
/* For REGISTER, we have no available parameter registers, and so we
PUBLIC ffi_closure_REGISTER
ffi_closure_REGISTER PROC C
L(UW24):
- // cfi_startproc
- // cfi_def_cfa(%esp, 8)
- // cfi_offset(%eip, -8)
+ /* cfi_startproc */
+ /* cfi_def_cfa(%esp, 8) */
+ /* cfi_offset(%eip, -8) */
sub esp, closure_FS-4
L(UW25):
- // cfi_def_cfa_offset(closure_FS + 4)
+ /* cfi_def_cfa_offset(closure_FS + 4) */
FFI_CLOSURE_SAVE_REGS
mov ecx, [esp+closure_FS-4] /* load retaddr */
mov eax, [esp+closure_FS] /* load closure */
mov [esp+closure_FS], ecx /* move retaddr */
jmp L(do_closure_REGISTER)
L(UW26):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_closure_REGISTER)
/* For STDCALL (and others), we need to pop N bytes of arguments off
PUBLIC ffi_closure_STDCALL
ffi_closure_STDCALL PROC C
L(UW27):
- // cfi_startproc
+ /* cfi_startproc */
sub esp, closure_FS
L(UW28):
- // cfi_def_cfa_offset(closure_FS + 4)
+ /* cfi_def_cfa_offset(closure_FS + 4) */
FFI_CLOSURE_SAVE_REGS
int 3
L(UW31):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_closure_STDCALL)
#if !FFI_NO_RAW_API
PUBLIC ffi_closure_raw_SYSV
ffi_closure_raw_SYSV PROC C
L(UW32):
- // cfi_startproc
+ /* cfi_startproc */
sub esp, raw_closure_S_FS
L(UW33):
- // cfi_def_cfa_offset(raw_closure_S_FS + 4)
+ /* cfi_def_cfa_offset(raw_closure_S_FS + 4) */
mov [esp+raw_closure_S_FS-4], ebx
L(UW34):
- // cfi_rel_offset(%ebx, raw_closure_S_FS-4)
+ /* cfi_rel_offset(%ebx, raw_closure_S_FS-4) */
mov edx, [eax+FFI_TRAMPOLINE_SIZE+8] /* load cl->user_data */
mov [esp+12], edx
mov eax, [ebx+20] /* load cif->flags */
and eax, X86_RET_TYPE_MASK
-// #ifdef __PIC__
-// call __x86.get_pc_thunk.bx
-// L(pc4):
-// lea ecx, L(load_table4)-L(pc4)(%ebx, %eax, 8), %ecx
-// #else
+/* #ifdef __PIC__ */
+/* call __x86.get_pc_thunk.bx */
+/* L(pc4): */
+/* lea ecx, L(load_table4)-L(pc4)(%ebx, %eax, 8), %ecx */
+/* #else */
lea ecx, [L(load_table4)+eax+8]
-// #endif
+/* #endif */
mov ebx, [esp+raw_closure_S_FS-4]
L(UW35):
- // cfi_restore(%ebx)
+ /* cfi_restore(%ebx) */
mov eax, [esp+16] /* Optimistic load */
jmp dword ptr [ecx]
L(e4):
add esp, raw_closure_S_FS
L(UW36):
- // cfi_adjust_cfa_offset(-raw_closure_S_FS)
+ /* cfi_adjust_cfa_offset(-raw_closure_S_FS) */
ret
L(UW37):
- // cfi_adjust_cfa_offset(raw_closure_S_FS)
+ /* cfi_adjust_cfa_offset(raw_closure_S_FS) */
E(L(load_table4), X86_RET_STRUCTPOP)
add esp, raw_closure_S_FS
L(UW38):
- // cfi_adjust_cfa_offset(-raw_closure_S_FS)
+ /* cfi_adjust_cfa_offset(-raw_closure_S_FS) */
ret 4
L(UW39):
- // cfi_adjust_cfa_offset(raw_closure_S_FS)
+ /* cfi_adjust_cfa_offset(raw_closure_S_FS) */
E(L(load_table4), X86_RET_STRUCTARG)
jmp L(e4)
E(L(load_table4), X86_RET_STRUCT_1B)
int 3
L(UW40):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_closure_raw_SYSV)
#define raw_closure_T_FS (16+16+8)
PUBLIC ffi_closure_raw_THISCALL
ffi_closure_raw_THISCALL PROC C
L(UW41):
- // cfi_startproc
+ /* cfi_startproc */
/* Rearrange the stack such that %ecx is the first argument.
This means moving the return address. */
pop edx
L(UW42):
- // cfi_def_cfa_offset(0)
- // cfi_register(%eip, %edx)
+ /* cfi_def_cfa_offset(0) */
+ /* cfi_register(%eip, %edx) */
push ecx
L(UW43):
- // cfi_adjust_cfa_offset(4)
+ /* cfi_adjust_cfa_offset(4) */
push edx
L(UW44):
- // cfi_adjust_cfa_offset(4)
- // cfi_rel_offset(%eip, 0)
+ /* cfi_adjust_cfa_offset(4) */
+ /* cfi_rel_offset(%eip, 0) */
sub esp, raw_closure_T_FS
L(UW45):
- // cfi_adjust_cfa_offset(raw_closure_T_FS)
+ /* cfi_adjust_cfa_offset(raw_closure_T_FS) */
mov [esp+raw_closure_T_FS-4], ebx
L(UW46):
- // cfi_rel_offset(%ebx, raw_closure_T_FS-4)
+ /* cfi_rel_offset(%ebx, raw_closure_T_FS-4) */
mov edx, [eax+FFI_TRAMPOLINE_SIZE+8] /* load cl->user_data */
mov [esp+12], edx
mov eax, [ebx+20] /* load cif->flags */
and eax, X86_RET_TYPE_MASK
-// #ifdef __PIC__
-// call __x86.get_pc_thunk.bx
-// L(pc5):
-// leal L(load_table5)-L(pc5)(%ebx, %eax, 8), %ecx
-// #else
+/* #ifdef __PIC__ */
+/* call __x86.get_pc_thunk.bx */
+/* L(pc5): */
+/* leal L(load_table5)-L(pc5)(%ebx, %eax, 8), %ecx */
+/* #else */
lea ecx, [L(load_table5)+eax*8]
-//#endif
+/*#endif */
mov ebx, [esp+raw_closure_T_FS-4]
L(UW47):
- // cfi_restore(%ebx)
+ /* cfi_restore(%ebx) */
mov eax, [esp+16] /* Optimistic load */
jmp DWORD PTR [ecx]
L(e5):
add esp, raw_closure_T_FS
L(UW48):
- // cfi_adjust_cfa_offset(-raw_closure_T_FS)
+ /* cfi_adjust_cfa_offset(-raw_closure_T_FS) */
/* Remove the extra %ecx argument we pushed. */
ret 4
L(UW49):
- // cfi_adjust_cfa_offset(raw_closure_T_FS)
+ /* cfi_adjust_cfa_offset(raw_closure_T_FS) */
E(L(load_table5), X86_RET_STRUCTPOP)
add esp, raw_closure_T_FS
L(UW50):
- // cfi_adjust_cfa_offset(-raw_closure_T_FS)
+ /* cfi_adjust_cfa_offset(-raw_closure_T_FS) */
ret 8
L(UW51):
- // cfi_adjust_cfa_offset(raw_closure_T_FS)
+ /* cfi_adjust_cfa_offset(raw_closure_T_FS) */
E(L(load_table5), X86_RET_STRUCTARG)
jmp L(e5)
E(L(load_table5), X86_RET_STRUCT_1B)
int 3
L(UW52):
- // cfi_endproc
+ /* cfi_endproc */
ENDF(ffi_closure_raw_THISCALL)
#endif /* !FFI_NO_RAW_API */
# define COMDAT(X)
#endif
-// #if defined(__PIC__)
-// COMDAT(C(__x86.get_pc_thunk.bx))
-// C(__x86.get_pc_thunk.bx):
-// movl (%esp), %ebx
-// ret
-// ENDF(C(__x86.get_pc_thunk.bx))
-// # if defined X86_DARWIN || defined HAVE_HIDDEN_VISIBILITY_ATTRIBUTE
-// COMDAT(C(__x86.get_pc_thunk.dx))
-// C(__x86.get_pc_thunk.dx):
-// movl (%esp), %edx
-// ret
-// ENDF(C(__x86.get_pc_thunk.dx))
-// #endif /* DARWIN || HIDDEN */
-// #endif /* __PIC__ */
+#if 0
+#if defined(__PIC__)
+ COMDAT(C(__x86.get_pc_thunk.bx))
+C(__x86.get_pc_thunk.bx):
+ movl (%esp), %ebx
+ ret
+ENDF(C(__x86.get_pc_thunk.bx))
+# if defined X86_DARWIN || defined HAVE_HIDDEN_VISIBILITY_ATTRIBUTE
+ COMDAT(C(__x86.get_pc_thunk.dx))
+C(__x86.get_pc_thunk.dx):
+ movl (%esp), %edx
+ ret
+ENDF(C(__x86.get_pc_thunk.dx))
+#endif /* DARWIN || HIDDEN */
+#endif /* __PIC__ */
+#endif
+
#if 0
/* Sadly, OSX cctools-as doesn't understand .cfi directives at all. */
#endif
#endif
-END
\ No newline at end of file
+END
actual table. The entry points into the table are all 8 bytes.
The use of ORG asserts that we're at the correct location. */
/* ??? The clang assembler doesn't handle .org with symbolic expressions. */
-#if defined(__clang__) || defined(__APPLE__) || (defined (__sun__) && defined(__svr4__))
+#ifdef __CET__
+/* Double slot size to 16 byte to add 4 bytes of ENDBR64. */
+# define E(BASE, X) .balign 8; .org BASE + X * 16
+#elif defined(__clang__) || defined(__APPLE__) || (defined (__sun__) && defined(__svr4__))
# define E(BASE, X) .balign 8
#else
-# ifdef __CET__
-# define E(BASE, X) .balign 8; .org BASE + X * 16
-# else
-# define E(BASE, X) .balign 8; .org BASE + X * 8
-# endif
+# define E(BASE, X) .balign 8; .org BASE + X * 8
#endif
/* ffi_call_unix64 (void *args, unsigned long bytes, unsigned flags,
* - restore the stack pointer to what it was when the trampoline was invoked.
*/
#ifdef ENDBR_PRESENT
-#define X86_DATA_OFFSET 4077
-#define X86_CODE_OFFSET 4073
+# define X86_DATA_OFFSET 4077
+# ifdef __ILP32__
+# define X86_CODE_OFFSET 4069
+# else
+# define X86_CODE_OFFSET 4073
+# endif
#else
-#define X86_DATA_OFFSET 4081
-#define X86_CODE_OFFSET 4077
+# define X86_DATA_OFFSET 4081
+# ifdef __ILP32__
+# define X86_CODE_OFFSET 4073
+# else
+# define X86_CODE_OFFSET 4077
+# endif
#endif
.align UNIX64_TRAMP_MAP_SIZE
_CET_ENDBR
subq $16, %rsp /* Make space on the stack */
movq %r10, (%rsp) /* Save %r10 on stack */
+#ifdef __ILP32__
+ movl X86_DATA_OFFSET(%rip), %r10d /* Copy data into %r10 */
+#else
movq X86_DATA_OFFSET(%rip), %r10 /* Copy data into %r10 */
+#endif
movq %r10, 8(%rsp) /* Save data on stack */
+#ifdef __ILP32__
+ movl X86_CODE_OFFSET(%rip), %r10d /* Copy code into %r10 */
+#else
movq X86_CODE_OFFSET(%rip), %r10 /* Copy code into %r10 */
+#endif
jmp *%r10 /* Jump to code */
.align 8
.endr
break;
}
- /* Round the stack up to a full 4 register frame, just in case
- (we use this size in movsp). This way, it's also a multiple of
- 8 bytes for 64-bit arguments. */
- cif->bytes = FFI_ALIGN(cif->bytes, 16);
-
+ /* Round up stack size needed for arguments.
+ Allocate FFI_REGISTER_ARGS_SPACE bytes when there are only arguments
+ passed in registers, round space reserved for arguments passed on stack
+ up to ABI-specified alignment. */
+ if (cif->bytes < FFI_REGISTER_NARGS * 4)
+ cif->bytes = FFI_REGISTER_ARGS_SPACE;
+ else
+ cif->bytes = FFI_REGISTER_ARGS_SPACE +
+ FFI_ALIGN(cif->bytes - FFI_REGISTER_NARGS * 4,
+ XTENSA_STACK_ALIGNMENT);
return FFI_OK;
}
void *user_data,
void *codeloc)
{
+ if (cif->abi != FFI_SYSV)
+ return FFI_BAD_ABI;
+
/* copye trampoline to stack and patch 'ffi_closure_SYSV' pointer */
memcpy(closure->tramp, ffi_trampoline, FFI_TRAMPOLINE_SIZE);
*(unsigned int*)(&closure->tramp[8]) = (unsigned int)ffi_closure_SYSV;
if (arg_types[i]->alignment == 8 && (areg & 1) != 0)
areg++;
- // skip the entry 16,a1 framework, add 16 bytes (4 registers)
+ // skip the entry a1, * framework, see ffi_trampoline
if (areg == FFI_REGISTER_NARGS)
- areg += 4;
+ areg = (FFI_REGISTER_ARGS_SPACE + 32) / 4;
if (arg_types[i]->type == FFI_TYPE_STRUCT)
{
int numregs = ((arg_types[i]->size + 3) & ~3) / 4;
if (areg < FFI_REGISTER_NARGS && areg + numregs > FFI_REGISTER_NARGS)
- areg = FFI_REGISTER_NARGS + 4;
+ areg = (FFI_REGISTER_ARGS_SPACE + 32) / 4;
}
avalue[i] = &values[areg];
#endif
#define FFI_REGISTER_NARGS 6
+#define XTENSA_STACK_ALIGNMENT 16
+#define FFI_REGISTER_ARGS_SPACE ((FFI_REGISTER_NARGS * 4 + \
+ XTENSA_STACK_ALIGNMENT - 1) & \
+ -XTENSA_STACK_ALIGNMENT)
/* ---- Definitions for closures ----------------------------------------- */
#error "xtensa/sysv.S out of sync with ffi.h"
#endif
+#define FFI_REGISTER_ARGS_OFFSET ((XTENSA_STACK_ALIGNMENT - \
+ FFI_REGISTER_NARGS * 4) & \
+ (XTENSA_STACK_ALIGNMENT - 1))
/* ffi_call_SYSV (rvalue, rbytes, flags, (*fnaddr)(), bytes, ecif)
void *rvalue; a2
mov a7, a1 # fp
movsp a1, a11 # set new sp = old_sp - bytes
+ # align ffi_prep_args stack argument so that arguments
+ # passed on stack if any start on 16-byte aligned boundary
+
+ addi a11, a11, FFI_REGISTER_ARGS_OFFSET
+
movi a8, ffi_prep_args
callx8 a8 # ffi_prep_args(ecif, stack)
- # prepare to move stack pointer back up to 6 arguments
- # note that 'bytes' is already aligned
-
- movi a10, 6*4
- sub a11, a6, a10
- movgez a6, a10, a11
- add a6, a1, a6
+ # prepare to move stack pointer back
+ # to point to arguments passed on stack
+ addi a6, a1, FFI_REGISTER_ARGS_SPACE
# we can pass up to 6 arguments in registers
# for simplicity, just load 6 arguments
- # (the stack size is at least 32 bytes, so no risk to cross boundaries)
- l32i a10, a1, 0
- l32i a11, a1, 4
- l32i a12, a1, 8
- l32i a13, a1, 12
- l32i a14, a1, 16
- l32i a15, a1, 20
+ l32i a10, a1, FFI_REGISTER_ARGS_OFFSET + 0
+ l32i a11, a1, FFI_REGISTER_ARGS_OFFSET + 4
+ l32i a12, a1, FFI_REGISTER_ARGS_OFFSET + 8
+ l32i a13, a1, FFI_REGISTER_ARGS_OFFSET + 12
+ l32i a14, a1, FFI_REGISTER_ARGS_OFFSET + 16
+ l32i a15, a1, FFI_REGISTER_ARGS_OFFSET + 20
# move stack pointer
ENTRY(ffi_cacheflush)
- entry a1, 16
+ entry a1, 32
1:
#if XCHAL_DCACHE_SIZE
ENTRY(ffi_trampoline)
- entry a1, 16 + (FFI_REGISTER_NARGS * 4) + (4 * 4) # [ 0]
+ /* 32 bytes for spill + spill overflow area of a frame that uses
+ call8,
+ FFI_REGISTER_NARGS * 4 bytes for arguments passed in registers,
+ aligned up to 4 to maintain 16 byte stack alignment,
+ 4 * 4 bytes for the result.
+ This size must be in sync with ffi_closure_SYSV_inner logic.
+ */
+ entry a1, 32 + FFI_REGISTER_ARGS_SPACE + (4 * 4) # [ 0]
j 2f # [ 3]
.align 4 # [ 6]
1: .long 0 # [ 8]
CLEANFILES = *.exe core* *.log *.sum
-EXTRA_DIST = lib/target-libpath.exp lib/libffi.exp lib/wrapper.exp \
-libffi.call/strlen4.c libffi.call/struct10.c libffi.call/many_mixed.c \
-libffi.call/float.c libffi.call/struct5.c libffi.call/return_fl3.c \
-libffi.call/return_fl1.c libffi.call/call.exp libffi.call/pyobjc-tc.c \
-libffi.call/float_va.c libffi.call/struct8.c libffi.call/pr1172638.c \
-libffi.call/return_sc.c libffi.call/va_struct1.c \
-libffi.call/align_stdcall.c libffi.call/struct9.c libffi.call/va_1.c \
-libffi.call/va_2.c libffi.call/va_struct2.c libffi.call/return_fl2.c \
-libffi.call/align_mixed.c libffi.call/ffitest.h libffi.call/struct4.c \
-libffi.call/return_ldl.c libffi.call/float3.c libffi.call/return_sl.c \
-libffi.call/return_dbl1.c libffi.call/err_bad_typedef.c \
-libffi.call/return_ll1.c libffi.call/return_dbl2.c \
-libffi.call/negint.c libffi.closures/nested_struct3.c \
-libffi.call/struct2.c libffi.call/struct3.c libffi.call/return_fl.c \
-libffi.call/offsets.c libffi.call/struct7.c libffi.call/va_struct3.c \
-libffi.call/float1.c libffi.call/uninitialized.c libffi.call/many2.c \
-libffi.call/struct6.c libffi.call/strlen2.c libffi.call/float2.c \
-libffi.call/return_ul.c libffi.call/struct1.c libffi.call/strlen3.c \
-libffi.call/return_dbl.c libffi.call/float4.c libffi.call/many.c \
-libffi.call/strlen.c libffi.call/return_uc.c libffi.call/many_double.c \
-libffi.call/return_ll.c libffi.call/promotion.c \
-libffi.complex/complex_defs_longdouble.inc \
-libffi.complex/cls_align_complex_float.c \
-libffi.complex/cls_complex_va_float.c \
-libffi.complex/cls_complex_struct_float.c \
-libffi.complex/return_complex2_longdouble.c \
-libffi.complex/cls_complex_float.c \
-libffi.complex/return_complex_longdouble.c \
-libffi.complex/return_complex2_float.c libffi.complex/cls_complex.inc \
-libffi.complex/cls_complex_va_longdouble.c \
-libffi.complex/return_complex_double.c \
-libffi.complex/return_complex.inc libffi.complex/many_complex.inc \
-libffi.complex/complex_float.c libffi.complex/cls_align_complex.inc \
-libffi.complex/return_complex2_double.c \
-libffi.complex/many_complex_float.c libffi.complex/ffitest.h \
-libffi.complex/return_complex1_double.c \
-libffi.complex/cls_complex_struct_longdouble.c \
-libffi.complex/complex_defs_double.inc \
-libffi.complex/cls_complex_va_double.c \
-libffi.complex/many_complex_double.c \
-libffi.complex/return_complex2.inc \
-libffi.complex/return_complex1_float.c \
-libffi.complex/complex_longdouble.c \
-libffi.complex/complex_defs_float.inc \
-libffi.complex/cls_complex_double.c \
-libffi.complex/cls_align_complex_double.c \
-libffi.complex/cls_align_complex_longdouble.c \
-libffi.complex/complex_double.c libffi.complex/cls_complex_va.inc \
-libffi.complex/many_complex_longdouble.c libffi.complex/complex.inc \
-libffi.complex/return_complex1_longdouble.c \
-libffi.complex/complex_int.c libffi.complex/cls_complex_longdouble.c \
-libffi.complex/cls_complex_struct_double.c \
-libffi.complex/return_complex1.inc libffi.complex/complex.exp \
-libffi.complex/cls_complex_struct.inc \
-libffi.complex/return_complex_float.c libffi.go/closure1.c \
-libffi.go/aa-direct.c libffi.go/ffitest.h libffi.go/go.exp \
-libffi.go/static-chain.h libffi.bhaible/bhaible.exp \
-libffi.bhaible/test-call.c libffi.bhaible/alignof.h \
-libffi.bhaible/testcases.c libffi.bhaible/test-callback.c \
-libffi.bhaible/Makefile libffi.bhaible/README config/default.exp \
-libffi.closures/cls_multi_sshort.c \
-libffi.closures/cls_align_longdouble_split2.c \
-libffi.closures/cls_1_1byte.c libffi.closures/cls_uint_va.c \
-libffi.closures/cls_3_1byte.c libffi.closures/cls_many_mixed_args.c \
-libffi.closures/cls_20byte1.c libffi.closures/cls_pointer_stack.c \
-libffi.closures/cls_align_float.c libffi.closures/cls_5_1_byte.c \
-libffi.closures/cls_9byte1.c libffi.closures/cls_align_uint32.c \
-libffi.closures/stret_medium.c libffi.closures/cls_3byte1.c \
-libffi.closures/cls_align_uint64.c libffi.closures/cls_longdouble_va.c \
-libffi.closures/cls_align_pointer.c libffi.closures/cls_19byte.c \
-libffi.closures/cls_ushort.c libffi.closures/cls_align_sint32.c \
-libffi.closures/cls_ulonglong.c libffi.closures/cls_struct_va1.c \
-libffi.closures/cls_9byte2.c libffi.closures/closure_fn5.c \
-libffi.closures/cls_5byte.c libffi.closures/cls_3float.c \
-libffi.closures/closure.exp libffi.closures/cls_schar.c \
-libffi.closures/closure_fn4.c \
-libffi.closures/closure_fn0.c libffi.closures/huge_struct.c \
-libffi.closures/cls_64byte.c libffi.closures/cls_longdouble.c \
-libffi.closures/cls_ulong_va.c libffi.closures/cls_6_1_byte.c \
-libffi.closures/cls_align_uint16.c libffi.closures/closure_fn2.c \
-libffi.closures/unwindtest_ffi_call.cc \
-libffi.closures/cls_multi_ushortchar.c libffi.closures/cls_8byte.c \
-libffi.closures/ffitest.h libffi.closures/nested_struct8.c \
-libffi.closures/cls_pointer.c libffi.closures/nested_struct2.c \
-libffi.closures/nested_struct.c libffi.closures/cls_multi_schar.c \
-libffi.closures/cls_align_longdouble_split.c \
-libffi.closures/cls_uchar.c libffi.closures/nested_struct9.c \
-libffi.closures/cls_float.c libffi.closures/stret_medium2.c \
-libffi.closures/closure_loc_fn0.c libffi.closures/cls_6byte.c \
-libffi.closures/closure_simple.c libffi.closures/cls_align_double.c \
-libffi.closures/cls_multi_uchar.c libffi.closures/cls_4_1byte.c \
-libffi.closures/closure_fn3.c libffi.closures/cls_align_sint64.c \
-libffi.closures/nested_struct1.c libffi.closures/unwindtest.cc \
-libffi.closures/nested_struct5.c libffi.closures/cls_multi_ushort.c \
-libffi.closures/nested_struct11.c \
-libffi.closures/nested_struct12.c \
-libffi.closures/nested_struct13.c \
-libffi.closures/cls_multi_sshortchar.c \
-libffi.closures/cls_align_longdouble.c \
-libffi.closures/cls_dbls_struct.c \
-libffi.closures/cls_many_mixed_float_double.c \
-libffi.closures/stret_large.c libffi.closures/stret_large2.c \
-libffi.closures/cls_align_sint16.c libffi.closures/cls_2byte.c \
-libffi.closures/nested_struct4.c libffi.closures/problem1.c \
-libffi.closures/testclosure.c libffi.closures/nested_struct6.c \
-libffi.closures/cls_4byte.c libffi.closures/cls_24byte.c \
-libffi.closures/nested_struct10.c libffi.closures/cls_uint.c \
-libffi.closures/cls_12byte.c libffi.closures/cls_sint.c \
-libffi.closures/cls_7_1_byte.c libffi.closures/cls_sshort.c \
-libffi.closures/cls_16byte.c libffi.closures/nested_struct7.c \
-libffi.closures/cls_double_va.c libffi.closures/cls_3byte2.c \
-libffi.closures/cls_double.c libffi.closures/cls_7byte.c \
-libffi.closures/closure_fn6.c libffi.closures/closure_fn1.c \
-libffi.closures/cls_20byte.c libffi.closures/cls_18byte.c \
-libffi.closures/err_bad_abi.c \
-libffi.closures/single_entry_structs1.c \
-libffi.closures/single_entry_structs2.c \
-libffi.closures/single_entry_structs3.c
+EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \
+ emscripten/node-tests.sh emscripten/test.html emscripten/test_libffi.py \
+ emscripten/build-tests.sh lib/libffi.exp lib/target-libpath.exp \
+ lib/wrapper.exp libffi.bhaible/Makefile libffi.bhaible/README \
+ libffi.bhaible/alignof.h libffi.bhaible/bhaible.exp libffi.bhaible/test-call.c \
+ libffi.bhaible/test-callback.c libffi.bhaible/testcases.c libffi.call/align_mixed.c \
+ libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \
+ libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \
+ libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \
+ libffi.call/float4.c libffi.call/float_va.c libffi.call/many.c \
+ libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \
+ libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \
+ libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \
+ libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \
+ libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \
+ libffi.call/return_ldl.c libffi.call/return_ll.c libffi.call/return_ll1.c \
+ libffi.call/return_sc.c libffi.call/return_sl.c libffi.call/return_uc.c \
+ libffi.call/return_ul.c libffi.call/s55.c libffi.call/strlen.c \
+ libffi.call/strlen2.c libffi.call/strlen3.c libffi.call/strlen4.c \
+ libffi.call/struct1.c libffi.call/struct10.c libffi.call/struct2.c \
+ libffi.call/struct3.c libffi.call/struct4.c libffi.call/struct5.c \
+ libffi.call/struct6.c libffi.call/struct7.c libffi.call/struct8.c \
+ libffi.call/struct9.c libffi.call/struct_by_value_2.c libffi.call/struct_by_value_3.c \
+ libffi.call/struct_by_value_3f.c libffi.call/struct_by_value_4.c libffi.call/struct_by_value_4f.c \
+ libffi.call/struct_by_value_big.c libffi.call/struct_by_value_small.c libffi.call/struct_return_2H.c \
+ libffi.call/struct_int_float.c libffi.call/longjmp.c \
+ libffi.call/struct_return_8H.c libffi.call/uninitialized.c libffi.call/va_1.c \
+ libffi.call/va_2.c libffi.call/va_3.c libffi.call/va_struct1.c \
+ libffi.call/va_struct2.c libffi.call/va_struct3.c libffi.call/callback.c \
+ libffi.call/callback2.c libffi.call/callback3.c libffi.call/callback4.c libffi.call/x32.c \
+ libffi.closures/closure.exp libffi.closures/closure_fn0.c libffi.closures/closure_fn1.c \
+ libffi.closures/closure_fn2.c libffi.closures/closure_fn3.c libffi.closures/closure_fn4.c \
+ libffi.closures/closure_fn5.c libffi.closures/closure_fn6.c libffi.closures/closure_loc_fn0.c \
+ libffi.closures/closure_simple.c libffi.closures/cls_12byte.c libffi.closures/cls_16byte.c \
+ libffi.closures/cls_18byte.c libffi.closures/cls_19byte.c libffi.closures/cls_1_1byte.c \
+ libffi.closures/cls_20byte.c libffi.closures/cls_20byte1.c libffi.closures/cls_24byte.c \
+ libffi.closures/cls_2byte.c libffi.closures/cls_3_1byte.c libffi.closures/cls_3byte1.c \
+ libffi.closures/cls_3byte2.c libffi.closures/cls_3float.c libffi.closures/cls_4_1byte.c \
+ libffi.closures/cls_4byte.c libffi.closures/cls_5_1_byte.c libffi.closures/cls_5byte.c \
+ libffi.closures/cls_64byte.c libffi.closures/cls_6_1_byte.c libffi.closures/cls_6byte.c \
+ libffi.closures/cls_7_1_byte.c libffi.closures/cls_7byte.c libffi.closures/cls_8byte.c \
+ libffi.closures/cls_9byte1.c libffi.closures/cls_9byte2.c libffi.closures/cls_align_double.c \
+ libffi.closures/cls_align_float.c libffi.closures/cls_align_longdouble.c libffi.closures/cls_align_longdouble_split.c \
+ libffi.closures/cls_align_longdouble_split2.c libffi.closures/cls_align_pointer.c libffi.closures/cls_align_sint16.c \
+ libffi.closures/cls_align_sint32.c libffi.closures/cls_align_sint64.c libffi.closures/cls_align_uint16.c \
+ libffi.closures/cls_align_uint32.c libffi.closures/cls_align_uint64.c libffi.closures/cls_dbls_struct.c \
+ libffi.closures/cls_double.c libffi.closures/cls_double_va.c libffi.closures/cls_float.c \
+ libffi.closures/cls_longdouble.c libffi.closures/cls_longdouble_va.c libffi.closures/cls_many_mixed_args.c \
+ libffi.closures/cls_many_mixed_float_double.c libffi.closures/cls_multi_schar.c libffi.closures/cls_multi_sshort.c \
+ libffi.closures/cls_multi_sshortchar.c libffi.closures/cls_multi_uchar.c libffi.closures/cls_multi_ushort.c \
+ libffi.closures/cls_multi_ushortchar.c libffi.closures/cls_pointer.c libffi.closures/cls_pointer_stack.c \
+ libffi.closures/cls_schar.c libffi.closures/cls_sint.c libffi.closures/cls_sshort.c \
+ libffi.closures/cls_struct_va1.c libffi.closures/cls_uchar.c libffi.closures/cls_uint.c \
+ libffi.closures/cls_uint_va.c libffi.closures/cls_ulong_va.c libffi.closures/cls_ulonglong.c \
+ libffi.closures/cls_ushort.c libffi.closures/err_bad_abi.c libffi.closures/ffitest.h \
+ libffi.closures/huge_struct.c libffi.closures/nested_struct.c libffi.closures/nested_struct1.c \
+ libffi.closures/nested_struct10.c libffi.closures/nested_struct11.c libffi.closures/nested_struct12.c \
+ libffi.closures/nested_struct13.c libffi.closures/nested_struct2.c libffi.closures/nested_struct3.c \
+ libffi.closures/nested_struct4.c libffi.closures/nested_struct5.c libffi.closures/nested_struct6.c \
+ libffi.closures/nested_struct7.c libffi.closures/nested_struct8.c libffi.closures/nested_struct9.c \
+ libffi.closures/problem1.c libffi.closures/single_entry_structs1.c libffi.closures/single_entry_structs2.c \
+ libffi.closures/single_entry_structs3.c libffi.closures/stret_large.c libffi.closures/stret_large2.c \
+ libffi.closures/stret_medium.c libffi.closures/stret_medium2.c libffi.closures/testclosure.c \
+ libffi.closures/unwindtest.cc libffi.closures/unwindtest_ffi_call.cc libffi.complex/cls_align_complex.inc \
+ libffi.complex/cls_align_complex_double.c libffi.complex/cls_align_complex_float.c libffi.complex/cls_align_complex_longdouble.c \
+ libffi.complex/cls_complex.inc libffi.complex/cls_complex_double.c libffi.complex/cls_complex_float.c \
+ libffi.complex/cls_complex_longdouble.c libffi.complex/cls_complex_struct.inc libffi.complex/cls_complex_struct_double.c \
+ libffi.complex/cls_complex_struct_float.c libffi.complex/cls_complex_struct_longdouble.c libffi.complex/cls_complex_va.inc \
+ libffi.complex/cls_complex_va_double.c libffi.complex/cls_complex_va_float.c libffi.complex/cls_complex_va_longdouble.c \
+ libffi.complex/complex.exp libffi.complex/complex.inc libffi.complex/complex_defs_double.inc \
+ libffi.complex/complex_defs_float.inc libffi.complex/complex_defs_longdouble.inc libffi.complex/complex_double.c \
+ libffi.complex/complex_float.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \
+ libffi.complex/ffitest.h libffi.complex/many_complex.inc libffi.complex/many_complex_double.c \
+ libffi.complex/many_complex_float.c libffi.complex/many_complex_longdouble.c libffi.complex/return_complex.inc \
+ libffi.complex/return_complex1.inc libffi.complex/return_complex1_double.c libffi.complex/return_complex1_float.c \
+ libffi.complex/return_complex1_longdouble.c libffi.complex/return_complex2.inc libffi.complex/return_complex2_double.c \
+ libffi.complex/return_complex2_float.c libffi.complex/return_complex2_longdouble.c libffi.complex/return_complex_double.c \
+ libffi.complex/return_complex_float.c libffi.complex/return_complex_longdouble.c libffi.go/aa-direct.c \
+ libffi.go/closure1.c libffi.go/ffitest.h libffi.go/go.exp \
+ libffi.go/static-chain.h Makefile.am Makefile.in \
+ libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c
--- /dev/null
+#!/usr/bin/env bash
+
+if ! [ -x "$(command -v emcc)" ]; then
+ echo "Error: emcc could not be found." >&2
+ exit 1
+fi
+
+set -e
+
+cd "$1"
+shift
+
+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 \
+ -sNO_DISABLE_EXCEPTION_CATCHING \
+ -sWASM_BIGINT \
+ $EXTRA_LD_FLAGS \
+"
+
+# 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 .
--- /dev/null
+#!/usr/bin/env bash
+
+if ! [ -x "$(command -v emcc)" ]; then
+ echo "Error: emcc could not be found." >&2
+ exit 1
+fi
+
+set -e
+
+SOURCE_DIR=$PWD
+
+# Working directories
+TARGET=$SOURCE_DIR/target
+mkdir -p "$TARGET"
+
+# Define default arguments
+DEBUG=false
+
+# Parse arguments
+while [ $# -gt 0 ]; do
+ case $1 in
+ --debug) DEBUG=true ;;
+ *) echo "ERROR: Unknown parameter: $1" >&2; exit 1 ;;
+ esac
+ shift
+done
+
+# Common compiler flags
+export CFLAGS="-O3 -fPIC"
+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="${TARGET_HOST}-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 ${EXTRA_CONFIGURE_FLAGS}
+make install
+cp fficonfig.h target/include/
+cp include/ffi_common.h target/include/
--- /dev/null
+from pathlib import Path
+from pytest import fixture
+from pytest_pyodide.server import spawn_web_server
+from pytest_pyodide import runner
+
+import logging
+
+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
+
+ logger = logging.getLogger('selenium')
+ logger.setLevel(logging.DEBUG)
+
+ 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
--- /dev/null
+#!/bin/bash
+
+if ! [ -x "$(command -v emcc)" ]; then
+ echo "Error: emcc could not be found." >&2
+ exit 1
+fi
+
+# Common compiler flags
+export CFLAGS="-fPIC $EXTRA_CFLAGS"
+export CXXFLAGS="$CFLAGS -sNO_DISABLE_EXCEPTION_CATCHING $EXTRA_CXXFLAGS"
+export LDFLAGS="-sEXPORTED_FUNCTIONS=_main,_malloc,_free -sALLOW_TABLE_GROWTH -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING -sWASM_BIGINT -Wno-main"
+
+# Specific variables for cross-compilation
+export CHOST="${TARGET_HOST}-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 ${EXTRA_CONFIGURE_FLAGS} ||
+ (cat config.log && exit 1)
+make
+
+EMMAKEN_JUST_CONFIGURE=1 emmake make check \
+ RUNTESTFLAGS="LDFLAGS_FOR_TARGET='$LDFLAGS $EXTRA_TEST_LDFLAGS'" || (cat testsuite/libffi.log && exit 1)
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="test.js"></script>
+ </head>
+ <body></body>
+</html>
--- /dev/null
+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"])
-# Copyright (C) 2003, 2005, 2008, 2009, 2010, 2011, 2014, 2019 Free Software Foundation, Inc.
+# Copyright (C) 2003, 2005, 2008, 2009, 2010, 2011, 2014, 2019, 2022, 2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# <http://www.gnu.org/licenses/>.
proc load_gcc_lib { filename } {
- global srcdir loaded_libs
- load_file $srcdir/../../gcc/testsuite/lib/$filename
- set loaded_libs($filename) ""
+ global srcdir
+ load_file $srcdir/lib/$filename
}
load_lib dg.exp
load_lib libgloss.exp
-load_gcc_lib target-supports.exp
-load_gcc_lib target-supports-dg.exp
load_gcc_lib target-libpath.exp
load_gcc_lib wrapper.exp
global et_index
set selected 0
if { ![info exists et_index] } {
- # Initialize the effective target index that is used in some
- # check_effective_target_* procs.
- set et_index 0
+ # Initialize the effective target index that is used in some
+ # check_effective_target_* procs.
+ set et_index 0
}
if { [info procs check_effective_target_${arg}] != [list] } {
- set selected [check_effective_target_${arg}]
+ set selected [check_effective_target_${arg}]
} else {
- error "unknown effective target keyword `$arg'"
+ error "unknown effective target keyword `$arg'"
}
verbose "is-effective-target: $arg $selected" 2
return $selected
proc is-effective-target-keyword { arg } {
if { [info procs check_effective_target_${arg}] != [list] } {
- return 1
+ return 1
} else {
- return 0
+ return 0
}
}
# Evaluate an operand within a selector expression.
proc selector_opd { op } {
- set selector "target"
- lappend selector $op
- set answer [ expr { [dg-process-target $selector] == "S" } ]
- verbose "selector_opd: `$op' $answer" 2
- return $answer
+ set selector "target"
+ lappend selector $op
+ set answer [ expr { [dg-process-target $selector] == "S" } ]
+ verbose "selector_opd: `$op' $answer" 2
+ return $answer
}
# Evaluate a target triplet list within a selector expression.
# Unlike other operands, this needs to be expanded from a list to
# the same string as "target".
proc selector_list { op } {
- set selector "target [join $op]"
- set answer [ expr { [dg-process-target $selector] == "S" } ]
- verbose "selector_list: `$op' $answer" 2
- return $answer
+ set selector "target [join $op]"
+ set answer [ expr { [dg-process-target $selector] == "S" } ]
+ verbose "selector_list: `$op' $answer" 2
+ return $answer
}
# Evaluate a selector expression.
proc selector_expression { exp } {
- if { [llength $exp] == 2 } {
- if [string match "!" [lindex $exp 0]] {
- set op1 [lindex $exp 1]
- set answer [expr { ! [selector_opd $op1] }]
- } else {
- # Assume it's a list of target triplets.
- set answer [selector_list $exp]
- }
- } elseif { [llength $exp] == 3 } {
- set op1 [lindex $exp 0]
- set opr [lindex $exp 1]
- set op2 [lindex $exp 2]
- if [string match "&&" $opr] {
- set answer [expr { [selector_opd $op1] && [selector_opd $op2] }]
- } elseif [string match "||" $opr] {
- set answer [expr { [selector_opd $op1] || [selector_opd $op2] }]
- } else {
- # Assume it's a list of target triplets.
- set answer [selector_list $exp]
- }
- } else {
- # Assume it's a list of target triplets.
- set answer [selector_list $exp]
- }
-
- verbose "selector_expression: `$exp' $answer" 2
- return $answer
+ if { [llength $exp] == 2 } {
+ if [string match "!" [lindex $exp 0]] {
+ set op1 [lindex $exp 1]
+ set answer [expr { ! [selector_opd $op1] }]
+ } else {
+ # Assume it's a list of target triplets.
+ set answer [selector_list $exp]
+ }
+ } elseif { [llength $exp] == 3 } {
+ set op1 [lindex $exp 0]
+ set opr [lindex $exp 1]
+ set op2 [lindex $exp 2]
+ if [string match "&&" $opr] {
+ set answer [expr { [selector_opd $op1] && [selector_opd $op2] }]
+ } elseif [string match "||" $opr] {
+ set answer [expr { [selector_opd $op1] || [selector_opd $op2] }]
+ } else {
+ # Assume it's a list of target triplets.
+ set answer [selector_list $exp]
+ }
+ } else {
+ # Assume it's a list of target triplets.
+ set answer [selector_list $exp]
+ }
+
+ verbose "selector_expression: `$exp' $answer" 2
+ return $answer
}
# Evaluate "target selector" or "xfail selector".
proc dg-process-target-1 { args } {
- verbose "dg-process-target-1: `$args'" 2
-
- # Extract the 'what' keyword from the argument list.
- set selector [string trim [lindex $args 0]]
- if [regexp "^xfail " $selector] {
- set what "xfail"
- } elseif [regexp "^target " $selector] {
- set what "target"
- } else {
- error "syntax error in target selector \"$selector\""
- }
-
- # Extract the rest of the list, which might be a keyword.
- regsub "^${what}" $selector "" rest
- set rest [string trim $rest]
-
- if [is-effective-target-keyword $rest] {
- # The selector is an effective target keyword.
- if [is-effective-target $rest] {
- return [expr { $what == "xfail" ? "F" : "S" }]
- } else {
- return [expr { $what == "xfail" ? "P" : "N" }]
- }
- }
-
- if [string match "{*}" $rest] {
- if [selector_expression [lindex $rest 0]] {
- return [expr { $what == "xfail" ? "F" : "S" }]
- } else {
- return [expr { $what == "xfail" ? "P" : "N" }]
- }
- }
-
- # The selector is not an effective-target keyword, so process
- # the list of target triplets.
- return [saved-dg-process-target $selector]
+ verbose "dg-process-target-1: `$args'" 2
+
+ # Extract the 'what' keyword from the argument list.
+ set selector [string trim [lindex $args 0]]
+ if [regexp "^xfail " $selector] {
+ set what "xfail"
+ } elseif [regexp "^target " $selector] {
+ set what "target"
+ } else {
+ error "syntax error in target selector \"$selector\""
+ }
+
+ # Extract the rest of the list, which might be a keyword.
+ regsub "^${what}" $selector "" rest
+ set rest [string trim $rest]
+
+ if [is-effective-target-keyword $rest] {
+ # The selector is an effective target keyword.
+ if [is-effective-target $rest] {
+ return [expr { $what == "xfail" ? "F" : "S" }]
+ } else {
+ return [expr { $what == "xfail" ? "P" : "N" }]
+ }
+ }
+
+ if [string match "{*}" $rest] {
+ if [selector_expression [lindex $rest 0]] {
+ return [expr { $what == "xfail" ? "F" : "S" }]
+ } else {
+ return [expr { $what == "xfail" ? "P" : "N" }]
+ }
+ }
+
+ # The selector is not an effective-target keyword, so process
+ # the list of target triplets.
+ return [saved-dg-process-target $selector]
}
# Intercept calls to the DejaGnu function. In addition to
# "target selector1 xfail selector2".
proc dg-process-target { args } {
- verbose "replacement dg-process-target: `$args'" 2
-
- set selector [string trim [lindex $args 0]]
-
- # If the argument list contains both 'target' and 'xfail',
- # process 'target' and, if that succeeds, process 'xfail'.
- if [regexp "^target .* xfail .*" $selector] {
- set xfail_index [string first "xfail" $selector]
- set xfail_selector [string range $selector $xfail_index end]
- set target_selector [string range $selector 0 [expr $xfail_index-1]]
- set target_selector [string trim $target_selector]
- if { [dg-process-target-1 $target_selector] == "N" } {
- return "N"
- }
- return [dg-process-target-1 $xfail_selector]
-
- }
- return [dg-process-target-1 $selector]
+ verbose "replacement dg-process-target: `$args'" 2
+
+ set selector [string trim [lindex $args 0]]
+
+ # If the argument list contains both 'target' and 'xfail',
+ # process 'target' and, if that succeeds, process 'xfail'.
+ if [regexp "^target .* xfail .*" $selector] {
+ set xfail_index [string first "xfail" $selector]
+ set xfail_selector [string range $selector $xfail_index end]
+ set target_selector [string range $selector 0 [expr $xfail_index-1]]
+ set target_selector [string trim $target_selector]
+ if { [dg-process-target-1 $target_selector] == "N" } {
+ return "N"
+ }
+ return [dg-process-target-1 $xfail_selector]
+
+ }
+ return [dg-process-target-1 $selector]
}
}
upvar 2 dg-output-text output_match
if { [llength $output_match] > 1 } {
- regsub -all "\n" [lindex $output_match 1] "\r?\n" x
- set output_match [lreplace $output_match 1 1 $x]
+ regsub -all "\n" [lindex $output_match 1] "\r?\n" x
+ 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]
switch $do_what {
- "compile" {
- set compile_type "assembly"
- set output_file "[file rootname [file tail $prog]].s"
- }
- "link" {
- set compile_type "executable"
- set output_file "[file rootname [file tail $prog]].exe"
- # The following line is needed for targets like the i960 where
- # the default output file is b.out. Sigh.
- }
- "run" {
- set compile_type "executable"
- # FIXME: "./" is to cope with "." not being in $PATH.
- # Should this be handled elsewhere?
- # YES.
- set output_file "./[file rootname [file tail $prog]].exe"
- # 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;
- }
- default {
- perror "$do_what: not a valid dg-do keyword"
- return ""
- }
+ "compile" {
+ set compile_type "assembly"
+ set output_file "[file rootname [file tail $prog]].s"
+ }
+ "link" {
+ set compile_type "executable"
+ 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.
+ }
+ "run" {
+ set compile_type "executable"
+ # FIXME: "./" is to cope with "." not being in $PATH.
+ # Should this be handled elsewhere?
+ # YES.
+ 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;
+ }
+ default {
+ perror "$do_what: not a valid dg-do keyword"
+ return ""
+ }
}
if { $extra_tool_flags != "" } {
- lappend options "additional_flags=$extra_tool_flags"
+ lappend options "additional_flags=$extra_tool_flags"
}
set comp_output [libffi_target_compile "$prog" "$output_file" "$compile_type" $options];
proc libffi-dg-prune { target_triplet text } {
# We get this with some qemu emulated systems (eg. ppc64le-linux-gnu)
regsub -all "(^|\n)\[^\n\]*unable to perform all requested operations" $text "" text
+ # We get this from sparc64 linux systems
+ regsub -all "(^|\n)\[^\n\]*warning: .* has a LOAD segment with RWX permissions" $text "" text
+ # Ignore Emscripten INFO messages
+ regsub -all "(^|\n)(cache|shared):INFO:\[^\n\]*" $text "" text
return $text
}
global srcdir
global blddirffi
global objdir
- global blddircxx
global TOOL_OPTIONS
global tool
global libffi_include
global ld_library_path
global compiler_vendor
- set blddirffi [lookfor_file [get_multilibs] libffi]
- verbose "libffi $blddirffi"
- set blddircxx [lookfor_file [get_multilibs] libstdc++-v3]
- verbose "libstdc++ $blddircxx"
+ if ![info exists blddirffi] {
+ set blddirffi [pwd]/..
+ }
- set compiler_vendor "gnu"
+ verbose "libffi $blddirffi"
+ # Which compiler are we building with?
if { [string match $compiler_vendor "gnu"] } {
set gccdir [lookfor_file $tool_root_dir gcc/libgcc.a]
if {$gccdir != ""} {
- set gccdir [file dirname $gccdir]
+ set gccdir [file dirname $gccdir]
}
verbose "gccdir $gccdir"
set compiler "${gccdir}/xgcc"
if { [is_remote host] == 0 && [which $compiler] != 0 } {
- foreach i "[exec $compiler --print-multi-lib]" {
- set mldir ""
- regexp -- "\[a-z0-9=_/\.-\]*;" $i mldir
- set mldir [string trimright $mldir "\;@"]
- if { "$mldir" == "." } {
- continue
- }
- if { [llength [glob -nocomplain ${gccdir}/${mldir}/libgcc_s*.so.*]] >= 1 } {
- append ld_library_path ":${gccdir}/${mldir}"
- }
- }
+ foreach i "[exec $compiler --print-multi-lib]" {
+ set mldir ""
+ regexp -- "\[a-z0-9=_/\.-\]*;" $i mldir
+ set mldir [string trimright $mldir "\;@"]
+ if { "$mldir" == "." } {
+ continue
+ }
+ if { [llength [glob -nocomplain ${gccdir}/${mldir}/libgcc_s*.so.*]] >= 1 } {
+ append ld_library_path ":${gccdir}/${mldir}"
+ }
+ }
}
}
# add the library path for libffi.
append ld_library_path ":${blddirffi}/.libs"
- # add the library path for libstdc++ as well.
- append ld_library_path ":${blddircxx}/src/.libs"
verbose "ld_library_path: $ld_library_path"
set libffi_dir "${blddirffi}/.libs"
verbose "libffi_dir $libffi_dir"
if { $libffi_dir != "" } {
- set libffi_dir [file dirname ${libffi_dir}]
- if [istarget *-*-darwin*] {
- set libffi_link_flags "-B${libffi_dir}/.libs"
- lappend libffi_link_flags "-B${blddircxx}/src/.libs"
- } else {
- set libffi_link_flags "-L${libffi_dir}/.libs"
- lappend libffi_link_flags "-L${blddircxx}/src/.libs"
- }
+ set libffi_dir [file dirname ${libffi_dir}]
+ set libffi_link_flags "-L ../.libs"
}
set_ld_library_path_env_vars
global gluefile;
if [info exists gluefile] {
- file_on_build delete $gluefile;
- unset gluefile;
+ file_on_build delete $gluefile;
+ unset gluefile;
}
}
global compiler_vendor
if { [target_info needs_status_wrapper]!="" && [info exists gluefile] } {
- lappend options "libs=${gluefile}"
- lappend options "ldflags=$wrap_flags"
- }
-
- if { $blddirffi != "" } {
- # If '--with-build-sysroot=[...]' was specified, use it for build-tree
- # testing.
- global SYSROOT_CFLAGS_FOR_TARGET
- lappend options "additional_flags=${SYSROOT_CFLAGS_FOR_TARGET}"
+ lappend options "libs=${gluefile}"
+ lappend options "ldflags=$wrap_flags"
}
# TOOL_OPTIONS must come first, so that it doesn't override testcase
# specific options.
if [info exists TOOL_OPTIONS] {
- lappend options "additional_flags=$TOOL_OPTIONS"
+ lappend options "additional_flags=$TOOL_OPTIONS"
}
# search for ffi_mips.h in srcdir, too
- lappend options "additional_flags=-I${libffi_include} -I${srcdir}/../include -I${libffi_include}/.."
- lappend options "additional_flags=${libffi_link_flags}"
-
- # Darwin needs a stack execution allowed flag.
+ # lappend options "additional_flags=-I${libffi_include} -I${srcdir}/../include -I${libffi_include}/.."
+ lappend options "additional_flags=-I ../include -I ${srcdir}/../include -I .."
+ if { [string match $type "executable"] } {
+
+ lappend options "additional_flags=${libffi_link_flags}"
+
+ # Darwin needs a stack execution allowed flag.
+ if { [istarget "*-*-darwin9*"] || [istarget "*-*-darwin1*"]
+ || [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 { [istarget "*-*-darwin9*"] || [istarget "*-*-darwin1*"]
- || [istarget "x86_64-*-darwin2*"] } {
- lappend options "additional_flags=-Wl,-allow_stack_execute"
- lappend options "additional_flags=-Wl,-search_paths_first"
- }
+ # If you're building the compiler with --prefix set to a place
+ # where it's not yet installed, then the linker won't be able to
+ # find the libgcc used by libffi.dylib. We could pass the
+ # -dylib_file option, but that's complicated, and it's much easier
+ # to just make the linker find libgcc using -L options.
+ if { [string match "*-*-darwin*" $target_triplet] } {
+ lappend options "libs= -shared-libgcc"
+ }
- # If you're building the compiler with --prefix set to a place
- # where it's not yet installed, then the linker won't be able to
- # find the libgcc used by libffi.dylib. We could pass the
- # -dylib_file option, but that's complicated, and it's much easier
- # to just make the linker find libgcc using -L options.
- if { [string match "*-*-darwin*" $target_triplet] } {
- lappend options "libs= -shared-libgcc"
- }
+ if { [string match "*-*-openbsd*" $target_triplet] } {
+ lappend options "libs= -lpthread"
+ }
- if { [string match "*-*-openbsd*" $target_triplet] } {
- lappend options "libs= -lpthread"
- }
+ lappend options "libs= -lffi"
- lappend options "libs= -lffi"
+ if { [string match "aarch64*-*-linux*" $target_triplet] } {
+ if { ! [string match "*android*" $target_triplet] } {
+ lappend options "libs= -lpthread"
+ }
+ }
- if { [string match "aarch64*-*-linux*" $target_triplet] } {
- lappend options "libs= -lpthread"
- }
+ # this may be required for g++, but just confused clang.
+ if { [string match "*.cc" $source] } {
+ lappend options "c++"
+ if { [string match "*-*-darwin*" $target_triplet] } {
+ if { [string match $compiler_vendor "gnu"] } {
+ lappend options "libs= -lc++"
+ }
+ } elseif { [string match "*android*" $target_triplet] } {
+ lappend options "libs= -lc++"
+ }
+ }
- if { [string match "*.cc" $source] } {
- lappend options "ldflags=-shared-libgcc -lstdc++"
+ if { [string match "arc*-*-linux*" $target_triplet] } {
+ lappend options "libs= -lpthread"
+ }
}
- if { [string match "arc*-*-linux*" $target_triplet] } {
- lappend options "libs= -lpthread"
+ # 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"
+ lappend options "libs= -lpthread"
}
verbose "options: $options"
puts $f "#else"
puts $f "# error Failed $test"
puts $f "#endif"
+ puts $f "int main() {return 0;}"
close $f
- set lines [libffi_target_compile $src /dev/null assembly ""]
+ set lines [libffi_target_compile $src $src.i preprocess ""]
file delete $src
+ file delete $src.i
return [string match "" $lines]
}
proc search_for { file pattern } {
set fd [open $file r]
while { [gets $fd cur_line]>=0 } {
- if [string match "*$pattern*" $cur_line] then {
- close $fd
- return 1
- }
+ if [string match "*$pattern*" $cur_line] then {
+ close $fd
+ return 1
+ }
}
close $fd
return 0
global runtests
foreach test $testcases {
- # If we're only testing specific files and this isn't one of
- # them, skip it.
- if ![runtest_file_p $runtests $test] {
- continue
- }
-
- # Look for a loop within the source code - if we don't find one,
- # don't pass -funroll[-all]-loops.
- global torture_with_loops torture_without_loops
- if [expr [search_for $test "for*("]+[search_for $test "while*("]] {
- set option_list $torture_with_loops
- } else {
- set option_list $torture_without_loops
- }
-
- set nshort [file tail [file dirname $test]]/[file tail $test]
-
- foreach flags $option_list {
- verbose "Testing $nshort, $flags" 1
- dg-test $test $flags ${default-extra-flags}
- }
+ # If we're only testing specific files and this isn't one of
+ # them, skip it.
+ if ![runtest_file_p $runtests $test] {
+ continue
+ }
+
+ # Look for a loop within the source code - if we don't find one,
+ # don't pass -funroll[-all]-loops.
+ global torture_with_loops torture_without_loops
+ if [expr [search_for $test "for*("]+[search_for $test "while*("]] {
+ set option_list $torture_with_loops
+ } else {
+ set option_list $torture_without_loops
+ }
+
+ set nshort [file tail [file dirname $test]]/[file tail $test]
+
+ foreach flags $option_list {
+ verbose "Testing $nshort, $flags" 1
+ dg-test $test $flags ${default-extra-flags}
+ }
}
}
global has_gccbug
global env
switch $compiler_vendor {
- "clang" {
- set common "-W -Wall"
- if [info exists env(LIBFFI_TEST_OPTIMIZATION)] {
- set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ]
- } else {
- set optimizations { "-O0" "-O2" }
+ "clang" {
+ set common "-W -Wall"
+ if [info exists env(LIBFFI_TEST_OPTIMIZATION)] {
+ set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ]
+ } else {
+ set optimizations { "-O0" "-O2" }
+ }
}
- }
- "gnu" {
- set common "-W -Wall -Wno-psabi"
- if [info exists env(LIBFFI_TEST_OPTIMIZATION)] {
- set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ]
- } else {
- set optimizations { "-O0" "-O2" }
+ "gnu" {
+ set common "-W -Wall -Wno-psabi -fno-diagnostics-color"
+ if [info exists env(LIBFFI_TEST_OPTIMIZATION)] {
+ set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ]
+ } else {
+ set optimizations { "-O0" "-O2" }
+ }
}
- }
- default {
- # Assume we are using the vendor compiler.
- set common ""
- if [info exists env(LIBFFI_TEST_OPTIMIZATION)] {
- set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ]
- } else {
- set optimizations { "" }
+ default {
+ # Assume we are using the vendor compiler.
+ set common ""
+ if [info exists env(LIBFFI_TEST_OPTIMIZATION)] {
+ set optimizations [ list $env(LIBFFI_TEST_OPTIMIZATION) ]
+ } else {
+ set optimizations { "" }
+ }
}
- }
}
info exists env(LD_LIBRARY_PATH)
"-DABI_NUM=FFI_FASTCALL -DABI_ATTR=__FASTCALL__"
}
} elseif { [istarget "x86_64-*-*"] \
- && [libffi_feature_test "#if !defined __ILP32__ \
+ && [libffi_feature_test "#if !defined __ILP32__ \
&& !defined __i386__"] } {
set targetabis {
""
foreach opt $optimizations {
foreach abi $abis {
set options [concat $common $opt $abi]
- set has_gccbug false;
- if { [string match $compiler_vendor "gnu"] \
- && [string match "*MSABI*" $abi] \
- && ( ( [string match "*DGTEST=57 *" $common] \
- && [string match "*call.c*" $testname] ) \
- || ( [string match "*DGTEST=54 *" $common] \
- && [string match "*callback*" $testname] ) \
- || [string match "*DGTEST=55 *" $common] \
- || [string match "*DGTEST=56 *" $common] ) } then {
- if [libffi_feature_test "#if (__GNUC__ < 9) || ((__GNUC__ == 9) && (__GNUC_MINOR__ < 3))"] {
- set has_gccbug true;
- }
- }
- verbose "Testing $testname, $options" 1
- verbose "has_gccbug = $has_gccbug" 1
- dg-test $test $options ""
+ set has_gccbug false;
+ if { [string match $compiler_vendor "gnu"] \
+ && [string match "*MSABI*" $abi] \
+ && ( ( [string match "*DGTEST=57 *" $common] \
+ && [string match "*call.c*" $testname] ) \
+ || ( [string match "*DGTEST=54 *" $common] \
+ && [string match "*callback*" $testname] ) \
+ || [string match "*DGTEST=55 *" $common] \
+ || [string match "*DGTEST=56 *" $common] ) } then {
+ if [libffi_feature_test "#if (__GNUC__ < 9) || ((__GNUC__ == 9) && (__GNUC_MINOR__ < 3))"] {
+ set has_gccbug true;
+ }
+ }
+ verbose "Testing $testname, $options" 1
+ verbose "has_gccbug = $has_gccbug" 1
+ dg-test $test $options ""
}
}
}
set args [lreplace $args 0 0]
set selector "target [join [lindex $args 1]]"
if { [dg-process-target $selector] == "S" } {
- global compiler_conditional_xfail_data
- set compiler_conditional_xfail_data $args
+ global compiler_conditional_xfail_data
+ set compiler_conditional_xfail_data $args
}
}
# The next two arguments are optional. If they were not specified,
# use the defaults.
if { [llength $args] == 2 } {
- lappend $args [list "*"]
+ lappend $args [list "*"]
}
if { [llength $args] == 3 } {
- lappend $args [list ""]
+ lappend $args [list ""]
}
# If the option strings are the defaults, or the same as the
# defaults, there is no need to call check_conditional_xfail to
# compare them to the actual options.
if { [string compare [lindex $args 2] "*"] == 0
- && [string compare [lindex $args 3] "" ] == 0 } {
- set result 1
+ && [string compare [lindex $args 3] "" ] == 0 } {
+ set result 1
} else {
- # The target list might be an effective-target keyword, so replace
- # the original list with "*-*-*", since we already know it matches.
- set result [check_conditional_xfail [lreplace $args 1 1 "*-*-*"]]
+ # The target list might be an effective-target keyword, so replace
+ # the original list with "*-*-*", since we already know it matches.
+ set result [check_conditional_xfail [lreplace $args 1 1 "*-*-*"]]
}
return $result
# Don't bother if we're already skipping the test.
upvar dg-do-what dg-do-what
if { [lindex ${dg-do-what} 1] == "N" } {
- return
+ return
}
set selector [list target [lindex $args 1]]
rename dg-test saved-dg-test
proc dg-test { args } {
- global additional_files
- global additional_sources
- global errorInfo
-
- if { [ catch { eval saved-dg-test $args } errmsg ] } {
- set saved_info $errorInfo
- set additional_files ""
- set additional_sources ""
- error $errmsg $saved_info
- }
- set additional_files ""
- set additional_sources ""
+ global additional_files
+ global additional_sources
+ global errorInfo
+
+ if { [ catch { eval saved-dg-test $args } errmsg ] } {
+ set saved_info $errorInfo
+ set additional_files ""
+ set additional_sources ""
+ error $errmsg $saved_info
+ }
+ set additional_files ""
+ set additional_sources ""
}
}
} else {
setenv DYLD_LIBRARY_PATH "$ld_library_path"
}
- if { [istarget *-*-cygwin*] || [istarget *-*-mingw*] } {
+ if { [istarget *-*-cygwin*] || [ istarget *-*-msys* ] || [istarget *-*-mingw*] } {
if { $orig_path_saved } {
setenv PATH "$ld_library_path:$orig_path"
} else {
if { [ istarget *-*-darwin* ] } {
set shlib_ext "dylib"
- } elseif { [ istarget *-*-cygwin* ] || [ istarget *-*-mingw* ] } {
+ } elseif { [ istarget *-*-cygwin* ] || [ istarget *-*-msys* ] || [ istarget *-*-mingw* ] } {
set shlib_ext "dll"
} elseif { [ istarget hppa*-*-hpux* ] } {
set shlib_ext "sl"
case 3:
vsprintf(&rbuf2[strlen(rbuf2)], format, args);
printf("%s", rbuf2);
+ fflush (stdout);
if (strcmp (rbuf1, rbuf2)) abort();
break;
}
#include <stdio.h>
-FILE* out;
+FILE* out = NULL;
#define uchar unsigned char
#define ushort unsigned short
--- /dev/null
+/* Area: bpo-38748
+ Purpose: test for stdcall alignment problem
+ Source: github.com/python/cpython/pull/26204 */
+
+/* { dg-do run } */
+
+#include "ffitest.h"
+#include "ffi_common.h"
+
+static UINT32 ABI_ATTR align_arguments(UINT32 l1,
+ UINT64 l2)
+{
+ return l1 + (UINT32) l2;
+}
+
+int main(void)
+{
+ ffi_cif cif;
+ ffi_type *args[4] = {
+ &ffi_type_uint32,
+ &ffi_type_uint64
+ };
+ ffi_arg lr1, lr2;
+ UINT32 l1 = 1;
+ UINT64 l2 = 2;
+ void *values[2] = {&l1, &l2};
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 2,
+ &ffi_type_uint32, args) == FFI_OK);
+
+ lr1 = align_arguments(l1, l2);
+
+ ffi_call(&cif, FFI_FN(align_arguments), &lr2, values);
+
+ if (lr1 == lr2)
+ printf("bpo-38748 arguments tests ok!\n");
+ else
+ CHECK(0);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures with array and callback.
+ Limitations: none.
+ PR: none.
+ Originator: David Tenty <daltenty@ibm.com> */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+static int i=5;
+
+static void callback(void) { i++; }
+
+typedef struct
+{
+ unsigned char c1;
+ double s[2];
+ unsigned char c2;
+} test_structure_12;
+
+static test_structure_12 ABI_ATTR struct12 (test_structure_12 ts, void (*func)(void))
+{
+ ts.c1 += 1;
+ ts.c2 += 1;
+ ts.s[0] += 1;
+ ts.s[1] += 1;
+
+ func();
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts12_type,ts12a_type;
+ ffi_type *ts12_type_elements[4];
+ ffi_type *ts12a_type_elements[3];
+
+ test_structure_12 ts12_arg;
+ void (*ptr)(void)=&callback;
+
+ test_structure_12 *ts12_result =
+ (test_structure_12 *) malloc (sizeof(test_structure_12));
+
+ ts12a_type.size = 0;
+ ts12a_type.alignment = 0;
+ ts12a_type.type = FFI_TYPE_STRUCT;
+ ts12a_type.elements = ts12a_type_elements;
+ ts12a_type_elements[0] = &ffi_type_double;
+ ts12a_type_elements[1] = &ffi_type_double;
+ ts12a_type_elements[2] = NULL;
+
+ ts12_type.size = 0;
+ ts12_type.alignment = 0;
+ ts12_type.type = FFI_TYPE_STRUCT;
+ ts12_type.elements = ts12_type_elements;
+ ts12_type_elements[0] = &ffi_type_uchar;
+ ts12_type_elements[1] = &ts12a_type;
+ ts12_type_elements[2] = &ffi_type_uchar;
+ ts12_type_elements[3] = NULL;
+
+
+ args[0] = &ts12_type;
+ args[1] = &ffi_type_pointer;
+ values[0] = &ts12_arg;
+ values[1] = &ptr;
+
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 2, &ts12_type, args) == FFI_OK);
+
+ ts12_arg.c1 = 5;
+ ts12_arg.c2 = 6;
+ ts12_arg.s[0] = 7.77;
+ ts12_arg.s[1] = 8.88;
+
+ printf ("%u\n", ts12_arg.c1);
+ printf ("%u\n", ts12_arg.c2);
+ printf ("%g\n", ts12_arg.s[0]);
+ printf ("%g\n", ts12_arg.s[1]);
+ printf ("%d\n", i);
+
+ ffi_call(&cif, FFI_FN(struct12), ts12_result, values);
+
+ printf ("%u\n", ts12_result->c1);
+ printf ("%u\n", ts12_result->c2);
+ printf ("%g\n", ts12_result->s[0]);
+ printf ("%g\n", ts12_result->s[1]);
+ printf ("%d\n", i);
+ CHECK(ts12_result->c1 == 5 + 1);
+ CHECK(ts12_result->c2 == 6 + 1);
+ CHECK(ts12_result->s[0] == 7.77 + 1);
+ CHECK(ts12_result->s[1] == 8.88 + 1);
+ CHECK(i == 5 + 1);
+ CHECK(ts12_type.size == sizeof(test_structure_12));
+
+ free (ts12_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures with nested array and callback.
+ Limitations: none.
+ PR: none.
+ Originator: David Tenty <daltenty@ibm.com> */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+static int i=5;
+
+static void callback(void) { i++; }
+
+typedef struct
+{
+ struct { double d; } s1;
+ double s[2];
+ unsigned char c2;
+} test_structure_12;
+
+static test_structure_12 ABI_ATTR struct12 (test_structure_12 ts, void (*func)(void))
+{
+ ts.s1.d += 1;
+ ts.c2 += 1;
+ ts.s[0] += 1;
+ ts.s[1] += 1;
+
+ func();
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts12_type,ts12a_type, ts12b_type;
+ ffi_type *ts12_type_elements[4];
+ ffi_type *ts12a_type_elements[2];
+ ffi_type *ts12b_type_elements[3];
+
+
+ test_structure_12 ts12_arg;
+ void (*ptr)(void)=&callback;
+
+ test_structure_12 *ts12_result =
+ (test_structure_12 *) malloc (sizeof(test_structure_12));
+
+ ts12a_type.size = 0;
+ ts12a_type.alignment = 0;
+ ts12a_type.type = FFI_TYPE_STRUCT;
+ ts12a_type.elements = ts12a_type_elements;
+ ts12a_type_elements[0] = &ffi_type_double;
+ ts12a_type_elements[1] = NULL;
+
+ ts12b_type.size = 0;
+ ts12b_type.alignment = 0;
+ ts12b_type.type = FFI_TYPE_STRUCT;
+ ts12b_type.elements = ts12b_type_elements;
+ ts12b_type_elements[0] = &ffi_type_double;
+ ts12b_type_elements[1] = &ffi_type_double;
+ ts12b_type_elements[2] = NULL;
+
+ ts12_type.size = 0;
+ ts12_type.alignment = 0;
+ ts12_type.type = FFI_TYPE_STRUCT;
+ ts12_type.elements = ts12_type_elements;
+ ts12_type_elements[0] = &ts12a_type;
+ ts12_type_elements[1] = &ts12b_type;
+ ts12_type_elements[2] = &ffi_type_uchar;
+ ts12_type_elements[3] = NULL;
+
+
+ args[0] = &ts12_type;
+ args[1] = &ffi_type_pointer;
+ values[0] = &ts12_arg;
+ values[1] = &ptr;
+
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 2, &ts12_type, args) == FFI_OK);
+
+ ts12_arg.s1.d = 5.55;
+ ts12_arg.c2 = 6;
+ ts12_arg.s[0] = 7.77;
+ ts12_arg.s[1] = 8.88;
+
+ printf ("%g\n", ts12_arg.s1.d);
+ printf ("%u\n", ts12_arg.c2);
+ printf ("%g\n", ts12_arg.s[0]);
+ printf ("%g\n", ts12_arg.s[1]);
+ printf ("%d\n", i);
+
+ ffi_call(&cif, FFI_FN(struct12), ts12_result, values);
+
+ printf ("%g\n", ts12_result->s1.d);
+ printf ("%u\n", ts12_result->c2);
+ printf ("%g\n", ts12_result->s[0]);
+ printf ("%g\n", ts12_result->s[1]);
+ printf ("%d\n", i);
+ CHECK(ts12_result->s1.d == 5.55 + 1);
+ CHECK(ts12_result->c2 == 6 + 1);
+ CHECK(ts12_result->s[0] == 7.77 + 1);
+ CHECK(ts12_result->s[1] == 8.88 + 1);
+ CHECK(i == 5 + 1);
+ CHECK(ts12_type.size == sizeof(test_structure_12));
+
+ free (ts12_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures with array and callback.
+ Limitations: none.
+ PR: none.
+ Originator: David Tenty <daltenty@ibm.com> */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+static int i=5;
+
+static void callback(void) { i++; }
+
+
+typedef struct
+{
+ struct { unsigned char c; double d; } s1;
+ double s[2];
+ unsigned char c2;
+} test_structure_12;
+
+static test_structure_12 ABI_ATTR struct12 (test_structure_12 ts, void (*func)(void))
+{
+ ts.s1.c += 1;
+ ts.s1.d += 1;
+ ts.c2 += 1;
+ ts.s[0] += 1;
+ ts.s[1] += 1;
+
+ func();
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts12_type,ts12b_type, ts12a_type;
+ ffi_type *ts12_type_elements[4];
+ ffi_type *ts12b_type_elements[3];
+ ffi_type *ts12a_type_elements[3];
+
+ test_structure_12 ts12_arg;
+ void (*ptr)(void)=&callback;
+
+ test_structure_12 *ts12_result =
+ (test_structure_12 *) malloc (sizeof(test_structure_12));
+
+ ts12a_type.size = 0;
+ ts12a_type.alignment = 0;
+ ts12a_type.type = FFI_TYPE_STRUCT;
+ ts12a_type.elements = ts12a_type_elements;
+ ts12a_type_elements[0] = &ffi_type_uchar;
+ ts12a_type_elements[1] = &ffi_type_double;
+ ts12a_type_elements[2] = NULL;
+
+ ts12b_type.size = 0;
+ ts12b_type.alignment = 0;
+ ts12b_type.type = FFI_TYPE_STRUCT;
+ ts12b_type.elements = ts12b_type_elements;
+ ts12b_type_elements[0] = &ffi_type_double;
+ ts12b_type_elements[1] = &ffi_type_double;
+ ts12b_type_elements[2] = NULL;
+
+ ts12_type.size = 0;
+ ts12_type.alignment = 0;
+ ts12_type.type = FFI_TYPE_STRUCT;
+ ts12_type.elements = ts12_type_elements;
+ ts12_type_elements[0] = &ts12a_type;
+ ts12_type_elements[1] = &ts12b_type;
+ ts12_type_elements[2] = &ffi_type_uchar;
+ ts12_type_elements[3] = NULL;
+
+
+ args[0] = &ts12_type;
+ args[1] = &ffi_type_pointer;
+ values[0] = &ts12_arg;
+ values[1] = &ptr;
+
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 2, &ts12_type, args) == FFI_OK);
+
+ ts12_arg.s1.c = 5;
+ ts12_arg.s1.d = 5.55;
+ ts12_arg.c2 = 6;
+ ts12_arg.s[0] = 7.77;
+ ts12_arg.s[1] = 8.88;
+
+ printf ("%d\n", ts12_arg.s1.c);
+ printf ("%g\n", ts12_arg.s1.d);
+ printf ("%u\n", ts12_arg.c2);
+ printf ("%g\n", ts12_arg.s[0]);
+ printf ("%g\n", ts12_arg.s[1]);
+ printf ("%d\n", i);
+
+ ffi_call(&cif, FFI_FN(struct12), ts12_result, values);
+
+ printf ("%d\n", ts12_result->s1.c);
+ printf ("%g\n", ts12_result->s1.d);
+ printf ("%u\n", ts12_result->c2);
+ printf ("%g\n", ts12_result->s[0]);
+ printf ("%g\n", ts12_result->s[1]);
+ printf ("%d\n", i);
+ CHECK(ts12_result->s1.c == 5 + 1);
+ CHECK(ts12_result->s1.d == 5.55 + 1);
+ CHECK(ts12_result->c2 == 6 + 1);
+ CHECK(ts12_result->s[0] == 7.77 + 1);
+ CHECK(ts12_result->s[1] == 8.88 + 1);
+ CHECK(i == 5 + 1);
+ CHECK(ts12_type.size == sizeof(test_structure_12));
+
+ free (ts12_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures with array and callback.
+ Limitations: none.
+ PR: none.
+ Originator: David Tenty <daltenty@ibm.com> */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+static int i=5;
+
+static void callback(void) { i++; }
+
+
+typedef struct
+{
+ unsigned char c1;
+ struct { double d; unsigned char c; } s[2];
+ unsigned char c2;
+} test_structure_12;
+
+static test_structure_12 ABI_ATTR struct12 (test_structure_12 ts, void (*func)(void))
+{
+ ts.c1 += 1;
+ ts.s[0].d += 1;
+ ts.s[0].c += 1;
+ ts.s[1].d += 1;
+ ts.s[1].c += 1;
+ ts.c2 += 1;
+
+ func();
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts12_type,ts12b_type, ts12a_type;
+ ffi_type *ts12_type_elements[4];
+ ffi_type *ts12b_type_elements[3];
+ ffi_type *ts12a_type_elements[3];
+
+ test_structure_12 ts12_arg;
+ void (*ptr)(void)=&callback;
+
+ test_structure_12 *ts12_result =
+ (test_structure_12 *) malloc (sizeof(test_structure_12));
+
+ ts12a_type.size = 0;
+ ts12a_type.alignment = 0;
+ ts12a_type.type = FFI_TYPE_STRUCT;
+ ts12a_type.elements = ts12a_type_elements;
+ ts12a_type_elements[0] = &ffi_type_double;
+ ts12a_type_elements[1] = &ffi_type_uchar;
+ ts12a_type_elements[2] = NULL;
+
+ ts12b_type.size = 0;
+ ts12b_type.alignment = 0;
+ ts12b_type.type = FFI_TYPE_STRUCT;
+ ts12b_type.elements = ts12b_type_elements;
+ ts12b_type_elements[0] = &ts12a_type;
+ ts12b_type_elements[1] = &ts12a_type;
+ ts12b_type_elements[2] = NULL;
+
+ ts12_type.size = 0;
+ ts12_type.alignment = 0;
+ ts12_type.type = FFI_TYPE_STRUCT;
+ ts12_type.elements = ts12_type_elements;
+ ts12_type_elements[0] = &ffi_type_uchar;
+ ts12_type_elements[1] = &ts12b_type;
+ ts12_type_elements[2] = &ffi_type_uchar;
+ ts12_type_elements[3] = NULL;
+
+
+ args[0] = &ts12_type;
+ args[1] = &ffi_type_pointer;
+ values[0] = &ts12_arg;
+ values[1] = &ptr;
+
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 2, &ts12_type, args) == FFI_OK);
+
+ ts12_arg.c1 = 5;
+ ts12_arg.s[0].d = 5.55;
+ ts12_arg.s[0].c = 6;
+ ts12_arg.s[1].d = 7.77;
+ ts12_arg.s[1].c = 8;
+ ts12_arg.c2 = 9;
+
+ printf ("%u\n", ts12_arg.c1);
+ printf ("%g\n", ts12_arg.s[0].d);
+ printf ("%u\n", ts12_arg.s[0].c);
+ printf ("%g\n", ts12_arg.s[1].d);
+ printf ("%u\n", ts12_arg.s[1].c);
+ printf ("%u\n", ts12_arg.c2);
+ printf ("%d\n", i);
+
+ ffi_call(&cif, FFI_FN(struct12), ts12_result, values);
+
+ printf ("%u\n", ts12_result->c1);
+ printf ("%g\n", ts12_result->s[0].d);
+ printf ("%u\n", ts12_result->s[0].c);
+ printf ("%g\n", ts12_result->s[1].d);
+ printf ("%u\n", ts12_result->s[1].c);
+ printf ("%u\n", ts12_result->c2);
+ printf ("%d\n", i);
+ CHECK(ts12_result->c1 == 5 + 1);
+ CHECK(ts12_result->s[0].d == 5.55 + 1);
+ CHECK(ts12_result->s[0].c == 6 + 1);
+ CHECK(ts12_result->s[1].d == 7.77 + 1);
+ CHECK(ts12_result->s[1].c == 8 + 1);
+ CHECK(ts12_result->c2 == 9 + 1);
+ CHECK(i == 5 + 1);
+ CHECK(ts12_type.size == sizeof(test_structure_12));
+
+ free (ts12_result);
+ exit(0);
+}
+#undef __USE_MINGW_ANSI_STDIO
+#define __USE_MINGW_ANSI_STDIO 1
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ffi.h>
#include "fficonfig.h"
+#include <float.h>
+#include <math.h>
+
#if defined HAVE_STDINT_H
#include <stdint.h>
#endif
} \
} while(0)
+#define CHECK_FLOAT_EQ(x, y) \
+ do { \
+ if(fabs((x) - (y)) > FLT_EPSILON){ \
+ printf("Check failed CHECK_FLOAT_EQ(%s, %s)\n", #x, #y); \
+ abort(); \
+ } \
+ } while(0)
+
+#define CHECK_DOUBLE_EQ(x, y) \
+ do { \
+ if(fabs((x) - (y)) > DBL_EPSILON){ \
+ printf("Check failed CHECK_FLOAT_EQ(%s, %s)\n", #x, #y); \
+ abort(); \
+ } \
+ } while(0)
+
/* Define macros so that compilers other than gcc can run the tests. */
#undef __UNUSED__
#if defined(__GNUC__)
#endif
-/* MinGW kludge. */
-#if defined(_WIN64) | defined(_WIN32)
+/* msvc kludge. */
+#if defined(_MSC_VER)
#define PRIdLL "I64d"
#define PRIuLL "I64u"
#else
/* { dg-output "\n0: 2.0 : total: 2.0" } */
printf("ffi: %.1f\n", resfp);
/* { dg-output "\nffi: 2.0" } */
+ CHECK_DOUBLE_EQ(resfp, 2);
/* Second test, float_va_fn(2,2.0,3.0,4.0), now with variadic params */
/* Call it statically and then via ffi */
/* { dg-output "\n2: 2.0 : 0:3.0 1:4.0 total: 11.0" } */
printf("compiled: %.1f\n", resfp);
/* { dg-output "\ncompiled: 11.0" } */
+ CHECK_DOUBLE_EQ(resfp, 11);
arg_types[0] = &ffi_type_uint;
arg_types[1] = &ffi_type_double;
/* { dg-output "\n2: 2.0 : 0:3.0 1:4.0 total: 11.0" } */
printf("ffi: %.1f\n", resfp);
/* { dg-output "\nffi: 11.0" } */
+ CHECK_DOUBLE_EQ(resfp, 11);
exit(0);
}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Test longjmp over ffi_call frames */
+
+/* Test code adapted from Lars Kanis' bug report:
+ https://github.com/libffi/libffi/issues/905 */
+
+/* { dg-do run } */
+
+#include "ffitest.h"
+#include "ffi_common.h"
+
+#include <setjmp.h>
+
+static jmp_buf buf;
+
+static void ABI_ATTR lev2(const char *str) {
+ printf("lev2 %s\n", str);
+ // jumps back to where setjmp was called - making setjmp now return 1
+ longjmp(buf, 1);
+}
+
+static void ABI_ATTR lev1(const char *str) {
+ lev2(str);
+
+ // will not be reached
+ printf("lev1 %s\n", str);
+}
+
+int main()
+{
+ ffi_cif cif;
+ ffi_type *args[1];
+ void *values[1];
+ char *s;
+ ffi_arg rc;
+ /* Initialize the argument info vectors */
+ args[0] = &ffi_type_pointer;
+ values[0] = &s;
+ /* Initialize the cif */
+ if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1,
+ &ffi_type_sint, args) == FFI_OK)
+ {
+ s = "direct call";
+ if (!setjmp(buf)){
+ // works on x64 and arm64
+ lev1(s);
+ } else {
+ printf("back to main\n");
+ }
+
+ s = "through libffi";
+ if (!setjmp(buf)){
+ // works on x64 but segfaults on arm64
+ ffi_call(&cif, (void (*)(void))lev1, &rc, values);
+ } else {
+ printf("back to main\n");
+ }
+ }
+ return 0;
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Tests if ffi_call reads data beyond end.
+ Limitations: needs mmap.
+ PR: 887
+ Originator: Mikulas Patocka <mikulas@twibright.com> */
+
+/* { dg-do run } */
+
+#include "ffitest.h"
+
+#ifdef __linux__
+#include <sys/mman.h>
+#include <unistd.h>
+
+static int ABI_ATTR fn(unsigned char a, unsigned short b, unsigned int c, unsigned long d)
+{
+ return (int)(a + b + c + d);
+}
+#endif
+
+int main(void)
+{
+#ifdef __linux__
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_arg rint;
+ char *m;
+ int ps;
+ args[0] = &ffi_type_uchar;
+ args[1] = &ffi_type_ushort;
+ args[2] = &ffi_type_uint;
+ args[3] = &ffi_type_ulong;
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 4, &ffi_type_sint, args) == FFI_OK);
+ ps = getpagesize();
+ m = mmap(NULL, ps * 3, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ CHECK(m != MAP_FAILED);
+ CHECK(mprotect(m, ps, PROT_NONE) == 0);
+ CHECK(mprotect(m + ps * 2, ps, PROT_NONE) == 0);
+ values[0] = m + ps * 2 - sizeof(unsigned char);
+ values[1] = m + ps * 2 - sizeof(unsigned short);
+ values[2] = m + ps * 2 - sizeof(unsigned int);
+ values[3] = m + ps * 2 - sizeof(unsigned long);
+ ffi_call(&cif, FFI_FN(fn), &rint, values);
+ CHECK((int)rint == 0);
+ values[0] = m + ps;
+ values[1] = m + ps;
+ values[2] = m + ps;
+ values[3] = m + ps;
+ ffi_call(&cif, FFI_FN(fn), &rint, values);
+ CHECK((int)rint == 0);
+#endif
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check different structures.
+ Limitations: none.
+ PR: none.
+ Originator: Ronald Oussoren <oussoren@cistron.nl> 20030824 */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct Point {
+ float x;
+ float y;
+} Point;
+
+typedef struct Size {
+ float h;
+ float w;
+} Size;
+
+typedef struct Rect {
+ Point o;
+ Size s;
+} Rect;
+
+int doit(int o, char* s, Point p, Rect r, int last)
+{
+ printf("CALLED WITH %d %s {%f %f} {{%f %f} {%f %f}} %d\n",
+ o, s, p.x, p.y, r.o.x, r.o.y, r.s.h, r.s.w, last);
+ return 42;
+}
+
+
+int main(void)
+{
+ ffi_type point_type;
+ ffi_type size_type;
+ ffi_type rect_type;
+ ffi_cif cif;
+ ffi_type* arglist[6];
+ void* values[6];
+ int r;
+
+ /*
+ * First set up FFI types for the 3 struct types
+ */
+
+ point_type.size = 0; /*sizeof(Point);*/
+ point_type.alignment = 0; /*__alignof__(Point);*/
+ point_type.type = FFI_TYPE_STRUCT;
+ point_type.elements = malloc(3 * sizeof(ffi_type*));
+ point_type.elements[0] = &ffi_type_float;
+ point_type.elements[1] = &ffi_type_float;
+ point_type.elements[2] = NULL;
+
+ size_type.size = 0;/* sizeof(Size);*/
+ size_type.alignment = 0;/* __alignof__(Size);*/
+ size_type.type = FFI_TYPE_STRUCT;
+ size_type.elements = malloc(3 * sizeof(ffi_type*));
+ size_type.elements[0] = &ffi_type_float;
+ size_type.elements[1] = &ffi_type_float;
+ size_type.elements[2] = NULL;
+
+ rect_type.size = 0;/*sizeof(Rect);*/
+ rect_type.alignment =0;/* __alignof__(Rect);*/
+ rect_type.type = FFI_TYPE_STRUCT;
+ rect_type.elements = malloc(3 * sizeof(ffi_type*));
+ rect_type.elements[0] = &point_type;
+ rect_type.elements[1] = &size_type;
+ rect_type.elements[2] = NULL;
+
+ /*
+ * Create a CIF
+ */
+ arglist[0] = &ffi_type_sint;
+ arglist[1] = &ffi_type_pointer;
+ arglist[2] = &point_type;
+ arglist[3] = &rect_type;
+ arglist[4] = &ffi_type_sint;
+ arglist[5] = NULL;
+
+ r = ffi_prep_cif(&cif, FFI_DEFAULT_ABI,
+ 5, &ffi_type_sint, arglist);
+ if (r != FFI_OK) {
+ abort();
+ }
+
+
+ /* And call the function through the CIF */
+
+ {
+ Point p = { 1.0, 2.0 };
+ Rect r = { { 9.0, 10.0}, { -1.0, -2.0 } };
+ int o = 0;
+ int l = 42;
+ char* m = "myMethod";
+ ffi_arg result;
+
+ values[0] = &o;
+ values[1] = &m;
+ values[2] = &p;
+ values[3] = &r;
+ values[4] = &l;
+ values[5] = NULL;
+
+ printf("CALLING WITH %d %s {%f %f} {{%f %f} {%f %f}} %d\n",
+ o, m, p.x, p.y, r.o.x, r.o.y, r.s.h, r.s.w, l);
+
+ ffi_call(&cif, FFI_FN(doit), &result, values);
+
+ printf ("The result is %d\n", (int)result);
+
+ }
+ exit(0);
+}
#include "ffitest.h"
static long long return_ll(int ll0, long long ll1, int ll2)
{
+ CHECK(ll0 == 11111111);
+ CHECK(ll1 == 11111111111000LL);
+ CHECK(ll2 == 11111111);
return ll0 + ll1 + ll2;
}
ffi_call(&cif, FFI_FN(return_ll), &rlonglong, values);
printf("res: %" PRIdLL ", %" PRIdLL "\n", rlonglong, ll0 + ll1 + ll2);
/* { dg-output "res: 11111133333222, 11111133333222" } */
+ CHECK(rlonglong == 11111133333222);
+ CHECK(ll0 + ll1 + ll2 == 11111133333222);
exit(0);
}
#include "ffitest.h"
static long return_sl(long l1, long l2)
{
+ CHECK(l1 == 1073741823L);
+ CHECK(l2 == 1073741824L);
return l1 - l2;
}
ffi_call(&cif, FFI_FN(return_sl), &res, values);
printf("res: %ld, %ld\n", (long)res, l1 - l2);
/* { dg-output "res: -1, -1" } */
+ CHECK((long)res == -1);
+ CHECK(l1 + 1 == l2);
exit(0);
}
#include "ffitest.h"
static unsigned long return_ul(unsigned long ul1, unsigned long ul2)
{
+ CHECK(ul1 == 1073741823L);
+ CHECK(ul2 == 1073741824L);
return ul1 + ul2;
}
ffi_call(&cif, FFI_FN(return_ul), &res, values);
printf("res: %lu, %lu\n", (unsigned long)res, ul1 + ul2);
/* { dg-output "res: 2147483647, 2147483647" } */
+ CHECK(res == 2147483647L);
+ CHECK(ul1 + ul2 == 2147483647L);
exit(0);
}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ float f;
+} s55;
+
+static s55 ABI_ATTR f55(s55 ts, float f)
+{
+ s55 r;
+ r.f = ts.f + f;
+ printf ("f55>> %g + %g = %g\n", ts.f, f, r.f);
+ return r;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ s55 F, Fr;
+ float f;
+ void *values[] = { &F, &f };
+ ffi_type s55_type;
+ ffi_type *args[] = { &s55_type, &ffi_type_float };
+ ffi_type *s55_type_elements[] = { &ffi_type_float, NULL };
+
+ /* This is a hack to get a properly aligned result buffer */
+ s55 *s55_result =
+ (s55 *) malloc (sizeof(s55));
+
+ s55_type.size = 0;
+ s55_type.alignment = 0;
+ s55_type.type = FFI_TYPE_STRUCT;
+ s55_type.elements = s55_type_elements;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 2, &s55_type, args) == FFI_OK);
+
+ F.f = 1;
+ Fr = f55(F, 2.14);
+ printf ("%g\n", Fr.f);
+
+ F.f = 1;
+ f = 2.14;
+ ffi_call(&cif, FFI_FN(f55), s55_result, values);
+ printf ("%g\n", s55_result->f);
+
+ fflush(0);
+
+ CHECK(fabs(Fr.f - s55_result->f) < FLT_EPSILON);
+
+ free (s55_result);
+ exit(0);
+}
/* { dg-do run } */
#include "ffitest.h"
-static size_t ABI_ATTR my_strlen(char *s)
+static unsigned int ABI_ATTR my_strlen(char *s)
{
- return (strlen(s));
+ return (unsigned int) (strlen(s));
}
int main (void)
args[0] = &ffi_type_pointer;
values[0] = (void*) &s;
-
+
/* Initialize the cif */
CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
- &ffi_type_sint, args) == FFI_OK);
-
+ &ffi_type_uint, args) == FFI_OK);
+
s = "a";
ffi_call(&cif, FFI_FN(my_strlen), &rint, values);
CHECK(rint == 1);
-
+
s = "1234567";
ffi_call(&cif, FFI_FN(my_strlen), &rint, values);
CHECK(rint == 7);
-
+
s = "1234567890123456789012345";
ffi_call(&cif, FFI_FN(my_strlen), &rint, values);
CHECK(rint == 25);
-
+
exit (0);
}
-
#include "ffitest.h"
-static size_t ABI_ATTR my_f(char *s, float a)
+static int ABI_ATTR my_f(char *s, float a)
{
- return (size_t) ((int) strlen(s) + (int) a);
+ return (int) strlen(s) + (int) a;
}
int main (void)
#include "ffitest.h"
-static size_t ABI_ATTR my_f(float a, char *s)
+static int ABI_ATTR my_f(float a, char *s)
{
- return (size_t) ((int) strlen(s) + (int) a);
+ return (int) strlen(s) + (int) a;
}
int main (void)
#include "ffitest.h"
-static size_t ABI_ATTR my_f(float a, char *s, int i)
+static int ABI_ATTR my_f(float a, char *s, int i)
{
- return (size_t) ((int) strlen(s) + (int) a + i);
+ return (int) strlen(s) + (int) a + i;
}
int main (void)
signed char s8;
};
-struct s make_s(void) {
+struct s ABI_ATTR make_s(void) {
struct s r;
r.s32 = 0x1234;
r.f32 = 7.0;
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ unsigned int ui01;
+ unsigned int ui02;
+} test_structure_1;
+
+static test_structure_1 ABI_ATTR struct1(test_structure_1 ts)
+{
+ ts.ui02++;
+
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts1_type;
+ ffi_type *ts1_type_elements[3];
+
+ test_structure_1 ts1_arg;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_1 *ts1_result =
+ (test_structure_1 *) malloc (sizeof(test_structure_1));
+
+ ts1_type.size = 0;
+ ts1_type.alignment = 0;
+ ts1_type.type = FFI_TYPE_STRUCT;
+ ts1_type.elements = ts1_type_elements;
+ ts1_type_elements[0] = &ffi_type_uint;
+ ts1_type_elements[1] = &ffi_type_uint;
+ ts1_type_elements[2] = NULL;
+
+ args[0] = &ts1_type;
+ values[0] = &ts1_arg;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
+ &ts1_type, args) == FFI_OK);
+
+ ts1_arg.ui02 = 555;
+
+ ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+ CHECK(ts1_result->ui02 == 556);
+
+ /* This will fail if ffi_call isn't passing the struct by value. */
+ CHECK(ts1_arg.ui02 == 555);
+
+ free (ts1_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ unsigned int ui01;
+ unsigned int ui02;
+ unsigned int ui03;
+} test_structure_1;
+
+static test_structure_1 ABI_ATTR struct1(test_structure_1 ts)
+{
+ ts.ui03++;
+
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts1_type;
+ ffi_type *ts1_type_elements[4];
+
+ test_structure_1 ts1_arg;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_1 *ts1_result =
+ (test_structure_1 *) malloc (sizeof(test_structure_1));
+
+ ts1_type.size = 0;
+ ts1_type.alignment = 0;
+ ts1_type.type = FFI_TYPE_STRUCT;
+ ts1_type.elements = ts1_type_elements;
+ ts1_type_elements[0] = &ffi_type_uint;
+ ts1_type_elements[1] = &ffi_type_uint;
+ ts1_type_elements[2] = &ffi_type_uint;
+ ts1_type_elements[3] = NULL;
+
+ args[0] = &ts1_type;
+ values[0] = &ts1_arg;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
+ &ts1_type, args) == FFI_OK);
+
+ ts1_arg.ui03 = 555;
+
+ ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+ CHECK(ts1_result->ui03 == 556);
+
+ /* This will fail if ffi_call isn't passing the struct by value. */
+ CHECK(ts1_arg.ui03 == 555);
+
+ free (ts1_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ float f01;
+ float f02;
+ float f03;
+} test_structure_1;
+
+static test_structure_1 ABI_ATTR struct1(test_structure_1 ts)
+{
+ ts.f03++;
+
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts1_type;
+ ffi_type *ts1_type_elements[5];
+
+ test_structure_1 ts1_arg;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_1 *ts1_result =
+ (test_structure_1 *) malloc (sizeof(test_structure_1));
+
+ ts1_type.size = 0;
+ ts1_type.alignment = 0;
+ ts1_type.type = FFI_TYPE_STRUCT;
+ ts1_type.elements = ts1_type_elements;
+ ts1_type_elements[0] = &ffi_type_float;
+ ts1_type_elements[1] = &ffi_type_float;
+ ts1_type_elements[2] = &ffi_type_float;
+ ts1_type_elements[3] = NULL;
+
+ args[0] = &ts1_type;
+ values[0] = &ts1_arg;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
+ &ts1_type, args) == FFI_OK);
+
+ ts1_arg.f03 = 555.5;
+
+ ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+ CHECK(fabs(ts1_result->f03 - 556.5) < FLT_EPSILON);
+
+ /* This will fail if ffi_call isn't passing the struct by value. */
+ CHECK(fabs(ts1_arg.f03 - 555.5) < FLT_EPSILON);
+
+ free (ts1_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ unsigned int ui01;
+ unsigned int ui02;
+ unsigned int ui03;
+ unsigned int ui04;
+} test_structure_1;
+
+static test_structure_1 ABI_ATTR struct1(test_structure_1 ts)
+{
+ ts.ui04++;
+
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts1_type;
+ ffi_type *ts1_type_elements[5];
+
+ test_structure_1 ts1_arg;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_1 *ts1_result =
+ (test_structure_1 *) malloc (sizeof(test_structure_1));
+
+ ts1_type.size = 0;
+ ts1_type.alignment = 0;
+ ts1_type.type = FFI_TYPE_STRUCT;
+ ts1_type.elements = ts1_type_elements;
+ ts1_type_elements[0] = &ffi_type_uint;
+ ts1_type_elements[1] = &ffi_type_uint;
+ ts1_type_elements[2] = &ffi_type_uint;
+ ts1_type_elements[3] = &ffi_type_uint;
+ ts1_type_elements[4] = NULL;
+
+ args[0] = &ts1_type;
+ values[0] = &ts1_arg;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
+ &ts1_type, args) == FFI_OK);
+
+ ts1_arg.ui04 = 555;
+
+ ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+ CHECK(ts1_result->ui04 == 556);
+
+ /* This will fail if ffi_call isn't passing the struct by value. */
+ CHECK(ts1_arg.ui04 == 555);
+
+ free (ts1_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ float f01;
+ float f02;
+ float f03;
+ float f04;
+} test_structure_1;
+
+static test_structure_1 ABI_ATTR struct1(test_structure_1 ts)
+{
+ ts.f04++;
+
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts1_type;
+ ffi_type *ts1_type_elements[5];
+
+ test_structure_1 ts1_arg;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_1 *ts1_result =
+ (test_structure_1 *) malloc (sizeof(test_structure_1));
+
+ ts1_type.size = 0;
+ ts1_type.alignment = 0;
+ ts1_type.type = FFI_TYPE_STRUCT;
+ ts1_type.elements = ts1_type_elements;
+ ts1_type_elements[0] = &ffi_type_float;
+ ts1_type_elements[1] = &ffi_type_float;
+ ts1_type_elements[2] = &ffi_type_float;
+ ts1_type_elements[3] = &ffi_type_float;
+ ts1_type_elements[4] = NULL;
+
+ args[0] = &ts1_type;
+ values[0] = &ts1_arg;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
+ &ts1_type, args) == FFI_OK);
+
+ ts1_arg.f04 = 555.5;
+
+ ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+ CHECK(fabs(ts1_result->f04 - 556.5) < FLT_EPSILON);
+
+ /* This will fail if ffi_call isn't passing the struct by value. */
+ CHECK(fabs(ts1_arg.f04 - 555.5) < FLT_EPSILON);
+
+ free (ts1_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ unsigned int ui01;
+ unsigned int ui02;
+ unsigned int ui03;
+ unsigned int ui04;
+ unsigned int ui05;
+ unsigned int ui06;
+ unsigned int ui07;
+ unsigned int ui08;
+ unsigned int ui09;
+ unsigned int ui10;
+ unsigned int ui11;
+ unsigned int ui12;
+ unsigned int ui13;
+ unsigned int ui14;
+ unsigned int ui15;
+ unsigned int ui16;
+ unsigned int ui17;
+} test_structure_1;
+
+static test_structure_1 ABI_ATTR struct1(test_structure_1 ts)
+{
+ ts.ui17++;
+
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts1_type;
+ ffi_type *ts1_type_elements[18];
+
+ test_structure_1 ts1_arg;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_1 *ts1_result =
+ (test_structure_1 *) malloc (sizeof(test_structure_1));
+
+ ts1_type.size = 0;
+ ts1_type.alignment = 0;
+ ts1_type.type = FFI_TYPE_STRUCT;
+ ts1_type.elements = ts1_type_elements;
+ ts1_type_elements[0] = &ffi_type_uint;
+ ts1_type_elements[1] = &ffi_type_uint;
+ ts1_type_elements[2] = &ffi_type_uint;
+ ts1_type_elements[3] = &ffi_type_uint;
+ ts1_type_elements[4] = &ffi_type_uint;
+ ts1_type_elements[5] = &ffi_type_uint;
+ ts1_type_elements[6] = &ffi_type_uint;
+ ts1_type_elements[7] = &ffi_type_uint;
+ ts1_type_elements[8] = &ffi_type_uint;
+ ts1_type_elements[9] = &ffi_type_uint;
+ ts1_type_elements[10] = &ffi_type_uint;
+ ts1_type_elements[11] = &ffi_type_uint;
+ ts1_type_elements[12] = &ffi_type_uint;
+ ts1_type_elements[13] = &ffi_type_uint;
+ ts1_type_elements[14] = &ffi_type_uint;
+ ts1_type_elements[15] = &ffi_type_uint;
+ ts1_type_elements[16] = &ffi_type_uint;
+ ts1_type_elements[17] = NULL;
+
+ args[0] = &ts1_type;
+ values[0] = &ts1_arg;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
+ &ts1_type, args) == FFI_OK);
+
+ ts1_arg.ui17 = 555;
+
+ ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+ CHECK(ts1_result->ui17 == 556);
+
+ /* This will fail if ffi_call isn't passing the struct by value. */
+ CHECK(ts1_arg.ui17 == 555);
+
+ free (ts1_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ unsigned int ui17;
+} test_structure_1;
+
+static test_structure_1 ABI_ATTR struct1(test_structure_1 ts)
+{
+ ts.ui17++;
+
+ return ts;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts1_type;
+ ffi_type *ts1_type_elements[2];
+
+ test_structure_1 ts1_arg;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_1 *ts1_result =
+ (test_structure_1 *) malloc (sizeof(test_structure_1));
+
+ ts1_type.size = 0;
+ ts1_type.alignment = 0;
+ ts1_type.type = FFI_TYPE_STRUCT;
+ ts1_type.elements = ts1_type_elements;
+ ts1_type_elements[0] = &ffi_type_uint;
+ ts1_type_elements[1] = NULL;
+
+ args[0] = &ts1_type;
+ values[0] = &ts1_arg;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1,
+ &ts1_type, args) == FFI_OK);
+
+ ts1_arg.ui17 = 555;
+
+ ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+ CHECK(ts1_result->ui17 == 556);
+
+ /* This will fail if ffi_call isn't passing the struct by value. */
+ CHECK(ts1_arg.ui17 == 555);
+
+ free (ts1_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Demonstrate structures with integers corrupting earlier floats
+ Limitations: none.
+ PR: #848
+ Originator: kellda */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+ unsigned long i;
+ float f;
+} test_structure_int_float;
+
+static float ABI_ATTR struct_int_float(test_structure_int_float ts1,
+ test_structure_int_float ts2 __UNUSED__,
+ test_structure_int_float ts3 __UNUSED__,
+ test_structure_int_float ts4 __UNUSED__,
+ test_structure_int_float ts5 __UNUSED__,
+ test_structure_int_float ts6 __UNUSED__)
+{
+ return ts1.f;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts_type;
+ ffi_type *ts_type_elements[3];
+ float rfloat;
+
+ test_structure_int_float ts_arg[6];
+
+ ts_type.size = 0;
+ ts_type.alignment = 0;
+ ts_type.type = FFI_TYPE_STRUCT;
+ ts_type.elements = ts_type_elements;
+ ts_type_elements[0] = &ffi_type_ulong;
+ ts_type_elements[1] = &ffi_type_float;
+ ts_type_elements[2] = NULL;
+
+ args[0] = &ts_type;
+ values[0] = &ts_arg[0];
+ args[1] = &ts_type;
+ values[1] = &ts_arg[1];
+ args[2] = &ts_type;
+ values[2] = &ts_arg[2];
+ args[3] = &ts_type;
+ values[3] = &ts_arg[3];
+ args[4] = &ts_type;
+ values[4] = &ts_arg[4];
+ args[5] = &ts_type;
+ values[5] = &ts_arg[5];
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 6, &ffi_type_float, args) == FFI_OK);
+
+ ts_arg[0].i = 1;
+ ts_arg[0].f = 11.11f;
+ ts_arg[1].i = 2;
+ ts_arg[1].f = 22.22f;
+ ts_arg[2].i = 3;
+ ts_arg[2].f = 33.33f;
+ ts_arg[3].i = 4;
+ ts_arg[3].f = 44.44f;
+ ts_arg[4].i = 5;
+ ts_arg[4].f = 55.55f;
+ ts_arg[5].i = 6;
+ ts_arg[5].f = 66.66f;
+
+ printf ("%g\n", ts_arg[0].f);
+ printf ("%g\n", ts_arg[1].f);
+ printf ("%g\n", ts_arg[2].f);
+ printf ("%g\n", ts_arg[3].f);
+ printf ("%g\n", ts_arg[4].f);
+ printf ("%g\n", ts_arg[5].f);
+
+ ffi_call(&cif, FFI_FN(struct_int_float), &rfloat, values);
+
+ printf ("%g\n", rfloat);
+
+ CHECK(fabs(rfloat - 11.11) < 3 * FLT_EPSILON);
+
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+typedef struct
+{
+ short x;
+ short y;
+} test_structure_5;
+
+static test_structure_5 ABI_ATTR struct5(test_structure_5 inp)
+{
+ inp.x *= 2;
+ inp.y *= 3;
+
+ return inp;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts5_type;
+ ffi_type *ts5_type_elements[3];
+
+ test_structure_5 ts5_arg1;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_5 *ts5_result =
+ (test_structure_5 *) malloc (sizeof(test_structure_5));
+
+ ts5_type.size = 0;
+ ts5_type.alignment = 0;
+ ts5_type.type = FFI_TYPE_STRUCT;
+ ts5_type.elements = ts5_type_elements;
+ ts5_type_elements[0] = &ffi_type_sshort;
+ ts5_type_elements[1] = &ffi_type_sshort;
+ ts5_type_elements[2] = NULL;
+
+ args[0] = &ts5_type;
+ values[0] = &ts5_arg1;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1, &ts5_type, args) == FFI_OK);
+
+ ts5_arg1.x = 99;
+ ts5_arg1.y = 88;
+
+ ffi_call (&cif, FFI_FN(struct5), ts5_result, values);
+
+ CHECK(ts5_result->x == 99*2);
+ CHECK(ts5_result->y == 88*3);
+ CHECK(ts5_arg1.x == 99);
+ CHECK(ts5_arg1.y == 88);
+
+ free (ts5_result);
+ exit(0);
+}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check structures.
+ Limitations: none.
+ PR: none.
+ Originator: From the original ffitest.c */
+
+/* { dg-do run } */
+#include "ffitest.h"
+typedef struct
+{
+ int a;
+ int b;
+ int c;
+ int d;
+ int e;
+ int f;
+ int g;
+ int h;
+} test_structure_5;
+
+static test_structure_5 ABI_ATTR struct5(test_structure_5 inp)
+{
+ inp.a *= 2;
+ inp.b *= 3;
+ inp.c *= 4;
+ inp.d *= 5;
+ inp.e *= 6;
+ inp.f *= 7;
+ inp.g *= 8;
+ inp.h *= 9;
+ return inp;
+}
+
+int main (void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ ffi_type ts5_type;
+ ffi_type *ts5_type_elements[9];
+
+ test_structure_5 ts5_arg1;
+
+ /* This is a hack to get a properly aligned result buffer */
+ test_structure_5 *ts5_result =
+ (test_structure_5 *) malloc (sizeof(test_structure_5));
+
+ ts5_type.size = 0;
+ ts5_type.alignment = 0;
+ ts5_type.type = FFI_TYPE_STRUCT;
+ ts5_type.elements = ts5_type_elements;
+ ts5_type_elements[0] = &ffi_type_sint;
+ ts5_type_elements[1] = &ffi_type_sint;
+ ts5_type_elements[2] = &ffi_type_sint;
+ ts5_type_elements[3] = &ffi_type_sint;
+ ts5_type_elements[4] = &ffi_type_sint;
+ ts5_type_elements[5] = &ffi_type_sint;
+ ts5_type_elements[6] = &ffi_type_sint;
+ ts5_type_elements[7] = &ffi_type_sint;
+ ts5_type_elements[8] = NULL;
+
+ args[0] = &ts5_type;
+ values[0] = &ts5_arg1;
+
+ /* Initialize the cif */
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1, &ts5_type, args) == FFI_OK);
+
+ ts5_arg1.a = 9;
+ ts5_arg1.b = 8;
+ ts5_arg1.c = 7;
+ ts5_arg1.d = 6;
+ ts5_arg1.e = 5;
+ ts5_arg1.f = 4;
+ ts5_arg1.g = 3;
+ ts5_arg1.h = 2;
+
+ ffi_call (&cif, FFI_FN(struct5), ts5_result, values);
+
+ CHECK(ts5_result->a == 9*2);
+ CHECK(ts5_result->b == 8*3);
+ CHECK(ts5_result->c == 7*4);
+ CHECK(ts5_result->d == 6*5);
+ CHECK(ts5_result->e == 5*6);
+ CHECK(ts5_result->f == 4*7);
+ CHECK(ts5_result->g == 3*8);
+ CHECK(ts5_result->h == 2*9);
+
+ free (ts5_result);
+ exit(0);
+}
Originator: ARM Ltd. */
/* { dg-do run } */
-/* { dg-output "" { xfail avr32*-*-* m68k-*-* alpha-*-* } } */
+/* { dg-output "" { xfail avr32*-*-* m68k-*-* } } */
#include "ffitest.h"
#include <stdarg.h>
unsigned e;
};
+
static int
test_fn (int n, ...)
{
ui, si,
ul, sl,
f, d);
+
va_end (ap);
+
+ CHECK(s1.a == 5);
+ CHECK(s1.b == 6);
+ CHECK(l.a == 10);
+ CHECK(l.b == 11);
+ CHECK(l.c == 12);
+ CHECK(l.d == 13);
+ CHECK(l.e == 14);
+ CHECK(s2.a == 7);
+ CHECK(s2.b == 8);
+ CHECK(uc == 9);
+ CHECK(sc == 10);
+ CHECK(us == 11);
+ CHECK(ss == 12);
+ CHECK(ui == 13);
+ CHECK(si == 14);
+ CHECK(ul == 15);
+ CHECK(sl == 16);
+ CHECK((int)f == 2);
+ CHECK((int)d == 3);
+
return n + 1;
}
/* { dg-output "5 6 10 11 12 13 14 7 8 uc=9 sc=10 11 12 13 14 15 16 2.120000 3.130000" } */
printf("res: %d\n", (int) res);
/* { dg-output "\nres: 42" } */
+ CHECK(res == 42);
return 0;
}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Test function with multiple fixed args and variable argument list.
+ Limitations: none.
+ PR: none.
+ Originator: ARM Ltd., Oracle */
+
+/* { dg-do run } */
+/* { dg-output "" { xfail avr32*-*-* m68k-*-* } } */
+
+#include "ffitest.h"
+#include <stdarg.h>
+
+/*
+ * This is a modified version of va_2.c that has fixed arguments with "small" types that
+ * are not allowed as variable arguments, but they should be still allowed as fixed args.
+ */
+
+static int
+test_fn (char a1, float a2, int n, ...)
+{
+ va_list ap;
+ unsigned char uc;
+ signed char sc;
+ unsigned short us;
+ signed short ss;
+ unsigned int ui;
+ signed int si;
+ unsigned long ul;
+ signed long sl;
+ float f;
+ double d;
+
+ va_start (ap, n);
+
+ uc = va_arg (ap, unsigned);
+ sc = va_arg (ap, signed);
+
+ us = va_arg (ap, unsigned);
+ ss = va_arg (ap, signed);
+
+ ui = va_arg (ap, unsigned int);
+ si = va_arg (ap, signed int);
+
+ ul = va_arg (ap, unsigned long);
+ sl = va_arg (ap, signed long);
+
+ f = va_arg (ap, double); /* C standard promotes float->double
+ when anonymous */
+ d = va_arg (ap, double);
+
+ printf ("%d %f uc=%u sc=%d %u %d %u %d %lu %ld %f %f\n",
+ a1, a2,
+ uc, sc,
+ us, ss,
+ ui, si,
+ ul, sl,
+ f, d);
+
+ va_end (ap);
+
+ CHECK(a1 == 1);
+ CHECK((int)a2 == 2);
+ CHECK(uc == 9);
+ CHECK(sc == 10);
+ CHECK(us == 11);
+ CHECK(ss == 12);
+ CHECK(ui == 13);
+ CHECK(si == 14);
+ CHECK(ul == 15);
+ CHECK(sl == 16);
+ CHECK((int)f == 2);
+ CHECK((int)d == 3);
+
+ return n + 1;
+}
+
+int
+main (void)
+{
+ ffi_cif cif;
+ void* args[14];
+ ffi_type* arg_types[14];
+
+ char a1;
+ float a2;
+ int n;
+ ffi_arg res;
+
+ unsigned int uc;
+ signed int sc;
+ unsigned int us;
+ signed int ss;
+ unsigned int ui;
+ signed int si;
+ unsigned long ul;
+ signed long sl;
+ double d1;
+ double f1;
+
+ arg_types[0] = &ffi_type_schar;
+ arg_types[1] = &ffi_type_float;
+ arg_types[2] = &ffi_type_sint;
+ arg_types[3] = &ffi_type_uint;
+ arg_types[4] = &ffi_type_sint;
+ arg_types[5] = &ffi_type_uint;
+ arg_types[6] = &ffi_type_sint;
+ arg_types[7] = &ffi_type_uint;
+ arg_types[8] = &ffi_type_sint;
+ arg_types[9] = &ffi_type_ulong;
+ arg_types[10] = &ffi_type_slong;
+ arg_types[11] = &ffi_type_double;
+ arg_types[12] = &ffi_type_double;
+ arg_types[13] = NULL;
+
+ CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 3, 13, &ffi_type_sint, arg_types) == FFI_OK);
+
+ a1 = 1;
+ a2 = 2.0f;
+ n = 41;
+
+ uc = 9;
+ sc = 10;
+ us = 11;
+ ss = 12;
+ ui = 13;
+ si = 14;
+ ul = 15;
+ sl = 16;
+ f1 = 2.12;
+ d1 = 3.13;
+
+ args[0] = &a1;
+ args[1] = &a2;
+ args[2] = &n;
+ args[3] = &uc;
+ args[4] = ≻
+ args[5] = &us;
+ args[6] = &ss;
+ args[7] = &ui;
+ args[8] = &si;
+ args[9] = &ul;
+ args[10] = &sl;
+ args[11] = &f1;
+ args[12] = &d1;
+ args[13] = NULL;
+
+ ffi_call(&cif, FFI_FN(test_fn), &res, args);
+ /* { dg-output "1 2.000000 uc=9 sc=10 11 12 13 14 15 16 2.120000 3.130000" } */
+ printf("res: %d\n", (int) res);
+ /* { dg-output "\nres: 42" } */
+ CHECK(res == 42);
+
+ return 0;
+}
printf ("%u %u %u %u %u %u %u %u %u\n", s1.a, s1.b, l.a, l.b, l.c, l.d, l.e,
s2.a, s2.b);
va_end (ap);
+
+ CHECK(s1.a == 5);
+ CHECK(s1.b == 6);
+
+ CHECK(l.a == 10);
+ CHECK(l.b == 11);
+ CHECK(l.c == 12);
+ CHECK(l.d == 13);
+ CHECK(l.e == 14);
+
+ CHECK(s2.a == 7);
+ CHECK(s2.b == 8);
return n + 1;
}
/* { dg-output "5 6 10 11 12 13 14 7 8" } */
printf("res: %d\n", (int) res);
/* { dg-output "\nres: 42" } */
+ CHECK(res == 42);
return 0;
}
s2 = va_arg (ap, struct small_tag);
printf ("%u %u %u %u %u %u %u %u %u\n", s1.a, s1.b, l.a, l.b, l.c, l.d, l.e,
s2.a, s2.b);
+ CHECK(s1.a == 5);
+ CHECK(s1.b == 6);
+ CHECK(l.a == 10);
+ CHECK(l.b == 11);
+ CHECK(l.c == 12);
+ CHECK(l.d == 13);
+ CHECK(l.e == 14);
+ CHECK(s2.a == 7);
+ CHECK(s2.b == 8);
va_end (ap);
s1.a += s2.a;
s1.b += s2.b;
/* { dg-output "5 6 10 11 12 13 14 7 8" } */
printf("res: %d %d\n", res.a, res.b);
/* { dg-output "\nres: 12 14" } */
+ CHECK(res.a == 12);
+ CHECK(res.b == 14);
return 0;
}
s2 = va_arg (ap, struct small_tag);
printf ("%u %u %u %u %u %u %u %u %u\n", s1.a, s1.b, l.a, l.b, l.c, l.d, l.e,
s2.a, s2.b);
+
+ CHECK(s1.a == 5);
+ CHECK(s1.b == 6);
+ CHECK(l.a == 10);
+ CHECK(l.b == 11);
+ CHECK(l.c == 12);
+ CHECK(l.d == 13);
+ CHECK(l.e == 14);
+ CHECK(s2.a == 7);
+ CHECK(s2.b == 8);
va_end (ap);
l.a += s1.a;
l.b += s1.b;
/* { dg-output "5 6 10 11 12 13 14 7 8" } */
printf("res: %d %d %d %d %d\n", res.a, res.b, res.c, res.d, res.e);
/* { dg-output "\nres: 15 17 19 21 14" } */
+ CHECK(res.a == 15);
+ CHECK(res.b == 17);
+ CHECK(res.c == 19);
+ CHECK(res.d == 21);
+ CHECK(res.e == 14);
return 0;
}
--- /dev/null
+/* Area: ffi_call
+ Purpose: Check zero-extension of pointers on x32.
+ Limitations: none.
+ PR: 887
+ Originator: Mikulas Patocka <mikulas@twibright.com> */
+
+/* { dg-do run } */
+
+#include "ffitest.h"
+
+static int ABI_ATTR fn(int *a)
+{
+ if (a)
+ return *a;
+ return -1;
+}
+
+int main(void)
+{
+ ffi_cif cif;
+ ffi_type *args[MAX_ARGS];
+ void *values[MAX_ARGS];
+ void *z[2] = { (void *)0, (void *)1 };
+ ffi_arg rint;
+ args[0] = &ffi_type_pointer;
+ values[0] = z;
+ CHECK(ffi_prep_cif(&cif, ABI_NUM, 1, &ffi_type_sint, args) == FFI_OK);
+ ffi_call(&cif, FFI_FN(fn), &rint, values);
+ CHECK((int)rint == -1);
+ exit(0);
+}
(int)*(int *)args[12], (int)(*(int *)args[13]),
(int)(*(int *)args[14]),*(int *)args[15],
(int)(intptr_t)userdata, (int)*(ffi_arg *)resp);
-
+ CHECK((int)*(ffi_arg *)resp == 680);
}
typedef int (*closure_test_type0)(unsigned long long, int, unsigned long long,
/* { dg-output "1 2 3 4 127 429 7 8 9 10 11 12 13 19 21 1 3: 680" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 680" } */
- exit(0);
+ CHECK(res == 680);
+ exit(0);
}
(int)*(int *)args[12], (int)(*(int *)args[13]),
(int)(*(int *)args[14]), *(int *)args[15],
(int)(intptr_t)userdata, (int)*(ffi_arg *)resp);
+ CHECK((int)*(ffi_arg *)resp == 255);
}
typedef int (*closure_test_type1)(float, float, float, float, signed short,
/* { dg-output "1 2 3 4 127 5 6 8 9 10 11 12 13 19 21 1 3: 255" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 255" } */
+ CHECK(res == 255);
exit(0);
}
(int)*(int *)args[12], (int)(*(float *)args[13]),
(int)(*(int *)args[14]), *(int *)args[15], (int)(intptr_t)userdata,
(int)*(ffi_arg *)resp);
+ CHECK((int)*(ffi_arg *)resp == 255);
}
typedef int (*closure_test_type2)(double, double, double, double, signed short,
/* { dg-output "1 2 3 4 127 5 6 8 9 10 11 12 13 19 21 1 3: 255" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 255" } */
+ CHECK(res == 255);
exit(0);
}
(int)(*(float *)args[10]), (int)(*(float *)args[11]),
(int)*(int *)args[12], (int)(*(float *)args[13]),
(int)(*(float *)args[14]), *(int *)args[15], (int)(intptr_t)userdata,
- (int)*(ffi_arg *)resp);
+ (int)*(ffi_arg *)resp);
+ CHECK((int)*(float *)args[0] == 1);
+ CHECK((int)(*(float *)args[1]) == 2);
+ CHECK((int)(*(float *)args[2]) == 3);
+ CHECK((int)(*(float *)args[3]) == 4);
+ CHECK((int)(*(float *)args[4]) == 5);
+ CHECK((int)(*(float *)args[5]) == 6);
+ CHECK((int)*(float *)args[6] == 7);
+ CHECK((int)(*(float *)args[7]) == 8);
+ CHECK((int)(*(double *)args[8]) == 9);
+ CHECK((int)*(int *)args[9] == 10);
+ CHECK((int)(*(float *)args[10]) == 11);
+ CHECK((int)(*(float *)args[11]) == 12);
+ CHECK((int)*(int *)args[12] == 13);
+ CHECK((int)(*(float *)args[13]) == 19);
+ CHECK((int)(*(float *)args[14]) == 21);
+ CHECK(*(int *)args[15] == 1);
+ CHECK((int)(intptr_t)userdata == 3);
+
+ CHECK((int)*(ffi_arg *)resp == 135);
}
typedef int (*closure_test_type3)(float, float, float, float, float, float,
/* { dg-output "1 2 3 4 5 6 7 8 9 10 11 12 13 19 21 1 3: 135" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 135" } */
+ CHECK(res == 135);
exit(0);
}
(int)*(unsigned long long *)args[14],
*(int *)args[15],
(int)(intptr_t)userdata, (int)*(ffi_arg *)resp);
+ CHECK((int)*(ffi_arg *)resp == 680);
}
/* { dg-output "1 2 3 4 127 429 7 8 9 10 11 12 13 19 21 1 3: 680" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 680" } */
+ CHECK(res == 680);
exit(0);
}
(int)*(unsigned long long *)args[14],
*(int *)args[15],
(int)(intptr_t)userdata, (int)*(ffi_arg *)resp);
+ CHECK((int)*(ffi_arg *)resp == 680);
}
/* { dg-output "1 2 3 4 127 429 7 8 9 10 11 12 13 19 21 1 3: 680" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 680" } */
+ CHECK(res == 680);
exit(0);
}
(int)*(int *)args[12], (int)(*(int *)args[13]),
(int)(*(double *)args[14]), (int)(*(double *)args[15]),
(int)(intptr_t)userdata, (int)*(ffi_arg *)resp);
+ CHECK((int)*(ffi_arg *)resp == 680);
}
/* { dg-output "1 2 3 4 127 429 7 8 9 10 11 12 13 19 21 1 3: 680" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 680" } */
+ CHECK(res == 680);
exit(0);
}
Originator: <andreast@gcc.gnu.org> 20030828 */
-
-
-/* { dg-do run } */
#include "ffitest.h"
static 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))
/* { dg-output "1 2 3 4 127 429 7 8 9 10 11 12 13 19 21 1 3: 680" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 680" } */
- exit(0);
+ CHECK(res == 680);
+ exit(0);
}
(int)(*(int *)args[2]), (int)(*(int *)args[3]),
(int)*(ffi_arg *)resp);
+ CHECK((int)*(int *)args[0] == 0);
+ CHECK((int)*(int *)args[1] == 1);
+ CHECK((int)*(int *)args[2] == 2);
+ CHECK((int)*(int *)args[3] == 3);
+ CHECK((int)*(ffi_arg *)resp == 9);
}
typedef int (ABI_ATTR *closure_test_type0)(int, int, int, int);
printf("res: %d\n",res);
/* { dg-output "\nres: 9" } */
+ CHECK(res == 9);
exit(0);
}
printf("%d %d %d %d %d %d: %d %d %d\n", b1.a, b1.b, b1.c, b2.a, b2.b, b2.c,
result.a, result.b, result.c);
+ CHECK(b1.a == 7);
+ CHECK(b1.b == 4);
+ CHECK(b1.c == 9);
+
+ CHECK(b2.a == 1);
+ CHECK(b2.b == 5);
+ CHECK(b2.c == 3);
+
+ CHECK(result.a == 8);
+ CHECK(result.b == 9);
+ CHECK(result.c == 12);
return result;
}
/* { dg-output "7 4 9 1 5 3: 8 9 12" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 8 9 12" } */
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 9);
+ CHECK(res_dbl.c == 12);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_12byte_gn, NULL, code) == FFI_OK);
res_dbl = ((cls_struct_12byte(*)(cls_struct_12byte, cls_struct_12byte))(code))(h_dbl, j_dbl);
/* { dg-output "\n7 4 9 1 5 3: 8 9 12" } */
+
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 8 9 12" } */
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 9);
+ CHECK(res_dbl.c == 12);
exit(0);
}
printf("%d %g %d %d %g %d: %d %g %d\n", b1.a, b1.b, b1.c, b2.a, b2.b, b2.c,
result.a, result.b, result.c);
+ CHECK(b1.a == 7);
+ CHECK(b1.b == 8);
+ CHECK(b1.c == 9);
+
+ CHECK(b2.a == 1);
+ CHECK(b2.b == 9);
+ CHECK(b2.c == 3);
+
+ CHECK(result.a == 8);
+ CHECK(result.b == 17);
+ CHECK(result.c == 12);
+
+
return result;
}
printf("res: %d %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 8 17 12" } */
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 17);
+ CHECK(res_dbl.c == 12);
+
res_dbl.a = 0;
res_dbl.b = 0.0;
res_dbl.c = 0;
printf("res: %d %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 8 17 12" } */
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 17);
+ CHECK(res_dbl.c == 12);
+
+
exit(0);
}
printf("%g %d %d %g %g %d %d %g: %g %d %d %g\n", a1.a, a1.b, a1.c, a1.d,
a2.a, a2.b, a2.c, a2.d,
result.a, result.b, result.c, result.d);
+
+ CHECK(a1.a == 1);
+ CHECK(a1.b == 127);
+ CHECK(a1.c == 126);
+ CHECK(a1.d == 3);
+
+ CHECK(a2.a == 4);
+ CHECK(a2.b == 125);
+ CHECK(a2.c == 124);
+ CHECK(a2.d == 5);
+
+ CHECK(result.a == 5);
+ CHECK(result.b == 252);
+ CHECK(result.c == 250);
+ CHECK(result.d == 8);
+
return result;
}
/* { dg-output "1 127 126 3 4 125 124 5: 5 252 250 8" } */
printf("res: %g %d %d %g\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 5 252 250 8" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 252);
+ CHECK(res_dbl.c == 250);
+ CHECK(res_dbl.d == 8);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_18byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n1 127 126 3 4 125 124 5: 5 252 250 8" } */
printf("res: %g %d %d %g\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 5 252 250 8" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 252);
+ CHECK(res_dbl.c == 250);
+ CHECK(res_dbl.d == 8);
exit(0);
}
a1.a, a1.b, a1.c, a1.d, a1.e,
a2.a, a2.b, a2.c, a2.d, a2.e,
result.a, result.b, result.c, result.d, result.e);
+
+ CHECK(a1.a == 1);
+ CHECK(a1.b == 127);
+ CHECK(a1.c == 126);
+ CHECK(a1.d == 3);
+ CHECK(a1.e == 120);
+
+ CHECK(a2.a == 4);
+ CHECK(a2.b == 125);
+ CHECK(a2.c == 124);
+ CHECK(a2.d == 5);
+ CHECK(a2.e == 119);
+
+ CHECK(result.a == 5);
+ CHECK(result.b == 252);
+ CHECK(result.c == 250);
+ CHECK(result.d == 8);
+ CHECK(result.e == 239);
+
return result;
}
printf("res: %g %d %d %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e);
/* { dg-output "\nres: 5 252 250 8 239" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 252);
+ CHECK(res_dbl.c == 250);
+ CHECK(res_dbl.d == 8);
+ CHECK(res_dbl.e == 239);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_19byte_gn, NULL, code) == FFI_OK);
printf("res: %g %d %d %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e);
/* { dg-output "\nres: 5 252 250 8 239" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 252);
+ CHECK(res_dbl.c == 250);
+ CHECK(res_dbl.d == 8);
+ CHECK(res_dbl.e == 239);
exit(0);
}
result.a = a1.a + a2.a;
printf("%d %d: %d\n", a1.a, a2.a, result.a);
+ CHECK(a1.a == 12);
+ CHECK(a2.a == 178);
+ CHECK(result.a == 190);
return result;
}
/* { dg-output "\n12 178: 190" } */
printf("res: %d\n", res_dbl.a);
/* { dg-output "\nres: 190" } */
+ CHECK(res_dbl.a == 190);
exit(0);
}
printf("%g %g %d %g %g %d: %g %g %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c,
result.a, result.b, result.c);
+
+ CHECK(a1.a == 1);
+ CHECK(a1.b == 2);
+ CHECK(a1.c == 3);
+
+ CHECK(a2.a == 4);
+ CHECK(a2.b == 5);
+ CHECK(a2.c == 7);
+
+ CHECK(result.a == 5);
+ CHECK(result.b == 7);
+ CHECK(result.c == 10);
return result;
}
/* { dg-output "1 2 3 4 5 7: 5 7 10" } */
printf("res: %g %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 5 7 10" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 7);
+ CHECK(res_dbl.c == 10);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_20byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n1 2 3 4 5 7: 5 7 10" } */
printf("res: %g %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 5 7 10" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 7);
+ CHECK(res_dbl.c == 10);
exit(0);
}
printf("%d %g %g %d %g %g: %d %g %g\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c,
result.a, result.b, result.c);
+
+ CHECK(a1.a == 1);
+ CHECK(a1.b == 2);
+ CHECK(a1.c == 3);
+
+ CHECK(a2.a == 4);
+ CHECK(a2.b == 5);
+ CHECK(a2.c == 7);
+
+ CHECK(result.a == 5);
+ CHECK(result.b == 7);
+ CHECK(result.c == 10);
+
return result;
}
/* { dg-output "1 2 3 4 5 7: 5 7 10" } */
printf("res: %d %g %g\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 5 7 10" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 7);
+ CHECK(res_dbl.c == 10);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_20byte_gn, NULL, code) == FFI_OK);
printf("res: %d %g %g\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 5 7 10" } */
+ CHECK(res_dbl.a == 5);
+ CHECK(res_dbl.b == 7);
+ CHECK(res_dbl.c == 10);
+
exit(0);
}
b2.a, b2.b, b2.c, b2.d,
b3.a, b3.b, b3.c, b3.d,
result.a, result.b, result.c, result.d);
+ CHECK_DOUBLE_EQ(b0.a, 9);
+ CHECK_DOUBLE_EQ(b0.b, 2);
+ CHECK(b0.c == 6);
+ CHECK_FLOAT_EQ(b0.d, 5);
+
+ CHECK_DOUBLE_EQ(b1.a, 1);
+ CHECK_DOUBLE_EQ(b1.b, 2);
+ CHECK(b1.c == 3);
+ CHECK_FLOAT_EQ(b1.d, 7);
+
+ CHECK_DOUBLE_EQ(b2.a, 4);
+ CHECK_DOUBLE_EQ(b2.b, 5);
+ CHECK(b2.c == 7);
+ CHECK_FLOAT_EQ(b2.d, 9);
+
+ CHECK_DOUBLE_EQ(b3.a, 8);
+ CHECK_DOUBLE_EQ(b3.b, 6);
+ CHECK(b3.c == 1);
+ CHECK_FLOAT_EQ(b3.d, 4);
+
+ CHECK_DOUBLE_EQ(result.a, 22);
+ CHECK_DOUBLE_EQ(result.b, 15);
+ CHECK(result.c == 17);
+ CHECK_FLOAT_EQ(result.d, 25);
return result;
}
/* { dg-output "9 2 6 5 1 2 3 7 4 5 7 9 8 6 1 4: 22 15 17 25" } */
printf("res: %g %g %d %g\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 22 15 17 25" } */
+ CHECK_DOUBLE_EQ(res_dbl.a, 22);
+ CHECK_DOUBLE_EQ(res_dbl.b, 15);
+ CHECK(res_dbl.c == 17);
+ CHECK_FLOAT_EQ(res_dbl.d, 25);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_24byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n9 2 6 5 1 2 3 7 4 5 7 9 8 6 1 4: 22 15 17 25" } */
printf("res: %g %g %d %g\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 22 15 17 25" } */
+ CHECK_DOUBLE_EQ(res_dbl.a, 22);
+ CHECK_DOUBLE_EQ(res_dbl.b, 15);
+ CHECK(res_dbl.c == 17);
+ CHECK_FLOAT_EQ(res_dbl.d, 25);
exit(0);
}
printf("%d %d %d %d: %d %d\n", a1.a, a1.b, a2.a, a2.b, result.a, result.b);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 140);
+
return result;
}
/* { dg-output "12 127 1 13: 13 140" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 13 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_2byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 127 1 13: 13 140" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 13 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 140);
exit(0);
}
a2.a, a2.b, a2.c,
result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 13);
+ CHECK(a1.c == 14);
+
+ CHECK(a2.a == 178);
+ CHECK(a2.b == 179);
+ CHECK(a2.c == 180);
+
+ CHECK(result.a == 190);
+ CHECK(result.b == 192);
+ CHECK(result.c == 194);
+
return result;
}
/* { dg-output "12 13 14 178 179 180: 190 192 194" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 190 192 194" } */
+ CHECK(res_dbl.a == 190);
+ CHECK(res_dbl.b == 192);
+ CHECK(res_dbl.c == 194);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_3_1byte_gn, NULL, code) == FFI_OK);
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 190 192 194" } */
+ CHECK(res_dbl.a == 190);
+ CHECK(res_dbl.b == 192);
+ CHECK(res_dbl.c == 194);
+
exit(0);
}
printf("%d %d %d %d: %d %d\n", a1.a, a1.b, a2.a, a2.b, result.a, result.b);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 119);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 15);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 134);
+
return result;
}
/* { dg-output "12 119 1 15: 13 134" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 13 134" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 134);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_3byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 119 1 15: 13 134" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 13 134" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 134);
exit(0);
}
printf("%d %d %d %d: %d %d\n", a1.a, a1.b, a2.a, a2.b, result.a, result.b);
+ CHECK(a1.a == 15);
+ CHECK(a1.b == 125);
+
+ CHECK(a2.a == 9);
+ CHECK(a2.b == 19);
+
+ CHECK(result.a == 24);
+ CHECK(result.b == 144);
+
return result;
}
/* { dg-output "15 125 9 19: 24 144" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 24 144" } */
+ CHECK(res_dbl.a == 24);
+ CHECK(res_dbl.b == 144);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_3byte_gn1, NULL, code) == FFI_OK);
/* { dg-output "\n15 125 9 19: 24 144" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 24 144" } */
+ CHECK(res_dbl.a == 24);
+ CHECK(res_dbl.b == 144);
exit(0);
}
printf("%g %g %g %g %g %g: %g %g %g\n", a1.f, a1.g, a1.h,
a2.f, a2.g, a2.h, result.f, result.g, result.h);
+ CHECK_FLOAT_EQ(a1.f, 1);
+ CHECK_FLOAT_EQ(a1.g, 2);
+ CHECK_FLOAT_EQ(a1.h, 3);
+
+ CHECK_FLOAT_EQ(a2.f, 1);
+ CHECK_FLOAT_EQ(a2.g, 2);
+ CHECK_FLOAT_EQ(a2.h, 3);
+
+ CHECK_FLOAT_EQ(result.f, 2);
+ CHECK_FLOAT_EQ(result.g, 4);
+ CHECK_FLOAT_EQ(result.h, 6);
+
return result;
}
/* { dg-output "1 2 3 1 2 3: 2 4 6" } */
printf("res: %g %g %g\n", res_dbl.f, res_dbl.g, res_dbl.h);
/* { dg-output "\nres: 2 4 6" } */
+ CHECK_FLOAT_EQ(res_dbl.f, 2);
+ CHECK_FLOAT_EQ(res_dbl.g, 4);
+ CHECK_FLOAT_EQ(res_dbl.h, 6);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_3float_gn, NULL, code) ==
FFI_OK);
/* { dg-output "\n1 2 3 1 2 3: 2 4 6" } */
printf("res: %g %g %g\n", res_dbl.f, res_dbl.g, res_dbl.h);
/* { dg-output "\nres: 2 4 6" } */
+ CHECK_FLOAT_EQ(res_dbl.f, 2);
+ CHECK_FLOAT_EQ(res_dbl.g, 4);
+ CHECK_FLOAT_EQ(res_dbl.h, 6);
exit(0);
}
a2.a, a2.b, a2.c, a2.d,
result.a, result.b, result.c, result.d);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 13);
+ CHECK(a1.c == 14);
+ CHECK(a1.d == 15);
+
+ CHECK(a2.a == 178);
+ CHECK(a2.b == 179);
+ CHECK(a2.c == 180);
+ CHECK(a2.d == 181);
+
+ CHECK(result.a == 190);
+ CHECK(result.b == 192);
+ CHECK(result.c == 194);
+ CHECK(result.d == 196);
return result;
}
/* { dg-output "12 13 14 15 178 179 180 181: 190 192 194 196" } */
printf("res: %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 190 192 194 196" } */
+ CHECK(res_dbl.a == 190);
+ CHECK(res_dbl.b == 192);
+ CHECK(res_dbl.c == 194);
+ CHECK(res_dbl.d == 196);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_4_1byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 13 14 15 178 179 180 181: 190 192 194 196" } */
printf("res: %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 190 192 194 196" } */
+ CHECK(res_dbl.a == 190);
+ CHECK(res_dbl.b == 192);
+ CHECK(res_dbl.c == 194);
+ CHECK(res_dbl.d == 196);
exit(0);
}
printf("%d %d %d %d: %d %d\n", a1.a, a1.b, a2.a, a2.b, result.a, result.b);
+ CHECK(a1.a == 127);
+ CHECK(a1.b == 120);
+
+ CHECK(a2.a == 12);
+ CHECK(a2.b == 128);
+
+ CHECK(result.a == 139);
+ CHECK(result.b == 248);
+
return result;
}
/* { dg-output "127 120 12 128: 139 248" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 139 248" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_4byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n127 120 12 128: 139 248" } */
printf("res: %d %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 139 248" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
exit(0);
}
a2.a, a2.b, a2.c, a2.d, a2.e,
result.a, result.b, result.c, result.d, result.e);
+ CHECK(a1.a == 127);
+ CHECK(a1.b == 120);
+ CHECK(a1.c == 1);
+ CHECK(a1.d == 3);
+ CHECK(a1.e == 4);
+
+ CHECK(a2.a == 12);
+ CHECK(a2.b == 128);
+ CHECK(a2.c == 9);
+ CHECK(a2.d == 3);
+ CHECK(a2.e == 4);
+
+ CHECK(result.a == 139);
+ CHECK(result.b == 248);
+ CHECK(result.c == 10);
+ CHECK(result.d == 6);
+ CHECK(result.e == 8);
+
return result;
}
printf("res: %d %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e);
/* { dg-output "\nres: 139 248 10 6 8" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 6);
+ CHECK(res_dbl.e == 8);
res_dbl.a = 0;
res_dbl.b = 0;
printf("res: %d %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e);
/* { dg-output "\nres: 139 248 10 6 8" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 6);
+ CHECK(res_dbl.e == 8);
exit(0);
}
a2.a, a2.b, a2.c,
result.a, result.b, result.c);
+ CHECK(a1.a == 127);
+ CHECK(a1.b == 120);
+ CHECK(a1.c == 1);
+
+ CHECK(a2.a == 12);
+ CHECK(a2.b == 128);
+ CHECK(a2.c == 9);
+
+ CHECK(result.a == 139);
+ CHECK(result.b == 248);
+ CHECK(result.c == 10);
+
return result;
}
/* { dg-output "127 120 1 12 128 9: 139 248 10" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 139 248 10" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
res_dbl.a = 0;
res_dbl.b = 0;
/* { dg-output "\n127 120 1 12 128 9: 139 248 10" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 139 248 10" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
exit(0);
}
printf("%g %g %g %g %g %g %g %g\n", result.a, result.b, result.c,
result.d, result.e, result.f, result.g, result.h);
+ CHECK(result.a == 22);
+ CHECK(result.b == 15);
+ CHECK(result.c == 17);
+ CHECK(result.d == 25);
+ CHECK(result.e == 6);
+ CHECK(result.f == 13);
+ CHECK(result.g == 19);
+ CHECK(result.h == 18);
return result;
}
printf("res: %g %g %g %g %g %g %g %g\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g, res_dbl.h);
/* { dg-output "\nres: 22 15 17 25 6 13 19 18" } */
+ CHECK(res_dbl.a == 22);
+ CHECK(res_dbl.b == 15);
+ CHECK(res_dbl.c == 17);
+ CHECK(res_dbl.d == 25);
+ CHECK(res_dbl.e == 6);
+ CHECK(res_dbl.f == 13);
+ CHECK(res_dbl.g == 19);
+ CHECK(res_dbl.h == 18);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_64byte_gn, NULL, code) == FFI_OK);
printf("res: %g %g %g %g %g %g %g %g\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g, res_dbl.h);
/* { dg-output "\nres: 22 15 17 25 6 13 19 18" } */
+ CHECK(res_dbl.a == 22);
+ CHECK(res_dbl.b == 15);
+ CHECK(res_dbl.c == 17);
+ CHECK(res_dbl.d == 25);
+ CHECK(res_dbl.e == 6);
+ CHECK(res_dbl.f == 13);
+ CHECK(res_dbl.g == 19);
+ CHECK(res_dbl.h == 18);
exit(0);
}
a2.a, a2.b, a2.c, a2.d, a2.e, a2.f,
result.a, result.b, result.c, result.d, result.e, result.f);
+ CHECK(a1.a == 127);
+ CHECK(a1.b == 120);
+ CHECK(a1.c == 1);
+ CHECK(a1.d == 3);
+ CHECK(a1.e == 4);
+ CHECK(a1.f == 5);
+
+ CHECK(a2.a == 12);
+ CHECK(a2.b == 128);
+ CHECK(a2.c == 9);
+ CHECK(a2.d == 3);
+ CHECK(a2.e == 4);
+ CHECK(a2.f == 5);
+
+ CHECK(result.a == 139);
+ CHECK(result.b == 248);
+ CHECK(result.c == 10);
+ CHECK(result.d == 6);
+ CHECK(result.e == 8);
+ CHECK(result.f == 10);
+
return result;
}
res_dbl.d, res_dbl.e, res_dbl.f);
/* { dg-output "\nres: 139 248 10 6 8 10" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 6);
+ CHECK(res_dbl.e == 8);
+ CHECK(res_dbl.f == 10);
exit(0);
}
a2.a, a2.b, a2.c, a2.d,
result.a, result.b, result.c, result.d);
+ CHECK(a1.a == 127);
+ CHECK(a1.b == 120);
+ CHECK(a1.c == 1);
+ CHECK(a1.d == 128);
+
+ CHECK(a2.a == 12);
+ CHECK(a2.b == 128);
+ CHECK(a2.c == 9);
+ CHECK(a2.d == 127);
+
+ CHECK(result.a == 139);
+ CHECK(result.b == 248);
+ CHECK(result.c == 10);
+ CHECK(result.d == 255);
+
return result;
}
/* { dg-output "127 120 1 128 12 128 9 127: 139 248 10 255" } */
printf("res: %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 139 248 10 255" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 255);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_6byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n127 120 1 128 12 128 9 127: 139 248 10 255" } */
printf("res: %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 139 248 10 255" } */
-
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 255);
exit(0);
}
a2.a, a2.b, a2.c, a2.d, a2.e, a2.f, a2.g,
result.a, result.b, result.c, result.d, result.e, result.f, result.g);
+ CHECK(a1.a == 127);
+ CHECK(a1.b == 120);
+ CHECK(a1.c == 1);
+ CHECK(a1.d == 3);
+ CHECK(a1.e == 4);
+ CHECK(a1.f == 5);
+ CHECK(a1.g == 6);
+
+ CHECK(a2.a == 12);
+ CHECK(a2.b == 128);
+ CHECK(a2.c == 9);
+ CHECK(a2.d == 3);
+ CHECK(a2.e == 4);
+ CHECK(a2.f == 5);
+ CHECK(a2.g == 6);
+
+ CHECK(result.a == 139);
+ CHECK(result.b == 248);
+ CHECK(result.c == 10);
+ CHECK(result.d == 6);
+ CHECK(result.e == 8);
+ CHECK(result.f == 10);
+ CHECK(result.g == 12);
+
return result;
}
printf("res: %d %d %d %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g);
/* { dg-output "\nres: 139 248 10 6 8 10 12" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 6);
+ CHECK(res_dbl.e == 8);
+ CHECK(res_dbl.f == 10);
+ CHECK(res_dbl.g == 12);
res_dbl.a = 0;
res_dbl.b = 0;
printf("res: %d %d %d %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c,
res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g);
/* { dg-output "\nres: 139 248 10 6 8 10 12" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 6);
+ CHECK(res_dbl.e == 8);
+ CHECK(res_dbl.f == 10);
+ CHECK(res_dbl.g == 12);
exit(0);
}
a2.a, a2.b, a2.c, a2.d,
result.a, result.b, result.c, result.d);
+ CHECK(a1.a == 127);
+ CHECK(a1.b == 120);
+ CHECK(a1.c == 1);
+ CHECK(a1.d == 254);
+
+ CHECK(a2.a == 12);
+ CHECK(a2.b == 128);
+ CHECK(a2.c == 9);
+ CHECK(a2.d == 255);
+
+ CHECK(result.a == 139);
+ CHECK(result.b == 248);
+ CHECK(result.c == 10);
+ CHECK(result.d == 509);
+
+
return result;
}
/* { dg-output "127 120 1 254 12 128 9 255: 139 248 10 509" } */
printf("res: %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 139 248 10 509" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 509);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_7byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n127 120 1 254 12 128 9 255: 139 248 10 509" } */
printf("res: %d %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c, res_dbl.d);
/* { dg-output "\nres: 139 248 10 509" } */
+ CHECK(res_dbl.a == 139);
+ CHECK(res_dbl.b == 248);
+ CHECK(res_dbl.c == 10);
+ CHECK(res_dbl.d == 509);
exit(0);
}
printf("%d %g %d %g: %d %g\n", a1.a, a1.b, a2.a, a2.b, result.a, result.b);
+ CHECK(a1.a == 1);
+ CHECK_FLOAT_EQ(a1.b, 2);
+
+ CHECK(a2.a == 4);
+ CHECK_FLOAT_EQ(a2.b, 5);
+
+ CHECK(result.a == 5);
+ CHECK_FLOAT_EQ(result.b, 7);
+
return result;
}
ffi_call(&cif, FFI_FN(cls_struct_8byte_fn), &res_dbl, args_dbl);
/* { dg-output "1 2 4 5: 5 7" } */
printf("res: %d %g\n", res_dbl.a, res_dbl.b);
+ CHECK(res_dbl.a == 5);
+ CHECK_FLOAT_EQ(res_dbl.b, 7);
+
/* { dg-output "\nres: 5 7" } */
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_8byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n1 2 4 5: 5 7" } */
printf("res: %d %g\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 5 7" } */
+ CHECK(res_dbl.a == 5);
+ CHECK_FLOAT_EQ(res_dbl.b, 7);
exit(0);
}
printf("%d %g %d %g: %d %g\n", b1.a, b1.b, b2.a, b2.b,
result.a, result.b);
+ CHECK(b1.a == 7);
+ CHECK(b1.b == 8);
+
+ CHECK(b2.a == 1);
+ CHECK(b2.b == 9);
+
+ CHECK(result.a == 8);
+ CHECK(result.b == 17);
+
return result;
}
/* { dg-output "7 8 1 9: 8 17" } */
printf("res: %d %g\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 8 17" } */
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 17);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_9byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n7 8 1 9: 8 17" } */
printf("res: %d %g\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 8 17" } */
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 17);
exit(0);
}
printf("%g %d %g %d: %g %d\n", b1.a, b1.b, b2.a, b2.b,
result.a, result.b);
+ CHECK(b1.a == 7);
+ CHECK(b1.b == 8);
+
+ CHECK(b2.a == 1);
+ CHECK(b2.b == 9);
+
+ CHECK(result.a == 8);
+ CHECK(result.b == 17);
+
return result;
}
/* { dg-output "7 8 1 9: 8 17" } */
printf("res: %g %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 8 17" } */
-
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 17);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_9byte_gn, NULL, code) == FFI_OK);
/* { dg-output "\n7 8 1 9: 8 17" } */
printf("res: %g %d\n", res_dbl.a, res_dbl.b);
/* { dg-output "\nres: 8 17" } */
+ CHECK(res_dbl.a == 8);
+ CHECK(res_dbl.b == 17);
exit(0);
}
printf("%d %g %d %d %g %d: %d %g %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c, result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
+
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %g %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
printf("%d %g %d %d %g %d: %d %g %d\n", a1.a, (double)a1.b, a1.c, a2.a, (double)a2.b, a2.c, result.a, (double)result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK_FLOAT_EQ(a1.b, 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK_FLOAT_EQ(a2.b, 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK_FLOAT_EQ(result.b, 14271);
+ CHECK(result.c == 140);
+
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %g %d\n", res_dbl.a, (double)res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK_FLOAT_EQ(res_dbl.b, 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %g %d\n", res_dbl.a, (double)res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK_FLOAT_EQ(res_dbl.b, 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
printf("%d %g %d %d %g %d: %d %g %d\n", a1.a, (double)a1.b, a1.c, a2.a, (double)a2.b, a2.c, result.a, (double)result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
+
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %g %d\n", res_dbl.a, (double)res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
+
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %g %d\n", res_dbl.a, (double)res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
Originator: <hos@tamanegi.org> 20031203 */
/* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
-/* { dg-options -mlong-double-128 { target powerpc64*-*-linux* } } */
+/* { dg-options -mlong-double-128 { target powerpc64*-*-linux-gnu* } } */
#include "ffitest.h"
a2.a, a2.b, a2.c, a2.d, a2.e, a2.f, a2.g,
r.a, r.b, r.c, r.d, r.e, r.f, r.g);
- return r;
-}
-
-cls_struct_align cls_struct_align_fn2(
- cls_struct_align a1)
-{
- struct cls_struct_align r;
-
- r.a = a1.a + 1;
- r.b = a1.b + 1;
- r.c = a1.c + 1;
- r.d = a1.d + 1;
- r.e = a1.e + 1;
- r.f = a1.f + 1;
- r.g = a1.g + 1;
-
- printf("%Lg %Lg %Lg %Lg %Lg %Lg %Lg: "
- "%Lg %Lg %Lg %Lg %Lg %Lg %Lg\n",
- a1.a, a1.b, a1.c, a1.d, a1.e, a1.f, a1.g,
- r.a, r.b, r.c, r.d, r.e, r.f, r.g);
-
+ CHECK(a1.a == 1);
+ CHECK(a1.b == 2);
+ CHECK(a1.c == 3);
+ CHECK(a1.d == 4);
+ CHECK(a1.e == 5);
+ CHECK(a1.f == 6);
+ CHECK(a1.g == 7);
+
+ CHECK(a2.a == 8);
+ CHECK(a2.b == 9);
+ CHECK(a2.c == 10);
+ CHECK(a2.d == 11);
+ CHECK(a2.e == 12);
+ CHECK(a2.f == 13);
+ CHECK(a2.g == 14);
+
+ CHECK(r.a == 9);
+ CHECK(r.b == 11);
+ CHECK(r.c == 13);
+ CHECK(r.d == 15);
+ CHECK(r.e == 17);
+ CHECK(r.f == 19);
+ CHECK(r.g == 21);
return r;
}
static void
-cls_struct_align_gn(ffi_cif* cif __UNUSED__, void* resp, void** args,
+cls_struct_align_gn(ffi_cif* cif __UNUSED__, void* resp, void** args,
void* userdata __UNUSED__)
{
struct cls_struct_align a1, a2;
printf("res: %Lg %Lg %Lg %Lg %Lg %Lg %Lg\n", res_dbl.a, res_dbl.b,
res_dbl.c, res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g);
/* { dg-output "\nres: 9 11 13 15 17 19 21" } */
+ CHECK(res_dbl.a == 9);
+ CHECK(res_dbl.b == 11);
+ CHECK(res_dbl.c == 13);
+ CHECK(res_dbl.d == 15);
+ CHECK(res_dbl.e == 17);
+ CHECK(res_dbl.f == 19);
+ CHECK(res_dbl.g == 21);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
printf("res: %Lg %Lg %Lg %Lg %Lg %Lg %Lg\n", res_dbl.a, res_dbl.b,
res_dbl.c, res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g);
/* { dg-output "\nres: 9 11 13 15 17 19 21" } */
-
+ CHECK(res_dbl.a == 9);
+ CHECK(res_dbl.b == 11);
+ CHECK(res_dbl.c == 13);
+ CHECK(res_dbl.d == 15);
+ CHECK(res_dbl.e == 17);
+ CHECK(res_dbl.f == 19);
+ CHECK(res_dbl.g == 21);
exit(0);
}
*/
/* { dg-do run { xfail strongarm*-*-* } } */
-/* { dg-options -mlong-double-128 { target powerpc64*-*-linux* } } */
+/* { dg-options -mlong-double-128 { target powerpc64*-*-linux-gnu* } } */
#include "ffitest.h"
a2.a, a2.b, a2.c, a2.d, a2.e, a2.f, a2.g,
r.a, r.b, r.c, r.d, r.e, r.f, r.g);
+ CHECK(a1.a == 1);
+ CHECK(a1.b == 2);
+ CHECK(a1.c == 3);
+ CHECK(a1.d == 4);
+ CHECK(a1.e == 5);
+ CHECK(a1.f == 6);
+ CHECK(a1.g == 7);
+
+ CHECK(a2.a == 8);
+ CHECK(a2.b == 9);
+ CHECK(a2.c == 10);
+ CHECK(a2.d == 11);
+ CHECK(a2.e == 12);
+ CHECK(a2.f == 13);
+ CHECK(a2.g == 14);
+
+ CHECK(r.a == 9);
+ CHECK(r.b == 11);
+ CHECK(r.c == 13);
+ CHECK(r.d == 15);
+ CHECK(r.e == 17);
+ CHECK(r.f == 19);
+ CHECK(r.g == 21);
+
return r;
}
printf("res: %Lg %Lg %Lg %Lg %Lg %g %Lg\n", res_dbl.a, res_dbl.b,
res_dbl.c, res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g);
/* { dg-output "\nres: 9 11 13 15 17 19 21" } */
+ CHECK(res_dbl.a == 9);
+ CHECK(res_dbl.b == 11);
+ CHECK(res_dbl.c == 13);
+ CHECK(res_dbl.d == 15);
+ CHECK(res_dbl.e == 17);
+ CHECK(res_dbl.f == 19);
+ CHECK(res_dbl.g == 21);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
printf("res: %Lg %Lg %Lg %Lg %Lg %g %Lg\n", res_dbl.a, res_dbl.b,
res_dbl.c, res_dbl.d, res_dbl.e, res_dbl.f, res_dbl.g);
/* { dg-output "\nres: 9 11 13 15 17 19 21" } */
-
+ CHECK(res_dbl.a == 9);
+ CHECK(res_dbl.b == 11);
+ CHECK(res_dbl.c == 13);
+ CHECK(res_dbl.d == 15);
+ CHECK(res_dbl.e == 17);
+ CHECK(res_dbl.f == 19);
+ CHECK(res_dbl.g == 21);
exit(0);
}
result.a, (uintptr_t)result.b,
result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == (void *)4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == (void *)9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == (void *)14271);
+ CHECK(result.c == 140);
+
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %" PRIuPTR " %d\n", res_dbl.a, (uintptr_t)res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == (void *)14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %" PRIuPTR " %d\n", res_dbl.a, (uintptr_t)res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == (void *)14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
result.c = a1.c + a2.c;
printf("%d %d %d %d %d %d: %d %d %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c, result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
result.c = a1.c + a2.c;
printf("%d %d %d %d %d %d: %d %d %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c, result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
-
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
res_dbl = ((cls_struct_align(*)(cls_struct_align, cls_struct_align))(code))(g_dbl, f_dbl);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
-
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
result.c = a1.c + a2.c;
printf("%d %" PRIdLL " %d %d %" PRIdLL " %d: %d %" PRIdLL " %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c, result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %" PRIdLL " %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %" PRIdLL " %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
result.c = a1.c + a2.c;
printf("%d %d %d %d %d %d: %d %d %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c, result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
printf("%d %d %d %d %d %d: %d %d %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c, result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
+
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %d %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
printf("%d %" PRIdLL " %d %d %" PRIdLL " %d: %d %" PRIdLL " %d\n", a1.a, a1.b, a1.c, a2.a, a2.b, a2.c, result.a, result.b, result.c);
+ CHECK(a1.a == 12);
+ CHECK(a1.b == 4951);
+ CHECK(a1.c == 127);
+
+ CHECK(a2.a == 1);
+ CHECK(a2.b == 9320);
+ CHECK(a2.c == 13);
+
+ CHECK(result.a == 13);
+ CHECK(result.b == 14271);
+ CHECK(result.c == 140);
+
return result;
}
/* { dg-output "12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %" PRIdLL " %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_align_gn, NULL, code) == FFI_OK);
/* { dg-output "\n12 4951 127 1 9320 13: 13 14271 140" } */
printf("res: %d %" PRIdLL " %d\n", res_dbl.a, res_dbl.b, res_dbl.c);
/* { dg-output "\nres: 13 14271 140" } */
+ CHECK(res_dbl.a == 13);
+ CHECK(res_dbl.b == 14271);
+ CHECK(res_dbl.c == 140);
exit(0);
}
closure_test_fn(Dbls p)
{
printf("%.1f %.1f\n", p.x, p.y);
+ CHECK(p.x == 1);
+ CHECK(p.y == 2);
}
void
closure_test_fn(*(Dbls*)args[0]);
}
-int main(int argc __UNUSED__, char** argv __UNUSED__)
+int main(void)
{
ffi_cif cif;
#include "ffitest.h"
+#define BUF_SIZE 50
+static char buffer[BUF_SIZE];
+
static void
-cls_double_va_fn(ffi_cif* cif __UNUSED__, void* resp,
+cls_double_va_fn(ffi_cif* cif __UNUSED__, void* resp,
void** args, void* userdata __UNUSED__)
{
char* format = *(char**)args[0];
double doubleValue = *(double*)args[1];
*(ffi_arg*)resp = printf(format, doubleValue);
+ CHECK(*(ffi_arg*)resp == 4);
+ snprintf(buffer, BUF_SIZE, format, doubleValue);
+ CHECK(strncmp(buffer, "7.0\n", 4) == 0);
}
int main (void)
/* { dg-output "7.0" } */
printf("res: %d\n", (int) res);
/* { dg-output "\nres: 4" } */
+ CHECK(res == 4);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL,
code) == FFI_OK);
/* { dg-output "\n7.0" } */
printf("res: %d\n", (int) res);
/* { dg-output "\nres: 4" } */
+ CHECK(res == 4);
exit(0);
}
printf("%g: %g\n",*(float *)args[0],
*(float *)resp);
+
+ CHECK((int)(*(float *)args[0]) == -2122);
+ CHECK((int)(*(float *)resp) == -2122);
}
typedef float (*cls_ret_float)(float);
/* { dg-output "\\-2122.12: \\-2122.12" } */
printf("res: %.6f\n", res);
/* { dg-output "\nres: \-2122.120117" } */
+ CHECK((int)res == -2122);
exit(0);
}
/* This test is known to PASS on armv7l-unknown-linux-gnueabihf, so I have
remove the xfail for arm*-*-* below, until we know more. */
/* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
-/* { dg-options -mlong-double-128 { target powerpc64*-*-linux* } } */
+/* { dg-options -mlong-double-128 { target powerpc64*-*-linux-gnu* } } */
#include "ffitest.h"
printf("%Lg %Lg %Lg %Lg %Lg %Lg %Lg %Lg: %Lg\n",
a1, a2, a3, a4, a5, a6, a7, a8, r);
+ CHECK(a1 == 1);
+ CHECK(a2 == 2);
+ CHECK(a3 == 3);
+ CHECK(a4 == 4);
+ CHECK(a5 == 5);
+ CHECK(a6 == 6);
+ CHECK(a7 == 7);
+ CHECK(a8 == 8);
return r;
}
/* { dg-output "1 2 3 4 5 6 7 8: 36" } */
printf("res: %Lg\n", res);
/* { dg-output "\nres: 36" } */
+ CHECK(res == 36);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ldouble_gn, NULL, code) == FFI_OK);
/* { dg-output "\n1 2 3 4 5 6 7 8: 36" } */
printf("res: %Lg\n", res);
/* { dg-output "\nres: 36" } */
+ CHECK(res == 36);
return 0;
}
Originator: Blake Chaffin 6/6/2007 */
/* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
-/* { dg-output "" { xfail avr32*-*-* x86_64-*-mingw* } } */
+/* { dg-output "" { xfail avr32*-*-* } } */
/* { dg-output "" { xfail mips-sgi-irix6* } } PR libffi/46660 */
#include "ffitest.h"
+#include <stdarg.h>
+
+#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,
+cls_longdouble_va_fn(ffi_cif* cif __UNUSED__, void* resp,
void** args, void* userdata __UNUSED__)
{
char* format = *(char**)args[0];
long double ldValue = *(long double*)args[1];
*(ffi_arg*)resp = printf(format, ldValue);
+ CHECK(*(ffi_arg*)resp == 4);
+ snprintf(buffer, BUF_SIZE, format, ldValue);
+ CHECK(strncmp(buffer, "7.0\n", BUF_SIZE) == 0);
}
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" } */
+ CHECK(res == 4);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_longdouble_va_fn, NULL,
code) == FFI_OK);
/* { dg-output "\n7.0" } */
printf("res: %d\n", (int) res);
/* { dg-output "\nres: 4" } */
+ CHECK(res == 4);
exit(0);
}
a1 = *(signed char *)avals[0];
a2 = *(signed char *)avals[1];
+ CHECK(a1 == 2);
+ CHECK(a2 == 125);
*(ffi_arg *)rval = test_func_fn(a1, a2);
/* { dg-output "2 125: 127" } */
printf("res: %d\n", (signed char)res_call);
/* { dg-output "\nres: 127" } */
+ CHECK((signed char)res_call == 127);
CHECK(ffi_prep_closure_loc(pcl, &cif, test_func_gn, NULL, code) == FFI_OK);
/* { dg-output "\n2 125: 127" } */
printf("res: %d\n", res_closure);
/* { dg-output "\nres: 127" } */
+ CHECK(res_closure == 127);
exit(0);
}
result = a1 + a2;
printf("%d %d: %d\n", a1, a2, result);
+ CHECK(a1 == 2);
+ CHECK(a2 == 32765);
+ CHECK(result == 32767);
return result;
/* { dg-output "2 32765: 32767" } */
printf("res: %d\n", (unsigned short)res_call);
/* { dg-output "\nres: 32767" } */
+ CHECK((unsigned short)res_call == 32767);
CHECK(ffi_prep_closure_loc(pcl, &cif, test_func_gn, NULL, code) == FFI_OK);
/* { dg-output "\n2 32765: 32767" } */
printf("res: %d\n", res_closure);
/* { dg-output "\nres: 32767" } */
+ CHECK(res_closure == 32767);
exit(0);
}
printf("%d %d %d %d: %d\n", a1, a2, a3, a4, result);
+ CHECK(a1 == 1);
+ CHECK(a2 == 32765);
+ CHECK(a3 == 127);
+ CHECK(a4 == -128);
+ CHECK(result == 32765);
+
return result;
}
/* { dg-output "1 32765 127 -128: 32765" } */
printf("res: %d\n", (signed short)res_call);
/* { dg-output "\nres: 32765" } */
+ CHECK((signed short)res_call == 32765);
CHECK(ffi_prep_closure_loc(pcl, &cif, test_func_gn, NULL, code) == FFI_OK);
/* { dg-output "\n1 32765 127 -128: 32765" } */
printf("res: %d\n", res_closure);
/* { dg-output "\nres: 32765" } */
+ CHECK(res_closure == 32765);
exit(0);
}
printf("%d %d %d %d: %d\n", a1, a2, a3, a4, result);
+ CHECK(a1 == 1);
+ CHECK(a2 == 2);
+ CHECK(a3 == 127);
+ CHECK(a4 == 125);
+ CHECK(result == 255);
+
return result;
}
/* { dg-output "1 2 127 125: 255" } */
printf("res: %d\n", (unsigned char)res_call);
/* { dg-output "\nres: 255" } */
+ CHECK((unsigned char)res_call == 255);
CHECK(ffi_prep_closure_loc(pcl, &cif, test_func_gn, NULL, code) == FFI_OK);
/* { dg-output "\n1 2 127 125: 255" } */
printf("res: %d\n", res_closure);
/* { dg-output "\nres: 255" } */
+ CHECK(res_closure == 255);
exit(0);
}
printf("%d %d: %d\n", a1, a2, result);
- return result;
+ CHECK(a1 == 2);
+ CHECK(a2 == 32765);
+ CHECK(result == 32767);
+ return result;
}
static void test_func_gn(ffi_cif *cif __UNUSED__, void *rval, void **avals,
/* { dg-output "\n2 32765: 32767" } */
printf("res: %d\n", res_closure);
/* { dg-output "\nres: 32767" } */
+ CHECK(res_closure == 32767);
exit(0);
}
printf("%d %d %d %d: %d\n", a1, a2, a3, a4, result);
+ CHECK(a1 == 1);
+ CHECK(a2 == 2);
+ CHECK(a3 == 127);
+ CHECK(a4 == 128);
+ CHECK(result == 258);
+
return result;
}
/* { dg-output "1 2 127 128: 258" } */
printf("res: %d\n", (unsigned short)res_call);
/* { dg-output "\nres: 258" } */
+ CHECK((unsigned short)res_call == 258);
CHECK(ffi_prep_closure_loc(pcl, &cif, test_func_gn, NULL, code) == FFI_OK);
/* { dg-output "\n1 2 127 128: 258" } */
printf("res: %d\n", res_closure);
/* { dg-output "\nres: 258" } */
+ CHECK(res_closure == 258);
exit(0);
}
(unsigned int)(uintptr_t) a2,
(unsigned int)(uintptr_t) result);
+ CHECK((unsigned int)(uintptr_t) a1 == 0x12345678);
+ CHECK((unsigned int)(uintptr_t) a2 == 0x89abcdef);
+ CHECK((unsigned int)(uintptr_t) result == 0x9be02467);
+
return result;
}
/* { dg-output "\n0x12345678 0x89abcdef: 0x9be02467" } */
printf("res: 0x%08x\n", (unsigned int) res);
/* { dg-output "\nres: 0x9be02467" } */
+ CHECK(res == 0x9be02467);
exit(0);
}
(unsigned int)(uintptr_t) a2,
(unsigned int)(uintptr_t) result);
+ CHECK((unsigned int)(uintptr_t) a1 == 0x8acf1356);
+ CHECK((unsigned int)(uintptr_t) a2 == 0x01234567);
+ CHECK((unsigned int)(uintptr_t) result == 0x8bf258bd);
+
return result;
}
(unsigned int)(intptr_t) a2,
(unsigned int)(intptr_t) result);
+ CHECK((unsigned int)(uintptr_t) a1 == 0x01234567);
+ CHECK((unsigned int)(uintptr_t) a2 == 0x89abcdef);
+ CHECK((unsigned int)(uintptr_t) result == 0x8acf1356);
+
result = cls_pointer_fn2(result, a1);
return result;
/* { dg-output "\n0x01234567 0x89abcdef: 0x8acf1356" } */
/* { dg-output "\n0x8acf1356 0x01234567: 0x8bf258bd" } */
/* { dg-output "\nres: 0x8bf258bd" } */
+ CHECK(res == 0x8bf258bd);
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_pointer_gn, NULL, code) == FFI_OK);
/* { dg-output "\n0x01234567 0x89abcdef: 0x8acf1356" } */
/* { dg-output "\n0x8acf1356 0x01234567: 0x8bf258bd" } */
/* { dg-output "\nres: 0x8bf258bd" } */
+ CHECK(res == 0x8bf258bd);
exit(0);
}
*(ffi_arg*)resp = *(signed char *)args[0];
printf("%d: %d\n",*(signed char *)args[0],
(int)*(ffi_arg *)(resp));
+ CHECK(*(signed char *)args[0] == 127);
+ CHECK((int)*(ffi_arg *)(resp) == 127);
}
typedef signed char (*cls_ret_schar)(signed char);
/* { dg-output "127: 127" } */
printf("res: %d\n", res);
/* { dg-output "\nres: 127" } */
+ CHECK(res == 127);
exit(0);
}
*(ffi_arg*)resp = *(signed int *)args[0];
printf("%d: %d\n",*(signed int *)args[0],
(int)*(ffi_arg *)(resp));
+ CHECK(*(signed int *)args[0] == 65534);
+ CHECK((int)*(ffi_arg *)(resp) == 65534);
}
typedef signed int (*cls_ret_sint)(signed int);
*(ffi_arg*)resp = *(signed short *)args[0];
printf("%d: %d\n",*(signed short *)args[0],
(int)*(ffi_arg *)(resp));
+ CHECK(*(signed short *)args[0] == 255);
+ CHECK((int)*(ffi_arg *)(resp) == 255);
}
typedef signed short (*cls_ret_sshort)(signed short);
/* { dg-output "255: 255" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 255" } */
+ CHECK(res == 255);
exit(0);
}
printf ("%d %d %d %d %d %d %d %d %d %d\n", n, s1.a, s1.b,
l1.a, l1.b, l1.c, l1.d, l1.e,
s2.a, s2.b);
+ CHECK(n == 4);
+ CHECK(s1.a == 5);
+ CHECK(s1.b == 6);
+ CHECK(l1.a == 10);
+ CHECK(l1.b == 11);
+ CHECK(l1.c == 12);
+ CHECK(l1.d == 13);
+ CHECK(l1.e == 14);
+ CHECK(s2.a == 20);
+ CHECK(s2.b == 21);
* (ffi_arg*) resp = 42;
}
/* { dg-output "4 5 6 10 11 12 13 14 20 21" } */
printf("res: %d\n", (int) res);
/* { dg-output "\nres: 42" } */
+ CHECK(res == 42);
exit(0);
}
*(ffi_arg*)resp = *(unsigned char *)args[0];
printf("%d: %d\n",*(unsigned char *)args[0],
(int)*(ffi_arg *)(resp));
+ CHECK(*(unsigned char *)args[0] == 127);
+ CHECK((int)*(ffi_arg *)(resp) == 127);
}
typedef unsigned char (*cls_ret_uchar)(unsigned char);
/* { dg-output "127: 127" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 127" } */
+ CHECK(res == 127);
exit(0);
}
printf("%d: %d\n",*(unsigned int *)args[0],
(int)*(ffi_arg *)(resp));
+
+ CHECK(*(unsigned int *)args[0] == 2147483647);
+ CHECK((int)*(ffi_arg *)(resp) == 2147483647);
}
typedef unsigned int (*cls_ret_uint)(unsigned int);
/* { dg-output "2147483647: 2147483647" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 2147483647" } */
+ CHECK(res == 2147483647);
exit(0);
}
*(ffi_arg *)resp = *(T *)args[0];
printf("%d: %d %d\n", (int)*(ffi_arg *)resp, *(T *)args[0], *(T *)args[1]);
+ CHECK(*(T *)args[0] == 67);
+ CHECK(*(T *)args[1] == 4);
+ CHECK((int)*(ffi_arg *)resp == 67);
}
typedef T (*cls_ret_T)(T, ...);
/* { dg-output "67: 67 4" } */
printf("res: %d\n", res);
/* { dg-output "\nres: 67" } */
+ CHECK(res == 67);
exit(0);
}
*(T *)resp = *(T *)args[0];
printf("%ld: %ld %ld\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
+ CHECK(*(T *)args[0] == 67);
+ CHECK(*(T *)args[1] == 4);
+ CHECK(*(T *)resp == 67);
}
typedef T (*cls_ret_T)(T, ...);
/* { dg-output "67: 67 4" } */
printf("res: %ld\n", res);
/* { dg-output "\nres: 67" } */
+ CHECK(res == 67);
exit(0);
}
/* { dg-output "214: 1152921504606846761" } */
printf("res: %" PRIdLL "\n", res);
/* { dg-output "\nres: 1152921504606846761" } */
+ CHECK(res == 1152921504606846761LL);
res = (*((cls_ret_ulonglong)code))(9223372035854775808LL);
/* { dg-output "\n9223372035854775808: 8070450533247928831" } */
printf("res: %" PRIdLL "\n", res);
/* { dg-output "\nres: 8070450533247928831" } */
+ CHECK(res == 8070450533247928831LL);
exit(0);
}
printf("%d: %d\n",*(unsigned short *)args[0],
(int)*(ffi_arg *)(resp));
+ CHECK(*(unsigned short *)args[0] == 65535);
+ CHECK((int)*(ffi_arg *)(resp) == 65535);
}
typedef unsigned short (*cls_ret_ushort)(unsigned short);
/* { dg-output "65535: 65535" } */
printf("res: %d\n",res);
/* { dg-output "\nres: 65535" } */
+ CHECK(res == 65535);
exit(0);
}
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <ffi.h>
-#include "fficonfig.h"
-
-#if defined HAVE_STDINT_H
-#include <stdint.h>
-#endif
-
-#if defined HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-
-#define MAX_ARGS 256
-
-#define CHECK(x) \
- do { \
- if(!(x)){ \
- printf("Check failed:\n%s\n", #x); \
- abort(); \
- } \
- } while(0)
-
-/* Define macros so that compilers other than gcc can run the tests. */
-#undef __UNUSED__
-#if defined(__GNUC__)
-#define __UNUSED__ __attribute__((__unused__))
-#define __STDCALL__ __attribute__((stdcall))
-#define __THISCALL__ __attribute__((thiscall))
-#define __FASTCALL__ __attribute__((fastcall))
-#define __MSABI__ __attribute__((ms_abi))
-#else
-#define __UNUSED__
-#define __STDCALL__ __stdcall
-#define __THISCALL__ __thiscall
-#define __FASTCALL__ __fastcall
-#endif
-
-#ifndef ABI_NUM
-#define ABI_NUM FFI_DEFAULT_ABI
-#define ABI_ATTR
-#endif
-
-/* Prefer MAP_ANON(YMOUS) to /dev/zero, since we don't need to keep a
- file open. */
-#ifdef HAVE_MMAP_ANON
-# undef HAVE_MMAP_DEV_ZERO
-
-# include <sys/mman.h>
-# ifndef MAP_FAILED
-# define MAP_FAILED -1
-# endif
-# if !defined (MAP_ANONYMOUS) && defined (MAP_ANON)
-# define MAP_ANONYMOUS MAP_ANON
-# endif
-# define USING_MMAP
-
-#endif
-
-#ifdef HAVE_MMAP_DEV_ZERO
-
-# include <sys/mman.h>
-# ifndef MAP_FAILED
-# define MAP_FAILED -1
-# endif
-# define USING_MMAP
-
-#endif
-
-/* MinGW kludge. */
-#if defined(_WIN64) | defined(_WIN32)
-#define PRIdLL "I64d"
-#define PRIuLL "I64u"
-#else
-#define PRIdLL "lld"
-#define PRIuLL "llu"
-#endif
-
-/* Tru64 UNIX kludge. */
-#if defined(__alpha__) && defined(__osf__)
-/* Tru64 UNIX V4.0 doesn't support %lld/%lld, but long is 64-bit. */
-#undef PRIdLL
-#define PRIdLL "ld"
-#undef PRIuLL
-#define PRIuLL "lu"
-#define PRId8 "hd"
-#define PRIu8 "hu"
-#define PRId64 "ld"
-#define PRIu64 "lu"
-#define PRIuPTR "lu"
-#endif
-
-/* PA HP-UX kludge. */
-#if defined(__hppa__) && defined(__hpux__) && !defined(PRIuPTR)
-#define PRIuPTR "lu"
-#endif
-
-/* IRIX kludge. */
-#if defined(__sgi)
-/* IRIX 6.5 <inttypes.h> provides all definitions, but only for C99
- compilations. */
-#define PRId8 "hhd"
-#define PRIu8 "hhu"
-#if (_MIPS_SZLONG == 32)
-#define PRId64 "lld"
-#define PRIu64 "llu"
-#endif
-/* This doesn't match <inttypes.h>, which always has "lld" here, but the
- arguments are uint64_t, int64_t, which are unsigned long, long for
- 64-bit in <sgidefs.h>. */
-#if (_MIPS_SZLONG == 64)
-#define PRId64 "ld"
-#define PRIu64 "lu"
-#endif
-/* This doesn't match <inttypes.h>, which has "u" here, but the arguments
- are uintptr_t, which is always unsigned long. */
-#define PRIuPTR "lu"
-#endif
-
-/* Solaris < 10 kludge. */
-#if defined(__sun__) && defined(__svr4__) && !defined(PRIuPTR)
-#if defined(__arch64__) || defined (__x86_64__)
-#define PRIuPTR "lu"
-#else
-#define PRIuPTR "u"
-#endif
-#endif
-
-/* MSVC kludge. */
-#if defined _MSC_VER
-#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS)
-#define PRIuPTR "lu"
-#define PRIu8 "u"
-#define PRId8 "d"
-#define PRIu64 "I64u"
-#define PRId64 "I64d"
-#endif
-#endif
-
-#ifndef PRIuPTR
-#define PRIuPTR "u"
-#endif
+#include "../libffi.call/ffitest.h"
*/
/* { dg-do run { xfail strongarm*-*-* xscale*-*-* } } */
-/* { dg-options -mlong-double-128 { target powerpc64*-*-linux* } } */
+/* { dg-options -mlong-double-128 { target powerpc64*-*-linux-gnu* } } */
/* { dg-options -Wformat=0 { target moxie*-*-elf or1k-*-* } } */
#include <inttypes.h>
{
BigStruct retVal = {
ui8_1 + 1, si8_1 + 1, ui16_1 + 1, si16_1 + 1, ui32_1 + 1, si32_1 + 1,
- ui64_1 + 1, si64_1 + 1, f_1 + 1, d_1 + 1, ld_1 + 1, (char*)((intptr_t)p_1 + 1),
+ ui64_1 + 1, si64_1 + 1, f_1 + 1, d_1 + 1, ld_1 + 1, (char*)((intptr_t)p_1 + 1),
ui8_2 + 2, si8_2 + 2, ui16_2 + 2, si16_2 + 2, ui32_2 + 2, si32_2 + 2,
- ui64_2 + 2, si64_2 + 2, f_2 + 2, d_2 + 2, ld_2 + 2, (char*)((intptr_t)p_2 + 2),
+ ui64_2 + 2, si64_2 + 2, f_2 + 2, d_2 + 2, ld_2 + 2, (char*)((intptr_t)p_2 + 2),
ui8_3 + 3, si8_3 + 3, ui16_3 + 3, si16_3 + 3, ui32_3 + 3, si32_3 + 3,
- ui64_3 + 3, si64_3 + 3, f_3 + 3, d_3 + 3, ld_3 + 3, (char*)((intptr_t)p_3 + 3),
+ ui64_3 + 3, si64_3 + 3, f_3 + 3, d_3 + 3, ld_3 + 3, (char*)((intptr_t)p_3 + 3),
ui8_4 + 4, si8_4 + 4, ui16_4 + 4, si16_4 + 4, ui32_4 + 4, si32_4 + 4,
- ui64_4 + 4, si64_4 + 4, f_4 + 4, d_4 + 4, ld_4 + 4, (char*)((intptr_t)p_4 + 4),
+ ui64_4 + 4, si64_4 + 4, f_4 + 4, d_4 + 4, ld_4 + 4, (char*)((intptr_t)p_4 + 4),
ui8_5 + 5, si8_5 + 5};
printf("%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx "
"%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx "
"%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx "
"%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx %" PRIu8 " %" PRId8 "\n",
- ui8_1, si8_1, ui16_1, si16_1, ui32_1, si32_1, ui64_1, si64_1, f_1, d_1, ld_1, (unsigned long)p_1,
- ui8_2, si8_2, ui16_2, si16_2, ui32_2, si32_2, ui64_2, si64_2, f_2, d_2, ld_2, (unsigned long)p_2,
- ui8_3, si8_3, ui16_3, si16_3, ui32_3, si32_3, ui64_3, si64_3, f_3, d_3, ld_3, (unsigned long)p_3,
- ui8_4, si8_4, ui16_4, si16_4, ui32_4, si32_4, ui64_4, si64_4, f_4, d_4, ld_4, (unsigned long)p_4, ui8_5, si8_5,
+ ui8_1, si8_1, ui16_1, si16_1, ui32_1, si32_1, ui64_1, si64_1, f_1, d_1, ld_1, (long)(intptr_t)p_1,
+ ui8_2, si8_2, ui16_2, si16_2, ui32_2, si32_2, ui64_2, si64_2, f_2, d_2, ld_2, (long)(intptr_t)p_2,
+ ui8_3, si8_3, ui16_3, si16_3, ui32_3, si32_3, ui64_3, si64_3, f_3, d_3, ld_3, (long)(intptr_t)p_3,
+ ui8_4, si8_4, ui16_4, si16_4, ui32_4, si32_4, ui64_4, si64_4, f_4, d_4, ld_4, (long)(intptr_t)p_4, ui8_5, si8_5,
retVal.a, retVal.b, retVal.c, retVal.d, retVal.e, retVal.f,
- retVal.g, retVal.h, retVal.i, retVal.j, retVal.k, (unsigned long)retVal.l,
+ retVal.g, retVal.h, retVal.i, retVal.j, retVal.k, (long)(intptr_t)retVal.l,
retVal.m, retVal.n, retVal.o, retVal.p, retVal.q, retVal.r,
- retVal.s, retVal.t, retVal.u, retVal.v, retVal.w, (unsigned long)retVal.x,
+ retVal.s, retVal.t, retVal.u, retVal.v, retVal.w, (long)(intptr_t)retVal.x,
retVal.y, retVal.z, retVal.aa, retVal.bb, retVal.cc, retVal.dd,
- retVal.ee, retVal.ff, retVal.gg, retVal.hh, retVal.ii, (unsigned long)retVal.jj,
+ retVal.ee, retVal.ff, retVal.gg, retVal.hh, retVal.ii, (long)(intptr_t)retVal.jj,
retVal.kk, retVal.ll, retVal.mm, retVal.nn, retVal.oo, retVal.pp,
- retVal.qq, retVal.rr, retVal.ss, retVal.tt, retVal.uu, (unsigned long)retVal.vv, retVal.ww, retVal.xx);
+ retVal.qq, retVal.rr, retVal.ss, retVal.tt, retVal.uu, (long)(intptr_t)retVal.vv, retVal.ww, retVal.xx);
return retVal;
}
ui8_5, si8_5);
}
-int
-main(int argc __UNUSED__, const char** argv __UNUSED__)
+int main (void)
{
void *code;
ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
"%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx "
"%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx %" PRIu8 " %" PRId8 "\n",
retVal.a, retVal.b, retVal.c, retVal.d, retVal.e, retVal.f,
- retVal.g, retVal.h, retVal.i, retVal.j, retVal.k, (unsigned long)retVal.l,
+ retVal.g, retVal.h, retVal.i, retVal.j, retVal.k, (long)(intptr_t)retVal.l,
retVal.m, retVal.n, retVal.o, retVal.p, retVal.q, retVal.r,
- retVal.s, retVal.t, retVal.u, retVal.v, retVal.w, (unsigned long)retVal.x,
+ retVal.s, retVal.t, retVal.u, retVal.v, retVal.w, (long)(intptr_t)retVal.x,
retVal.y, retVal.z, retVal.aa, retVal.bb, retVal.cc, retVal.dd,
- retVal.ee, retVal.ff, retVal.gg, retVal.hh, retVal.ii, (unsigned long)retVal.jj,
+ retVal.ee, retVal.ff, retVal.gg, retVal.hh, retVal.ii, (long)(intptr_t)retVal.jj,
retVal.kk, retVal.ll, retVal.mm, retVal.nn, retVal.oo, retVal.pp,
- retVal.qq, retVal.rr, retVal.ss, retVal.tt, retVal.uu, (unsigned long)retVal.vv, retVal.ww, retVal.xx);
+ retVal.qq, retVal.rr, retVal.ss, retVal.tt, retVal.uu, (long)(intptr_t)retVal.vv, retVal.ww, retVal.xx);
/* { dg-output "\nres: 2 3 4 5 6 7 8 9 10 11 12 0x12345679 3 4 5 6 7 8 9 10 11 12 13 0x1234567a 4 5 6 7 8 9 10 11 12 13 14 0x1234567b 5 6 7 8 9 10 11 12 13 14 15 0x1234567c 6 7" } */
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_large_fn, NULL, code) == FFI_OK);
"%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx "
"%" PRIu8 " %" PRId8 " %hu %hd %u %d %" PRIu64 " %" PRId64 " %.0f %.0f %.0Lf %#lx %" PRIu8 " %" PRId8 "\n",
retVal.a, retVal.b, retVal.c, retVal.d, retVal.e, retVal.f,
- retVal.g, retVal.h, retVal.i, retVal.j, retVal.k, (unsigned long)retVal.l,
+ retVal.g, retVal.h, retVal.i, retVal.j, retVal.k, (long)(intptr_t)retVal.l,
retVal.m, retVal.n, retVal.o, retVal.p, retVal.q, retVal.r,
- retVal.s, retVal.t, retVal.u, retVal.v, retVal.w, (unsigned long)retVal.x,
+ retVal.s, retVal.t, retVal.u, retVal.v, retVal.w, (long)(intptr_t)retVal.x,
retVal.y, retVal.z, retVal.aa, retVal.bb, retVal.cc, retVal.dd,
- retVal.ee, retVal.ff, retVal.gg, retVal.hh, retVal.ii, (unsigned long)retVal.jj,
+ retVal.ee, retVal.ff, retVal.gg, retVal.hh, retVal.ii, (long)(intptr_t)retVal.jj,
retVal.kk, retVal.ll, retVal.mm, retVal.nn, retVal.oo, retVal.pp,
- retVal.qq, retVal.rr, retVal.ss, retVal.tt, retVal.uu, (unsigned long)retVal.vv, retVal.ww, retVal.xx);
+ retVal.qq, retVal.rr, retVal.ss, retVal.tt, retVal.uu, (long)(intptr_t)retVal.vv, retVal.ww, retVal.xx);
/* { dg-output "\nres: 2 3 4 5 6 7 8 9 10 11 12 0x12345679 3 4 5 6 7 8 9 10 11 12 13 0x1234567a 4 5 6 7 8 9 10 11 12 13 14 0x1234567b 5 6 7 8 9 10 11 12 13 14 15 0x1234567c 6 7" } */
return 0;
result.d.a, result.d.b, result.d.c,
result.e.ii, result.e.dd, result.e.ff);
+ CHECK_DOUBLE_EQ(b0.a, 9);
+ CHECK_FLOAT_EQ(b0.b, 2);
+ CHECK(b0.c == 6);
+
+ CHECK(b1.ii == 1);
+ CHECK_DOUBLE_EQ(b1.dd, 2);
+ CHECK_FLOAT_EQ(b1.ff, 3);
+
+ CHECK_DOUBLE_EQ(b2.d.a, 4);
+ CHECK_FLOAT_EQ(b2.d.b, 5);
+ CHECK(b2.d.c == 6);
+
+ CHECK(b2.e.ii == 3);
+ CHECK_DOUBLE_EQ(b2.e.dd, 1);
+ CHECK_FLOAT_EQ(b2.e.ff, 8);
+
+ CHECK_DOUBLE_EQ(result.d.a, 15);
+ CHECK_FLOAT_EQ(result.d.b, 10);
+ CHECK(result.d.c == 13);
+ CHECK(result.e.ii == 10);
+ CHECK_DOUBLE_EQ(result.e.dd, 12);
+ CHECK_FLOAT_EQ(result.e.ff, 13);
+
return result;
}
ffi_call(&cif, FFI_FN(cls_struct_combined_fn), &res_dbl, args_dbl);
/* { dg-output "9 2 6 1 2 3 4 5 6 3 1 8: 15 10 13 10 12 13" } */
- CHECK( res_dbl.d.a == (e_dbl.a + f_dbl.dd + g_dbl.d.a));
- CHECK( res_dbl.d.b == (e_dbl.b + f_dbl.ff + g_dbl.d.b));
+ CHECK_DOUBLE_EQ( res_dbl.d.a, (e_dbl.a + f_dbl.dd + g_dbl.d.a));
+ CHECK_FLOAT_EQ( res_dbl.d.b, (e_dbl.b + f_dbl.ff + g_dbl.d.b));
CHECK( res_dbl.d.c == (e_dbl.c + f_dbl.ii + g_dbl.d.c));
CHECK( res_dbl.e.ii == (e_dbl.c + f_dbl.ii + g_dbl.e.ii));
- CHECK( res_dbl.e.dd == (e_dbl.a + f_dbl.dd + g_dbl.e.dd));
- CHECK( res_dbl.e.ff == (e_dbl.b + f_dbl.ff + g_dbl.e.ff));
+ CHECK_DOUBLE_EQ( res_dbl.e.dd, (e_dbl.a + f_dbl.dd + g_dbl.e.dd));
+ CHECK_FLOAT_EQ( res_dbl.e.ff, (e_dbl.b + f_dbl.ff + g_dbl.e.ff));
CHECK(ffi_prep_closure_loc(pcl, &cif, cls_struct_combined_gn, NULL, code) == FFI_OK);
cls_struct_combined))
(code))(e_dbl, f_dbl, g_dbl);
/* { dg-output "\n9 2 6 1 2 3 4 5 6 3 1 8: 15 10 13 10 12 13" } */
- CHECK( res_dbl.d.a == (e_dbl.a + f_dbl.dd + g_dbl.d.a));
- CHECK( res_dbl.d.b == (e_dbl.b + f_dbl.ff + g_dbl.d.b));
+ CHECK_DOUBLE_EQ( res_dbl.d.a, (e_dbl.a + f_dbl.dd + g_dbl.d.a));
+ CHECK_FLOAT_EQ( res_dbl.d.b, (e_dbl.b + f_dbl.ff + g_dbl.d.b));
CHECK( res_dbl.d.c == (e_dbl.c + f_dbl.ii + g_dbl.d.c));
CHECK( res_dbl.e.ii == (e_dbl.c + f_dbl.ii + g_dbl.e.ii));
- CHECK( res_dbl.e.dd == (e_dbl.a + f_dbl.dd + g_dbl.e.dd));
- CHECK( res_dbl.e.ff == (e_dbl.b + f_dbl.ff + g_dbl.e.ff));
+ CHECK_DOUBLE_EQ( res_dbl.e.dd, (e_dbl.a + f_dbl.dd + g_dbl.e.dd));
+ CHECK_FLOAT_EQ( res_dbl.e.ff, (e_dbl.b + f_dbl.ff + g_dbl.e.ff));
exit(0);
}
(int)b3.x.a, b3.x.b, b3.y, b3.z, (int)b4.d, b4.e,
(int)result.x.a, result.x.b, result.y);
+ CHECK((int)b2.a == 1);
+ CHECK(b2.b == 7);
+ CHECK((int)b3.x.a == 12);
+ CHECK(b3.x.b == 127);
+ CHECK(b3.y == 99);
+ CHECK(b3.z == 255);
+ CHECK((int)b4.d == 2);
+ CHECK(b4.e == 9);
+ CHECK((int)result.x.a == 270);
+ CHECK(result.x.b == 242);
+ CHECK(result.y == 143);
+
return result;
}
printf ("%d, %d, %d, %d, %d, %d\n", x, y, z, i, j, k);
+ CHECK(x == 1);
+ CHECK(y == 1);
+ CHECK(z == 1);
+ CHECK(i == 1);
+ CHECK(j == 1);
+ CHECK(k == 1);
+
printf ("%.1f, %.1f, %.1f, %.1f, "
"%.1f, %.1f, %.1f, %.1f\n",
source.a.a_x, source.a.a_y,
result.a.a_x, result.a.a_y,
result.b.b_x, result.b.b_y);
+ CHECK_FLOAT_EQ(source.a.a_x, 1.0);
+ CHECK_FLOAT_EQ(source.a.a_y, 2.0);
+ CHECK_FLOAT_EQ(source.b.b_x, 4.0);
+ CHECK_FLOAT_EQ(source.b.b_y, 8.0);
+ CHECK_FLOAT_EQ(result.a.a_x, 1.0);
+ CHECK_FLOAT_EQ(result.a.a_y, 2.0);
+ CHECK_FLOAT_EQ(result.b.b_x, 4.0);
+ CHECK_FLOAT_EQ(result.b.b_y, 8.0);
+
return result;
}
ffi_call (&cif, FFI_FN (C_fn), &result, args);
/* { dg-output "1, 1, 1, 1, 1, 1\n" } */
/* { dg-output "1.0, 2.0, 4.0, 8.0, 1.0, 2.0, 4.0, 8.0" } */
- CHECK (result.a.a_x == source.a.a_x);
- CHECK (result.a.a_y == source.a.a_y);
- CHECK (result.b.b_x == source.b.b_x);
- CHECK (result.b.b_y == source.b.b_y);
- exit (0);
+ CHECK_FLOAT_EQ(result.a.a_x, source.a.a_x);
+ CHECK_FLOAT_EQ(result.a.a_y, source.a.a_y);
+ CHECK_FLOAT_EQ(result.b.b_x, source.b.b_x);
+ CHECK_FLOAT_EQ(result.b.b_y, source.b.b_y);
+ exit(0);
}
CHECK( ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, retType, args) == FFI_OK);
ffi_call(&cif, FFI_FN(testNestedFloatStruct), &ts12_result, values);
- CHECK(ts12_result == 138.2f);
+ CHECK_FLOAT_EQ(ts12_result, 138.2f);
free(struct_float1);
free(struct_float2);
printf("%g %g %g %g: %g %g %g\n", b0, b1.x, b1.y.a, b1.y.b,
result.x, result.y.a, result.y.b);
+ CHECK_FLOAT_EQ(b0, 12.125);
+ CHECK_FLOAT_EQ(b1.x, 24.75);
+ CHECK_FLOAT_EQ(b1.y.a, 31.625);
+ CHECK_FLOAT_EQ(b1.y.b, 32.25);
+ CHECK_FLOAT_EQ(result.x, 36.875);
+ CHECK_FLOAT_EQ(result.y.a, 43.75);
+ CHECK_FLOAT_EQ(result.y.b, 44.375);
+
return result;
}
ffi_call(&cif, FFI_FN(B_fn), &res_dbl, args_dbl);
/* { dg-output "12.125 24.75 31.625 32.25: 36.875 43.75 44.375" } */
- CHECK( res_dbl.x == (e_dbl + f_dbl.x));
- CHECK( res_dbl.y.a == (e_dbl + f_dbl.y.a));
- CHECK( res_dbl.y.b == (e_dbl + f_dbl.y.b));
+ CHECK_FLOAT_EQ( res_dbl.x, (e_dbl + f_dbl.x));
+ CHECK_FLOAT_EQ( res_dbl.y.a, (e_dbl + f_dbl.y.a));
+ CHECK_FLOAT_EQ( res_dbl.y.b, (e_dbl + f_dbl.y.b));
CHECK(ffi_prep_closure_loc(pcl, &cif, B_gn, NULL, code) == FFI_OK);
res_dbl = ((B(*)(float, B))(code))(e_dbl, f_dbl);
/* { dg-output "\n12.125 24.75 31.625 32.25: 36.875 43.75 44.375" } */
- CHECK( res_dbl.x == (e_dbl + f_dbl.x));
- CHECK( res_dbl.y.a == (e_dbl + f_dbl.y.a));
- CHECK( res_dbl.y.b == (e_dbl + f_dbl.y.b));
+ CHECK_FLOAT_EQ( res_dbl.x, (e_dbl + f_dbl.x));
+ CHECK_FLOAT_EQ( res_dbl.y.a, (e_dbl + f_dbl.y.a));
+ CHECK_FLOAT_EQ( res_dbl.y.b, (e_dbl + f_dbl.y.b));
exit(0);
}
printf("%lu %d %lu %d %d: %lu %d %d\n", b0.a, b0.b, b1.x.a, b1.x.b, b1.y,
result.x.a, result.x.b, result.y);
+ CHECK(b0.a == 1);
+ CHECK(b0.b == 7);
+ CHECK(b1.x.a == 12);
+ CHECK(b1.x.b == 127);
+ CHECK(b1.y == 99);
+ CHECK(result.x.a == 13);
+ CHECK(result.x.b == 233);
+ CHECK(result.y == 134);
+
return result;
}
(int)b1.x.a, b1.x.b, b1.y,
(int)result.x.a, result.x.b, result.y);
+ CHECK((int)b0.a == 1);
+ CHECK(b0.b == 7);
+ CHECK((int)b1.x.a == 12);
+ CHECK(b1.x.b == 127);
+ CHECK(b1.y == 99);
+ CHECK((int)result.x.a == 13);
+ CHECK(result.x.b == 233);
+ CHECK(result.y == 134);
+
return result;
}
(int)b3.x.a, b3.x.b, b3.y,
(int)result.x.a, result.x.b, result.y);
+ CHECK((int)b2.a == 1);
+ CHECK(b2.b == 7);
+ CHECK((int)b3.x.a == 12);
+ CHECK(b3.x.b == 127);
+ CHECK(b3.y == 99);
+ CHECK((int)result.x.a == 13);
+ CHECK(result.x.b == 233);
+ CHECK(result.y == 134);
+
return result;
}
(int)b3.x.a, b3.x.b, b3.y,
(int)result.x.a, result.x.b, result.y);
+ CHECK((int)b2.a == 1);
+ CHECK(b2.b == 7);
+ CHECK((int)b3.x.a == 12);
+ CHECK(b3.x.b == 127);
+ CHECK(b3.y == 99);
+ CHECK((int)result.x.a == 13);
+ CHECK(result.x.b == 233);
+ CHECK(result.y == 134);
+
return result;
}
(int)b3.x.a, b3.x.b, b3.y, (int)b4.d, b4.e,
(int)result.x.a, result.x.b, result.y);
+ CHECK((int)b2.a == 1);
+ CHECK(b2.b == 7);
+ CHECK((int)b3.x.a == 12);
+ CHECK(b3.x.b == 127);
+ CHECK(b3.y == 99);
+ CHECK((int)b4.d == 2);
+ CHECK(b4.e == 9);
+ CHECK((int)result.x.a == 15);
+ CHECK(result.x.b == 242);
+ CHECK(result.y == 143);
+
return result;
}
(int)b3.x.a, b3.x.b, b3.y,
(int)result.x.a, result.x.b, result.y);
+ CHECK((int)b2.a == 1);
+ CHECK(b2.b == 7);
+ CHECK((int)b3.x.a == 12);
+ CHECK(b3.x.b == 127);
+ CHECK(b3.y == 99);
+ CHECK((int)result.x.a == 13);
+ CHECK(result.x.b == 233);
+ CHECK(result.y == 134);
+
return result;
}
(int)b3.x.a, b3.x.b, b3.y, (int)b4.d, b4.e,
(int)result.x.a, result.x.b, result.y);
+ CHECK((int)b2.a == 1);
+ CHECK(b2.b == 7);
+ CHECK((int)b3.x.a == 12);
+ CHECK(b3.x.b == 127);
+ CHECK(b3.y == 99);
+ CHECK((int)b4.d == 2);
+ CHECK(b4.e == 9);
+ CHECK((int)result.x.a == 15);
+ CHECK(result.x.b == 242);
+ CHECK(result.y == 143);
+
return result;
}
b3.x.a, (int)b3.x.b, b3.y, (int)b4.d, b4.e,
result.x.a, (int)result.x.b, result.y);
+ CHECK((int)b2.a == 1);
+ CHECK(b2.b == 7);
+ CHECK((int)b3.x.a == 12);
+ CHECK(b3.x.b == 127);
+ CHECK(b3.y == 99);
+ CHECK((int)b4.d == 2);
+ CHECK(b4.e == 9);
+ CHECK((int)result.x.a == 15);
+ CHECK(result.x.b == 242);
+ CHECK(result.y == 143);
+
return result;
}
arg.a, arg.b,
arg.c, arg.d);
fflush(stdout);
+
+ CHECK_FLOAT_EQ(arg.a, 4);
+ CHECK_FLOAT_EQ(arg.b, 5);
+ CHECK_FLOAT_EQ(arg.c, 1);
+ CHECK_FLOAT_EQ(arg.d, 8);
}
static void
PR: none.
Originator: Jeff Sturm <jsturm@one-point.com> */
-/* { dg-do run { xfail x86_64-apple-darwin* moxie*-*-* } } */
+/* { dg-do run { xfail moxie*-*-* } } */
#include "ffitest.h"
float, float, int, double, int, int, float,
int, int, int, int);
+#ifdef __EMSCRIPTEN__
+extern "C"
+#endif
int main (void)
{
ffi_cif cif;
throw 9;
}
+#ifdef __EMSCRIPTEN__
+extern "C"
+#endif
int main (void)
{
ffi_cif cif;
printf ("%d,%di %d,%di, x %d 1234, y %d 11110\n",
(int)tc_result, (int)(tc_result * -I), 2, 8, tc_int_arg_x, tc_y);
- /* dg-output "-2,8i 2,8i, x 1234 1234, y 11110 11110" */
+ /* { dg-output "-2,8i 2,8i, x 1234 1234, y 11110 11110" } */
CHECK (creal (tc_result) == -2);
CHECK (cimag (tc_result) == 8);
CHECK (tc_int_arg_x == 1234);
--- /dev/null
+#include "../libffi.call/ffitest.h"
--- /dev/null
+# Copyright (C) 2003, 2006, 2009, 2010, 2014 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+dg-init
+libffi-init
+
+global srcdir subdir
+
+if { [string match $compiler_vendor "microsoft"] } {
+ # -wd4005 macro redefinition
+ # -wd4244 implicit conversion to type of smaller size
+ # -wd4305 truncation to smaller type
+ # -wd4477 printf %lu of uintptr_t
+ # -wd4312 implicit conversion to type of greater size
+ # -wd4311 pointer truncation to unsigned long
+ # -EHsc C++ Exception Handling (no SEH exceptions)
+ set additional_options "-wd4005 -wd4244 -wd4305 -wd4477 -wd4312 -wd4311 -EHsc";
+} else {
+ set additional_options "";
+}
+
+set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.c]]
+
+# No pthreads for windows or wasm
+if { [string match $compiler_vendor "microsoft"] || [istarget "wasm*-*-*"] } {
+ foreach test $tlist {
+ unsupported "$test"
+ }
+} else {
+ run-many-tests $tlist $additional_options
+}
+
+dg-finish
+
+# Local Variables:
+# tcl-indent-level:4
+# End:
--- /dev/null
+/* { dg-do run } */
+
+#include "ffitest.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NUM_THREADS 20
+
+#ifdef _POSIX_BARRIERS
+pthread_barrier_t barrier;
+#endif
+
+typedef float (*callback_fn)(float, float);
+
+void callback(ffi_cif *cif __UNUSED__, void *ret, void **args, void *userdata __UNUSED__) {
+ float a = *(float *)args[0];
+ float b = *(float *)args[1];
+ *(float *)ret = a / 2 + b / 2;
+}
+
+void *thread_func(void *arg) {
+#ifdef _POSIX_BARRIERS
+ pthread_barrier_wait(&barrier);
+#endif
+
+ ffi_cif cif;
+ ffi_type *args[2] = { &ffi_type_float, &ffi_type_float };
+
+ if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_float, args) != FFI_OK) {
+ fprintf(stderr, "ffi_prep_cif failed\n");
+ return NULL;
+ }
+
+ ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), (void **)&arg);
+
+ if (ffi_prep_closure_loc(closure, &cif, callback, NULL, arg) != FFI_OK) {
+ fprintf(stderr, "ffi_prep_closure_loc failed\n");
+ return NULL;
+ }
+
+ callback_fn fn = (callback_fn)arg;
+ (void) fn(4.0f, 6.0f);
+
+ ffi_closure_free(closure);
+ return NULL;
+}
+
+int main() {
+ pthread_t threads[NUM_THREADS];
+
+#ifdef _POSIX_BARRIERS
+ pthread_barrier_init(&barrier, NULL, NUM_THREADS);
+#endif
+
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ if (pthread_create(&threads[i], NULL, thread_func, NULL) != 0) {
+ perror("pthread_create");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ pthread_join(threads[i], NULL);
+ }
+
+#ifdef _POSIX_BARRIERS
+ pthread_barrier_destroy(&barrier);
+#endif
+
+ printf("Completed\n");
+ return 0;
+}