AArch64 port
authorAnthony Green <green@moxielogic.com>
Tue, 30 Oct 2012 11:07:19 +0000 (07:07 -0400)
committerAnthony Green <green@moxielogic.com>
Tue, 30 Oct 2012 11:07:19 +0000 (07:07 -0400)
36 files changed:
.pc/aarch64/.timestamp [new file with mode: 0644]
.pc/aarch64/README [new file with mode: 0644]
.pc/aarch64/src/aarch64/ffi.c [new file with mode: 0644]
.pc/aarch64/src/aarch64/ffitarget.h [new file with mode: 0644]
.pc/aarch64/src/aarch64/sysv.S [new file with mode: 0644]
.pc/aarch64/testsuite/lib/libffi.exp [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/cls_struct_va1.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/cls_uchar_va.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/cls_uint_va.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/cls_ulong_va.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/cls_ushort_va.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/nested_struct11.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/uninitialized.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/va_1.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/va_struct1.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/va_struct2.c [new file with mode: 0644]
.pc/aarch64/testsuite/libffi.call/va_struct3.c [new file with mode: 0644]
.pc/applied-patches
README
patches/aarch64 [new file with mode: 0644]
patches/series
src/aarch64/ffi.c [new file with mode: 0644]
src/aarch64/ffitarget.h [new file with mode: 0644]
src/aarch64/sysv.S [new file with mode: 0644]
testsuite/lib/libffi.exp
testsuite/libffi.call/cls_struct_va1.c [new file with mode: 0644]
testsuite/libffi.call/cls_uchar_va.c [new file with mode: 0644]
testsuite/libffi.call/cls_uint_va.c [new file with mode: 0644]
testsuite/libffi.call/cls_ulong_va.c [new file with mode: 0644]
testsuite/libffi.call/cls_ushort_va.c [new file with mode: 0644]
testsuite/libffi.call/nested_struct11.c [new file with mode: 0644]
testsuite/libffi.call/uninitialized.c [new file with mode: 0644]
testsuite/libffi.call/va_1.c [new file with mode: 0644]
testsuite/libffi.call/va_struct1.c [new file with mode: 0644]
testsuite/libffi.call/va_struct2.c [new file with mode: 0644]
testsuite/libffi.call/va_struct3.c [new file with mode: 0644]

diff --git a/.pc/aarch64/.timestamp b/.pc/aarch64/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/README b/.pc/aarch64/README
new file mode 100644 (file)
index 0000000..ccd4f8b
--- /dev/null
@@ -0,0 +1,366 @@
+Status
+======
+
+libffi-3.0.12 was released on XXXXXXX.  Check the libffi web page for
+updates: <URL:http://sourceware.org/libffi/>.
+
+
+What is libffi?
+===============
+
+Compilers for high level languages generate code that follow certain
+conventions. These conventions are necessary, in part, for separate
+compilation to work. One such convention is the "calling
+convention". The "calling convention" is essentially a set of
+assumptions made by the compiler about where function arguments will
+be found on entry to a function. A "calling convention" also specifies
+where the return value for a function is found.
+
+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. Libffi can be used in such programs to provide a
+bridge from the interpreter program to compiled code.
+
+The 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.  
+
+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
+libffi library really only provides the lowest, machine dependent
+layer of a fully featured foreign function interface. A layer must
+exist above libffi that handles type conversions for values passed
+between the two languages.
+
+
+Supported Platforms
+===================
+
+Libffi has been ported to many different platforms.
+For specific configuration details and testing status, please
+refer to the wiki page here:
+
+ http://www.moxielogic.org/wiki/index.php?title=Libffi_3.0.11
+
+At the time of release, the following basic configurations have been
+tested:
+
+|-----------------+------------------|
+| Architecture    | Operating System |
+|-----------------+------------------|
+| Alpha           | Linux            |
+| Alpha           | Tru64            |
+| ARM             | Linux            |
+| ARM             | iOS              |
+| AVR32           | Linux            |
+| Blackfin        | uClinux          |
+| HPPA            | HPUX             |
+| IA-64           | Linux            |
+| M68K            | FreeMiNT         |
+| M68K            | RTEMS            |
+| MIPS            | IRIX             |
+| MIPS            | Linux            |
+| MIPS            | RTEMS            |
+| MIPS64          | Linux            |
+| PowerPC         | AMIGA            |
+| PowerPC         | Linux            |
+| PowerPC         | Mac OSX          |
+| PowerPC         | FreeBSD          |
+| PowerPC64       | Linux            |
+| S390            | Linux            |
+| S390X           | Linux            |
+| SPARC           | Linux            |
+| SPARC           | Solaris          |
+| SPARC64         | Linux            |
+| SPARC64         | FreeBSD          |
+| TILE-Gx/TILEPro | Linux            |
+| X86             | FreeBSD          |
+| X86             | Interix          |
+| X86             | kFreeBSD         |
+| X86             | Linux            |
+| X86             | Mac OSX          |
+| X86             | OpenBSD          |
+| X86             | OS/2             |
+| X86             | Solaris          |
+| X86             | Windows/Cygwin   |
+| X86             | Windows/MingW    |
+| X86-64          | FreeBSD          |
+| X86-64          | Linux            |
+| X86-64          | Linux/x32        |
+| X86-64          | OpenBSD          |
+| X86-64          | Windows/MingW    |
+|-----------------+------------------|
+
+Please send additional platform test results to
+libffi-discuss@sourceware.org and feel free to update the wiki page
+above.
+
+Installing libffi
+=================
+
+First you must configure the distribution for your particular
+system. Go to the directory you wish to build libffi in and run the
+"configure" program found in the root directory of the libffi source
+distribution.
+
+You may want to tell configure where to install the libffi library and
+header files. To do that, use the --prefix configure switch.  Libffi
+will install under /usr/local by default. 
+
+If you want to enable extra run-time debugging checks use the the
+--enable-debug configure switch. This is useful when your program dies
+mysteriously while using libffi. 
+
+Another useful configure switch is --enable-purify-safety. Using this
+will add some extra code which will suppress certain warnings when you
+are using Purify with libffi. Only use this switch when using 
+Purify, as it will slow down the library.
+
+It's also possible to build libffi on Windows platforms with
+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 LD=link CPP=\"cl -nologo -EP\"
+
+For 64-bit Windows builds, use CC="path/to/msvcc.sh -m64".
+You may also need to specify --build appropriately. When building with MSVC
+under a MingW environment, you may need to remove the line in configure
+that sets 'fix_srcfile_path' to a 'cygpath' command. ('cygpath' is not
+present in MingW, and is not required when using MingW-style paths.)
+
+For iOS builds, the 'libffi.xcodeproj' Xcode project is available.
+
+Configure has many other options. Use "configure --help" to see them all.
+
+Once configure has finished, type "make". Note that you must be using
+GNU make.  You can ftp GNU make from prep.ai.mit.edu:/pub/gnu.
+
+To ensure that libffi is working as advertised, type "make check".
+This will require that you have DejaGNU installed.
+
+To install the library and header files, type "make install".
+
+
+History
+=======
+
+See the ChangeLog files for details.
+
+3.0.12 XXX-XX-XX
+       Add Blackfin support.
+       Add TILE-Gx/TILEPro support.
+
+3.0.11 Apr-11-12
+        Lots of build fixes.
+       Add Amiga newer MacOS support.
+       Add support for variadic functions (ffi_prep_cif_var).
+       Add Linux/x32 support.
+       Add thiscall, fastcall and MSVC cdecl support on Windows.
+       Add Amiga and newer MacOS support.
+       Add m68k FreeMiNT support.
+       Integration with iOS' xcode build tools.
+       Fix Octeon and MC68881 support.
+       Fix code pessimizations.
+       Lots of build fixes.
+
+3.0.10 Aug-23-11
+        Add support for Apple's iOS.
+       Add support for ARM VFP ABI.
+        Add RTEMS support for MIPS and M68K.
+       Fix instruction cache clearing problems on
+         ARM and SPARC.
+       Fix the N64 build on mips-sgi-irix6.5.
+       Enable builds with Microsoft's compiler.
+       Enable x86 builds with Oracle's Solaris compiler.
+       Fix support for calling code compiled with Oracle's Sparc
+         Solaris compiler.
+       Testsuite fixes for Tru64 Unix.
+       Additional platform support.
+
+3.0.9 Dec-31-09
+        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
+        Add *BSD, BeOS, and PA-Linux support.
+
+3.0.7 Nov-11-08
+        Fix for ppc FreeBSD.
+       (thanks to Andreas Tobler)
+
+3.0.6 Jul-17-08
+        Fix for closures on sh.
+       Mark the sh/sh64 stack as non-executable.
+       (both thanks to Kaz Kojima)
+
+3.0.5 Apr-3-08
+        Fix libffi.pc file.
+       Fix #define ARM for IcedTea users.
+       Fix x86 closure bug.
+
+3.0.4 Feb-24-08
+        Fix x86 OpenBSD configury.
+
+3.0.3 Feb-22-08
+        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
+        Improved x86 FreeBSD support.
+       Thanks to Björn König.
+
+3.0.1 Feb-15-08
+        Fix instruction cache flushing bug on MIPS.
+       Thanks to David Daney.
+
+3.0.0 Feb-15-08
+        Many changes, mostly thanks to the GCC project.
+       Cygnus Solutions is now Red Hat.
+
+  [10 years go by...]
+
+1.20 Oct-5-98
+       Raffaele Sena produces ARM port.
+
+1.19 Oct-5-98
+       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
+       Bug fixes and MIPS configuration changes.
+
+1.17 Feb-24-98
+       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
+       Richard Henderson produces Alpha port.
+
+1.15 Dec-4-97
+       Fixed an n32 ABI bug. New libtool, auto* support.
+
+1.14 May-13-97
+       libtool is now used to generate shared and static libraries.
+       Fixed a minor portability problem reported by Russ McManus
+       <mcmanr@eq.gs.com>.
+
+1.13 Dec-2-96
+       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
+       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
+       Added notes about GNU make.
+
+1.10 Oct-29-96
+       Added configuration fix for non GNU compilers.
+
+1.09 Oct-29-96
+       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
+       Fixed n32 problem. Many clean-ups.
+
+1.07 Oct-14-96
+       Gordon Irlam rewrites v8.S again. Bug fixes.
+
+1.06 Oct-14-96
+       Gordon Irlam improved the sparc port. 
+
+1.05 Oct-14-96
+       Interface changes based on feedback.
+
+1.04 Oct-11-96
+       Sparc port complete (modulo struct passing bug).
+
+1.03 Oct-10-96
+       Passing struct args, and returning struct values works for
+       all architectures/calling conventions. Expanded tests.
+
+1.02 Oct-9-96
+       Added SGI n32 support. Fixed bugs in both o32 and Linux support.
+       Added "make test".
+
+1.01 Oct-8-96
+       Fixed float passing bug in mips version. Restructured some
+       of the code. Builds cleanly with SGI tools.
+
+1.00 Oct-7-96
+       First release. No public announcement.
+
+
+Authors & Credits
+=================
+
+libffi was originally written by Anthony Green <green@redhat.com>.
+
+The developers of the GNU Compiler Collection project have made
+innumerable valuable contributions.  See the ChangeLog file for
+details.
+
+Some of the ideas behind libffi were inspired by Gianni Mariani's free
+gencall library for Silicon Graphics machines.
+
+The closure mechanism was designed and implemented by Kresten Krab
+Thorup.
+
+Major processor architecture ports were contributed by the following
+developers:
+
+alpha          Richard Henderson
+arm            Raffaele Sena
+blackfin        Alexandre Keunecke I. de Mendonca
+cris           Simon Posnjak, Hans-Peter Nilsson
+frv            Anthony Green
+ia64           Hans Boehm
+m32r           Kazuhiro Inaoka
+m68k           Andreas Schwab
+mips           Anthony Green, Casey Marshall
+mips64         David Daney
+pa             Randolph Chung, Dave Anglin, Andreas Tobler
+powerpc                Geoffrey Keating, Andreas Tobler, 
+                        David Edelsohn, John Hornkvist
+powerpc64      Jakub Jelinek
+s390           Gerhard Tonn, Ulrich Weigand
+sh             Kaz Kojima
+sh64           Kaz Kojima
+sparc          Anthony Green, Gordon Irlam
+tile-gx/tilepro Walter Lee
+x86            Anthony Green, Jon Beniston
+x86-64         Bo Thorsen
+
+Jesper Skov and Andrew Haley both did more than their fair share of
+stepping through the code and tracking down bugs.
+
+Thanks also to Tom Tromey for bug fixes, documentation and
+configuration help.
+
+Thanks to Jim Blandy, who provided some useful feedback on the libffi
+interface.
+
+Andreas Tobler has done a tremendous amount of work on the testsuite.
+
+Alex Oliva solved the executable page problem for SElinux.
+
+The list above is almost certainly incomplete and inaccurate.  I'm
+happy to make corrections or additions upon request.
+
+If you have a problem, or have found a bug, please send a note to the
+author at green@moxielogic.com, or the project mailing list at
+libffi-discuss@sourceware.org.
diff --git a/.pc/aarch64/src/aarch64/ffi.c b/.pc/aarch64/src/aarch64/ffi.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/src/aarch64/ffitarget.h b/.pc/aarch64/src/aarch64/ffitarget.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/src/aarch64/sysv.S b/.pc/aarch64/src/aarch64/sysv.S
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/lib/libffi.exp b/.pc/aarch64/testsuite/lib/libffi.exp
new file mode 100644 (file)
index 0000000..4a65ed1
--- /dev/null
@@ -0,0 +1,350 @@
+#   Copyright (C) 2003, 2005, 2008, 2009, 2010, 2011 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/>.
+
+proc load_gcc_lib { filename } {
+    global srcdir
+    load_file $srcdir/lib/$filename
+}
+
+load_lib dg.exp
+load_lib libgloss.exp
+load_gcc_lib target-libpath.exp
+load_gcc_lib wrapper.exp
+
+
+# Define libffi callbacks for dg.exp.
+
+proc libffi-dg-test-1 { target_compile prog do_what extra_tool_flags } {
+
+    # To get all \n in dg-output test strings to match printf output
+    # in a system that outputs it as \015\012 (i.e. not just \012), we
+    # need to change all \n into \r?\n.  As there is no dejagnu flag
+    # or hook to do that, we simply change the text being tested.
+    # Unfortunately, we have to know that the variable is called
+    # dg-output-text and lives in the caller of libffi-dg-test, which
+    # is two calls up.  Overriding proc dg-output would be longer and
+    # would necessarily have the same assumption.
+    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]
+    }
+
+    # 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 ""
+       }
+    }
+
+    if { $extra_tool_flags != "" } {
+       lappend options "additional_flags=$extra_tool_flags"
+    }
+
+    set comp_output [libffi_target_compile "$prog" "$output_file" "$compile_type" $options];
+
+
+    return [list $comp_output $output_file]
+}
+
+
+proc libffi-dg-test { prog do_what extra_tool_flags } {
+    return [libffi-dg-test-1 target_compile $prog $do_what $extra_tool_flags]
+}
+
+proc libffi-init { args } {
+    global gluefile wrap_flags;
+    global srcdir
+    global blddirffi
+    global objdir
+    global TOOL_OPTIONS
+    global tool
+    global libffi_include
+    global libffi_link_flags
+    global tool_root_dir
+    global ld_library_path
+
+    set blddirffi [pwd]/.. 
+    verbose "libffi $blddirffi"
+
+    set gccdir [lookfor_file $tool_root_dir gcc/libgcc.a]
+    if {$gccdir != ""} {
+       set gccdir [file dirname $gccdir]
+    }
+    verbose "gccdir $gccdir"
+
+    set ld_library_path "."
+    append ld_library_path ":${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}"
+           }
+       }
+    }
+    # add the library path for libffi.
+    append ld_library_path ":${blddirffi}/.libs"
+
+    verbose "ld_library_path: $ld_library_path"
+
+    # Point to the Libffi headers in libffi.
+    set libffi_include "${blddirffi}/include"
+    verbose "libffi_include $libffi_include"
+
+    set libffi_dir  "${blddirffi}/.libs"
+    verbose "libffi_dir $libffi_dir"
+    if { $libffi_dir != "" } {
+       set libffi_dir [file dirname ${libffi_dir}]
+       set libffi_link_flags "-L${libffi_dir}/.libs"
+    }
+
+    set_ld_library_path_env_vars
+    libffi_maybe_build_wrapper "${objdir}/testglue.o"
+}
+
+proc libffi_exit { } {
+    global gluefile;
+
+    if [info exists gluefile] {
+       file_on_build delete $gluefile;
+       unset gluefile;
+    }
+}
+
+proc libffi_target_compile { source dest type options } {
+    global gluefile wrap_flags;
+    global srcdir
+    global blddirffi
+    global TOOL_OPTIONS
+    global libffi_link_flags
+    global libffi_include
+    global target_triplet
+
+
+    if { [target_info needs_status_wrapper]!="" && [info exists gluefile] } {
+       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 [concat "additional_flags=$TOOL_OPTIONS" $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.
+
+    if { [istarget "*-*-darwin9*"] || [istarget "*-*-darwin1*"]
+        || [istarget "*-*-darwin2*"] } {
+       lappend options "additional_flags=-Wl,-allow_stack_execute"
+    }
+
+    # 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"
+    }
+
+    lappend options "libs= -lffi"
+
+    verbose "options: $options"
+    return [target_compile $source $dest $type $options]
+}
+
+# Utility routines.
+
+#
+# search_for -- looks for a string match in a file
+#
+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
+       }
+    }
+    close $fd
+    return 0
+}
+
+# Modified dg-runtest that can cycle through a list of optimization options
+# as c-torture does.
+proc libffi-dg-runtest { testcases default-extra-flags } {
+    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}
+       }
+    }
+}
+
+
+# Like check_conditional_xfail, but callable from a dg test.
+
+proc dg-xfail-if { args } {
+    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
+    }
+}
+
+proc check-flags { args } {
+
+    # The args are within another list; pull them out.
+    set args [lindex $args 0]
+
+    # The next two arguments are optional.  If they were not specified,
+    # use the defaults.
+    if { [llength $args] == 2 } {
+       lappend $args [list "*"]
+    }
+    if { [llength $args] == 3 } {
+       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    
+    } 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 "*-*-*"]]
+    }
+
+    return $result
+}
+
+proc dg-skip-if { args } {
+    # Verify the number of arguments.  The last two are optional.
+    set args [lreplace $args 0 0]
+    if { [llength $args] < 2 || [llength $args] > 4 } {
+        error "dg-skip-if 2: need 2, 3, or 4 arguments"
+    }
+
+    # 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
+    }
+
+    set selector [list target [lindex $args 1]]
+    if { [dg-process-target $selector] == "S" } {
+        if [check-flags $args] {
+            upvar dg-do-what dg-do-what
+            set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
+        }
+    }
+}
+
+# We need to make sure that additional_files and additional_sources
+# are both cleared out after every test.  It is not enough to clear
+# them out *before* the next test run because gcc-target-compile gets
+# run directly from some .exp files (outside of any test).  (Those
+# uses should eventually be eliminated.)
+
+# Because the DG framework doesn't provide a hook that is run at the
+# end of a test, we must replace dg-test with a wrapper.
+
+if { [info procs saved-dg-test] == [list] } {
+    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 ""
+    }
+}
+
+# Local Variables:
+# tcl-indent-level:4
+# End:
diff --git a/.pc/aarch64/testsuite/libffi.call/cls_struct_va1.c b/.pc/aarch64/testsuite/libffi.call/cls_struct_va1.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/cls_uchar_va.c b/.pc/aarch64/testsuite/libffi.call/cls_uchar_va.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/cls_uint_va.c b/.pc/aarch64/testsuite/libffi.call/cls_uint_va.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/cls_ulong_va.c b/.pc/aarch64/testsuite/libffi.call/cls_ulong_va.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/cls_ushort_va.c b/.pc/aarch64/testsuite/libffi.call/cls_ushort_va.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/nested_struct11.c b/.pc/aarch64/testsuite/libffi.call/nested_struct11.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/uninitialized.c b/.pc/aarch64/testsuite/libffi.call/uninitialized.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/va_1.c b/.pc/aarch64/testsuite/libffi.call/va_1.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/va_struct1.c b/.pc/aarch64/testsuite/libffi.call/va_struct1.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/va_struct2.c b/.pc/aarch64/testsuite/libffi.call/va_struct2.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/aarch64/testsuite/libffi.call/va_struct3.c b/.pc/aarch64/testsuite/libffi.call/va_struct3.c
new file mode 100644 (file)
index 0000000..e69de29
index 9f2b7a8..ae4c534 100644 (file)
@@ -22,3 +22,4 @@ ios-fix
 mingw-check-fix
 whitespace-fix
 tile
+aarch64
diff --git a/README b/README
index ccd4f8b..71bd42a 100644 (file)
--- a/README
+++ b/README
@@ -51,6 +51,7 @@ tested:
 |-----------------+------------------|
 | Architecture    | Operating System |
 |-----------------+------------------|
+| AArch64         | Linux            |
 | Alpha           | Linux            |
 | Alpha           | Tru64            |
 | ARM             | Linux            |
@@ -152,6 +153,7 @@ See the ChangeLog files for details.
 3.0.12 XXX-XX-XX
        Add Blackfin support.
        Add TILE-Gx/TILEPro support.
