From fa5d747905472571fd472c07d4726017624f66b3 Mon Sep 17 00:00:00 2001 From: Anthony Green Date: Tue, 30 Oct 2012 07:07:19 -0400 Subject: [PATCH] AArch64 port --- .pc/aarch64/.timestamp | 0 .pc/aarch64/README | 366 +++ .pc/aarch64/src/aarch64/ffi.c | 0 .pc/aarch64/src/aarch64/ffitarget.h | 0 .pc/aarch64/src/aarch64/sysv.S | 0 .pc/aarch64/testsuite/lib/libffi.exp | 350 +++ .../testsuite/libffi.call/cls_struct_va1.c | 0 .../testsuite/libffi.call/cls_uchar_va.c | 0 .../testsuite/libffi.call/cls_uint_va.c | 0 .../testsuite/libffi.call/cls_ulong_va.c | 0 .../testsuite/libffi.call/cls_ushort_va.c | 0 .../testsuite/libffi.call/nested_struct11.c | 0 .../testsuite/libffi.call/uninitialized.c | 0 .pc/aarch64/testsuite/libffi.call/va_1.c | 0 .../testsuite/libffi.call/va_struct1.c | 0 .../testsuite/libffi.call/va_struct2.c | 0 .../testsuite/libffi.call/va_struct3.c | 0 .pc/applied-patches | 1 + README | 3 + patches/aarch64 | 2594 +++++++++++++++++ patches/series | 1 + src/aarch64/ffi.c | 1076 +++++++ src/aarch64/ffitarget.h | 59 + src/aarch64/sysv.S | 307 ++ testsuite/lib/libffi.exp | 4 + testsuite/libffi.call/cls_struct_va1.c | 114 + testsuite/libffi.call/cls_uchar_va.c | 44 + testsuite/libffi.call/cls_uint_va.c | 45 + testsuite/libffi.call/cls_ulong_va.c | 45 + testsuite/libffi.call/cls_ushort_va.c | 44 + testsuite/libffi.call/nested_struct11.c | 121 + testsuite/libffi.call/uninitialized.c | 61 + testsuite/libffi.call/va_1.c | 196 ++ testsuite/libffi.call/va_struct1.c | 121 + testsuite/libffi.call/va_struct2.c | 123 + testsuite/libffi.call/va_struct3.c | 125 + 36 files changed, 5800 insertions(+) create mode 100644 .pc/aarch64/.timestamp create mode 100644 .pc/aarch64/README create mode 100644 .pc/aarch64/src/aarch64/ffi.c create mode 100644 .pc/aarch64/src/aarch64/ffitarget.h create mode 100644 .pc/aarch64/src/aarch64/sysv.S create mode 100644 .pc/aarch64/testsuite/lib/libffi.exp create mode 100644 .pc/aarch64/testsuite/libffi.call/cls_struct_va1.c create mode 100644 .pc/aarch64/testsuite/libffi.call/cls_uchar_va.c create mode 100644 .pc/aarch64/testsuite/libffi.call/cls_uint_va.c create mode 100644 .pc/aarch64/testsuite/libffi.call/cls_ulong_va.c create mode 100644 .pc/aarch64/testsuite/libffi.call/cls_ushort_va.c create mode 100644 .pc/aarch64/testsuite/libffi.call/nested_struct11.c create mode 100644 .pc/aarch64/testsuite/libffi.call/uninitialized.c create mode 100644 .pc/aarch64/testsuite/libffi.call/va_1.c create mode 100644 .pc/aarch64/testsuite/libffi.call/va_struct1.c create mode 100644 .pc/aarch64/testsuite/libffi.call/va_struct2.c create mode 100644 .pc/aarch64/testsuite/libffi.call/va_struct3.c create mode 100644 patches/aarch64 create mode 100644 src/aarch64/ffi.c create mode 100644 src/aarch64/ffitarget.h create mode 100644 src/aarch64/sysv.S create mode 100644 testsuite/libffi.call/cls_struct_va1.c create mode 100644 testsuite/libffi.call/cls_uchar_va.c create mode 100644 testsuite/libffi.call/cls_uint_va.c create mode 100644 testsuite/libffi.call/cls_ulong_va.c create mode 100644 testsuite/libffi.call/cls_ushort_va.c create mode 100644 testsuite/libffi.call/nested_struct11.c create mode 100644 testsuite/libffi.call/uninitialized.c create mode 100644 testsuite/libffi.call/va_1.c create mode 100644 testsuite/libffi.call/va_struct1.c create mode 100644 testsuite/libffi.call/va_struct2.c create mode 100644 testsuite/libffi.call/va_struct3.c diff --git a/.pc/aarch64/.timestamp b/.pc/aarch64/.timestamp new file mode 100644 index 0000000..e69de29 diff --git a/.pc/aarch64/README b/.pc/aarch64/README new file mode 100644 index 0000000..ccd4f8b --- /dev/null +++ b/.pc/aarch64/README @@ -0,0 +1,366 @@ +Status +====== + +libffi-3.0.12 was released on XXXXXXX. Check the libffi web page for +updates: . + + +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 + . + +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 . + +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 index 0000000..e69de29 diff --git a/.pc/aarch64/src/aarch64/ffitarget.h b/.pc/aarch64/src/aarch64/ffitarget.h new file mode 100644 index 0000000..e69de29 diff --git a/.pc/aarch64/src/aarch64/sysv.S b/.pc/aarch64/src/aarch64/sysv.S new file mode 100644 index 0000000..e69de29 diff --git a/.pc/aarch64/testsuite/lib/libffi.exp b/.pc/aarch64/testsuite/lib/libffi.exp new file mode 100644 index 0000000..4a65ed1 --- /dev/null +++ b/.pc/aarch64/testsuite/lib/libffi.exp @@ -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 +# . + +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 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 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 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 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 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 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 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 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 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 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 index 0000000..e69de29 diff --git a/.pc/applied-patches b/.pc/applied-patches index 9f2b7a8..ae4c534 100644 --- a/.pc/applied-patches +++ b/.pc/applied-patches @@ -22,3 +22,4 @@ ios-fix mingw-check-fix whitespace-fix tile +aarch64 diff --git a/README b/README index ccd4f8b..71bd42a 100644 --- 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 index 0000000..2217be0 --- /dev/null +++ b/patches/aarch64 @@ -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 ++ ++#include ++#include ++ ++#include ++ ++/* 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 ++#include ++ ++#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 ++ ++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] = ≻ ++ 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 ++ ++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 ++ ++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 ++ ++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; ++} diff --git a/patches/series b/patches/series index 26e4d78..18aef73 100644 --- a/patches/series +++ b/patches/series @@ -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 index 0000000..1405665 --- /dev/null +++ b/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 + +#include +#include + +#include + +/* 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 index 0000000..6f1a348 --- /dev/null +++ b/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 diff --git a/src/aarch64/sysv.S b/src/aarch64/sysv.S new file mode 100644 index 0000000..b8cd421 --- /dev/null +++ b/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 +#include + +#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 diff --git a/testsuite/lib/libffi.exp b/testsuite/lib/libffi.exp index 4a65ed1..8ee3f15 100644 --- a/testsuite/lib/libffi.exp +++ b/testsuite/lib/libffi.exp @@ -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 index 0000000..91772bd --- /dev/null +++ b/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); +} diff --git a/testsuite/libffi.call/cls_uchar_va.c b/testsuite/libffi.call/cls_uchar_va.c new file mode 100644 index 0000000..19cd4f3 --- /dev/null +++ b/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); +} diff --git a/testsuite/libffi.call/cls_uint_va.c b/testsuite/libffi.call/cls_uint_va.c new file mode 100644 index 0000000..150fddd --- /dev/null +++ b/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); +} diff --git a/testsuite/libffi.call/cls_ulong_va.c b/testsuite/libffi.call/cls_ulong_va.c new file mode 100644 index 0000000..0315082 --- /dev/null +++ b/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); +} diff --git a/testsuite/libffi.call/cls_ushort_va.c b/testsuite/libffi.call/cls_ushort_va.c new file mode 100644 index 0000000..b2b5a3b --- /dev/null +++ b/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); +} diff --git a/testsuite/libffi.call/nested_struct11.c b/testsuite/libffi.call/nested_struct11.c new file mode 100644 index 0000000..fce6948 --- /dev/null +++ b/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); +} diff --git a/testsuite/libffi.call/uninitialized.c b/testsuite/libffi.call/uninitialized.c new file mode 100644 index 0000000..f00d830 --- /dev/null +++ b/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); +} diff --git a/testsuite/libffi.call/va_1.c b/testsuite/libffi.call/va_1.c new file mode 100644 index 0000000..5c7cce9 --- /dev/null +++ b/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 + +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] = ≻ + 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 index 0000000..11d1f10 --- /dev/null +++ b/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 + +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 index 0000000..56f5b9c --- /dev/null +++ b/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 + +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 index 0000000..9a27e7f --- /dev/null +++ b/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 + +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; +}