+       Add AArch64 support.
 
 3.0.11 Apr-11-12
         Lots of build fixes.
@@ -323,6 +325,7 @@ Thorup.
 Major processor architecture ports were contributed by the following
 developers:
 
+aarch64                Marcus Shawcroft, James Greenhalgh
 alpha          Richard Henderson
 arm            Raffaele Sena
 blackfin        Alexandre Keunecke I. de Mendonca
diff --git a/patches/aarch64 b/patches/aarch64
new file mode 100644 (file)
index 0000000..2217be0
--- /dev/null
@@ -0,0 +1,2594 @@
+Index: libffi/README
+===================================================================
+--- libffi.orig/README
++++ libffi/README
+@@ -51,6 +51,7 @@ tested:
+ |-----------------+------------------|
+ | Architecture    | Operating System |
+ |-----------------+------------------|
++| AArch64         | Linux            |
+ | Alpha           | Linux            |
+ | Alpha           | Tru64            |
+ | ARM             | Linux            |
+@@ -152,6 +153,7 @@ See the ChangeLog files for details.
+ 3.0.12 XXX-XX-XX
+       Add Blackfin support.
+       Add TILE-Gx/TILEPro support.
++      Add AArch64 support.
+ 3.0.11 Apr-11-12
+         Lots of build fixes.
+@@ -323,6 +325,7 @@ Thorup.
+ Major processor architecture ports were contributed by the following
+ developers:
++aarch64               Marcus Shawcroft, James Greenhalgh
+ alpha         Richard Henderson
+ arm           Raffaele Sena
+ blackfin        Alexandre Keunecke I. de Mendonca
+Index: libffi/src/aarch64/ffi.c
+===================================================================
+--- /dev/null
++++ libffi/src/aarch64/ffi.c
+@@ -0,0 +1,1076 @@
++/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd.
++
++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 <stdio.h>
++
++#include <ffi.h>
++#include <ffi_common.h>
++
++#include <stdlib.h>
++
++/* Stack alignment requirement in bytes */
++#define AARCH64_STACK_ALIGN 16
++
++#define N_X_ARG_REG 8
++#define N_V_ARG_REG 8
++
++#define AARCH64_FFI_WITH_V (1 << AARCH64_FFI_WITH_V_BIT)
++
++union _d
++{
++  UINT64 d;
++  UINT32 s[2];
++};
++
++struct call_context
++{
++  UINT64 x [AARCH64_N_XREG];
++  struct
++  {
++    union _d d[2];
++  } v [AARCH64_N_VREG];
++};
++
++static void *
++get_x_addr (struct call_context *context, unsigned n)
++{
++  return &context->x[n];
++}
++
++static void *
++get_s_addr (struct call_context *context, unsigned n)
++{
++#if defined __AARCH64EB__
++  return &context->v[n].d[1].s[1];
++#else
++  return &context->v[n].d[0].s[0];
++#endif
++}
++
++static void *
++get_d_addr (struct call_context *context, unsigned n)
++{
++#if defined __AARCH64EB__
++  return &context->v[n].d[1];
++#else
++  return &context->v[n].d[0];
++#endif
++}
++
++static void *
++get_v_addr (struct call_context *context, unsigned n)
++{
++  return &context->v[n];
++}
++
++/* Return the memory location at which a basic type would reside
++   were it to have been stored in register n.  */
++
++static void *
++get_basic_type_addr (unsigned short type, struct call_context *context,
++                   unsigned n)
++{
++  switch (type)
++    {
++    case FFI_TYPE_FLOAT:
++      return get_s_addr (context, n);
++    case FFI_TYPE_DOUBLE:
++      return get_d_addr (context, n);
++    case FFI_TYPE_LONGDOUBLE:
++      return get_v_addr (context, n);
++    case FFI_TYPE_UINT8:
++    case FFI_TYPE_SINT8:
++    case FFI_TYPE_UINT16:
++    case FFI_TYPE_SINT16:
++    case FFI_TYPE_UINT32:
++    case FFI_TYPE_SINT32:
++    case FFI_TYPE_INT:
++    case FFI_TYPE_POINTER:
++    case FFI_TYPE_UINT64:
++    case FFI_TYPE_SINT64:
++      return get_x_addr (context, n);
++    default:
++      FFI_ASSERT (0);
++      return NULL;
++    }
++}
++
++/* Return the alignment width for each of the basic types.  */
++
++static size_t
++get_basic_type_alignment (unsigned short type)
++{
++  switch (type)
++    {
++    case FFI_TYPE_FLOAT:
++    case FFI_TYPE_DOUBLE:
++      return sizeof (UINT64);
++    case FFI_TYPE_LONGDOUBLE:
++      return sizeof (long double);
++    case FFI_TYPE_UINT8:
++    case FFI_TYPE_SINT8:
++    case FFI_TYPE_UINT16:
++    case FFI_TYPE_SINT16:
++    case FFI_TYPE_UINT32:
++    case FFI_TYPE_INT:
++    case FFI_TYPE_SINT32:
++    case FFI_TYPE_POINTER:
++    case FFI_TYPE_UINT64:
++    case FFI_TYPE_SINT64:
++      return sizeof (UINT64);
++
++    default:
++      FFI_ASSERT (0);
++      return 0;
++    }
++}
++
++/* Return the size in bytes for each of the basic types.  */
++
++static size_t
++get_basic_type_size (unsigned short type)
++{
++  switch (type)
++    {
++    case FFI_TYPE_FLOAT:
++      return sizeof (UINT32);
++    case FFI_TYPE_DOUBLE:
++      return sizeof (UINT64);
++    case FFI_TYPE_LONGDOUBLE:
++      return sizeof (long double);
++    case FFI_TYPE_UINT8:
++      return sizeof (UINT8);
++    case FFI_TYPE_SINT8:
++      return sizeof (SINT8);
++    case FFI_TYPE_UINT16:
++      return sizeof (UINT16);
++    case FFI_TYPE_SINT16:
++      return sizeof (SINT16);
++    case FFI_TYPE_UINT32:
++      return sizeof (UINT32);
++    case FFI_TYPE_INT:
++    case FFI_TYPE_SINT32:
++      return sizeof (SINT32);
++    case FFI_TYPE_POINTER:
++    case FFI_TYPE_UINT64:
++      return sizeof (UINT64);
++    case FFI_TYPE_SINT64:
++      return sizeof (SINT64);
++
++    default:
++      FFI_ASSERT (0);
++      return 0;
++    }
++}
++
++extern void
++ffi_call_SYSV (unsigned (*)(struct call_context *context, unsigned char *,
++                          extended_cif *),
++               struct call_context *context,
++               extended_cif *,
++               unsigned,
++               void (*fn)(void));
++
++extern void
++ffi_closure_SYSV (ffi_closure *);
++
++/* Test for an FFI floating point representation.  */
++
++static unsigned
++is_floating_type (unsigned short type)
++{
++  return (type == FFI_TYPE_FLOAT || type == FFI_TYPE_DOUBLE
++        || type == FFI_TYPE_LONGDOUBLE);
++}
++
++/* Test for a homogeneous structure.  */
++
++static unsigned short
++get_homogeneous_type (ffi_type *ty)
++{
++  if (ty->type == FFI_TYPE_STRUCT && ty->elements)
++    {
++      unsigned i;
++      unsigned short candidate_type
++      = get_homogeneous_type (ty->elements[0]);
++      for (i =1; ty->elements[i]; i++)
++      {
++        unsigned short iteration_type = 0;
++        /* If we have a nested struct, we must find its homogeneous type.
++           If that fits with our candidate type, we are still
++           homogeneous.  */
++        if (ty->elements[i]->type == FFI_TYPE_STRUCT
++            && ty->elements[i]->elements)
++          {
++            iteration_type = get_homogeneous_type (ty->elements[i]);
++          }
++        else
++          {
++            iteration_type = ty->elements[i]->type;
++          }
++
++        /* If we are not homogeneous, return FFI_TYPE_STRUCT.  */
++        if (candidate_type != iteration_type)
++          return FFI_TYPE_STRUCT;
++      }
++      return candidate_type;
++    }
++
++  /* Base case, we have no more levels of nesting, so we
++     are a basic type, and so, trivially homogeneous in that type.  */
++  return ty->type;
++}
++
++/* Determine the number of elements within a STRUCT.
++
++   Note, we must handle nested structs.
++
++   If ty is not a STRUCT this function will return 0.  */
++
++static unsigned
++element_count (ffi_type *ty)
++{
++  if (ty->type == FFI_TYPE_STRUCT && ty->elements)
++    {
++      unsigned n;
++      unsigned elems = 0;
++      for (n = 0; ty->elements[n]; n++)
++      {
++        if (ty->elements[n]->type == FFI_TYPE_STRUCT
++            && ty->elements[n]->elements)
++          elems += element_count (ty->elements[n]);
++        else
++          elems++;
++      }
++      return elems;
++    }
++  return 0;
++}
++
++/* Test for a homogeneous floating point aggregate.
++
++   A homogeneous floating point aggregate is a homogeneous aggregate of
++   a half- single- or double- precision floating point type with one
++   to four elements.  Note that this includes nested structs of the
++   basic type.  */
++
++static int
++is_hfa (ffi_type *ty)
++{
++  if (ty->type == FFI_TYPE_STRUCT
++      && ty->elements[0]
++      && is_floating_type (get_homogeneous_type (ty)))
++    {
++      unsigned n = element_count (ty);
++      return n >= 1 && n <= 4;
++    }
++  return 0;
++}
++
++/* Test if an ffi_type is a candidate for passing in a register.
++
++   This test does not check that sufficient registers of the
++   appropriate class are actually available, merely that IFF
++   sufficient registers are available then the argument will be passed
++   in register(s).
++
++   Note that an ffi_type that is deemed to be a register candidate
++   will always be returned in registers.
++
++   Returns 1 if a register candidate else 0.  */
++
++static int
++is_register_candidate (ffi_type *ty)
++{
++  switch (ty->type)
++    {
++    case FFI_TYPE_VOID:
++    case FFI_TYPE_FLOAT:
++    case FFI_TYPE_DOUBLE:
++    case FFI_TYPE_LONGDOUBLE:
++    case FFI_TYPE_UINT8:
++    case FFI_TYPE_UINT16:
++    case FFI_TYPE_UINT32:
++    case FFI_TYPE_UINT64:
++    case FFI_TYPE_POINTER:
++    case FFI_TYPE_SINT8:
++    case FFI_TYPE_SINT16:
++    case FFI_TYPE_SINT32:
++    case FFI_TYPE_INT:
++    case FFI_TYPE_SINT64:
++      return 1;
++
++    case FFI_TYPE_STRUCT:
++      if (is_hfa (ty))
++        {
++          return 1;
++        }
++      else if (ty->size > 16)
++        {
++          /* Too large. Will be replaced with a pointer to memory. The
++             pointer MAY be passed in a register, but the value will
++             not. This test specifically fails since the argument will
++             never be passed by value in registers. */
++          return 0;
++        }
++      else
++        {
++          /* Might be passed in registers depending on the number of
++             registers required. */
++          return (ty->size + 7) / 8 < N_X_ARG_REG;
++        }
++      break;
++
++    default:
++      FFI_ASSERT (0);
++      break;
++    }
++
++  return 0;
++}
++
++/* Test if an ffi_type argument or result is a candidate for a vector
++   register.  */
++
++static int
++is_v_register_candidate (ffi_type *ty)
++{
++  return is_floating_type (ty->type)
++         || (ty->type == FFI_TYPE_STRUCT && is_hfa (ty));
++}
++
++/* Representation of the procedure call argument marshalling
++   state.
++
++   The terse state variable names match the names used in the AARCH64
++   PCS. */
++
++struct arg_state
++{
++  unsigned ngrn;                /* Next general-purpose register number. */
++  unsigned nsrn;                /* Next vector register number. */
++  unsigned nsaa;                /* Next stack offset. */
++};
++
++/* Initialize a procedure call argument marshalling state.  */
++static void
++arg_init (struct arg_state *state, unsigned call_frame_size)
++{
++  state->ngrn = 0;
++  state->nsrn = 0;
++  state->nsaa = 0;
++}
++
++/* Return the number of available consecutive core argument
++   registers.  */
++
++static unsigned
++available_x (struct arg_state *state)
++{
++  return N_X_ARG_REG - state->ngrn;
++}
++
++/* Return the number of available consecutive vector argument
++   registers.  */
++
++static unsigned
++available_v (struct arg_state *state)
++{
++  return N_V_ARG_REG - state->nsrn;
++}
++
++static void *
++allocate_to_x (struct call_context *context, struct arg_state *state)
++{
++  FFI_ASSERT (state->ngrn < N_X_ARG_REG)
++  return get_x_addr (context, (state->ngrn)++);
++}
++
++static void *
++allocate_to_s (struct call_context *context, struct arg_state *state)
++{
++  FFI_ASSERT (state->nsrn < N_V_ARG_REG)
++  return get_s_addr (context, (state->nsrn)++);
++}
++
++static void *
++allocate_to_d (struct call_context *context, struct arg_state *state)
++{
++  FFI_ASSERT (state->nsrn < N_V_ARG_REG)
++  return get_d_addr (context, (state->nsrn)++);
++}
++
++static void *
++allocate_to_v (struct call_context *context, struct arg_state *state)
++{
++  FFI_ASSERT (state->nsrn < N_V_ARG_REG)
++  return get_v_addr (context, (state->nsrn)++);
++}
++
++/* Allocate an aligned slot on the stack and return a pointer to it.  */
++static void *
++allocate_to_stack (struct arg_state *state, void *stack, unsigned alignment,
++                 unsigned size)
++{
++  void *allocation;
++
++  /* Round up the NSAA to the larger of 8 or the natural
++     alignment of the argument's type.  */
++  state->nsaa = ALIGN (state->nsaa, alignment);
++  state->nsaa = ALIGN (state->nsaa, alignment);
++  state->nsaa = ALIGN (state->nsaa, 8);
++
++  allocation = stack + state->nsaa;
++
++  state->nsaa += size;
++  return allocation;
++}
++
++static void
++copy_basic_type (void *dest, void *source, unsigned short type)
++{
++  /* This is neccessary to ensure that basic types are copied
++     sign extended to 64-bits as libffi expects.  */
++  switch (type)
++    {
++    case FFI_TYPE_FLOAT:
++      *(float *) dest = *(float *) source;
++      break;
++    case FFI_TYPE_DOUBLE:
++      *(double *) dest = *(double *) source;
++      break;
++    case FFI_TYPE_LONGDOUBLE:
++      *(long double *) dest = *(long double *) source;
++      break;
++    case FFI_TYPE_UINT8:
++      *(ffi_arg *) dest = *(UINT8 *) source;
++      break;
++    case FFI_TYPE_SINT8:
++      *(ffi_sarg *) dest = *(SINT8 *) source;
++      break;
++    case FFI_TYPE_UINT16:
++      *(ffi_arg *) dest = *(UINT16 *) source;
++      break;
++    case FFI_TYPE_SINT16:
++      *(ffi_sarg *) dest = *(SINT16 *) source;
++      break;
++    case FFI_TYPE_UINT32:
++      *(ffi_arg *) dest = *(UINT32 *) source;
++      break;
++    case FFI_TYPE_INT:
++    case FFI_TYPE_SINT32:
++      *(ffi_sarg *) dest = *(SINT32 *) source;
++      break;
++    case FFI_TYPE_POINTER:
++    case FFI_TYPE_UINT64:
++      *(ffi_arg *) dest = *(UINT64 *) source;
++      break;
++    case FFI_TYPE_SINT64:
++      *(ffi_sarg *) dest = *(SINT64 *) source;
++      break;
++
++    default:
++      FFI_ASSERT (0);
++    }
++}
++
++static void
++copy_hfa_to_reg_or_stack (void *memory,
++                        ffi_type *ty,
++                        struct call_context *context,
++                        unsigned char *stack,
++                        struct arg_state *state)
++{
++  unsigned elems = element_count (ty);
++  if (available_v (state) < elems)
++    {
++      /* There are insufficient V registers. Further V register allocations
++       are prevented, the NSAA is adjusted (by allocate_to_stack ())
++       and the argument is copied to memory at the adjusted NSAA.  */
++      state->nsrn = N_V_ARG_REG;
++      memcpy (allocate_to_stack (state, stack, ty->alignment, ty->size),
++            memory,
++            ty->size);
++    }
++  else
++    {
++      int i;
++      unsigned short type = get_homogeneous_type (ty);
++      unsigned elems = element_count (ty);
++      for (i = 0; i < elems; i++)
++      {
++        void *reg = allocate_to_v (context, state);
++        copy_basic_type (reg, memory, type);
++        memory += get_basic_type_size (type);
++      }
++    }
++}
++
++/* Either allocate an appropriate register for the argument type, or if
++   none are available, allocate a stack slot and return a pointer
++   to the allocated space.  */
++
++static void *
++allocate_to_register_or_stack (struct call_context *context,
++                             unsigned char *stack,
++                             struct arg_state *state,
++                             unsigned short type)
++{
++  size_t alignment = get_basic_type_alignment (type);
++  size_t size = alignment;
++  switch (type)
++    {
++    case FFI_TYPE_FLOAT:
++      /* This is the only case for which the allocated stack size
++       should not match the alignment of the type.  */
++      size = sizeof (UINT32);
++      /* Fall through.  */
++    case FFI_TYPE_DOUBLE:
++      if (state->nsrn < N_V_ARG_REG)
++      return allocate_to_d (context, state);
++      state->nsrn = N_V_ARG_REG;
++      break;
++    case FFI_TYPE_LONGDOUBLE:
++      if (state->nsrn < N_V_ARG_REG)
++      return allocate_to_v (context, state);
++      state->nsrn = N_V_ARG_REG;
++      break;
++    case FFI_TYPE_UINT8:
++    case FFI_TYPE_SINT8:
++    case FFI_TYPE_UINT16:
++    case FFI_TYPE_SINT16:
++    case FFI_TYPE_UINT32:
++    case FFI_TYPE_SINT32:
++    case FFI_TYPE_INT:
++    case FFI_TYPE_POINTER:
++    case FFI_TYPE_UINT64:
++    case FFI_TYPE_SINT64:
++      if (state->ngrn < N_X_ARG_REG)
++      return allocate_to_x (context, state);
++      state->ngrn = N_X_ARG_REG;
++      break;
++    default:
++      FFI_ASSERT (0);
++    }
++
++    return allocate_to_stack (state, stack, alignment, size);
++}
++
++/* Copy a value to an appropriate register, or if none are
++   available, to the stack.  */
++
++static void
++copy_to_register_or_stack (struct call_context *context,
++                         unsigned char *stack,
++                         struct arg_state *state,
++                         void *value,
++                         unsigned short type)
++{
++  copy_basic_type (
++        allocate_to_register_or_stack (context, stack, state, type),
++        value,
++        type);
++}
++
++/* Marshall the arguments from FFI representation to procedure call
++   context and stack.  */
++
++static unsigned
++aarch64_prep_args (struct call_context *context, unsigned char *stack,
++                 extended_cif *ecif)
++{
++  int i;
++  struct arg_state state;
++
++  arg_init (&state, ALIGN(ecif->cif->bytes, 16));
++
++  for (i = 0; i < ecif->cif->nargs; i++)
++    {
++      ffi_type *ty = ecif->cif->arg_types[i];
++      switch (ty->type)
++      {
++      case FFI_TYPE_VOID:
++        FFI_ASSERT (0);
++        break;
++
++      /* If the argument is a basic type the argument is allocated to an
++         appropriate register, or if none are available, to the stack.  */
++      case FFI_TYPE_FLOAT:
++      case FFI_TYPE_DOUBLE:
++      case FFI_TYPE_LONGDOUBLE:
++      case FFI_TYPE_UINT8:
++      case FFI_TYPE_SINT8:
++      case FFI_TYPE_UINT16:
++      case FFI_TYPE_SINT16:
++      case FFI_TYPE_UINT32:
++      case FFI_TYPE_INT:
++      case FFI_TYPE_SINT32:
++      case FFI_TYPE_POINTER:
++      case FFI_TYPE_UINT64:
++      case FFI_TYPE_SINT64:
++        copy_to_register_or_stack (context, stack, &state,
++                                   ecif->avalue[i], ty->type);
++        break;
++
++      case FFI_TYPE_STRUCT:
++        if (is_hfa (ty))
++          {
++            copy_hfa_to_reg_or_stack (ecif->avalue[i], ty, context,
++                                      stack, &state);
++          }
++        else if (ty->size > 16)
++          {
++            /* If the argument is a composite type that is larger than 16
++               bytes, then the argument has been copied to memory, and
++               the argument is replaced by a pointer to the copy.  */
++
++            copy_to_register_or_stack (context, stack, &state,
++                                       &(ecif->avalue[i]), FFI_TYPE_POINTER);
++          }
++        else if (available_x (&state) >= (ty->size + 7) / 8)
++          {
++            /* If the argument is a composite type and the size in
++               double-words is not more than the number of available
++               X registers, then the argument is copied into consecutive
++               X registers.  */
++            int j;
++            for (j = 0; j < (ty->size + 7) / 8; j++)
++              {
++                memcpy (allocate_to_x (context, &state),
++                        &(((UINT64 *) ecif->avalue[i])[j]),
++                        sizeof (UINT64));
++              }
++          }
++        else
++          {
++            /* Otherwise, there are insufficient X registers. Further X
++               register allocations are prevented, the NSAA is adjusted
++               (by allocate_to_stack ()) and the argument is copied to
++               memory at the adjusted NSAA.  */
++            state.ngrn = N_X_ARG_REG;
++
++            memcpy (allocate_to_stack (&state, stack, ty->alignment,
++                                       ty->size), ecif->avalue + i, ty->size);
++          }
++        break;
++
++      default:
++        FFI_ASSERT (0);
++        break;
++      }
++    }
++
++  return ecif->cif->aarch64_flags;
++}
++
++ffi_status
++ffi_prep_cif_machdep (ffi_cif *cif)
++{
++  /* Round the stack up to a multiple of the stack alignment requirement. */
++  cif->bytes =
++    (cif->bytes + (AARCH64_STACK_ALIGN - 1)) & ~ (AARCH64_STACK_ALIGN - 1);
++
++  /* Initialize our flags. We are interested if this CIF will touch a
++     vector register, if so we will enable context save and load to
++     those registers, otherwise not. This is intended to be friendly
++     to lazy float context switching in the kernel.  */
++  cif->aarch64_flags = 0;
++
++  if (is_v_register_candidate (cif->rtype))
++    {
++      cif->aarch64_flags |= AARCH64_FFI_WITH_V;
++    }
++  else
++    {
++      int i;
++      for (i = 0; i < cif->nargs; i++)
++        if (is_v_register_candidate (cif->arg_types[i]))
++          {
++            cif->aarch64_flags |= AARCH64_FFI_WITH_V;
++            break;
++          }
++    }
++
++  return FFI_OK;
++}
++
++/* Call a function with the provided arguments and capture the return
++   value.  */
++void
++ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
++{
++  extended_cif ecif;
++
++  ecif.cif = cif;
++  ecif.avalue = avalue;
++  ecif.rvalue = rvalue;
++
++  switch (cif->abi)
++    {
++    case FFI_SYSV:
++      {
++        struct call_context context;
++      unsigned stack_bytes;
++
++      /* Figure out the total amount of stack space we need, the
++         above call frame space needs to be 16 bytes aligned to
++         ensure correct alignment of the first object inserted in
++         that space hence the ALIGN applied to cif->bytes.*/
++      stack_bytes = ALIGN(cif->bytes, 16);
++
++      memset (&context, 0, sizeof (context));
++        if (is_register_candidate (cif->rtype))
++          {
++            ffi_call_SYSV (aarch64_prep_args, &context, &ecif, stack_bytes, fn);
++            switch (cif->rtype->type)
++              {
++              case FFI_TYPE_VOID:
++              case FFI_TYPE_FLOAT:
++              case FFI_TYPE_DOUBLE:
++              case FFI_TYPE_LONGDOUBLE:
++              case FFI_TYPE_UINT8:
++              case FFI_TYPE_SINT8:
++              case FFI_TYPE_UINT16:
++              case FFI_TYPE_SINT16:
++              case FFI_TYPE_UINT32:
++              case FFI_TYPE_SINT32:
++              case FFI_TYPE_POINTER:
++              case FFI_TYPE_UINT64:
++              case FFI_TYPE_INT:
++              case FFI_TYPE_SINT64:
++              {
++                void *addr = get_basic_type_addr (cif->rtype->type,
++                                                  &context, 0);
++                copy_basic_type (rvalue, addr, cif->rtype->type);
++                break;
++              }
++
++              case FFI_TYPE_STRUCT:
++                if (is_hfa (cif->rtype))
++                {
++                  int j;
++                  unsigned short type = get_homogeneous_type (cif->rtype);
++                  unsigned elems = element_count (cif->rtype);
++                  for (j = 0; j < elems; j++)
++                    {
++                      void *reg = get_basic_type_addr (type, &context, j);
++                      copy_basic_type (rvalue, reg, type);
++                      rvalue += get_basic_type_size (type);
++                    }
++                }
++                else if ((cif->rtype->size + 7) / 8 < N_X_ARG_REG)
++                  {
++                    unsigned size = ALIGN (cif->rtype->size, sizeof (UINT64));
++                    memcpy (rvalue, get_x_addr (&context, 0), size);
++                  }
++                else
++                  {
++                    FFI_ASSERT (0);
++                  }
++                break;
++
++              default:
++                FFI_ASSERT (0);
++                break;
++              }
++          }
++        else
++          {
++            memcpy (get_x_addr (&context, 8), &rvalue, sizeof (UINT64));
++            ffi_call_SYSV (aarch64_prep_args, &context, &ecif,
++                         stack_bytes, fn);
++          }
++        break;
++      }
++
++    default:
++      FFI_ASSERT (0);
++      break;
++    }
++}
++
++static unsigned char trampoline [] =
++{ 0x70, 0x00, 0x00, 0x58,     /* ldr  x16, 1f */
++  0x91, 0x00, 0x00, 0x10,     /* adr  x17, 2f */
++  0x00, 0x02, 0x1f, 0xd6      /* br   x16     */
++};
++
++/* Build a trampoline.  */
++
++#define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX,FLAGS)                      \
++  ({unsigned char *__tramp = (unsigned char*)(TRAMP);                 \
++    UINT64  __fun = (UINT64)(FUN);                                    \
++    UINT64  __ctx = (UINT64)(CTX);                                    \
++    UINT64  __flags = (UINT64)(FLAGS);                                        \
++    memcpy (__tramp, trampoline, sizeof (trampoline));                        \
++    memcpy (__tramp + 12, &__fun, sizeof (__fun));                    \
++    memcpy (__tramp + 20, &__ctx, sizeof (__ctx));                    \
++    memcpy (__tramp + 28, &__flags, sizeof (__flags));                        \
++    __clear_cache(__tramp, __tramp + FFI_TRAMPOLINE_SIZE);            \
++  })
++
++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_SYSV)
++    return FFI_BAD_ABI;
++
++  FFI_INIT_TRAMPOLINE (&closure->tramp[0], &ffi_closure_SYSV, codeloc,
++                     cif->aarch64_flags);
++
++  closure->cif  = cif;
++  closure->user_data = user_data;
++  closure->fun  = fun;
++
++  return FFI_OK;
++}
++
++/* Primary handler to setup and invoke a function within a closure.
++
++   A closure when invoked enters via the assembler wrapper
++   ffi_closure_SYSV(). The wrapper allocates a call context on the
++   stack, saves the interesting registers (from the perspective of
++   the calling convention) into the context then passes control to
++   ffi_closure_SYSV_inner() passing the saved context and a pointer to
++   the stack at the point ffi_closure_SYSV() was invoked.
++
++   On the return path the assembler wrapper will reload call context
++   regsiters.
++
++   ffi_closure_SYSV_inner() marshalls the call context into ffi value
++   desriptors, invokes the wrapped function, then marshalls the return
++   value back into the call context.  */
++
++void
++ffi_closure_SYSV_inner (ffi_closure *closure, struct call_context *context,
++                      void *stack)
++{
++  ffi_cif *cif = closure->cif;
++  void **avalue = (void**) alloca (cif->nargs * sizeof (void*));
++  void *rvalue = NULL;
++  int i;
++  struct arg_state state;
++
++  arg_init (&state, ALIGN(cif->bytes, 16));
++
++  for (i = 0; i < cif->nargs; i++)
++    {
++      ffi_type *ty = cif->arg_types[i];
++
++      switch (ty->type)
++      {
++      case FFI_TYPE_VOID:
++        FFI_ASSERT (0);
++        break;
++
++      case FFI_TYPE_UINT8:
++      case FFI_TYPE_SINT8:
++      case FFI_TYPE_UINT16:
++      case FFI_TYPE_SINT16:
++      case FFI_TYPE_UINT32:
++      case FFI_TYPE_SINT32:
++      case FFI_TYPE_INT:
++      case FFI_TYPE_POINTER:
++      case FFI_TYPE_UINT64:
++      case FFI_TYPE_SINT64:
++      case  FFI_TYPE_FLOAT:
++      case  FFI_TYPE_DOUBLE:
++      case  FFI_TYPE_LONGDOUBLE:
++        avalue[i] = allocate_to_register_or_stack (context, stack,
++                                                   &state, ty->type);
++        break;
++
++      case FFI_TYPE_STRUCT:
++        if (is_hfa (ty))
++          {
++            unsigned n = element_count (ty);
++            if (available_v (&state) < n)
++              {
++                state.nsrn = N_V_ARG_REG;
++                avalue[i] = allocate_to_stack (&state, stack, ty->alignment,
++                                               ty->size);
++              }
++            else
++              {
++                switch (get_homogeneous_type (ty))
++                  {
++                  case FFI_TYPE_FLOAT:
++                    {
++                      /* Eeek! We need a pointer to the structure,
++                         however the homogeneous float elements are
++                         being passed in individual S registers,
++                         therefore the structure is not represented as
++                         a contiguous sequence of bytes in our saved
++                         register context. We need to fake up a copy
++                         of the structure layed out in memory
++                         correctly. The fake can be tossed once the
++                         closure function has returned hence alloca()
++                         is sufficient. */
++                      int j;
++                      UINT32 *p = avalue[i] = alloca (ty->size);
++                      for (j = 0; j < element_count (ty); j++)
++                        memcpy (&p[j],
++                                allocate_to_s (context, &state),
++                                sizeof (*p));
++                      break;
++                    }
++
++                  case FFI_TYPE_DOUBLE:
++                    {
++                      /* Eeek! We need a pointer to the structure,
++                         however the homogeneous float elements are
++                         being passed in individual S registers,
++                         therefore the structure is not represented as
++                         a contiguous sequence of bytes in our saved
++                         register context. We need to fake up a copy
++                         of the structure layed out in memory
++                         correctly. The fake can be tossed once the
++                         closure function has returned hence alloca()
++                         is sufficient. */
++                      int j;
++                      UINT64 *p = avalue[i] = alloca (ty->size);
++                      for (j = 0; j < element_count (ty); j++)
++                        memcpy (&p[j],
++                                allocate_to_d (context, &state),
++                                sizeof (*p));
++                      break;
++                    }
++
++                  case FFI_TYPE_LONGDOUBLE:
++                        memcpy (&avalue[i],
++                                allocate_to_v (context, &state),
++                                sizeof (*avalue));
++                    break;
++
++                  default:
++                    FFI_ASSERT (0);
++                    break;
++                  }
++              }
++          }
++        else if (ty->size > 16)
++          {
++            /* Replace Composite type of size greater than 16 with a
++               pointer.  */
++            memcpy (&avalue[i],
++                    allocate_to_register_or_stack (context, stack,
++                                                   &state, FFI_TYPE_POINTER),
++                    sizeof (avalue[i]));
++          }
++        else if (available_x (&state) >= (ty->size + 7) / 8)
++          {
++            avalue[i] = get_x_addr (context, state.ngrn);
++            state.ngrn += (ty->size + 7) / 8;
++          }
++        else
++          {
++            state.ngrn = N_X_ARG_REG;
++
++            avalue[i] = allocate_to_stack (&state, stack, ty->alignment,
++                                           ty->size);
++          }
++        break;
++
++      default:
++        FFI_ASSERT (0);
++        break;
++      }
++    }
++
++  /* Figure out where the return value will be passed, either in
++     registers or in a memory block allocated by the caller and passed
++     in x8.  */
++
++  if (is_register_candidate (cif->rtype))
++    {
++      /* Register candidates are *always* returned in registers. */
++
++      /* Allocate a scratchpad for the return value, we will let the
++         callee scrible the result into the scratch pad then move the
++         contents into the appropriate return value location for the
++         call convention.  */
++      rvalue = alloca (cif->rtype->size);
++      (closure->fun) (cif, rvalue, avalue, closure->user_data);
++
++      /* Copy the return value into the call context so that it is returned
++         as expected to our caller.  */
++      switch (cif->rtype->type)
++        {
++        case FFI_TYPE_VOID:
++          break;
++
++        case FFI_TYPE_UINT8:
++        case FFI_TYPE_UINT16:
++        case FFI_TYPE_UINT32:
++        case FFI_TYPE_POINTER:
++        case FFI_TYPE_UINT64:
++        case FFI_TYPE_SINT8:
++        case FFI_TYPE_SINT16:
++        case FFI_TYPE_INT:
++        case FFI_TYPE_SINT32:
++        case FFI_TYPE_SINT64:
++        case FFI_TYPE_FLOAT:
++        case FFI_TYPE_DOUBLE:
++        case FFI_TYPE_LONGDOUBLE:
++        {
++          void *addr = get_basic_type_addr (cif->rtype->type, context, 0);
++          copy_basic_type (addr, rvalue, cif->rtype->type);
++            break;
++        }
++        case FFI_TYPE_STRUCT:
++          if (is_hfa (cif->rtype))
++          {
++            int i;
++            unsigned short type = get_homogeneous_type (cif->rtype);
++            unsigned elems = element_count (cif->rtype);
++            for (i = 0; i < elems; i++)
++              {
++                void *reg = get_basic_type_addr (type, context, i);
++                copy_basic_type (reg, rvalue, type);
++                rvalue += get_basic_type_size (type);
++              }
++          }
++          else if ((cif->rtype->size + 7) / 8 < N_X_ARG_REG)
++            {
++              unsigned size = ALIGN (cif->rtype->size, sizeof (UINT64)) ;
++              memcpy (get_x_addr (context, 0), rvalue, size);
++            }
++          else
++            {
++              FFI_ASSERT (0);
++            }
++          break;
++        default:
++          FFI_ASSERT (0);
++          break;
++        }
++    }
++  else
++    {
++      memcpy (&rvalue, get_x_addr (context, 8), sizeof (UINT64));
++      (closure->fun) (cif, rvalue, avalue, closure->user_data);
++    }
++}
++
+Index: libffi/src/aarch64/ffitarget.h
+===================================================================
+--- /dev/null
++++ libffi/src/aarch64/ffitarget.h
+@@ -0,0 +1,59 @@
++/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd.
++
++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
++
++#ifndef LIBFFI_ASM
++typedef unsigned long ffi_arg;
++typedef signed long ffi_sarg;
++
++typedef enum ffi_abi
++  {
++    FFI_FIRST_ABI = 0,
++    FFI_SYSV,
++    FFI_LAST_ABI,
++    FFI_DEFAULT_ABI = FFI_SYSV
++  } ffi_abi;
++#endif
++
++/* ---- Definitions for closures ----------------------------------------- */
++
++#define FFI_CLOSURES 1
++#define FFI_TRAMPOLINE_SIZE 36
++#define FFI_NATIVE_RAW_API 0
++
++/* ---- Internal ---- */
++
++
++#define FFI_EXTRA_CIF_FIELDS unsigned aarch64_flags
++
++#define AARCH64_FFI_WITH_V_BIT 0
++
++#define AARCH64_N_XREG 32
++#define AARCH64_N_VREG 32
++#define AARCH64_CALL_CONTEXT_SIZE (AARCH64_N_XREG * 8 + AARCH64_N_VREG * 16)
++
++#endif
+Index: libffi/src/aarch64/sysv.S
+===================================================================
+--- /dev/null
++++ libffi/src/aarch64/sysv.S
+@@ -0,0 +1,307 @@
++/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd.
++
++Permission is hereby granted, free of charge, to any person obtaining
++a copy of this software and associated documentation files (the
++``Software''), to deal in the Software without restriction, including
++without limitation the rights to use, copy, modify, merge, publish,
++distribute, sublicense, and/or sell copies of the Software, and to
++permit persons to whom the Software is furnished to do so, subject to
++the following conditions:
++
++The above copyright notice and this permission notice shall be
++included in all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
++TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
++SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
++
++#define LIBFFI_ASM
++#include <fficonfig.h>
++#include <ffi.h>
++
++#define cfi_adjust_cfa_offset(off)    .cfi_adjust_cfa_offset off
++#define cfi_rel_offset(reg, off)      .cfi_rel_offset reg, off
++#define cfi_restore(reg)              .cfi_restore reg
++#define cfi_def_cfa_register(reg)     .cfi_def_cfa_register reg
++
++        .text
++        .globl ffi_call_SYSV
++        .type ffi_call_SYSV, #function
++
++/* ffi_call_SYSV()
++
++   Create a stack frame, setup an argument context, call the callee
++   and extract the result.
++
++   The maximum required argument stack size is provided,
++   ffi_call_SYSV() allocates that stack space then calls the
++   prepare_fn to populate register context and stack.  The
++   argument passing registers are loaded from the register
++   context and the callee called, on return the register passing
++   register are saved back to the context.  Our caller will
++   extract the return value from the final state of the saved
++   register context.
++
++   Prototype:
++
++   extern unsigned
++   ffi_call_SYSV (void (*)(struct call_context *context, unsigned char *,
++                         extended_cif *),
++                  struct call_context *context,
++                  extended_cif *,
++                  unsigned required_stack_size,
++                  void (*fn)(void));
++
++   Therefore on entry we have:
++
++   x0 prepare_fn
++   x1 &context
++   x2 &ecif
++   x3 bytes
++   x4 fn
++
++   This function uses the following stack frame layout:
++
++   ==
++                saved x30(lr)
++   x29(fp)->    saved x29(fp)
++                saved x24
++                saved x23
++                saved x22
++   sp'    ->    saved x21
++                ...
++   sp     ->    (constructed callee stack arguments)
++   ==
++
++   Voila! */
++
++#define ffi_call_SYSV_FS (8 * 4)
++
++        .cfi_startproc
++ffi_call_SYSV:
++        stp     x29, x30, [sp, #-16]!
++      cfi_adjust_cfa_offset (16)
++        cfi_rel_offset (x29, 0)
++        cfi_rel_offset (x30, 8)
++
++        mov     x29, sp
++      cfi_def_cfa_register (x29)
++        sub     sp, sp, #ffi_call_SYSV_FS
++
++        stp     x21, x22, [sp, 0]
++        cfi_rel_offset (x21, 0 - ffi_call_SYSV_FS)
++        cfi_rel_offset (x22, 8 - ffi_call_SYSV_FS)
++
++        stp     x23, x24, [sp, 16]
++        cfi_rel_offset (x23, 16 - ffi_call_SYSV_FS)
++        cfi_rel_offset (x24, 24 - ffi_call_SYSV_FS)
++
++        mov     x21, x1
++        mov     x22, x2
++        mov     x24, x4
++
++        /* Allocate the stack space for the actual arguments, many
++           arguments will be passed in registers, but we assume
++           worst case and allocate sufficient stack for ALL of
++           the arguments.  */
++        sub     sp, sp, x3
++
++        /* unsigned (*prepare_fn) (struct call_context *context,
++                                 unsigned char *stack, extended_cif *ecif);
++       */
++        mov     x23, x0
++        mov     x0, x1
++        mov     x1, sp
++        /* x2 already in place */
++        blr     x23
++
++        /* Preserve the flags returned.  */
++        mov     x23, x0
++
++        /* Figure out if we should touch the vector registers.  */
++        tbz     x23, #AARCH64_FFI_WITH_V_BIT, 1f
++
++        /* Load the vector argument passing registers.  */
++        ldp     q0, q1, [x21, #8*32 +  0]
++        ldp     q2, q3, [x21, #8*32 + 32]
++        ldp     q4, q5, [x21, #8*32 + 64]
++        ldp     q6, q7, [x21, #8*32 + 96]
++1:
++        /* Load the core argument passing registers.  */
++        ldp     x0, x1, [x21,  #0]
++        ldp     x2, x3, [x21, #16]
++        ldp     x4, x5, [x21, #32]
++        ldp     x6, x7, [x21, #48]
++
++        /* Don't forget x8 which may be holding the address of a return buffer.
++       */
++        ldr     x8,     [x21, #8*8]
++
++        blr     x24
++
++        /* Save the core argument passing registers.  */
++        stp     x0, x1, [x21,  #0]
++        stp     x2, x3, [x21, #16]
++        stp     x4, x5, [x21, #32]
++        stp     x6, x7, [x21, #48]
++
++        /* Note nothing useful ever comes back in x8!  */
++
++        /* Figure out if we should touch the vector registers.  */
++        tbz     x23, #AARCH64_FFI_WITH_V_BIT, 1f
++
++        /* Save the vector argument passing registers.  */
++        stp     q0, q1, [x21, #8*32 + 0]
++        stp     q2, q3, [x21, #8*32 + 32]
++        stp     q4, q5, [x21, #8*32 + 64]
++        stp     q6, q7, [x21, #8*32 + 96]
++1:
++        /* All done, unwind our stack frame.  */
++        ldp     x21, x22, [x29,  # - ffi_call_SYSV_FS]
++        cfi_restore (x21)
++        cfi_restore (x22)
++
++        ldp     x23, x24, [x29,  # - ffi_call_SYSV_FS + 16]
++        cfi_restore (x23)
++        cfi_restore (x24)
++
++        mov     sp, x29
++      cfi_def_cfa_register (sp)
++
++        ldp     x29, x30, [sp], #16
++      cfi_adjust_cfa_offset (-16)
++        cfi_restore (x29)
++        cfi_restore (x30)
++
++        ret
++
++        .cfi_endproc
++        .size ffi_call_SYSV, .-ffi_call_SYSV
++
++#define ffi_closure_SYSV_FS (8 * 2 + AARCH64_CALL_CONTEXT_SIZE)
++
++/* ffi_closure_SYSV
++
++   Closure invocation glue. This is the low level code invoked directly by
++   the closure trampoline to setup and call a closure.
++
++   On entry x17 points to a struct trampoline_data, x16 has been clobbered
++   all other registers are preserved.
++
++   We allocate a call context and save the argument passing registers,
++   then invoked the generic C ffi_closure_SYSV_inner() function to do all
++   the real work, on return we load the result passing registers back from
++   the call context.
++
++   On entry
++
++   extern void
++   ffi_closure_SYSV (struct trampoline_data *);
++
++   struct trampoline_data
++   {
++        UINT64 *ffi_closure;
++        UINT64 flags;
++   };
++
++   This function uses the following stack frame layout:
++
++   ==
++                saved x30(lr)
++   x29(fp)->    saved x29(fp)
++                saved x22
++                saved x21
++                ...
++   sp     ->    call_context
++   ==
++
++   Voila!  */
++
++        .text
++        .globl ffi_closure_SYSV
++        .cfi_startproc
++ffi_closure_SYSV:
++        stp     x29, x30, [sp, #-16]!
++      cfi_adjust_cfa_offset (16)
++        cfi_rel_offset (x29, 0)
++        cfi_rel_offset (x30, 8)
++
++        mov     x29, sp
++
++        sub     sp, sp, #ffi_closure_SYSV_FS
++      cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
++
++        stp     x21, x22, [x29, #-16]
++        cfi_rel_offset (x21, 0)
++        cfi_rel_offset (x22, 8)
++
++        /* Load x21 with &call_context.  */
++        mov     x21, sp
++        /* Preserve our struct trampoline_data *  */
++        mov     x22, x17
++
++        /* Save the rest of the argument passing registers.  */
++        stp     x0, x1, [x21, #0]
++        stp     x2, x3, [x21, #16]
++        stp     x4, x5, [x21, #32]
++        stp     x6, x7, [x21, #48]
++        /* Don't forget we may have been given a result scratch pad address.
++       */
++        str     x8,     [x21, #64]
++
++        /* Figure out if we should touch the vector registers.  */
++        ldr     x0, [x22, #8]
++        tbz     x0, #AARCH64_FFI_WITH_V_BIT, 1f
++
++        /* Save the argument passing vector registers.  */
++        stp     q0, q1, [x21, #8*32 + 0]
++        stp     q2, q3, [x21, #8*32 + 32]
++        stp     q4, q5, [x21, #8*32 + 64]
++        stp     q6, q7, [x21, #8*32 + 96]
++1:
++        /* Load &ffi_closure..  */
++        ldr     x0, [x22, #0]
++        mov     x1, x21
++        /* Compute the location of the stack at the point that the
++           trampoline was called.  */
++        add     x2, x29, #16
++
++        bl      ffi_closure_SYSV_inner
++
++        /* Figure out if we should touch the vector registers.  */
++        ldr     x0, [x22, #8]
++        tbz     x0, #AARCH64_FFI_WITH_V_BIT, 1f
++
++        /* Load the result passing vector registers.  */
++        ldp     q0, q1, [x21, #8*32 + 0]
++        ldp     q2, q3, [x21, #8*32 + 32]
++        ldp     q4, q5, [x21, #8*32 + 64]
++        ldp     q6, q7, [x21, #8*32 + 96]
++1:
++        /* Load the result passing core registers.  */
++        ldp     x0, x1, [x21,  #0]
++        ldp     x2, x3, [x21, #16]
++        ldp     x4, x5, [x21, #32]
++        ldp     x6, x7, [x21, #48]
++        /* Note nothing usefull is returned in x8.  */
++
++        /* We are done, unwind our frame.  */
++        ldp     x21, x22, [x29,  #-16]
++        cfi_restore (x21)
++        cfi_restore (x22)
++
++        mov     sp, x29
++      cfi_adjust_cfa_offset (-ffi_closure_SYSV_FS)
++
++        ldp     x29, x30, [sp], #16
++      cfi_adjust_cfa_offset (-16)
++        cfi_restore (x29)
++        cfi_restore (x30)
++
++        ret
++        .cfi_endproc
++        .size ffi_closure_SYSV, .-ffi_closure_SYSV
+Index: libffi/testsuite/lib/libffi.exp
+===================================================================
+--- libffi.orig/testsuite/lib/libffi.exp
++++ libffi/testsuite/lib/libffi.exp
+@@ -203,6 +203,10 @@ proc libffi_target_compile { source dest
+     lappend options "libs= -lffi"
++    if { [string match "aarch64*-*-linux*" $target_triplet] } {
++      lappend options "libs= -lpthread"
++    }
++
+     verbose "options: $options"
+     return [target_compile $source $dest $type $options]
+ }
+Index: libffi/testsuite/libffi.call/cls_struct_va1.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/cls_struct_va1.c
+@@ -0,0 +1,114 @@
++/* Area:              ffi_call, closure_call
++   Purpose:           Test doubles passed in variable argument lists.
++   Limitations:       none.
++   PR:                        none.
++   Originator:        Blake Chaffin 6/6/2007   */
++
++/* { dg-do run } */
++/* { dg-output "" { xfail avr32*-*-* } } */
++#include "ffitest.h"
++
++struct small_tag
++{
++  unsigned char a;
++  unsigned char b;
++};
++
++struct large_tag
++{
++  unsigned a;
++  unsigned b;
++  unsigned c;
++  unsigned d;
++  unsigned e;
++};
++
++static void
++test_fn (ffi_cif* cif __UNUSED__, void* resp,
++       void** args, void* userdata __UNUSED__)
++{
++  int n = *(int*)args[0];
++  struct small_tag s1 = * (struct small_tag *) args[1];
++  struct large_tag l1 = * (struct large_tag *) args[2];
++  struct small_tag s2 = * (struct small_tag *) args[3];
++
++  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);
++  * (int*) resp = 42;
++}
++
++int
++main (void)
++{
++  ffi_cif cif;
++  void *code;
++  ffi_closure *pcl = ffi_closure_alloc (sizeof (ffi_closure), &code);
++  ffi_type* arg_types[5];
++
++  ffi_arg res = 0;
++
++  ffi_type s_type;
++  ffi_type *s_type_elements[3];
++
++  ffi_type l_type;
++  ffi_type *l_type_elements[6];
++
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l1;
++
++  int si;
++
++  s_type.size = 0;
++  s_type.alignment = 0;
++  s_type.type = FFI_TYPE_STRUCT;
++  s_type.elements = s_type_elements;
++
++  s_type_elements[0] = &ffi_type_uchar;
++  s_type_elements[1] = &ffi_type_uchar;
++  s_type_elements[2] = NULL;
++
++  l_type.size = 0;
++  l_type.alignment = 0;
++  l_type.type = FFI_TYPE_STRUCT;
++  l_type.elements = l_type_elements;
++
++  l_type_elements[0] = &ffi_type_uint;
++  l_type_elements[1] = &ffi_type_uint;
++  l_type_elements[2] = &ffi_type_uint;
++  l_type_elements[3] = &ffi_type_uint;
++  l_type_elements[4] = &ffi_type_uint;
++  l_type_elements[5] = NULL;
++
++  arg_types[0] = &ffi_type_sint;
++  arg_types[1] = &s_type;
++  arg_types[2] = &l_type;
++  arg_types[3] = &s_type;
++  arg_types[4] = NULL;
++
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &ffi_type_sint,
++                       arg_types) == FFI_OK);
++
++  si = 4;
++  s1.a = 5;
++  s1.b = 6;
++
++  s2.a = 20;
++  s2.b = 21;
++
++  l1.a = 10;
++  l1.b = 11;
++  l1.c = 12;
++  l1.d = 13;
++  l1.e = 14;
++
++  CHECK(ffi_prep_closure_loc(pcl, &cif, test_fn, NULL, code) == FFI_OK);
++
++  res = ((int (*)(int, ...))(code))(si, s1, l1, s2);
++  // { dg-output "4 5 6 10 11 12 13 14 20 21" }
++  printf("res: %d\n", (int) res);
++  // { dg-output "\nres: 42" }
++
++  exit(0);
++}
+Index: libffi/testsuite/libffi.call/cls_uchar_va.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/cls_uchar_va.c
+@@ -0,0 +1,44 @@
++/* Area:      closure_call
++   Purpose:   Test anonymous unsigned char argument.
++   Limitations:       none.
++   PR:                none.
++   Originator:        ARM Ltd. */
++
++/* { dg-do run } */
++#include "ffitest.h"
++
++typedef unsigned char T;
++
++static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
++                       void* userdata __UNUSED__)
++ {
++   *(T *)resp = *(T *)args[0];
++
++   printf("%d: %d %d\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
++ }
++
++typedef T (*cls_ret_T)(T, ...);
++
++int main (void)
++{
++  ffi_cif cif;
++  void *code;
++  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
++  ffi_type * cl_arg_types[3];
++  T res;
++
++  cl_arg_types[0] = &ffi_type_uchar;
++  cl_arg_types[1] = &ffi_type_uchar;
++  cl_arg_types[2] = NULL;
++
++  /* Initialize the cif */
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
++                       &ffi_type_uchar, cl_arg_types) == FFI_OK);
++
++  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
++  res = ((((cls_ret_T)code)(67, 4)));
++  /* { dg-output "67: 67 4" } */
++  printf("res: %d\n", res);
++  /* { dg-output "\nres: 67" } */
++  exit(0);
++}
+Index: libffi/testsuite/libffi.call/cls_uint_va.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/cls_uint_va.c
+@@ -0,0 +1,45 @@
++/* Area:      closure_call
++   Purpose:   Test anonymous unsigned int argument.
++   Limitations:       none.
++   PR:                none.
++   Originator:        ARM Ltd. */
++
++/* { dg-do run } */
++
++#include "ffitest.h"
++
++typedef unsigned int T;
++
++static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
++                       void* userdata __UNUSED__)
++ {
++   *(T *)resp = *(T *)args[0];
++
++   printf("%d: %d %d\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
++ }
++
++typedef T (*cls_ret_T)(T, ...);
++
++int main (void)
++{
++  ffi_cif cif;
++  void *code;
++  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
++  ffi_type * cl_arg_types[3];
++  T res;
++
++  cl_arg_types[0] = &ffi_type_uint;
++  cl_arg_types[1] = &ffi_type_uint;
++  cl_arg_types[2] = NULL;
++
++  /* Initialize the cif */
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
++                       &ffi_type_uint, cl_arg_types) == FFI_OK);
++
++  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
++  res = ((((cls_ret_T)code)(67, 4)));
++  /* { dg-output "67: 67 4" } */
++  printf("res: %d\n", res);
++  /* { dg-output "\nres: 67" } */
++  exit(0);
++}
+Index: libffi/testsuite/libffi.call/cls_ulong_va.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/cls_ulong_va.c
+@@ -0,0 +1,45 @@
++/* Area:      closure_call
++   Purpose:   Test anonymous unsigned long argument.
++   Limitations:       none.
++   PR:                none.
++   Originator:        ARM Ltd. */
++
++/* { dg-do run } */
++
++#include "ffitest.h"
++
++typedef unsigned long T;
++
++static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
++                       void* userdata __UNUSED__)
++ {
++   *(T *)resp = *(T *)args[0];
++
++   printf("%ld: %ld %ld\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
++ }
++
++typedef T (*cls_ret_T)(T, ...);
++
++int main (void)
++{
++  ffi_cif cif;
++  void *code;
++  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
++  ffi_type * cl_arg_types[3];
++  T res;
++
++  cl_arg_types[0] = &ffi_type_ulong;
++  cl_arg_types[1] = &ffi_type_ulong;
++  cl_arg_types[2] = NULL;
++
++  /* Initialize the cif */
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
++                       &ffi_type_ulong, cl_arg_types) == FFI_OK);
++
++  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
++  res = ((((cls_ret_T)code)(67, 4)));
++  /* { dg-output "67: 67 4" } */
++  printf("res: %ld\n", res);
++  /* { dg-output "\nres: 67" } */
++  exit(0);
++}
+Index: libffi/testsuite/libffi.call/cls_ushort_va.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/cls_ushort_va.c
+@@ -0,0 +1,44 @@
++/* Area:      closure_call
++   Purpose:   Test anonymous unsigned short argument.
++   Limitations:       none.
++   PR:                none.
++   Originator:        ARM Ltd. */
++
++/* { dg-do run } */
++#include "ffitest.h"
++
++typedef unsigned short T;
++
++static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
++                       void* userdata __UNUSED__)
++ {
++   *(T *)resp = *(T *)args[0];
++
++   printf("%d: %d %d\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
++ }
++
++typedef T (*cls_ret_T)(T, ...);
++
++int main (void)
++{
++  ffi_cif cif;
++  void *code;
++  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
++  ffi_type * cl_arg_types[3];
++  T res;
++
++  cl_arg_types[0] = &ffi_type_ushort;
++  cl_arg_types[1] = &ffi_type_ushort;
++  cl_arg_types[2] = NULL;
++
++  /* Initialize the cif */
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
++                       &ffi_type_ushort, cl_arg_types) == FFI_OK);
++
++  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
++  res = ((((cls_ret_T)code)(67, 4)));
++  /* { dg-output "67: 67 4" } */
++  printf("res: %d\n", res);
++  /* { dg-output "\nres: 67" } */
++  exit(0);
++}
+Index: libffi/testsuite/libffi.call/nested_struct11.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/nested_struct11.c
+@@ -0,0 +1,121 @@
++/* Area:      ffi_call, closure_call
++   Purpose:   Check parameter passing with nested structs
++              of a single type.  This tests the special cases
++              for homogenous floating-point aggregates in the
++              AArch64 PCS.
++   Limitations:       none.
++   PR:                none.
++   Originator:  ARM Ltd.  */
++
++/* { dg-do run } */
++#include "ffitest.h"
++
++typedef struct A {
++  float a_x;
++  float a_y;
++} A;
++
++typedef struct B {
++  float b_x;
++  float b_y;
++} B;
++
++typedef struct C {
++  A a;
++  B b;
++} C;
++
++static C C_fn (int x, int y, int z, C source, int i, int j, int k)
++{
++  C result;
++  result.a.a_x = source.a.a_x;
++  result.a.a_y = source.a.a_y;
++  result.b.b_x = source.b.b_x;
++  result.b.b_y = source.b.b_y;
++
++  printf ("%d, %d, %d, %d, %d, %d\n", x, y, z, i, j, k);
++
++  printf ("%.1f, %.1f, %.1f, %.1f, "
++        "%.1f, %.1f, %.1f, %.1f\n",
++        source.a.a_x, source.a.a_y,
++        source.b.b_x, source.b.b_y,
++        result.a.a_x, result.a.a_y,
++        result.b.b_x, result.b.b_y);
++
++  return result;
++}
++
++int main (void)
++{
++  ffi_cif cif;
++
++  ffi_type* struct_fields_source_a[3];
++  ffi_type* struct_fields_source_b[3];
++  ffi_type* struct_fields_source_c[3];
++  ffi_type* arg_types[8];
++
++  ffi_type struct_type_a, struct_type_b, struct_type_c;
++
++  struct A source_fld_a = {1.0, 2.0};
++  struct B source_fld_b = {4.0, 8.0};
++  int k = 1;
++
++  struct C result;
++  struct C source = {source_fld_a, source_fld_b};
++
++  struct_type_a.size = 0;
++  struct_type_a.alignment = 0;
++  struct_type_a.type = FFI_TYPE_STRUCT;
++  struct_type_a.elements = struct_fields_source_a;
++
++  struct_type_b.size = 0;
++  struct_type_b.alignment = 0;
++  struct_type_b.type = FFI_TYPE_STRUCT;
++  struct_type_b.elements = struct_fields_source_b;
++
++  struct_type_c.size = 0;
++  struct_type_c.alignment = 0;
++  struct_type_c.type = FFI_TYPE_STRUCT;
++  struct_type_c.elements = struct_fields_source_c;
++
++  struct_fields_source_a[0] = &ffi_type_float;
++  struct_fields_source_a[1] = &ffi_type_float;
++  struct_fields_source_a[2] = NULL;
++
++  struct_fields_source_b[0] = &ffi_type_float;
++  struct_fields_source_b[1] = &ffi_type_float;
++  struct_fields_source_b[2] = NULL;
++
++  struct_fields_source_c[0] = &struct_type_a;
++  struct_fields_source_c[1] = &struct_type_b;
++  struct_fields_source_c[2] = NULL;
++
++  arg_types[0] = &ffi_type_sint32;
++  arg_types[1] = &ffi_type_sint32;
++  arg_types[2] = &ffi_type_sint32;
++  arg_types[3] = &struct_type_c;
++  arg_types[4] = &ffi_type_sint32;
++  arg_types[5] = &ffi_type_sint32;
++  arg_types[6] = &ffi_type_sint32;
++  arg_types[7] = NULL;
++
++  void *args[7];
++  args[0] = &k;
++  args[1] = &k;
++  args[2] = &k;
++  args[3] = &source;
++  args[4] = &k;
++  args[5] = &k;
++  args[6] = &k;
++  CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 7, &struct_type_c,
++                     arg_types) == FFI_OK);
++
++  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);
++}
+Index: libffi/testsuite/libffi.call/uninitialized.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/uninitialized.c
+@@ -0,0 +1,61 @@
++/* { dg-do run } */
++#include "ffitest.h"
++
++typedef struct
++{
++  unsigned char uc;
++  double d;
++  unsigned int ui;
++} test_structure_1;
++
++static test_structure_1 struct1(test_structure_1 ts)
++{
++  ts.uc++;
++  ts.d--;
++  ts.ui++;
++
++  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];
++
++  memset(&cif, 1, sizeof(cif));
++  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_uchar;
++  ts1_type_elements[1] = &ffi_type_double;
++  ts1_type_elements[2] = &ffi_type_uint;
++  ts1_type_elements[3] = NULL;
++
++  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));
++
++  args[0] = &ts1_type;
++  values[0] = &ts1_arg;
++
++  /* Initialize the cif */
++  CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1,
++                   &ts1_type, args) == FFI_OK);
++
++  ts1_arg.uc = '\x01';
++  ts1_arg.d = 3.14159;
++  ts1_arg.ui = 555;
++
++  ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
++
++  CHECK(ts1_result->ui == 556);
++  CHECK(ts1_result->d == 3.14159 - 1);
++
++  free (ts1_result);
++  exit(0);
++}
+Index: libffi/testsuite/libffi.call/va_1.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/va_1.c
+@@ -0,0 +1,196 @@
++/* Area:              ffi_call
++   Purpose:           Test passing struct in variable argument lists.
++   Limitations:       none.
++   PR:                        none.
++   Originator:                ARM Ltd. */
++
++/* { dg-do run } */
++/* { dg-output "" { xfail avr32*-*-* x86_64-*-*-* } } */
++
++#include "ffitest.h"
++#include <stdarg.h>
++
++struct small_tag
++{
++  unsigned char a;
++  unsigned char b;
++};
++
++struct large_tag
++{
++  unsigned a;
++  unsigned b;
++  unsigned c;
++  unsigned d;
++  unsigned e;
++};
++
++static int
++test_fn (int n, ...)
++{
++  va_list ap;
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l;
++  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);
++  s1 = va_arg (ap, struct small_tag);
++  l = va_arg (ap, struct large_tag);
++  s2 = va_arg (ap, struct small_tag);
++
++  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 ("%u %u %u %u %u %u %u %u %u uc=%u sc=%d %u %d %u %d %lu %ld %f %f\n",
++        s1.a, s1.b, l.a, l.b, l.c, l.d, l.e,
++        s2.a, s2.b,
++        uc, sc,
++        us, ss,
++        ui, si,
++        ul, sl,
++        f, d);
++  va_end (ap);
++  return n + 1;
++}
++
++int
++main (void)
++{
++  ffi_cif cif;
++  void* args[15];
++  ffi_type* arg_types[15];
++
++  ffi_type s_type;
++  ffi_type *s_type_elements[3];
++
++  ffi_type l_type;
++  ffi_type *l_type_elements[6];
++
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l1;
++
++  int n;
++  int res;
++
++  unsigned char uc;
++  signed char sc;
++  unsigned short us;
++  signed short ss;
++  unsigned int ui;
++  signed int si;
++  unsigned long ul;
++  signed long sl;
++  double d1;
++  double f1;
++
++  s_type.size = 0;
++  s_type.alignment = 0;
++  s_type.type = FFI_TYPE_STRUCT;
++  s_type.elements = s_type_elements;
++
++  s_type_elements[0] = &ffi_type_uchar;
++  s_type_elements[1] = &ffi_type_uchar;
++  s_type_elements[2] = NULL;
++
++  l_type.size = 0;
++  l_type.alignment = 0;
++  l_type.type = FFI_TYPE_STRUCT;
++  l_type.elements = l_type_elements;
++
++  l_type_elements[0] = &ffi_type_uint;
++  l_type_elements[1] = &ffi_type_uint;
++  l_type_elements[2] = &ffi_type_uint;
++  l_type_elements[3] = &ffi_type_uint;
++  l_type_elements[4] = &ffi_type_uint;
++  l_type_elements[5] = NULL;
++
++  arg_types[0] = &ffi_type_sint;
++  arg_types[1] = &s_type;
++  arg_types[2] = &l_type;
++  arg_types[3] = &s_type;
++  arg_types[4] = &ffi_type_uint;
++  arg_types[5] = &ffi_type_sint;
++  arg_types[6] = &ffi_type_uint;
++  arg_types[7] = &ffi_type_sint;
++  arg_types[8] = &ffi_type_uint;
++  arg_types[9] = &ffi_type_sint;
++  arg_types[10] = &ffi_type_ulong;
++  arg_types[11] = &ffi_type_slong;
++  arg_types[12] = &ffi_type_double;
++  arg_types[13] = &ffi_type_double;
++  arg_types[14] = NULL;
++
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 14, &ffi_type_sint, arg_types) == FFI_OK);
++
++  s1.a = 5;
++  s1.b = 6;
++
++  l1.a = 10;
++  l1.b = 11;
++  l1.c = 12;
++  l1.d = 13;
++  l1.e = 14;
++
++  s2.a = 7;
++  s2.b = 8;
++
++  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] = &n;
++  args[1] = &s1;
++  args[2] = &l1;
++  args[3] = &s2;
++  args[4] = &uc;
++  args[5] = &sc;
++  args[6] = &us;
++  args[7] = &ss;
++  args[8] = &ui;
++  args[9] = &si;
++  args[10] = &ul;
++  args[11] = &sl;
++  args[12] = &f1;
++  args[13] = &d1;
++  args[14] = NULL;
++
++  ffi_call(&cif, FFI_FN(test_fn), &res, args);
++  /* { 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" } */
++
++  return 0;
++}
+Index: libffi/testsuite/libffi.call/va_struct1.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/va_struct1.c
+@@ -0,0 +1,121 @@
++/* Area:              ffi_call
++   Purpose:           Test passing struct in variable argument lists.
++   Limitations:       none.
++   PR:                        none.
++   Originator: ARM Ltd. */
++
++/* { dg-do run } */
++/* { dg-output "" { xfail avr32*-*-* } } */
++
++#include "ffitest.h"
++#include <stdarg.h>
++
++struct small_tag
++{
++  unsigned char a;
++  unsigned char b;
++};
++
++struct large_tag
++{
++  unsigned a;
++  unsigned b;
++  unsigned c;
++  unsigned d;
++  unsigned e;
++};
++
++static int
++test_fn (int n, ...)
++{
++  va_list ap;
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l;
++
++  va_start (ap, n);
++  s1 = va_arg (ap, struct small_tag);
++  l = va_arg (ap, struct large_tag);
++  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);
++  va_end (ap);
++  return n + 1;
++}
++
++int
++main (void)
++{
++  ffi_cif cif;
++  void* args[5];
++  ffi_type* arg_types[5];
++
++  ffi_type s_type;
++  ffi_type *s_type_elements[3];
++
++  ffi_type l_type;
++  ffi_type *l_type_elements[6];
++
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l1;
++
++  int n;
++  int res;
++
++  s_type.size = 0;
++  s_type.alignment = 0;
++  s_type.type = FFI_TYPE_STRUCT;
++  s_type.elements = s_type_elements;
++
++  s_type_elements[0] = &ffi_type_uchar;
++  s_type_elements[1] = &ffi_type_uchar;
++  s_type_elements[2] = NULL;
++
++  l_type.size = 0;
++  l_type.alignment = 0;
++  l_type.type = FFI_TYPE_STRUCT;
++  l_type.elements = l_type_elements;
++
++  l_type_elements[0] = &ffi_type_uint;
++  l_type_elements[1] = &ffi_type_uint;
++  l_type_elements[2] = &ffi_type_uint;
++  l_type_elements[3] = &ffi_type_uint;
++  l_type_elements[4] = &ffi_type_uint;
++  l_type_elements[5] = NULL;
++
++  arg_types[0] = &ffi_type_sint;
++  arg_types[1] = &s_type;
++  arg_types[2] = &l_type;
++  arg_types[3] = &s_type;
++  arg_types[4] = NULL;
++
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &ffi_type_sint, arg_types) == FFI_OK);
++
++  s1.a = 5;
++  s1.b = 6;
++
++  l1.a = 10;
++  l1.b = 11;
++  l1.c = 12;
++  l1.d = 13;
++  l1.e = 14;
++
++  s2.a = 7;
++  s2.b = 8;
++
++  n = 41;
++
++  args[0] = &n;
++  args[1] = &s1;
++  args[2] = &l1;
++  args[3] = &s2;
++  args[4] = NULL;
++
++  ffi_call(&cif, FFI_FN(test_fn), &res, args);
++  /* { dg-output "5 6 10 11 12 13 14 7 8" } */
++  printf("res: %d\n", (int) res);
++  /* { dg-output "\nres: 42" } */
++
++  return 0;
++}
+Index: libffi/testsuite/libffi.call/va_struct2.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/va_struct2.c
+@@ -0,0 +1,123 @@
++/* Area:              ffi_call
++   Purpose:           Test passing struct in variable argument lists.
++   Limitations:       none.
++   PR:                        none.
++   Originator: ARM Ltd. */
++
++/* { dg-do run } */
++/* { dg-output "" { xfail avr32*-*-* } } */
++
++#include "ffitest.h"
++#include <stdarg.h>
++
++struct small_tag
++{
++  unsigned char a;
++  unsigned char b;
++};
++
++struct large_tag
++{
++  unsigned a;
++  unsigned b;
++  unsigned c;
++  unsigned d;
++  unsigned e;
++};
++
++static struct small_tag
++test_fn (int n, ...)
++{
++  va_list ap;
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l;
++
++  va_start (ap, n);
++  s1 = va_arg (ap, struct small_tag);
++  l = va_arg (ap, struct large_tag);
++  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);
++  va_end (ap);
++  s1.a += s2.a;
++  s1.b += s2.b;
++  return s1;
++}
++
++int
++main (void)
++{
++  ffi_cif cif;
++  void* args[5];
++  ffi_type* arg_types[5];
++
++  ffi_type s_type;
++  ffi_type *s_type_elements[3];
++
++  ffi_type l_type;
++  ffi_type *l_type_elements[6];
++
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l1;
++
++  int n;
++  struct small_tag res;
++
++  s_type.size = 0;
++  s_type.alignment = 0;
++  s_type.type = FFI_TYPE_STRUCT;
++  s_type.elements = s_type_elements;
++
++  s_type_elements[0] = &ffi_type_uchar;
++  s_type_elements[1] = &ffi_type_uchar;
++  s_type_elements[2] = NULL;
++
++  l_type.size = 0;
++  l_type.alignment = 0;
++  l_type.type = FFI_TYPE_STRUCT;
++  l_type.elements = l_type_elements;
++
++  l_type_elements[0] = &ffi_type_uint;
++  l_type_elements[1] = &ffi_type_uint;
++  l_type_elements[2] = &ffi_type_uint;
++  l_type_elements[3] = &ffi_type_uint;
++  l_type_elements[4] = &ffi_type_uint;
++  l_type_elements[5] = NULL;
++
++  arg_types[0] = &ffi_type_sint;
++  arg_types[1] = &s_type;
++  arg_types[2] = &l_type;
++  arg_types[3] = &s_type;
++  arg_types[4] = NULL;
++
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &s_type, arg_types) == FFI_OK);
++
++  s1.a = 5;
++  s1.b = 6;
++
++  l1.a = 10;
++  l1.b = 11;
++  l1.c = 12;
++  l1.d = 13;
++  l1.e = 14;
++
++  s2.a = 7;
++  s2.b = 8;
++
++  n = 41;
++
++  args[0] = &n;
++  args[1] = &s1;
++  args[2] = &l1;
++  args[3] = &s2;
++  args[4] = NULL;
++
++  ffi_call(&cif, FFI_FN(test_fn), &res, args);
++  /* { 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" } */
++
++  return 0;
++}
+Index: libffi/testsuite/libffi.call/va_struct3.c
+===================================================================
+--- /dev/null
++++ libffi/testsuite/libffi.call/va_struct3.c
+@@ -0,0 +1,125 @@
++/* Area:              ffi_call
++   Purpose:           Test passing struct in variable argument lists.
++   Limitations:       none.
++   PR:                        none.
++   Originator:        ARM Ltd. */
++
++/* { dg-do run } */
++/* { dg-output "" { xfail avr32*-*-* } } */
++
++#include "ffitest.h"
++#include <stdarg.h>
++
++struct small_tag
++{
++  unsigned char a;
++  unsigned char b;
++};
++
++struct large_tag
++{
++  unsigned a;
++  unsigned b;
++  unsigned c;
++  unsigned d;
++  unsigned e;
++};
++
++static struct large_tag
++test_fn (int n, ...)
++{
++  va_list ap;
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l;
++
++  va_start (ap, n);
++  s1 = va_arg (ap, struct small_tag);
++  l = va_arg (ap, struct large_tag);
++  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);
++  va_end (ap);
++  l.a += s1.a;
++  l.b += s1.b;
++  l.c += s2.a;
++  l.d += s2.b;
++  return l;
++}
++
++int
++main (void)
++{
++  ffi_cif cif;
++  void* args[5];
++  ffi_type* arg_types[5];
++
++  ffi_type s_type;
++  ffi_type *s_type_elements[3];
++
++  ffi_type l_type;
++  ffi_type *l_type_elements[6];
++
++  struct small_tag s1;
++  struct small_tag s2;
++  struct large_tag l1;
++
++  int n;
++  struct large_tag res;
++
++  s_type.size = 0;
++  s_type.alignment = 0;
++  s_type.type = FFI_TYPE_STRUCT;
++  s_type.elements = s_type_elements;
++
++  s_type_elements[0] = &ffi_type_uchar;
++  s_type_elements[1] = &ffi_type_uchar;
++  s_type_elements[2] = NULL;
++
++  l_type.size = 0;
++  l_type.alignment = 0;
++  l_type.type = FFI_TYPE_STRUCT;
++  l_type.elements = l_type_elements;
++
++  l_type_elements[0] = &ffi_type_uint;
++  l_type_elements[1] = &ffi_type_uint;
++  l_type_elements[2] = &ffi_type_uint;
++  l_type_elements[3] = &ffi_type_uint;
++  l_type_elements[4] = &ffi_type_uint;
++  l_type_elements[5] = NULL;
++
++  arg_types[0] = &ffi_type_sint;
++  arg_types[1] = &s_type;
++  arg_types[2] = &l_type;
++  arg_types[3] = &s_type;
++  arg_types[4] = NULL;
++
++  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &l_type, arg_types) == FFI_OK);
++
++  s1.a = 5;
++  s1.b = 6;
++
++  l1.a = 10;
++  l1.b = 11;
++  l1.c = 12;
++  l1.d = 13;
++  l1.e = 14;
++
++  s2.a = 7;
++  s2.b = 8;
++
++  n = 41;
++
++  args[0] = &n;
++  args[1] = &s1;
++  args[2] = &l1;
++  args[3] = &s2;
++  args[4] = NULL;
++
++  ffi_call(&cif, FFI_FN(test_fn), &res, args);
++  /* { 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" } */
++
++  return 0;
++}
index 26e4d78..18aef73 100644 (file)
@@ -23,3 +23,4 @@ ios-fix
 mingw-check-fix
 whitespace-fix
 tile
+aarch64
diff --git a/src/aarch64/ffi.c b/src/aarch64/ffi.c
new file mode 100644 (file)
index 0000000..1405665
--- /dev/null
@@ -0,0 +1,1076 @@
+/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd.
+
+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 <stdio.h>
+
+#include <ffi.h>
+#include <ffi_common.h>
+
+#include <stdlib.h>
+
+/* Stack alignment requirement in bytes */
+#define AARCH64_STACK_ALIGN 16
+
+#define N_X_ARG_REG 8
+#define N_V_ARG_REG 8
+
+#define AARCH64_FFI_WITH_V (1 << AARCH64_FFI_WITH_V_BIT)
+
+union _d
+{
+  UINT64 d;
+  UINT32 s[2];
+};
+
+struct call_context
+{
+  UINT64 x [AARCH64_N_XREG];
+  struct
+  {
+    union _d d[2];
+  } v [AARCH64_N_VREG];
+};
+
+static void *
+get_x_addr (struct call_context *context, unsigned n)
+{
+  return &context->x[n];
+}
+
+static void *
+get_s_addr (struct call_context *context, unsigned n)
+{
+#if defined __AARCH64EB__
+  return &context->v[n].d[1].s[1];
+#else
+  return &context->v[n].d[0].s[0];
+#endif
+}
+
+static void *
+get_d_addr (struct call_context *context, unsigned n)
+{
+#if defined __AARCH64EB__
+  return &context->v[n].d[1];
+#else
+  return &context->v[n].d[0];
+#endif
+}
+
+static void *
+get_v_addr (struct call_context *context, unsigned n)
+{
+  return &context->v[n];
+}
+
+/* Return the memory location at which a basic type would reside
+   were it to have been stored in register n.  */
+
+static void *
+get_basic_type_addr (unsigned short type, struct call_context *context,
+                    unsigned n)
+{
+  switch (type)
+    {
+    case FFI_TYPE_FLOAT:
+      return get_s_addr (context, n);
+    case FFI_TYPE_DOUBLE:
+      return get_d_addr (context, n);
+    case FFI_TYPE_LONGDOUBLE:
+      return get_v_addr (context, n);
+    case FFI_TYPE_UINT8:
+    case FFI_TYPE_SINT8:
+    case FFI_TYPE_UINT16:
+    case FFI_TYPE_SINT16:
+    case FFI_TYPE_UINT32:
+    case FFI_TYPE_SINT32:
+    case FFI_TYPE_INT:
+    case FFI_TYPE_POINTER:
+    case FFI_TYPE_UINT64:
+    case FFI_TYPE_SINT64:
+      return get_x_addr (context, n);
+    default:
+      FFI_ASSERT (0);
+      return NULL;
+    }
+}
+
+/* Return the alignment width for each of the basic types.  */
+
+static size_t
+get_basic_type_alignment (unsigned short type)
+{
+  switch (type)
+    {
+    case FFI_TYPE_FLOAT:
+    case FFI_TYPE_DOUBLE:
+      return sizeof (UINT64);
+    case FFI_TYPE_LONGDOUBLE:
+      return sizeof (long double);
+    case FFI_TYPE_UINT8:
+    case FFI_TYPE_SINT8:
+    case FFI_TYPE_UINT16:
+    case FFI_TYPE_SINT16:
+    case FFI_TYPE_UINT32:
+    case FFI_TYPE_INT:
+    case FFI_TYPE_SINT32:
+    case FFI_TYPE_POINTER:
+    case FFI_TYPE_UINT64:
+    case FFI_TYPE_SINT64:
+      return sizeof (UINT64);
+
+    default:
+      FFI_ASSERT (0);
+      return 0;
+    }
+}
+
+/* Return the size in bytes for each of the basic types.  */
+
+static size_t
+get_basic_type_size (unsigned short type)
+{
+  switch (type)
+    {
+    case FFI_TYPE_FLOAT:
+      return sizeof (UINT32);
+    case FFI_TYPE_DOUBLE:
+      return sizeof (UINT64);
+    case FFI_TYPE_LONGDOUBLE:
+      return sizeof (long double);
+    case FFI_TYPE_UINT8:
+      return sizeof (UINT8);
+    case FFI_TYPE_SINT8:
+      return sizeof (SINT8);
+    case FFI_TYPE_UINT16:
+      return sizeof (UINT16);
+    case FFI_TYPE_SINT16:
+      return sizeof (SINT16);
+    case FFI_TYPE_UINT32:
+      return sizeof (UINT32);
+    case FFI_TYPE_INT:
+    case FFI_TYPE_SINT32:
+      return sizeof (SINT32);
+    case FFI_TYPE_POINTER:
+    case FFI_TYPE_UINT64:
+      return sizeof (UINT64);
+    case FFI_TYPE_SINT64:
+      return sizeof (SINT64);
+
+    default:
+      FFI_ASSERT (0);
+      return 0;
+    }
+}
+
+extern void
+ffi_call_SYSV (unsigned (*)(struct call_context *context, unsigned char *,
+                           extended_cif *),
+               struct call_context *context,
+               extended_cif *,
+               unsigned,
+               void (*fn)(void));
+
+extern void
+ffi_closure_SYSV (ffi_closure *);
+
+/* Test for an FFI floating point representation.  */
+
+static unsigned
+is_floating_type (unsigned short type)
+{
+  return (type == FFI_TYPE_FLOAT || type == FFI_TYPE_DOUBLE
+         || type == FFI_TYPE_LONGDOUBLE);
+}
+
+/* Test for a homogeneous structure.  */
+
+static unsigned short
+get_homogeneous_type (ffi_type *ty)
+{
+  if (ty->type == FFI_TYPE_STRUCT && ty->elements)
+    {
+      unsigned i;
+      unsigned short candidate_type
+       = get_homogeneous_type (ty->elements[0]);
+      for (i =1; ty->elements[i]; i++)
+       {
+         unsigned short iteration_type = 0;
+         /* If we have a nested struct, we must find its homogeneous type.
+            If that fits with our candidate type, we are still
+            homogeneous.  */
+         if (ty->elements[i]->type == FFI_TYPE_STRUCT
+             && ty->elements[i]->elements)
+           {
+             iteration_type = get_homogeneous_type (ty->elements[i]);
+           }
+         else
+           {
+             iteration_type = ty->elements[i]->type;
+           }
+
+         /* If we are not homogeneous, return FFI_TYPE_STRUCT.  */
+         if (candidate_type != iteration_type)
+           return FFI_TYPE_STRUCT;
+       }
+      return candidate_type;
+    }
+
+  /* Base case, we have no more levels of nesting, so we
+     are a basic type, and so, trivially homogeneous in that type.  */
+  return ty->type;
+}
+
+/* Determine the number of elements within a STRUCT.
+
+   Note, we must handle nested structs.
+
+   If ty is not a STRUCT this function will return 0.  */
+
+static unsigned
+element_count (ffi_type *ty)
+{
+  if (ty->type == FFI_TYPE_STRUCT && ty->elements)
+    {
+      unsigned n;
+      unsigned elems = 0;
+      for (n = 0; ty->elements[n]; n++)
+       {
+         if (ty->elements[n]->type == FFI_TYPE_STRUCT
+             && ty->elements[n]->elements)
+           elems += element_count (ty->elements[n]);
+         else
+           elems++;
+       }
+      return elems;
+    }
+  return 0;
+}
+
+/* Test for a homogeneous floating point aggregate.
+
+   A homogeneous floating point aggregate is a homogeneous aggregate of
+   a half- single- or double- precision floating point type with one
+   to four elements.  Note that this includes nested structs of the
+   basic type.  */
+
+static int
+is_hfa (ffi_type *ty)
+{
+  if (ty->type == FFI_TYPE_STRUCT
+      && ty->elements[0]
+      && is_floating_type (get_homogeneous_type (ty)))
+    {
+      unsigned n = element_count (ty);
+      return n >= 1 && n <= 4;
+    }
+  return 0;
+}
+
+/* Test if an ffi_type is a candidate for passing in a register.
+
+   This test does not check that sufficient registers of the
+   appropriate class are actually available, merely that IFF
+   sufficient registers are available then the argument will be passed
+   in register(s).
+
+   Note that an ffi_type that is deemed to be a register candidate
+   will always be returned in registers.
+
+   Returns 1 if a register candidate else 0.  */
+
+static int
+is_register_candidate (ffi_type *ty)
+{
+  switch (ty->type)
+    {
+    case FFI_TYPE_VOID:
+    case FFI_TYPE_FLOAT:
+    case FFI_TYPE_DOUBLE:
+    case FFI_TYPE_LONGDOUBLE:
+    case FFI_TYPE_UINT8:
+    case FFI_TYPE_UINT16:
+    case FFI_TYPE_UINT32:
+    case FFI_TYPE_UINT64:
+    case FFI_TYPE_POINTER:
+    case FFI_TYPE_SINT8:
+    case FFI_TYPE_SINT16:
+    case FFI_TYPE_SINT32:
+    case FFI_TYPE_INT:
+    case FFI_TYPE_SINT64:
+      return 1;
+
+    case FFI_TYPE_STRUCT:
+      if (is_hfa (ty))
+        {
+          return 1;
+        }
+      else if (ty->size > 16)
+        {
+          /* Too large. Will be replaced with a pointer to memory. The
+             pointer MAY be passed in a register, but the value will
+             not. This test specifically fails since the argument will
+             never be passed by value in registers. */
+          return 0;
+        }
+      else
+        {
+          /* Might be passed in registers depending on the number of
+             registers required. */
+          return (ty->size + 7) / 8 < N_X_ARG_REG;
+        }
+      break;
+
+    default:
+      FFI_ASSERT (0);
+      break;
+    }
+
+  return 0;
+}
+
+/* Test if an ffi_type argument or result is a candidate for a vector
+   register.  */
+
+static int
+is_v_register_candidate (ffi_type *ty)
+{
+  return is_floating_type (ty->type)
+          || (ty->type == FFI_TYPE_STRUCT && is_hfa (ty));
+}
+
+/* Representation of the procedure call argument marshalling
+   state.
+
+   The terse state variable names match the names used in the AARCH64
+   PCS. */
+
+struct arg_state
+{
+  unsigned ngrn;                /* Next general-purpose register number. */
+  unsigned nsrn;                /* Next vector register number. */
+  unsigned nsaa;                /* Next stack offset. */
+};
+
+/* Initialize a procedure call argument marshalling state.  */
+static void
+arg_init (struct arg_state *state, unsigned call_frame_size)
+{
+  state->ngrn = 0;
+  state->nsrn = 0;
+  state->nsaa = 0;
+}
+
+/* Return the number of available consecutive core argument
+   registers.  */
+
+static unsigned
+available_x (struct arg_state *state)
+{
+  return N_X_ARG_REG - state->ngrn;
+}
+
+/* Return the number of available consecutive vector argument
+   registers.  */
+
+static unsigned
+available_v (struct arg_state *state)
+{
+  return N_V_ARG_REG - state->nsrn;
+}
+
+static void *
+allocate_to_x (struct call_context *context, struct arg_state *state)
+{
+  FFI_ASSERT (state->ngrn < N_X_ARG_REG)
+  return get_x_addr (context, (state->ngrn)++);
+}
+
+static void *
+allocate_to_s (struct call_context *context, struct arg_state *state)
+{
+  FFI_ASSERT (state->nsrn < N_V_ARG_REG)
+  return get_s_addr (context, (state->nsrn)++);
+}
+
+static void *
+allocate_to_d (struct call_context *context, struct arg_state *state)
+{
+  FFI_ASSERT (state->nsrn < N_V_ARG_REG)
+  return get_d_addr (context, (state->nsrn)++);
+}
+
+static void *
+allocate_to_v (struct call_context *context, struct arg_state *state)
+{
+  FFI_ASSERT (state->nsrn < N_V_ARG_REG)
+  return get_v_addr (context, (state->nsrn)++);
+}
+
+/* Allocate an aligned slot on the stack and return a pointer to it.  */
+static void *
+allocate_to_stack (struct arg_state *state, void *stack, unsigned alignment,
+                  unsigned size)
+{
+  void *allocation;
+
+  /* Round up the NSAA to the larger of 8 or the natural
+     alignment of the argument's type.  */
+  state->nsaa = ALIGN (state->nsaa, alignment);
+  state->nsaa = ALIGN (state->nsaa, alignment);
+  state->nsaa = ALIGN (state->nsaa, 8);
+
+  allocation = stack + state->nsaa;
+
+  state->nsaa += size;
+  return allocation;
+}
+
+static void
+copy_basic_type (void *dest, void *source, unsigned short type)
+{
+  /* This is neccessary to ensure that basic types are copied
+     sign extended to 64-bits as libffi expects.  */
+  switch (type)
+    {
+    case FFI_TYPE_FLOAT:
+      *(float *) dest = *(float *) source;
+      break;
+    case FFI_TYPE_DOUBLE:
+      *(double *) dest = *(double *) source;
+      break;
+    case FFI_TYPE_LONGDOUBLE:
+      *(long double *) dest = *(long double *) source;
+      break;
+    case FFI_TYPE_UINT8:
+      *(ffi_arg *) dest = *(UINT8 *) source;
+      break;
+    case FFI_TYPE_SINT8:
+      *(ffi_sarg *) dest = *(SINT8 *) source;
+      break;
+    case FFI_TYPE_UINT16:
+      *(ffi_arg *) dest = *(UINT16 *) source;
+      break;
+    case FFI_TYPE_SINT16:
+      *(ffi_sarg *) dest = *(SINT16 *) source;
+      break;
+    case FFI_TYPE_UINT32:
+      *(ffi_arg *) dest = *(UINT32 *) source;
+      break;
+    case FFI_TYPE_INT:
+    case FFI_TYPE_SINT32:
+      *(ffi_sarg *) dest = *(SINT32 *) source;
+      break;
+    case FFI_TYPE_POINTER:
+    case FFI_TYPE_UINT64:
+      *(ffi_arg *) dest = *(UINT64 *) source;
+      break;
+    case FFI_TYPE_SINT64:
+      *(ffi_sarg *) dest = *(SINT64 *) source;
+      break;
+
+    default:
+      FFI_ASSERT (0);
+    }
+}
+
+static void
+copy_hfa_to_reg_or_stack (void *memory,
+                         ffi_type *ty,
+                         struct call_context *context,
+                         unsigned char *stack,
+                         struct arg_state *state)
+{
+  unsigned elems = element_count (ty);
+  if (available_v (state) < elems)
+    {
+      /* There are insufficient V registers. Further V register allocations
+        are prevented, the NSAA is adjusted (by allocate_to_stack ())
+        and the argument is copied to memory at the adjusted NSAA.  */
+      state->nsrn = N_V_ARG_REG;
+      memcpy (allocate_to_stack (state, stack, ty->alignment, ty->size),
+             memory,
+             ty->size);
+    }
+  else
+    {
+      int i;
+      unsigned short type = get_homogeneous_type (ty);
+      unsigned elems = element_count (ty);
+      for (i = 0; i < elems; i++)
+       {
+         void *reg = allocate_to_v (context, state);
+         copy_basic_type (reg, memory, type);
+         memory += get_basic_type_size (type);
+       }
+    }
+}
+
+/* Either allocate an appropriate register for the argument type, or if
+   none are available, allocate a stack slot and return a pointer
+   to the allocated space.  */
+
+static void *
+allocate_to_register_or_stack (struct call_context *context,
+                              unsigned char *stack,
+                              struct arg_state *state,
+                              unsigned short type)
+{
+  size_t alignment = get_basic_type_alignment (type);
+  size_t size = alignment;
+  switch (type)
+    {
+    case FFI_TYPE_FLOAT:
+      /* This is the only case for which the allocated stack size
+        should not match the alignment of the type.  */
+      size = sizeof (UINT32);
+      /* Fall through.  */
+    case FFI_TYPE_DOUBLE:
+      if (state->nsrn < N_V_ARG_REG)
+       return allocate_to_d (context, state);
+      state->nsrn = N_V_ARG_REG;
+      break;
+    case FFI_TYPE_LONGDOUBLE:
+      if (state->nsrn < N_V_ARG_REG)
+       return allocate_to_v (context, state);
+      state->nsrn = N_V_ARG_REG;
+      break;
+    case FFI_TYPE_UINT8:
+    case FFI_TYPE_SINT8:
+    case FFI_TYPE_UINT16:
+    case FFI_TYPE_SINT16:
+    case FFI_TYPE_UINT32:
+    case FFI_TYPE_SINT32:
+    case FFI_TYPE_INT:
+    case FFI_TYPE_POINTER:
+    case FFI_TYPE_UINT64:
+    case FFI_TYPE_SINT64:
+      if (state->ngrn < N_X_ARG_REG)
+       return allocate_to_x (context, state);
+      state->ngrn = N_X_ARG_REG;
+      break;
+    default:
+      FFI_ASSERT (0);
+    }
+
+    return allocate_to_stack (state, stack, alignment, size);
+}
+
+/* Copy a value to an appropriate register, or if none are
+   available, to the stack.  */
+
+static void
+copy_to_register_or_stack (struct call_context *context,
+                          unsigned char *stack,
+                          struct arg_state *state,
+                          void *value,
+                          unsigned short type)
+{
+  copy_basic_type (
+         allocate_to_register_or_stack (context, stack, state, type),
+         value,
+         type);
+}
+
+/* Marshall the arguments from FFI representation to procedure call
+   context and stack.  */
+
+static unsigned
+aarch64_prep_args (struct call_context *context, unsigned char *stack,
+                  extended_cif *ecif)
+{
+  int i;
+  struct arg_state state;
+
+  arg_init (&state, ALIGN(ecif->cif->bytes, 16));
+
+  for (i = 0; i < ecif->cif->nargs; i++)
+    {
+      ffi_type *ty = ecif->cif->arg_types[i];
+      switch (ty->type)
+       {
+       case FFI_TYPE_VOID:
+         FFI_ASSERT (0);
+         break;
+
+       /* If the argument is a basic type the argument is allocated to an
+          appropriate register, or if none are available, to the stack.  */
+       case FFI_TYPE_FLOAT:
+       case FFI_TYPE_DOUBLE:
+       case FFI_TYPE_LONGDOUBLE:
+       case FFI_TYPE_UINT8:
+       case FFI_TYPE_SINT8:
+       case FFI_TYPE_UINT16:
+       case FFI_TYPE_SINT16:
+       case FFI_TYPE_UINT32:
+       case FFI_TYPE_INT:
+       case FFI_TYPE_SINT32:
+       case FFI_TYPE_POINTER:
+       case FFI_TYPE_UINT64:
+       case FFI_TYPE_SINT64:
+         copy_to_register_or_stack (context, stack, &state,
+                                    ecif->avalue[i], ty->type);
+         break;
+
+       case FFI_TYPE_STRUCT:
+         if (is_hfa (ty))
+           {
+             copy_hfa_to_reg_or_stack (ecif->avalue[i], ty, context,
+                                       stack, &state);
+           }
+         else if (ty->size > 16)
+           {
+             /* If the argument is a composite type that is larger than 16
+                bytes, then the argument has been copied to memory, and
+                the argument is replaced by a pointer to the copy.  */
+
+             copy_to_register_or_stack (context, stack, &state,
+                                        &(ecif->avalue[i]), FFI_TYPE_POINTER);
+           }
+         else if (available_x (&state) >= (ty->size + 7) / 8)
+           {
+             /* If the argument is a composite type and the size in
+                double-words is not more than the number of available
+                X registers, then the argument is copied into consecutive
+                X registers.  */
+             int j;
+             for (j = 0; j < (ty->size + 7) / 8; j++)
+               {
+                 memcpy (allocate_to_x (context, &state),
+                         &(((UINT64 *) ecif->avalue[i])[j]),
+                         sizeof (UINT64));
+               }
+           }
+         else
+           {
+             /* Otherwise, there are insufficient X registers. Further X
+                register allocations are prevented, the NSAA is adjusted
+                (by allocate_to_stack ()) and the argument is copied to
+                memory at the adjusted NSAA.  */
+             state.ngrn = N_X_ARG_REG;
+
+             memcpy (allocate_to_stack (&state, stack, ty->alignment,
+                                        ty->size), ecif->avalue + i, ty->size);
+           }
+         break;
+
+       default:
+         FFI_ASSERT (0);
+         break;
+       }
+    }
+
+  return ecif->cif->aarch64_flags;
+}
+
+ffi_status
+ffi_prep_cif_machdep (ffi_cif *cif)
+{
+  /* Round the stack up to a multiple of the stack alignment requirement. */
+  cif->bytes =
+    (cif->bytes + (AARCH64_STACK_ALIGN - 1)) & ~ (AARCH64_STACK_ALIGN - 1);
+
+  /* Initialize our flags. We are interested if this CIF will touch a
+     vector register, if so we will enable context save and load to
+     those registers, otherwise not. This is intended to be friendly
+     to lazy float context switching in the kernel.  */
+  cif->aarch64_flags = 0;
+
+  if (is_v_register_candidate (cif->rtype))
+    {
+      cif->aarch64_flags |= AARCH64_FFI_WITH_V;
+    }
+  else
+    {
+      int i;
+      for (i = 0; i < cif->nargs; i++)
+        if (is_v_register_candidate (cif->arg_types[i]))
+          {
+            cif->aarch64_flags |= AARCH64_FFI_WITH_V;
+            break;
+          }
+    }
+
+  return FFI_OK;
+}
+
+/* Call a function with the provided arguments and capture the return
+   value.  */
+void
+ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+{
+  extended_cif ecif;
+
+  ecif.cif = cif;
+  ecif.avalue = avalue;
+  ecif.rvalue = rvalue;
+
+  switch (cif->abi)
+    {
+    case FFI_SYSV:
+      {
+        struct call_context context;
+       unsigned stack_bytes;
+
+       /* Figure out the total amount of stack space we need, the
+          above call frame space needs to be 16 bytes aligned to
+          ensure correct alignment of the first object inserted in
+          that space hence the ALIGN applied to cif->bytes.*/
+       stack_bytes = ALIGN(cif->bytes, 16);
+
+       memset (&context, 0, sizeof (context));
+        if (is_register_candidate (cif->rtype))
+          {
+            ffi_call_SYSV (aarch64_prep_args, &context, &ecif, stack_bytes, fn);
+            switch (cif->rtype->type)
+              {
+              case FFI_TYPE_VOID:
+              case FFI_TYPE_FLOAT:
+              case FFI_TYPE_DOUBLE:
+              case FFI_TYPE_LONGDOUBLE:
+              case FFI_TYPE_UINT8:
+              case FFI_TYPE_SINT8:
+              case FFI_TYPE_UINT16:
+              case FFI_TYPE_SINT16:
+              case FFI_TYPE_UINT32:
+              case FFI_TYPE_SINT32:
+              case FFI_TYPE_POINTER:
+              case FFI_TYPE_UINT64:
+              case FFI_TYPE_INT:
+              case FFI_TYPE_SINT64:
+               {
+                 void *addr = get_basic_type_addr (cif->rtype->type,
+                                                   &context, 0);
+                 copy_basic_type (rvalue, addr, cif->rtype->type);
+                 break;
+               }
+
+              case FFI_TYPE_STRUCT:
+                if (is_hfa (cif->rtype))
+                 {
+                   int j;
+                   unsigned short type = get_homogeneous_type (cif->rtype);
+                   unsigned elems = element_count (cif->rtype);
+                   for (j = 0; j < elems; j++)
+                     {
+                       void *reg = get_basic_type_addr (type, &context, j);
+                       copy_basic_type (rvalue, reg, type);
+                       rvalue += get_basic_type_size (type);
+                     }
+                 }
+                else if ((cif->rtype->size + 7) / 8 < N_X_ARG_REG)
+                  {
+                    unsigned size = ALIGN (cif->rtype->size, sizeof (UINT64));
+                    memcpy (rvalue, get_x_addr (&context, 0), size);
+                  }
+                else
+                  {
+                    FFI_ASSERT (0);
+                  }
+                break;
+
+              default:
+                FFI_ASSERT (0);
+                break;
+              }
+          }
+        else
+          {
+            memcpy (get_x_addr (&context, 8), &rvalue, sizeof (UINT64));
+            ffi_call_SYSV (aarch64_prep_args, &context, &ecif,
+                          stack_bytes, fn);
+          }
+        break;
+      }
+
+    default:
+      FFI_ASSERT (0);
+      break;
+    }
+}
+
+static unsigned char trampoline [] =
+{ 0x70, 0x00, 0x00, 0x58,      /* ldr  x16, 1f */
+  0x91, 0x00, 0x00, 0x10,      /* adr  x17, 2f */
+  0x00, 0x02, 0x1f, 0xd6       /* br   x16     */
+};
+
+/* Build a trampoline.  */
+
+#define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX,FLAGS)                       \
+  ({unsigned char *__tramp = (unsigned char*)(TRAMP);                  \
+    UINT64  __fun = (UINT64)(FUN);                                     \
+    UINT64  __ctx = (UINT64)(CTX);                                     \
+    UINT64  __flags = (UINT64)(FLAGS);                                 \
+    memcpy (__tramp, trampoline, sizeof (trampoline));                 \
+    memcpy (__tramp + 12, &__fun, sizeof (__fun));                     \
+    memcpy (__tramp + 20, &__ctx, sizeof (__ctx));                     \
+    memcpy (__tramp + 28, &__flags, sizeof (__flags));                 \
+    __clear_cache(__tramp, __tramp + FFI_TRAMPOLINE_SIZE);             \
+  })
+
+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_SYSV)
+    return FFI_BAD_ABI;
+
+  FFI_INIT_TRAMPOLINE (&closure->tramp[0], &ffi_closure_SYSV, codeloc,
+                      cif->aarch64_flags);
+
+  closure->cif  = cif;
+  closure->user_data = user_data;
+  closure->fun  = fun;
+
+  return FFI_OK;
+}
+
+/* Primary handler to setup and invoke a function within a closure.
+
+   A closure when invoked enters via the assembler wrapper
+   ffi_closure_SYSV(). The wrapper allocates a call context on the
+   stack, saves the interesting registers (from the perspective of
+   the calling convention) into the context then passes control to
+   ffi_closure_SYSV_inner() passing the saved context and a pointer to
+   the stack at the point ffi_closure_SYSV() was invoked.
+
+   On the return path the assembler wrapper will reload call context
+   regsiters.
+
+   ffi_closure_SYSV_inner() marshalls the call context into ffi value
+   desriptors, invokes the wrapped function, then marshalls the return
+   value back into the call context.  */
+
+void
+ffi_closure_SYSV_inner (ffi_closure *closure, struct call_context *context,
+                       void *stack)
+{
+  ffi_cif *cif = closure->cif;
+  void **avalue = (void**) alloca (cif->nargs * sizeof (void*));
+  void *rvalue = NULL;
+  int i;
+  struct arg_state state;
+
+  arg_init (&state, ALIGN(cif->bytes, 16));
+
+  for (i = 0; i < cif->nargs; i++)
+    {
+      ffi_type *ty = cif->arg_types[i];
+
+      switch (ty->type)
+       {
+       case FFI_TYPE_VOID:
+         FFI_ASSERT (0);
+         break;
+
+       case FFI_TYPE_UINT8:
+       case FFI_TYPE_SINT8:
+       case FFI_TYPE_UINT16:
+       case FFI_TYPE_SINT16:
+       case FFI_TYPE_UINT32:
+       case FFI_TYPE_SINT32:
+       case FFI_TYPE_INT:
+       case FFI_TYPE_POINTER:
+       case FFI_TYPE_UINT64:
+       case FFI_TYPE_SINT64:
+       case  FFI_TYPE_FLOAT:
+       case  FFI_TYPE_DOUBLE:
+       case  FFI_TYPE_LONGDOUBLE:
+         avalue[i] = allocate_to_register_or_stack (context, stack,
+                                                    &state, ty->type);
+         break;
+
+       case FFI_TYPE_STRUCT:
+         if (is_hfa (ty))
+           {
+             unsigned n = element_count (ty);
+             if (available_v (&state) < n)
+               {
+                 state.nsrn = N_V_ARG_REG;
+                 avalue[i] = allocate_to_stack (&state, stack, ty->alignment,
+                                                ty->size);
+               }
+             else
+               {
+                 switch (get_homogeneous_type (ty))
+                   {
+                   case FFI_TYPE_FLOAT:
+                     {
+                       /* Eeek! We need a pointer to the structure,
+                          however the homogeneous float elements are
+                          being passed in individual S registers,
+                          therefore the structure is not represented as
+                          a contiguous sequence of bytes in our saved
+                          register context. We need to fake up a copy
+                          of the structure layed out in memory
+                          correctly. The fake can be tossed once the
+                          closure function has returned hence alloca()
+                          is sufficient. */
+                       int j;
+                       UINT32 *p = avalue[i] = alloca (ty->size);
+                       for (j = 0; j < element_count (ty); j++)
+                         memcpy (&p[j],
+                                 allocate_to_s (context, &state),
+                                 sizeof (*p));
+                       break;
+                     }
+
+                   case FFI_TYPE_DOUBLE:
+                     {
+                       /* Eeek! We need a pointer to the structure,
+                          however the homogeneous float elements are
+                          being passed in individual S registers,
+                          therefore the structure is not represented as
+                          a contiguous sequence of bytes in our saved
+                          register context. We need to fake up a copy
+                          of the structure layed out in memory
+                          correctly. The fake can be tossed once the
+                          closure function has returned hence alloca()
+                          is sufficient. */
+                       int j;
+                       UINT64 *p = avalue[i] = alloca (ty->size);
+                       for (j = 0; j < element_count (ty); j++)
+                         memcpy (&p[j],
+                                 allocate_to_d (context, &state),
+                                 sizeof (*p));
+                       break;
+                     }
+
+                   case FFI_TYPE_LONGDOUBLE:
+                         memcpy (&avalue[i],
+                                 allocate_to_v (context, &state),
+                                 sizeof (*avalue));
+                     break;
+
+                   default:
+                     FFI_ASSERT (0);
+                     break;
+                   }
+               }
+           }
+         else if (ty->size > 16)
+           {
+             /* Replace Composite type of size greater than 16 with a
+                pointer.  */
+             memcpy (&avalue[i],
+                     allocate_to_register_or_stack (context, stack,
+                                                    &state, FFI_TYPE_POINTER),
+                     sizeof (avalue[i]));
+           }
+         else if (available_x (&state) >= (ty->size + 7) / 8)
+           {
+             avalue[i] = get_x_addr (context, state.ngrn);
+             state.ngrn += (ty->size + 7) / 8;
+           }
+         else
+           {
+             state.ngrn = N_X_ARG_REG;
+
+             avalue[i] = allocate_to_stack (&state, stack, ty->alignment,
+                                            ty->size);
+           }
+         break;
+
+       default:
+         FFI_ASSERT (0);
+         break;
+       }
+    }
+
+  /* Figure out where the return value will be passed, either in
+     registers or in a memory block allocated by the caller and passed
+     in x8.  */
+
+  if (is_register_candidate (cif->rtype))
+    {
+      /* Register candidates are *always* returned in registers. */
+
+      /* Allocate a scratchpad for the return value, we will let the
+         callee scrible the result into the scratch pad then move the
+         contents into the appropriate return value location for the
+         call convention.  */
+      rvalue = alloca (cif->rtype->size);
+      (closure->fun) (cif, rvalue, avalue, closure->user_data);
+
+      /* Copy the return value into the call context so that it is returned
+         as expected to our caller.  */
+      switch (cif->rtype->type)
+        {
+        case FFI_TYPE_VOID:
+          break;
+
+        case FFI_TYPE_UINT8:
+        case FFI_TYPE_UINT16:
+        case FFI_TYPE_UINT32:
+        case FFI_TYPE_POINTER:
+        case FFI_TYPE_UINT64:
+        case FFI_TYPE_SINT8:
+        case FFI_TYPE_SINT16:
+        case FFI_TYPE_INT:
+        case FFI_TYPE_SINT32:
+        case FFI_TYPE_SINT64:
+        case FFI_TYPE_FLOAT:
+        case FFI_TYPE_DOUBLE:
+        case FFI_TYPE_LONGDOUBLE:
+         {
+           void *addr = get_basic_type_addr (cif->rtype->type, context, 0);
+           copy_basic_type (addr, rvalue, cif->rtype->type);
+            break;
+         }
+        case FFI_TYPE_STRUCT:
+          if (is_hfa (cif->rtype))
+           {
+             int i;
+             unsigned short type = get_homogeneous_type (cif->rtype);
+             unsigned elems = element_count (cif->rtype);
+             for (i = 0; i < elems; i++)
+               {
+                 void *reg = get_basic_type_addr (type, context, i);
+                 copy_basic_type (reg, rvalue, type);
+                 rvalue += get_basic_type_size (type);
+               }
+           }
+          else if ((cif->rtype->size + 7) / 8 < N_X_ARG_REG)
+            {
+              unsigned size = ALIGN (cif->rtype->size, sizeof (UINT64)) ;
+              memcpy (get_x_addr (context, 0), rvalue, size);
+            }
+          else
+            {
+              FFI_ASSERT (0);
+            }
+          break;
+        default:
+          FFI_ASSERT (0);
+          break;
+        }
+    }
+  else
+    {
+      memcpy (&rvalue, get_x_addr (context, 8), sizeof (UINT64));
+      (closure->fun) (cif, rvalue, avalue, closure->user_data);
+    }
+}
+
diff --git a/src/aarch64/ffitarget.h b/src/aarch64/ffitarget.h
new file mode 100644 (file)
index 0000000..6f1a348
--- /dev/null
@@ -0,0 +1,59 @@
+/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd.
+
+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
+
+#ifndef LIBFFI_ASM
+typedef unsigned long ffi_arg;
+typedef signed long ffi_sarg;
+
+typedef enum ffi_abi
+  {
+    FFI_FIRST_ABI = 0,
+    FFI_SYSV,
+    FFI_LAST_ABI,
+    FFI_DEFAULT_ABI = FFI_SYSV
+  } ffi_abi;
+#endif
+
+/* ---- Definitions for closures ----------------------------------------- */
+
+#define FFI_CLOSURES 1
+#define FFI_TRAMPOLINE_SIZE 36
+#define FFI_NATIVE_RAW_API 0
+
+/* ---- Internal ---- */
+
+
+#define FFI_EXTRA_CIF_FIELDS unsigned aarch64_flags
+
+#define AARCH64_FFI_WITH_V_BIT 0
+
+#define AARCH64_N_XREG 32
+#define AARCH64_N_VREG 32
+#define AARCH64_CALL_CONTEXT_SIZE (AARCH64_N_XREG * 8 + AARCH64_N_VREG * 16)
+
+#endif
diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S
new file mode 100644 (file)
index 0000000..b8cd421
--- /dev/null
@@ -0,0 +1,307 @@
+/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+``Software''), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
+
+#define LIBFFI_ASM
+#include <fficonfig.h>
+#include <ffi.h>
+
+#define cfi_adjust_cfa_offset(off)     .cfi_adjust_cfa_offset off
+#define cfi_rel_offset(reg, off)       .cfi_rel_offset reg, off
+#define cfi_restore(reg)               .cfi_restore reg
+#define cfi_def_cfa_register(reg)      .cfi_def_cfa_register reg
+
+        .text
+        .globl ffi_call_SYSV
+        .type ffi_call_SYSV, #function
+
+/* ffi_call_SYSV()
+
+   Create a stack frame, setup an argument context, call the callee
+   and extract the result.
+
+   The maximum required argument stack size is provided,
+   ffi_call_SYSV() allocates that stack space then calls the
+   prepare_fn to populate register context and stack.  The
+   argument passing registers are loaded from the register
+   context and the callee called, on return the register passing
+   register are saved back to the context.  Our caller will
+   extract the return value from the final state of the saved
+   register context.
+
+   Prototype:
+
+   extern unsigned
+   ffi_call_SYSV (void (*)(struct call_context *context, unsigned char *,
+                          extended_cif *),
+                  struct call_context *context,
+                  extended_cif *,
+                  unsigned required_stack_size,
+                  void (*fn)(void));
+
+   Therefore on entry we have:
+
+   x0 prepare_fn
+   x1 &context
+   x2 &ecif
+   x3 bytes
+   x4 fn
+
+   This function uses the following stack frame layout:
+
+   ==
+                saved x30(lr)
+   x29(fp)->    saved x29(fp)
+                saved x24
+                saved x23
+                saved x22
+   sp'    ->    saved x21
+                ...
+   sp     ->    (constructed callee stack arguments)
+   ==
+
+   Voila! */
+
+#define ffi_call_SYSV_FS (8 * 4)
+
+        .cfi_startproc
+ffi_call_SYSV:
+        stp     x29, x30, [sp, #-16]!
+       cfi_adjust_cfa_offset (16)
+        cfi_rel_offset (x29, 0)
+        cfi_rel_offset (x30, 8)
+
+        mov     x29, sp
+       cfi_def_cfa_register (x29)
+        sub     sp, sp, #ffi_call_SYSV_FS
+
+        stp     x21, x22, [sp, 0]
+        cfi_rel_offset (x21, 0 - ffi_call_SYSV_FS)
+        cfi_rel_offset (x22, 8 - ffi_call_SYSV_FS)
+
+        stp     x23, x24, [sp, 16]
+        cfi_rel_offset (x23, 16 - ffi_call_SYSV_FS)
+        cfi_rel_offset (x24, 24 - ffi_call_SYSV_FS)
+
+        mov     x21, x1
+        mov     x22, x2
+        mov     x24, x4
+
+        /* Allocate the stack space for the actual arguments, many
+           arguments will be passed in registers, but we assume
+           worst case and allocate sufficient stack for ALL of
+           the arguments.  */
+        sub     sp, sp, x3
+
+        /* unsigned (*prepare_fn) (struct call_context *context,
+                                  unsigned char *stack, extended_cif *ecif);
+        */
+        mov     x23, x0
+        mov     x0, x1
+        mov     x1, sp
+        /* x2 already in place */
+        blr     x23
+
+        /* Preserve the flags returned.  */
+        mov     x23, x0
+
+        /* Figure out if we should touch the vector registers.  */
+        tbz     x23, #AARCH64_FFI_WITH_V_BIT, 1f
+
+        /* Load the vector argument passing registers.  */
+        ldp     q0, q1, [x21, #8*32 +  0]
+        ldp     q2, q3, [x21, #8*32 + 32]
+        ldp     q4, q5, [x21, #8*32 + 64]
+        ldp     q6, q7, [x21, #8*32 + 96]
+1:
+        /* Load the core argument passing registers.  */
+        ldp     x0, x1, [x21,  #0]
+        ldp     x2, x3, [x21, #16]
+        ldp     x4, x5, [x21, #32]
+        ldp     x6, x7, [x21, #48]
+
+        /* Don't forget x8 which may be holding the address of a return buffer.
+        */
+        ldr     x8,     [x21, #8*8]
+
+        blr     x24
+
+        /* Save the core argument passing registers.  */
+        stp     x0, x1, [x21,  #0]
+        stp     x2, x3, [x21, #16]
+        stp     x4, x5, [x21, #32]
+        stp     x6, x7, [x21, #48]
+
+        /* Note nothing useful ever comes back in x8!  */
+
+        /* Figure out if we should touch the vector registers.  */
+        tbz     x23, #AARCH64_FFI_WITH_V_BIT, 1f
+
+        /* Save the vector argument passing registers.  */
+        stp     q0, q1, [x21, #8*32 + 0]
+        stp     q2, q3, [x21, #8*32 + 32]
+        stp     q4, q5, [x21, #8*32 + 64]
+        stp     q6, q7, [x21, #8*32 + 96]
+1:
+        /* All done, unwind our stack frame.  */
+        ldp     x21, x22, [x29,  # - ffi_call_SYSV_FS]
+        cfi_restore (x21)
+        cfi_restore (x22)
+
+        ldp     x23, x24, [x29,  # - ffi_call_SYSV_FS + 16]
+        cfi_restore (x23)
+        cfi_restore (x24)
+
+        mov     sp, x29
+       cfi_def_cfa_register (sp)
+
+        ldp     x29, x30, [sp], #16
+       cfi_adjust_cfa_offset (-16)
+        cfi_restore (x29)
+        cfi_restore (x30)
+
+        ret
+
+        .cfi_endproc
+        .size ffi_call_SYSV, .-ffi_call_SYSV
+
+#define ffi_closure_SYSV_FS (8 * 2 + AARCH64_CALL_CONTEXT_SIZE)
+
+/* ffi_closure_SYSV
+
+   Closure invocation glue. This is the low level code invoked directly by
+   the closure trampoline to setup and call a closure.
+
+   On entry x17 points to a struct trampoline_data, x16 has been clobbered
+   all other registers are preserved.
+
+   We allocate a call context and save the argument passing registers,
+   then invoked the generic C ffi_closure_SYSV_inner() function to do all
+   the real work, on return we load the result passing registers back from
+   the call context.
+
+   On entry
+
+   extern void
+   ffi_closure_SYSV (struct trampoline_data *);
+
+   struct trampoline_data
+   {
+        UINT64 *ffi_closure;
+        UINT64 flags;
+   };
+
+   This function uses the following stack frame layout:
+
+   ==
+                saved x30(lr)
+   x29(fp)->    saved x29(fp)
+                saved x22
+                saved x21
+                ...
+   sp     ->    call_context
+   ==
+
+   Voila!  */
+
+        .text
+        .globl ffi_closure_SYSV
+        .cfi_startproc
+ffi_closure_SYSV:
+        stp     x29, x30, [sp, #-16]!
+       cfi_adjust_cfa_offset (16)
+        cfi_rel_offset (x29, 0)
+        cfi_rel_offset (x30, 8)
+
+        mov     x29, sp
+
+        sub     sp, sp, #ffi_closure_SYSV_FS
+       cfi_adjust_cfa_offset (ffi_closure_SYSV_FS)
+
+        stp     x21, x22, [x29, #-16]
+        cfi_rel_offset (x21, 0)
+        cfi_rel_offset (x22, 8)
+
+        /* Load x21 with &call_context.  */
+        mov     x21, sp
+        /* Preserve our struct trampoline_data *  */
+        mov     x22, x17
+
+        /* Save the rest of the argument passing registers.  */
+        stp     x0, x1, [x21, #0]
+        stp     x2, x3, [x21, #16]
+        stp     x4, x5, [x21, #32]
+        stp     x6, x7, [x21, #48]
+        /* Don't forget we may have been given a result scratch pad address.
+        */
+        str     x8,     [x21, #64]
+
+        /* Figure out if we should touch the vector registers.  */
+        ldr     x0, [x22, #8]
+        tbz     x0, #AARCH64_FFI_WITH_V_BIT, 1f
+
+        /* Save the argument passing vector registers.  */
+        stp     q0, q1, [x21, #8*32 + 0]
+        stp     q2, q3, [x21, #8*32 + 32]
+        stp     q4, q5, [x21, #8*32 + 64]
+        stp     q6, q7, [x21, #8*32 + 96]
+1:
+        /* Load &ffi_closure..  */
+        ldr     x0, [x22, #0]
+        mov     x1, x21
+        /* Compute the location of the stack at the point that the
+           trampoline was called.  */
+        add     x2, x29, #16
+
+        bl      ffi_closure_SYSV_inner
+
+        /* Figure out if we should touch the vector registers.  */
+        ldr     x0, [x22, #8]
+        tbz     x0, #AARCH64_FFI_WITH_V_BIT, 1f
+
+        /* Load the result passing vector registers.  */
+        ldp     q0, q1, [x21, #8*32 + 0]
+        ldp     q2, q3, [x21, #8*32 + 32]
+        ldp     q4, q5, [x21, #8*32 + 64]
+        ldp     q6, q7, [x21, #8*32 + 96]
+1:
+        /* Load the result passing core registers.  */
+        ldp     x0, x1, [x21,  #0]
+        ldp     x2, x3, [x21, #16]
+        ldp     x4, x5, [x21, #32]
+        ldp     x6, x7, [x21, #48]
+        /* Note nothing usefull is returned in x8.  */
+
+        /* We are done, unwind our frame.  */
+        ldp     x21, x22, [x29,  #-16]
+        cfi_restore (x21)
+        cfi_restore (x22)
+
+        mov     sp, x29
+       cfi_adjust_cfa_offset (-ffi_closure_SYSV_FS)
+
+        ldp     x29, x30, [sp], #16
+       cfi_adjust_cfa_offset (-16)
+        cfi_restore (x29)
+        cfi_restore (x30)
+
+        ret
+        .cfi_endproc
+        .size ffi_closure_SYSV, .-ffi_closure_SYSV
index 4a65ed1..8ee3f15 100644 (file)
@@ -203,6 +203,10 @@ proc libffi_target_compile { source dest type options } {
 
     lappend options "libs= -lffi"
 
+    if { [string match "aarch64*-*-linux*" $target_triplet] } {
+       lappend options "libs= -lpthread"
+    }
+
     verbose "options: $options"
     return [target_compile $source $dest $type $options]
 }
diff --git a/testsuite/libffi.call/cls_struct_va1.c b/testsuite/libffi.call/cls_struct_va1.c
new file mode 100644 (file)
index 0000000..91772bd
--- /dev/null
@@ -0,0 +1,114 @@
+/* Area:               ffi_call, closure_call
+   Purpose:            Test doubles passed in variable argument lists.
+   Limitations:        none.
+   PR:                 none.
+   Originator: Blake Chaffin 6/6/2007   */
+
+/* { dg-do run } */
+/* { dg-output "" { xfail avr32*-*-* } } */
+#include "ffitest.h"
+
+struct small_tag
+{
+  unsigned char a;
+  unsigned char b;
+};
+
+struct large_tag
+{
+  unsigned a;
+  unsigned b;
+  unsigned c;
+  unsigned d;
+  unsigned e;
+};
+
+static void
+test_fn (ffi_cif* cif __UNUSED__, void* resp,
+        void** args, void* userdata __UNUSED__)
+{
+  int n = *(int*)args[0];
+  struct small_tag s1 = * (struct small_tag *) args[1];
+  struct large_tag l1 = * (struct large_tag *) args[2];
+  struct small_tag s2 = * (struct small_tag *) args[3];
+
+  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);
+  * (int*) resp = 42;
+}
+
+int
+main (void)
+{
+  ffi_cif cif;
+  void *code;
+  ffi_closure *pcl = ffi_closure_alloc (sizeof (ffi_closure), &code);
+  ffi_type* arg_types[5];
+
+  ffi_arg res = 0;
+
+  ffi_type s_type;
+  ffi_type *s_type_elements[3];
+
+  ffi_type l_type;
+  ffi_type *l_type_elements[6];
+
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l1;
+
+  int si;
+
+  s_type.size = 0;
+  s_type.alignment = 0;
+  s_type.type = FFI_TYPE_STRUCT;
+  s_type.elements = s_type_elements;
+
+  s_type_elements[0] = &ffi_type_uchar;
+  s_type_elements[1] = &ffi_type_uchar;
+  s_type_elements[2] = NULL;
+
+  l_type.size = 0;
+  l_type.alignment = 0;
+  l_type.type = FFI_TYPE_STRUCT;
+  l_type.elements = l_type_elements;
+
+  l_type_elements[0] = &ffi_type_uint;
+  l_type_elements[1] = &ffi_type_uint;
+  l_type_elements[2] = &ffi_type_uint;
+  l_type_elements[3] = &ffi_type_uint;
+  l_type_elements[4] = &ffi_type_uint;
+  l_type_elements[5] = NULL;
+
+  arg_types[0] = &ffi_type_sint;
+  arg_types[1] = &s_type;
+  arg_types[2] = &l_type;
+  arg_types[3] = &s_type;
+  arg_types[4] = NULL;
+
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &ffi_type_sint,
+                        arg_types) == FFI_OK);
+
+  si = 4;
+  s1.a = 5;
+  s1.b = 6;
+
+  s2.a = 20;
+  s2.b = 21;
+
+  l1.a = 10;
+  l1.b = 11;
+  l1.c = 12;
+  l1.d = 13;
+  l1.e = 14;
+
+  CHECK(ffi_prep_closure_loc(pcl, &cif, test_fn, NULL, code) == FFI_OK);
+
+  res = ((int (*)(int, ...))(code))(si, s1, l1, s2);
+  // { dg-output "4 5 6 10 11 12 13 14 20 21" }
+  printf("res: %d\n", (int) res);
+  // { dg-output "\nres: 42" }
+
+  exit(0);
+}
diff --git a/testsuite/libffi.call/cls_uchar_va.c b/testsuite/libffi.call/cls_uchar_va.c
new file mode 100644 (file)
index 0000000..19cd4f3
--- /dev/null
@@ -0,0 +1,44 @@
+/* Area:       closure_call
+   Purpose:    Test anonymous unsigned char argument.
+   Limitations:        none.
+   PR:         none.
+   Originator: ARM Ltd. */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef unsigned char T;
+
+static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
+                        void* userdata __UNUSED__)
+ {
+   *(T *)resp = *(T *)args[0];
+
+   printf("%d: %d %d\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
+ }
+
+typedef T (*cls_ret_T)(T, ...);
+
+int main (void)
+{
+  ffi_cif cif;
+  void *code;
+  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
+  ffi_type * cl_arg_types[3];
+  T res;
+
+  cl_arg_types[0] = &ffi_type_uchar;
+  cl_arg_types[1] = &ffi_type_uchar;
+  cl_arg_types[2] = NULL;
+
+  /* Initialize the cif */
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
+                        &ffi_type_uchar, cl_arg_types) == FFI_OK);
+
+  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
+  res = ((((cls_ret_T)code)(67, 4)));
+  /* { dg-output "67: 67 4" } */
+  printf("res: %d\n", res);
+  /* { dg-output "\nres: 67" } */
+  exit(0);
+}
diff --git a/testsuite/libffi.call/cls_uint_va.c b/testsuite/libffi.call/cls_uint_va.c
new file mode 100644 (file)
index 0000000..150fddd
--- /dev/null
@@ -0,0 +1,45 @@
+/* Area:       closure_call
+   Purpose:    Test anonymous unsigned int argument.
+   Limitations:        none.
+   PR:         none.
+   Originator: ARM Ltd. */
+
+/* { dg-do run } */
+
+#include "ffitest.h"
+
+typedef unsigned int T;
+
+static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
+                        void* userdata __UNUSED__)
+ {
+   *(T *)resp = *(T *)args[0];
+
+   printf("%d: %d %d\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
+ }
+
+typedef T (*cls_ret_T)(T, ...);
+
+int main (void)
+{
+  ffi_cif cif;
+  void *code;
+  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
+  ffi_type * cl_arg_types[3];
+  T res;
+
+  cl_arg_types[0] = &ffi_type_uint;
+  cl_arg_types[1] = &ffi_type_uint;
+  cl_arg_types[2] = NULL;
+
+  /* Initialize the cif */
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
+                        &ffi_type_uint, cl_arg_types) == FFI_OK);
+
+  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
+  res = ((((cls_ret_T)code)(67, 4)));
+  /* { dg-output "67: 67 4" } */
+  printf("res: %d\n", res);
+  /* { dg-output "\nres: 67" } */
+  exit(0);
+}
diff --git a/testsuite/libffi.call/cls_ulong_va.c b/testsuite/libffi.call/cls_ulong_va.c
new file mode 100644 (file)
index 0000000..0315082
--- /dev/null
@@ -0,0 +1,45 @@
+/* Area:       closure_call
+   Purpose:    Test anonymous unsigned long argument.
+   Limitations:        none.
+   PR:         none.
+   Originator: ARM Ltd. */
+
+/* { dg-do run } */
+
+#include "ffitest.h"
+
+typedef unsigned long T;
+
+static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
+                        void* userdata __UNUSED__)
+ {
+   *(T *)resp = *(T *)args[0];
+
+   printf("%ld: %ld %ld\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
+ }
+
+typedef T (*cls_ret_T)(T, ...);
+
+int main (void)
+{
+  ffi_cif cif;
+  void *code;
+  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
+  ffi_type * cl_arg_types[3];
+  T res;
+
+  cl_arg_types[0] = &ffi_type_ulong;
+  cl_arg_types[1] = &ffi_type_ulong;
+  cl_arg_types[2] = NULL;
+
+  /* Initialize the cif */
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
+                        &ffi_type_ulong, cl_arg_types) == FFI_OK);
+
+  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
+  res = ((((cls_ret_T)code)(67, 4)));
+  /* { dg-output "67: 67 4" } */
+  printf("res: %ld\n", res);
+  /* { dg-output "\nres: 67" } */
+  exit(0);
+}
diff --git a/testsuite/libffi.call/cls_ushort_va.c b/testsuite/libffi.call/cls_ushort_va.c
new file mode 100644 (file)
index 0000000..b2b5a3b
--- /dev/null
@@ -0,0 +1,44 @@
+/* Area:       closure_call
+   Purpose:    Test anonymous unsigned short argument.
+   Limitations:        none.
+   PR:         none.
+   Originator: ARM Ltd. */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef unsigned short T;
+
+static void cls_ret_T_fn(ffi_cif* cif __UNUSED__, void* resp, void** args,
+                        void* userdata __UNUSED__)
+ {
+   *(T *)resp = *(T *)args[0];
+
+   printf("%d: %d %d\n", *(T *)resp, *(T *)args[0], *(T *)args[1]);
+ }
+
+typedef T (*cls_ret_T)(T, ...);
+
+int main (void)
+{
+  ffi_cif cif;
+  void *code;
+  ffi_closure *pcl = ffi_closure_alloc(sizeof(ffi_closure), &code);
+  ffi_type * cl_arg_types[3];
+  T res;
+
+  cl_arg_types[0] = &ffi_type_ushort;
+  cl_arg_types[1] = &ffi_type_ushort;
+  cl_arg_types[2] = NULL;
+
+  /* Initialize the cif */
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2,
+                        &ffi_type_ushort, cl_arg_types) == FFI_OK);
+
+  CHECK(ffi_prep_closure_loc(pcl, &cif, cls_ret_T_fn, NULL, code)  == FFI_OK);
+  res = ((((cls_ret_T)code)(67, 4)));
+  /* { dg-output "67: 67 4" } */
+  printf("res: %d\n", res);
+  /* { dg-output "\nres: 67" } */
+  exit(0);
+}
diff --git a/testsuite/libffi.call/nested_struct11.c b/testsuite/libffi.call/nested_struct11.c
new file mode 100644 (file)
index 0000000..fce6948
--- /dev/null
@@ -0,0 +1,121 @@
+/* Area:       ffi_call, closure_call
+   Purpose:    Check parameter passing with nested structs
+               of a single type.  This tests the special cases
+               for homogenous floating-point aggregates in the
+               AArch64 PCS.
+   Limitations:        none.
+   PR:         none.
+   Originator:  ARM Ltd.  */
+
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct A {
+  float a_x;
+  float a_y;
+} A;
+
+typedef struct B {
+  float b_x;
+  float b_y;
+} B;
+
+typedef struct C {
+  A a;
+  B b;
+} C;
+
+static C C_fn (int x, int y, int z, C source, int i, int j, int k)
+{
+  C result;
+  result.a.a_x = source.a.a_x;
+  result.a.a_y = source.a.a_y;
+  result.b.b_x = source.b.b_x;
+  result.b.b_y = source.b.b_y;
+
+  printf ("%d, %d, %d, %d, %d, %d\n", x, y, z, i, j, k);
+
+  printf ("%.1f, %.1f, %.1f, %.1f, "
+         "%.1f, %.1f, %.1f, %.1f\n",
+         source.a.a_x, source.a.a_y,
+         source.b.b_x, source.b.b_y,
+         result.a.a_x, result.a.a_y,
+         result.b.b_x, result.b.b_y);
+
+  return result;
+}
+
+int main (void)
+{
+  ffi_cif cif;
+
+  ffi_type* struct_fields_source_a[3];
+  ffi_type* struct_fields_source_b[3];
+  ffi_type* struct_fields_source_c[3];
+  ffi_type* arg_types[8];
+
+  ffi_type struct_type_a, struct_type_b, struct_type_c;
+
+  struct A source_fld_a = {1.0, 2.0};
+  struct B source_fld_b = {4.0, 8.0};
+  int k = 1;
+
+  struct C result;
+  struct C source = {source_fld_a, source_fld_b};
+
+  struct_type_a.size = 0;
+  struct_type_a.alignment = 0;
+  struct_type_a.type = FFI_TYPE_STRUCT;
+  struct_type_a.elements = struct_fields_source_a;
+
+  struct_type_b.size = 0;
+  struct_type_b.alignment = 0;
+  struct_type_b.type = FFI_TYPE_STRUCT;
+  struct_type_b.elements = struct_fields_source_b;
+
+  struct_type_c.size = 0;
+  struct_type_c.alignment = 0;
+  struct_type_c.type = FFI_TYPE_STRUCT;
+  struct_type_c.elements = struct_fields_source_c;
+
+  struct_fields_source_a[0] = &ffi_type_float;
+  struct_fields_source_a[1] = &ffi_type_float;
+  struct_fields_source_a[2] = NULL;
+
+  struct_fields_source_b[0] = &ffi_type_float;
+  struct_fields_source_b[1] = &ffi_type_float;
+  struct_fields_source_b[2] = NULL;
+
+  struct_fields_source_c[0] = &struct_type_a;
+  struct_fields_source_c[1] = &struct_type_b;
+  struct_fields_source_c[2] = NULL;
+
+  arg_types[0] = &ffi_type_sint32;
+  arg_types[1] = &ffi_type_sint32;
+  arg_types[2] = &ffi_type_sint32;
+  arg_types[3] = &struct_type_c;
+  arg_types[4] = &ffi_type_sint32;
+  arg_types[5] = &ffi_type_sint32;
+  arg_types[6] = &ffi_type_sint32;
+  arg_types[7] = NULL;
+
+  void *args[7];
+  args[0] = &k;
+  args[1] = &k;
+  args[2] = &k;
+  args[3] = &source;
+  args[4] = &k;
+  args[5] = &k;
+  args[6] = &k;
+  CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 7, &struct_type_c,
+                      arg_types) == FFI_OK);
+
+  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);
+}
diff --git a/testsuite/libffi.call/uninitialized.c b/testsuite/libffi.call/uninitialized.c
new file mode 100644 (file)
index 0000000..f00d830
--- /dev/null
@@ -0,0 +1,61 @@
+/* { dg-do run } */
+#include "ffitest.h"
+
+typedef struct
+{
+  unsigned char uc;
+  double d;
+  unsigned int ui;
+} test_structure_1;
+
+static test_structure_1 struct1(test_structure_1 ts)
+{
+  ts.uc++;
+  ts.d--;
+  ts.ui++;
+
+  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];
+
+  memset(&cif, 1, sizeof(cif));
+  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_uchar;
+  ts1_type_elements[1] = &ffi_type_double;
+  ts1_type_elements[2] = &ffi_type_uint;
+  ts1_type_elements[3] = NULL;
+
+  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));
+
+  args[0] = &ts1_type;
+  values[0] = &ts1_arg;
+
+  /* Initialize the cif */
+  CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1,
+                    &ts1_type, args) == FFI_OK);
+
+  ts1_arg.uc = '\x01';
+  ts1_arg.d = 3.14159;
+  ts1_arg.ui = 555;
+
+  ffi_call(&cif, FFI_FN(struct1), ts1_result, values);
+
+  CHECK(ts1_result->ui == 556);
+  CHECK(ts1_result->d == 3.14159 - 1);
+
+  free (ts1_result);
+  exit(0);
+}
diff --git a/testsuite/libffi.call/va_1.c b/testsuite/libffi.call/va_1.c
new file mode 100644 (file)
index 0000000..5c7cce9
--- /dev/null
@@ -0,0 +1,196 @@
+/* Area:               ffi_call
+   Purpose:            Test passing struct in variable argument lists.
+   Limitations:        none.
+   PR:                 none.
+   Originator:         ARM Ltd. */
+
+/* { dg-do run } */
+/* { dg-output "" { xfail avr32*-*-* x86_64-*-*-* } } */
+
+#include "ffitest.h"
+#include <stdarg.h>
+
+struct small_tag
+{
+  unsigned char a;
+  unsigned char b;
+};
+
+struct large_tag
+{
+  unsigned a;
+  unsigned b;
+  unsigned c;
+  unsigned d;
+  unsigned e;
+};
+
+static int
+test_fn (int n, ...)
+{
+  va_list ap;
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l;
+  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);
+  s1 = va_arg (ap, struct small_tag);
+  l = va_arg (ap, struct large_tag);
+  s2 = va_arg (ap, struct small_tag);
+
+  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 ("%u %u %u %u %u %u %u %u %u uc=%u sc=%d %u %d %u %d %lu %ld %f %f\n",
+         s1.a, s1.b, l.a, l.b, l.c, l.d, l.e,
+         s2.a, s2.b,
+         uc, sc,
+         us, ss,
+         ui, si,
+         ul, sl,
+         f, d);
+  va_end (ap);
+  return n + 1;
+}
+
+int
+main (void)
+{
+  ffi_cif cif;
+  void* args[15];
+  ffi_type* arg_types[15];
+
+  ffi_type s_type;
+  ffi_type *s_type_elements[3];
+
+  ffi_type l_type;
+  ffi_type *l_type_elements[6];
+
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l1;
+
+  int n;
+  int res;
+
+  unsigned char uc;
+  signed char sc;
+  unsigned short us;
+  signed short ss;
+  unsigned int ui;
+  signed int si;
+  unsigned long ul;
+  signed long sl;
+  double d1;
+  double f1;
+
+  s_type.size = 0;
+  s_type.alignment = 0;
+  s_type.type = FFI_TYPE_STRUCT;
+  s_type.elements = s_type_elements;
+
+  s_type_elements[0] = &ffi_type_uchar;
+  s_type_elements[1] = &ffi_type_uchar;
+  s_type_elements[2] = NULL;
+
+  l_type.size = 0;
+  l_type.alignment = 0;
+  l_type.type = FFI_TYPE_STRUCT;
+  l_type.elements = l_type_elements;
+
+  l_type_elements[0] = &ffi_type_uint;
+  l_type_elements[1] = &ffi_type_uint;
+  l_type_elements[2] = &ffi_type_uint;
+  l_type_elements[3] = &ffi_type_uint;
+  l_type_elements[4] = &ffi_type_uint;
+  l_type_elements[5] = NULL;
+
+  arg_types[0] = &ffi_type_sint;
+  arg_types[1] = &s_type;
+  arg_types[2] = &l_type;
+  arg_types[3] = &s_type;
+  arg_types[4] = &ffi_type_uint;
+  arg_types[5] = &ffi_type_sint;
+  arg_types[6] = &ffi_type_uint;
+  arg_types[7] = &ffi_type_sint;
+  arg_types[8] = &ffi_type_uint;
+  arg_types[9] = &ffi_type_sint;
+  arg_types[10] = &ffi_type_ulong;
+  arg_types[11] = &ffi_type_slong;
+  arg_types[12] = &ffi_type_double;
+  arg_types[13] = &ffi_type_double;
+  arg_types[14] = NULL;
+
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 14, &ffi_type_sint, arg_types) == FFI_OK);
+
+  s1.a = 5;
+  s1.b = 6;
+
+  l1.a = 10;
+  l1.b = 11;
+  l1.c = 12;
+  l1.d = 13;
+  l1.e = 14;
+
+  s2.a = 7;
+  s2.b = 8;
+
+  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] = &n;
+  args[1] = &s1;
+  args[2] = &l1;
+  args[3] = &s2;
+  args[4] = &uc;
+  args[5] = &sc;
+  args[6] = &us;
+  args[7] = &ss;
+  args[8] = &ui;
+  args[9] = &si;
+  args[10] = &ul;
+  args[11] = &sl;
+  args[12] = &f1;
+  args[13] = &d1;
+  args[14] = NULL;
+
+  ffi_call(&cif, FFI_FN(test_fn), &res, args);
+  /* { 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" } */
+
+  return 0;
+}
diff --git a/testsuite/libffi.call/va_struct1.c b/testsuite/libffi.call/va_struct1.c
new file mode 100644 (file)
index 0000000..11d1f10
--- /dev/null
@@ -0,0 +1,121 @@
+/* Area:               ffi_call
+   Purpose:            Test passing struct in variable argument lists.
+   Limitations:        none.
+   PR:                 none.
+   Originator: ARM Ltd. */
+
+/* { dg-do run } */
+/* { dg-output "" { xfail avr32*-*-* } } */
+
+#include "ffitest.h"
+#include <stdarg.h>
+
+struct small_tag
+{
+  unsigned char a;
+  unsigned char b;
+};
+
+struct large_tag
+{
+  unsigned a;
+  unsigned b;
+  unsigned c;
+  unsigned d;
+  unsigned e;
+};
+
+static int
+test_fn (int n, ...)
+{
+  va_list ap;
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l;
+
+  va_start (ap, n);
+  s1 = va_arg (ap, struct small_tag);
+  l = va_arg (ap, struct large_tag);
+  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);
+  va_end (ap);
+  return n + 1;
+}
+
+int
+main (void)
+{
+  ffi_cif cif;
+  void* args[5];
+  ffi_type* arg_types[5];
+
+  ffi_type s_type;
+  ffi_type *s_type_elements[3];
+
+  ffi_type l_type;
+  ffi_type *l_type_elements[6];
+
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l1;
+
+  int n;
+  int res;
+
+  s_type.size = 0;
+  s_type.alignment = 0;
+  s_type.type = FFI_TYPE_STRUCT;
+  s_type.elements = s_type_elements;
+
+  s_type_elements[0] = &ffi_type_uchar;
+  s_type_elements[1] = &ffi_type_uchar;
+  s_type_elements[2] = NULL;
+
+  l_type.size = 0;
+  l_type.alignment = 0;
+  l_type.type = FFI_TYPE_STRUCT;
+  l_type.elements = l_type_elements;
+
+  l_type_elements[0] = &ffi_type_uint;
+  l_type_elements[1] = &ffi_type_uint;
+  l_type_elements[2] = &ffi_type_uint;
+  l_type_elements[3] = &ffi_type_uint;
+  l_type_elements[4] = &ffi_type_uint;
+  l_type_elements[5] = NULL;
+
+  arg_types[0] = &ffi_type_sint;
+  arg_types[1] = &s_type;
+  arg_types[2] = &l_type;
+  arg_types[3] = &s_type;
+  arg_types[4] = NULL;
+
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &ffi_type_sint, arg_types) == FFI_OK);
+
+  s1.a = 5;
+  s1.b = 6;
+
+  l1.a = 10;
+  l1.b = 11;
+  l1.c = 12;
+  l1.d = 13;
+  l1.e = 14;
+
+  s2.a = 7;
+  s2.b = 8;
+
+  n = 41;
+
+  args[0] = &n;
+  args[1] = &s1;
+  args[2] = &l1;
+  args[3] = &s2;
+  args[4] = NULL;
+
+  ffi_call(&cif, FFI_FN(test_fn), &res, args);
+  /* { dg-output "5 6 10 11 12 13 14 7 8" } */
+  printf("res: %d\n", (int) res);
+  /* { dg-output "\nres: 42" } */
+
+  return 0;
+}
diff --git a/testsuite/libffi.call/va_struct2.c b/testsuite/libffi.call/va_struct2.c
new file mode 100644 (file)
index 0000000..56f5b9c
--- /dev/null
@@ -0,0 +1,123 @@
+/* Area:               ffi_call
+   Purpose:            Test passing struct in variable argument lists.
+   Limitations:        none.
+   PR:                 none.
+   Originator: ARM Ltd. */
+
+/* { dg-do run } */
+/* { dg-output "" { xfail avr32*-*-* } } */
+
+#include "ffitest.h"
+#include <stdarg.h>
+
+struct small_tag
+{
+  unsigned char a;
+  unsigned char b;
+};
+
+struct large_tag
+{
+  unsigned a;
+  unsigned b;
+  unsigned c;
+  unsigned d;
+  unsigned e;
+};
+
+static struct small_tag
+test_fn (int n, ...)
+{
+  va_list ap;
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l;
+
+  va_start (ap, n);
+  s1 = va_arg (ap, struct small_tag);
+  l = va_arg (ap, struct large_tag);
+  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);
+  va_end (ap);
+  s1.a += s2.a;
+  s1.b += s2.b;
+  return s1;
+}
+
+int
+main (void)
+{
+  ffi_cif cif;
+  void* args[5];
+  ffi_type* arg_types[5];
+
+  ffi_type s_type;
+  ffi_type *s_type_elements[3];
+
+  ffi_type l_type;
+  ffi_type *l_type_elements[6];
+
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l1;
+
+  int n;
+  struct small_tag res;
+
+  s_type.size = 0;
+  s_type.alignment = 0;
+  s_type.type = FFI_TYPE_STRUCT;
+  s_type.elements = s_type_elements;
+
+  s_type_elements[0] = &ffi_type_uchar;
+  s_type_elements[1] = &ffi_type_uchar;
+  s_type_elements[2] = NULL;
+
+  l_type.size = 0;
+  l_type.alignment = 0;
+  l_type.type = FFI_TYPE_STRUCT;
+  l_type.elements = l_type_elements;
+
+  l_type_elements[0] = &ffi_type_uint;
+  l_type_elements[1] = &ffi_type_uint;
+  l_type_elements[2] = &ffi_type_uint;
+  l_type_elements[3] = &ffi_type_uint;
+  l_type_elements[4] = &ffi_type_uint;
+  l_type_elements[5] = NULL;
+
+  arg_types[0] = &ffi_type_sint;
+  arg_types[1] = &s_type;
+  arg_types[2] = &l_type;
+  arg_types[3] = &s_type;
+  arg_types[4] = NULL;
+
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &s_type, arg_types) == FFI_OK);
+
+  s1.a = 5;
+  s1.b = 6;
+
+  l1.a = 10;
+  l1.b = 11;
+  l1.c = 12;
+  l1.d = 13;
+  l1.e = 14;
+
+  s2.a = 7;
+  s2.b = 8;
+
+  n = 41;
+
+  args[0] = &n;
+  args[1] = &s1;
+  args[2] = &l1;
+  args[3] = &s2;
+  args[4] = NULL;
+
+  ffi_call(&cif, FFI_FN(test_fn), &res, args);
+  /* { 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" } */
+
+  return 0;
+}
diff --git a/testsuite/libffi.call/va_struct3.c b/testsuite/libffi.call/va_struct3.c
new file mode 100644 (file)
index 0000000..9a27e7f
--- /dev/null
@@ -0,0 +1,125 @@
+/* Area:               ffi_call
+   Purpose:            Test passing struct in variable argument lists.
+   Limitations:        none.
+   PR:                 none.
+   Originator: ARM Ltd. */
+
+/* { dg-do run } */
+/* { dg-output "" { xfail avr32*-*-* } } */
+
+#include "ffitest.h"
+#include <stdarg.h>
+
+struct small_tag
+{
+  unsigned char a;
+  unsigned char b;
+};
+
+struct large_tag
+{
+  unsigned a;
+  unsigned b;
+  unsigned c;
+  unsigned d;
+  unsigned e;
+};
+
+static struct large_tag
+test_fn (int n, ...)
+{
+  va_list ap;
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l;
+
+  va_start (ap, n);
+  s1 = va_arg (ap, struct small_tag);
+  l = va_arg (ap, struct large_tag);
+  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);
+  va_end (ap);
+  l.a += s1.a;
+  l.b += s1.b;
+  l.c += s2.a;
+  l.d += s2.b;
+  return l;
+}
+
+int
+main (void)
+{
+  ffi_cif cif;
+  void* args[5];
+  ffi_type* arg_types[5];
+
+  ffi_type s_type;
+  ffi_type *s_type_elements[3];
+
+  ffi_type l_type;
+  ffi_type *l_type_elements[6];
+
+  struct small_tag s1;
+  struct small_tag s2;
+  struct large_tag l1;
+
+  int n;
+  struct large_tag res;
+
+  s_type.size = 0;
+  s_type.alignment = 0;
+  s_type.type = FFI_TYPE_STRUCT;
+  s_type.elements = s_type_elements;
+
+  s_type_elements[0] = &ffi_type_uchar;
+  s_type_elements[1] = &ffi_type_uchar;
+  s_type_elements[2] = NULL;
+
+  l_type.size = 0;
+  l_type.alignment = 0;
+  l_type.type = FFI_TYPE_STRUCT;
+  l_type.elements = l_type_elements;
+
+  l_type_elements[0] = &ffi_type_uint;
+  l_type_elements[1] = &ffi_type_uint;
+  l_type_elements[2] = &ffi_type_uint;
+  l_type_elements[3] = &ffi_type_uint;
+  l_type_elements[4] = &ffi_type_uint;
+  l_type_elements[5] = NULL;
+
+  arg_types[0] = &ffi_type_sint;
+  arg_types[1] = &s_type;
+  arg_types[2] = &l_type;
+  arg_types[3] = &s_type;
+  arg_types[4] = NULL;
+
+  CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 4, &l_type, arg_types) == FFI_OK);
+
+  s1.a = 5;
+  s1.b = 6;
+
+  l1.a = 10;
+  l1.b = 11;
+  l1.c = 12;
+  l1.d = 13;
+  l1.e = 14;
+
+  s2.a = 7;
+  s2.b = 8;
+
+  n = 41;
+
+  args[0] = &n;
+  args[1] = &s1;
+  args[2] = &l1;
+  args[3] = &s2;
+  args[4] = NULL;
+
+  ffi_call(&cif, FFI_FN(test_fn), &res, args);
+  /* { 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" } */
+
+  return 0;
+}