Merge pull request #104 from joshtriplett/efi64
authorAnthony Green <green@moxielogic.com>
Sat, 20 Feb 2016 11:49:19 +0000 (06:49 -0500)
committerAnthony Green <green@moxielogic.com>
Sat, 20 Feb 2016 11:49:19 +0000 (06:49 -0500)
Support the Windows/EFI calling convention on all x86-64 targets

20 files changed:
.travis.yml
configure.ac
doc/libffi.texi
include/ffi.h.in
include/ffi_common.h
msvcc.sh
src/aarch64/ffi.c
src/aarch64/ffitarget.h
src/aarch64/sysv.S
src/arm/ffi.c
src/arm/ffitarget.h
src/arm/sysv.S
src/closures.c
src/mips/ffi.c
src/mips/ffitarget.h
src/mips/n32.S
src/mips/o32.S
testsuite/lib/libffi.exp
testsuite/libffi.call/align_mixed.c [new file with mode: 0644]
testsuite/libffi.call/call.exp

index 6c14fcd..289076a 100644 (file)
@@ -11,6 +11,7 @@ matrix:
       env: HOST=i386-pc-linux-gnu
 
 before_script:
+  - sudo apt-get update
   - sudo apt-get install dejagnu texinfo
   - if [ "$HOST" = i386-pc-linux-gnu ] ; then sudo apt-get install gcc-multilib g++-multilib && CC="$CC -m32" && CXX="$CXX -m32" ; fi
 
index 445c589..74658c3 100644 (file)
@@ -337,6 +337,10 @@ AC_ARG_ENABLE(purify-safety,
     AC_DEFINE(USING_PURIFY, 1, [Define this if you are using Purify and want to suppress spurious messages.])
   fi)
 
+AC_ARG_ENABLE(multi-os-directory,
+[  --disable-multi-os-directory
+                          disable use of gcc --print-multi-os-directory to change the library installation directory])
+                          
 # These variables are only ever used when we cross-build to X86_WIN32.
 # And we only support this with GCC, so...
 if test "x$GCC" = "xyes"; then
@@ -348,11 +352,13 @@ if test "x$GCC" = "xyes"; then
     toolexecdir="${libdir}"/gcc-lib/'$(target_alias)'
     toolexeclibdir="${libdir}"
   fi
-  multi_os_directory=`$CC $CFLAGS -print-multi-os-directory`
-  case $multi_os_directory in
-    .) ;; # Avoid trailing /.
-    ../*) toolexeclibdir=$toolexeclibdir/$multi_os_directory ;;
-  esac
+  if test x"$enable_multi_os_directory" != x"no"; then
+    multi_os_directory=`$CC $CFLAGS -print-multi-os-directory`
+    case $multi_os_directory in
+      .) ;; # Avoid trailing /.
+      ../*) toolexeclibdir=$toolexeclibdir/$multi_os_directory ;;
+    esac
+  fi
   AC_SUBST(toolexecdir)
 else
   toolexeclibdir="${libdir}"
index b9887a8..5c9fddd 100644 (file)
@@ -107,6 +107,7 @@ values passed between the two languages.
 * Multiple ABIs::               Different passing styles on one platform.
 * The Closure API::             Writing a generic function.
 * Closure Example::             A closure example.
+* Thread Safety::               Thread safety.
 @end menu
 
 
@@ -152,7 +153,7 @@ If the function being called is variadic (varargs) then
 @code{ffi_prep_cif_var} must be used instead of @code{ffi_prep_cif}.
 
 @findex ffi_prep_cif_var
-@defun ffi_status ffi_prep_cif_var (ffi_cif *@var{cif}, ffi_abi var{abi}, unsigned int @var{nfixedargs}, unsigned int var{ntotalargs}, ffi_type *@var{rtype}, ffi_type **@var{argtypes})
+@defun ffi_status ffi_prep_cif_var (ffi_cif *@var{cif}, ffi_abi @var{abi}, unsigned int @var{nfixedargs}, unsigned int @var{ntotalargs}, ffi_type *@var{rtype}, ffi_type **@var{argtypes})
 This initializes @var{cif} according to the given parameters for
 a call to a variadic function.  In general it's operation is the
 same as for @code{ffi_prep_cif} except that:
@@ -161,7 +162,7 @@ same as for @code{ffi_prep_cif} except that:
 variadic arguments.  It must be greater than zero.
 
 @var{ntotalargs} the total number of arguments, including variadic
-and fixed arguments.
+and fixed arguments.  @var{argtypes} must have this many elements.
 
 Note that, different cif's must be prepped for calls to the same
 function when different numbers of arguments are passed.
@@ -172,6 +173,10 @@ Also note that a call to @code{ffi_prep_cif_var} with
 
 @end defun
 
+Note that the resulting @code{ffi_cif} holds pointers to all the
+@code{ffi_type} objects that were used during initialization.  You
+must ensure that these type objects have a lifetime at least as long
+as that of the @code{ffi_cif}.
 
 To call a function using an initialized @code{ffi_cif}, use the
 @code{ffi_call} function:
@@ -190,12 +195,29 @@ to ensure this.  If @var{cif} declares that the function returns
 @code{void} (using @code{ffi_type_void}), then @var{rvalue} is
 ignored.
 
+In most situations, @samp{libffi} will handle promotion according to
+the ABI.  However, for historical reasons, there is a special case
+with return values that must be handled by your code.  In particular,
+for integral (not @code{struct}) types that are narrower than the
+system register size, the return value will be widened by
+@samp{libffi}.  @samp{libffi} provides a type, @code{ffi_arg}, that
+can be used as the return type.  For example, if the CIF was defined
+with a return type of @code{char}, @samp{libffi} will try to store a
+full @code{ffi_arg} into the return value.
+
 @var{avalues} is a vector of @code{void *} pointers that point to the
 memory locations holding the argument values for a call.  If @var{cif}
 declares that the function has no arguments (i.e., @var{nargs} was 0),
 then @var{avalues} is ignored.  Note that argument values may be
 modified by the callee (for instance, structs passed by value); the
 burden of copying pass-by-value arguments is placed on the caller.
+
+Note that while the return value must be register-sized, arguments
+should exactly match their declared type.  For example, if an argument
+is a @code{short}, then the entry is @var{avalues} should point to an
+object declared as @code{short}; but if the return type is
+@code{short}, then @var{rvalue} should point to an object declared as
+a larger type -- usually @code{ffi_arg}.
 @end defun
 
 
@@ -246,6 +268,8 @@ int main()
 @menu
 * Primitive Types::             Built-in types.
 * Structures::                  Structure types.
+* Size and Alignment::          Size and alignment of types.
+* Arrays Unions Enums::         Arrays, unions, and enumerations.
 * Type Example::                Structure type example.
 * Complex::                     Complex types.
 * Complex Type Example::        Complex type example.
@@ -370,8 +394,7 @@ when passing to @code{ffi_prep_cif}.
 @node Structures
 @subsection Structures
 
-Although @samp{libffi} has no special support for unions or
-bit-fields, it is perfectly happy passing structures back and forth.
+@samp{libffi} is perfectly happy passing structures back and forth.
 You must first describe the structure to @samp{libffi} by creating a
 new @code{ffi_type} object for it.
 
@@ -391,9 +414,142 @@ For a structure, this should be set to @code{FFI_TYPE_STRUCT}.
 @item ffi_type **elements
 This is a @samp{NULL}-terminated array of pointers to @code{ffi_type}
 objects.  There is one element per field of the struct.
+
+Note that @samp{libffi} has no special support for bit-fields.  You
+must manage these manually.
 @end table
 @end deftp
 
+The @code{size} and @code{alignment} fields will be filled in by
+@code{ffi_prep_cif} or @code{ffi_prep_cif_var}, as needed.
+
+@node Size and Alignment
+@subsection Size and Alignment
+
+@code{libffi} will set the @code{size} and @code{alignment} fields of
+an @code{ffi_type} object for you.  It does so using its knowledge of
+the ABI.
+
+You might expect that you can simply read these fields for a type that
+has been laid out by @code{libffi}.  However, there are some caveats.
+
+@itemize @bullet
+@item
+The size or alignment of some of the built-in types may vary depending
+on the chosen ABI.
+
+@item
+The size and alignment of a new structure type will not be set by
+@code{libffi} until it has been passed to @code{ffi_prep_cif}.
+
+@item
+A structure type cannot be shared across ABIs.  Instead each ABI needs
+its own copy of the structure type.
+@end itemize
+
+So, before examining these fields, it is safest to pass the
+@code{ffi_type} object to @code{ffi_prep_cif} first.  This function
+will do all the needed setup.
+
+@example
+ffi_type *desired_type;
+ffi_abi desired_abi;
+@dots{}
+ffi_cif cif;
+if (ffi_prep_cif (&cif, desired_abi, 0, desired_type, NULL) == FFI_OK)
+  @{
+    size_t size = desired_type->size;
+    unsigned short alignment = desired_type->alignment;
+  @}
+@end example
+
+@node Arrays Unions Enums
+@subsection Arrays, Unions, and Enumerations
+
+@subsubsection Arrays
+
+@samp{libffi} does not have direct support for arrays or unions.
+However, they can be emulated using structures.
+
+To emulate an array, simply create an @code{ffi_type} using
+@code{FFI_TYPE_STRUCT} with as many members as there are elements in
+the array.
+
+@example
+ffi_type array_type;
+ffi_type **elements
+int i;
+
+elements = malloc ((n + 1) * sizeof (ffi_type *));
+for (i = 0; i < n; ++i)
+  elements[i] = array_element_type;
+elements[n] = NULL;
+
+array_type.size = array_type.alignment = 0;
+array_type.type = FFI_TYPE_STRUCT;
+array_type.elements = elements;
+@end example
+
+Note that arrays cannot be passed or returned by value in C --
+structure types created like this should only be used to refer to
+members of real @code{FFI_TYPE_STRUCT} objects.
+
+However, a phony array type like this will not cause any errors from
+@samp{libffi} if you use it as an argument or return type.  This may
+be confusing.
+
+@subsubsection Unions
+
+A union can also be emulated using @code{FFI_TYPE_STRUCT}.  In this
+case, however, you must make sure that the size and alignment match
+the real requirements of the union.
+
+One simple way to do this is to ensue that each element type is laid
+out.  Then, give the new structure type a single element; the size of
+the largest element; and the largest alignment seen as well.
+
+This example uses the @code{ffi_prep_cif} trick to ensure that each
+element type is laid out.
+
+@example
+ffi_abi desired_abi;
+ffi_type union_type;
+ffi_type **union_elements;
+
+int i;
+ffi_type element_types[2];
+
+element_types[1] = NULL;
+
+union_type.size = union_type.alignment = 0;
+union_type.type = FFI_TYPE_STRUCT;
+union_type.elements = element_types;
+
+for (i = 0; union_elements[i]; ++i)
+  @{
+    ffi_cif cif;
+    if (ffi_prep_cif (&cif, desired_abi, 0, union_elements[i], NULL) == FFI_OK)
+      @{
+        if (union_elements[i]->size > union_type.size)
+          @{
+            union_type.size = union_elements[i];
+            size = union_elements[i]->size;
+          @}
+        if (union_elements[i]->alignment > union_type.alignment)
+          union_type.alignment = union_elements[i]->alignment;
+      @}
+  @}
+@end example
+
+@subsubsection Enumerations
+
+@code{libffi} does not have any special support for C @code{enum}s.
+Although any given @code{enum} is implemented using a specific
+underlying integral type, exactly which type will be used cannot be
+determined by @code{libffi} -- it may depend on the values in the
+enumeration or on compiler flags such as @option{-fshort-enums}.
+@xref{Structures unions enumerations and bit-fields implementation, , , gcc},
+for more information about how GCC handles enumerations.
 
 @node Type Example
 @subsection Type Example
@@ -630,30 +786,47 @@ the closure function:
 
 @findex ffi_prep_closure_loc
 @defun ffi_status ffi_prep_closure_loc (ffi_closure *@var{closure}, ffi_cif *@var{cif}, void (*@var{fun}) (ffi_cif *@var{cif}, void *@var{ret}, void **@var{args}, void *@var{user_data}), void *@var{user_data}, void *@var{codeloc})
-Prepare a closure function.
+Prepare a closure function.  The arguments to
+@code{ffi_prep_closure_loc} are:
 
-@var{closure} is the address of a @code{ffi_closure} object; this is
-the writable address returned by @code{ffi_closure_alloc}.
+@table @var
+@item closure
+The address of a @code{ffi_closure} object; this is the writable
+address returned by @code{ffi_closure_alloc}.
 
-@var{cif} is the @code{ffi_cif} describing the function parameters.
+@item cif
+The @code{ffi_cif} describing the function parameters.  Note that this
+object, and the types to which it refers, must be kept alive until the
+closure itself is freed.
+
+@item user_data
+An arbitrary datum that is passed, uninterpreted, to your closure
+function.
 
-@var{user_data} is an arbitrary datum that is passed, uninterpreted,
-to your closure function.
+@item codeloc
+The executable address returned by @code{ffi_closure_alloc}.
 
-@var{codeloc} is the executable address returned by
-@code{ffi_closure_alloc}.
+@item fun
+The function which will be called when the closure is invoked.  It is
+called with the arguments:
 
-@var{fun} is the function which will be called when the closure is
-invoked.  It is called with the arguments:
 @table @var
 @item cif
 The @code{ffi_cif} passed to @code{ffi_prep_closure_loc}.
 
 @item ret
 A pointer to the memory used for the function's return value.
-@var{fun} must fill this, unless the function is declared as returning
-@code{void}.
-@c FIXME: is this NULL for void-returning functions?
+
+If the function is declared as returning @code{void}, then this value
+is garbage and should not be used.
+
+Otherwise, @var{fun} must fill the object to which this points,
+following the same special promotion behavior as @code{ffi_call}.
+That is, in most cases, @var{ret} points to an object of exactly the
+size of the type specified when @var{cif} was constructed.  However,
+integral types narrower than the system register size are widened.  In
+these cases your program may assume that @var{ret} points to an
+@code{ffi_arg} object.
 
 @item args
 A vector of pointers to memory holding the arguments to the function.
@@ -662,10 +835,10 @@ A vector of pointers to memory holding the arguments to the function.
 The same @var{user_data} that was passed to
 @code{ffi_prep_closure_loc}.
 @end table
+@end table
 
 @code{ffi_prep_closure_loc} will return @code{FFI_OK} if everything
-went ok, and something else on error.
-@c FIXME: what?
+went ok, and one of the other @code{ffi_status} values on error.
 
 After calling @code{ffi_prep_closure_loc}, you can cast @var{codeloc}
 to the appropriate pointer-to-function type.
@@ -733,6 +906,28 @@ int main()
 
 @end example
 
+@node Thread Safety
+@section Thread Safety
+
+@code{libffi} is not completely thread-safe.  However, many parts are,
+and if you follow some simple rules, you can use it safely in a
+multi-threaded program.
+
+@itemize @bullet
+@item
+@code{ffi_prep_cif} may modify the @code{ffi_type} objects passed to
+it.  It is best to ensure that only a single thread prepares a given
+@code{ffi_cif} at a time.
+
+@item
+On some platforms, @code{ffi_prep_cif} may modify the size and
+alignment of some types, depending on the chosen ABI.  On these
+platforms, if you switch between ABIs, you must ensure that there is
+only one call to @code{ffi_prep_cif} at a time.
+
+Currently the only affected platform is PowerPC and the only affected
+type is @code{long double}.
+@end itemize
 
 @node Missing Features
 @chapter Missing Features
@@ -749,9 +944,10 @@ There is no support for bit fields in structures.
 
 @item
 The ``raw'' API is undocumented.
-@c argument promotion?
-@c unions?
 @c anything else?
+
+@item
+The Go API is undocumented.
 @end itemize
 
 Note that variadic support is very new and tested on a relatively
index c43d52f..cd3358f 100644 (file)
@@ -217,8 +217,6 @@ typedef enum {
   FFI_BAD_ABI
 } ffi_status;
 
-typedef unsigned FFI_TYPE;
-
 typedef struct {
   ffi_abi abi;
   unsigned nargs;
@@ -231,11 +229,6 @@ typedef struct {
 #endif
 } ffi_cif;
 
-#if @HAVE_LONG_DOUBLE_VARIANT@
-/* Used to adjust size/alignment of ffi types.  */
-void ffi_prep_types (ffi_abi abi);
-#endif
-
 /* Used internally, but overridden by some architectures */
 ffi_status ffi_prep_cif_core(ffi_cif *cif,
                             ffi_abi abi,
@@ -337,7 +330,8 @@ ffi_status
 ffi_prep_closure (ffi_closure*,
                  ffi_cif *,
                  void (*fun)(ffi_cif*,void*,void**,void*),
-                 void *user_data);
+                 void *user_data)
+  __attribute__((deprecated ("use ffi_prep_closure_loc instead")));
 
 ffi_status
 ffi_prep_closure_loc (ffi_closure*,
index 37f5a9e..b312297 100644 (file)
@@ -82,6 +82,11 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif);
 ffi_status ffi_prep_cif_machdep_var(ffi_cif *cif,
         unsigned int nfixedargs, unsigned int ntotalargs);
 
+#if HAVE_LONG_DOUBLE_VARIANT
+/* Used to adjust size/alignment of ffi types.  */
+void ffi_prep_types (ffi_abi abi);
+#endif
+
 /* Extended cif, used in callback from assembly routine */
 typedef struct
 {
index 65fbfef..eef61c8 100755 (executable)
--- a/msvcc.sh
+++ b/msvcc.sh
@@ -55,6 +55,10 @@ while [ $# -gt 0 ]
 do
   case $1
   in
+    --version)
+      args="-help"
+      shift 1
+    ;;
     -fexceptions)
       # Don't enable exceptions for now.
       #args="$args -EHac"
@@ -70,7 +74,6 @@ do
     ;;
     -clang-cl)
       cl="clang-cl"
-      safeseh=
       shift 1
     ;;
     -O0)
index f79602b..cf33abc 100644 (file)
@@ -22,6 +22,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <fficonfig.h>
 #include <ffi.h>
 #include <ffi_common.h>
 #include "internal.h"
@@ -70,6 +71,14 @@ ffi_clear_cache (void *start, void *end)
 #endif
 }
 
+#if FFI_EXEC_TRAMPOLINE_TABLE
+
+#ifdef __MACH__
+#include <mach/vm_param.h>
+#endif
+
+#endif
+
 /* A subroutine of is_vfp_type.  Given a structure type, return the type code
    of the first non-structure element.  Recurse for structure elements.
    Return -1 if the structure is in fact empty, i.e. no nested elements.  */
@@ -725,240 +734,6 @@ ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue,
 extern void ffi_closure_SYSV (void) FFI_HIDDEN;
 extern void ffi_closure_SYSV_V (void) FFI_HIDDEN;
 
-#if FFI_EXEC_TRAMPOLINE_TABLE
-
-#include <mach/mach.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-extern void *ffi_closure_trampoline_table_page;
-
-typedef struct ffi_trampoline_table ffi_trampoline_table;
-typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
-
-struct ffi_trampoline_table
-{
-  /* contiguous writable and executable pages */
-  vm_address_t config_page;
-  vm_address_t trampoline_page;
-
-  /* free list tracking */
-  uint16_t free_count;
-  ffi_trampoline_table_entry *free_list;
-  ffi_trampoline_table_entry *free_list_pool;
-
-  ffi_trampoline_table *prev;
-  ffi_trampoline_table *next;
-};
-
-struct ffi_trampoline_table_entry
-{
-  void *(*trampoline) ();
-  ffi_trampoline_table_entry *next;
-};
-
-/* The trampoline configuration is placed a page prior to the trampoline's entry point */
-#define FFI_TRAMPOLINE_CODELOC_CONFIG(codeloc) ((void **) (((uint8_t *) codeloc) - PAGE_SIZE));
-
-/* Total number of trampolines that fit in one trampoline table */
-#define FFI_TRAMPOLINE_COUNT (PAGE_SIZE / FFI_TRAMPOLINE_SIZE)
-
-static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
-static ffi_trampoline_table *ffi_trampoline_tables = NULL;
-
-static ffi_trampoline_table *
-ffi_trampoline_table_alloc ()
-{
-  ffi_trampoline_table *table = NULL;
-
-  /* Loop until we can allocate two contiguous pages */
-  while (table == NULL)
-    {
-      vm_address_t config_page = 0x0;
-      kern_return_t kt;
-
-      /* Try to allocate two pages */
-      kt =
-       vm_allocate (mach_task_self (), &config_page, PAGE_SIZE * 2,
-                    VM_FLAGS_ANYWHERE);
-      if (kt != KERN_SUCCESS)
-       {
-         fprintf (stderr, "vm_allocate() failure: %d at %s:%d\n", kt,
-                  __FILE__, __LINE__);
-         break;
-       }
-
-      /* Now drop the second half of the allocation to make room for the trampoline table */
-      vm_address_t trampoline_page = config_page + PAGE_SIZE;
-      kt = vm_deallocate (mach_task_self (), trampoline_page, PAGE_SIZE);
-      if (kt != KERN_SUCCESS)
-       {
-         fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
-                  __FILE__, __LINE__);
-         break;
-       }
-
-      /* Remap the trampoline table to directly follow the config page */
-      vm_prot_t cur_prot;
-      vm_prot_t max_prot;
-
-      kt =
-       vm_remap (mach_task_self (), &trampoline_page, PAGE_SIZE, 0x0, FALSE,
-                 mach_task_self (),
-                 (vm_address_t) & ffi_closure_trampoline_table_page, FALSE,
-                 &cur_prot, &max_prot, VM_INHERIT_SHARE);
-
-      /* If we lost access to the destination trampoline page, drop our config allocation mapping and retry */
-      if (kt != KERN_SUCCESS)
-       {
-         /* Log unexpected failures */
-         if (kt != KERN_NO_SPACE)
-           {
-             fprintf (stderr, "vm_remap() failure: %d at %s:%d\n", kt,
-                      __FILE__, __LINE__);
-           }
-
-         vm_deallocate (mach_task_self (), config_page, PAGE_SIZE);
-         continue;
-       }
-
-      /* We have valid trampoline and config pages */
-      table = calloc (1, sizeof (ffi_trampoline_table));
-      table->free_count = FFI_TRAMPOLINE_COUNT;
-      table->config_page = config_page;
-      table->trampoline_page = trampoline_page;
-
-      /* Create and initialize the free list */
-      table->free_list_pool =
-       calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
-
-      uint16_t i;
-      for (i = 0; i < table->free_count; i++)
-       {
-         ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
-         entry->trampoline =
-           (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
-
-         if (i < table->free_count - 1)
-           entry->next = &table->free_list_pool[i + 1];
-       }
-
-      table->free_list = table->free_list_pool;
-    }
-
-  return table;
-}
-
-void *
-ffi_closure_alloc (size_t size, void **code)
-{
-  /* Create the closure */
-  ffi_closure *closure = malloc (size);
-  if (closure == NULL)
-    return NULL;
-
-  pthread_mutex_lock (&ffi_trampoline_lock);
-
-  /* Check for an active trampoline table with available entries. */
-  ffi_trampoline_table *table = ffi_trampoline_tables;
-  if (table == NULL || table->free_list == NULL)
-    {
-      table = ffi_trampoline_table_alloc ();
-      if (table == NULL)
-       {
-         free (closure);
-         return NULL;
-       }
-
-      /* Insert the new table at the top of the list */
-      table->next = ffi_trampoline_tables;
-      if (table->next != NULL)
-       table->next->prev = table;
-
-      ffi_trampoline_tables = table;
-    }
-
-  /* Claim the free entry */
-  ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
-  ffi_trampoline_tables->free_list = entry->next;
-  ffi_trampoline_tables->free_count--;
-  entry->next = NULL;
-
-  pthread_mutex_unlock (&ffi_trampoline_lock);
-
-  /* Initialize the return values */
-  *code = entry->trampoline;
-  closure->trampoline_table = table;
-  closure->trampoline_table_entry = entry;
-
-  return closure;
-}
-
-void
-ffi_closure_free (void *ptr)
-{
-  ffi_closure *closure = ptr;
-
-  pthread_mutex_lock (&ffi_trampoline_lock);
-
-  /* Fetch the table and entry references */
-  ffi_trampoline_table *table = closure->trampoline_table;
-  ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
-
-  /* Return the entry to the free list */
-  entry->next = table->free_list;
-  table->free_list = entry;
-  table->free_count++;
-
-  /* If all trampolines within this table are free, and at least one other table exists, deallocate
-   * the table */
-  if (table->free_count == FFI_TRAMPOLINE_COUNT
-      && ffi_trampoline_tables != table)
-    {
-      /* Remove from the list */
-      if (table->prev != NULL)
-       table->prev->next = table->next;
-
-      if (table->next != NULL)
-       table->next->prev = table->prev;
-
-      /* Deallocate pages */
-      kern_return_t kt;
-      kt = vm_deallocate (mach_task_self (), table->config_page, PAGE_SIZE);
-      if (kt != KERN_SUCCESS)
-       fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
-                __FILE__, __LINE__);
-
-      kt =
-       vm_deallocate (mach_task_self (), table->trampoline_page, PAGE_SIZE);
-      if (kt != KERN_SUCCESS)
-       fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
-                __FILE__, __LINE__);
-
-      /* Deallocate free list */
-      free (table->free_list_pool);
-      free (table);
-    }
-  else if (ffi_trampoline_tables != table)
-    {
-      /* Otherwise, bump this table to the top of the list */
-      table->prev = NULL;
-      table->next = ffi_trampoline_tables;
-      if (ffi_trampoline_tables != NULL)
-       ffi_trampoline_tables->prev = table;
-
-      ffi_trampoline_tables = table;
-    }
-
-  pthread_mutex_unlock (&ffi_trampoline_lock);
-
-  /* Free the closure */
-  free (closure);
-}
-
-#endif
-
 ffi_status
 ffi_prep_closure_loc (ffi_closure *closure,
                       ffi_cif* cif,
@@ -977,9 +752,11 @@ ffi_prep_closure_loc (ffi_closure *closure,
     start = ffi_closure_SYSV;
 
 #if FFI_EXEC_TRAMPOLINE_TABLE
-  void **config = FFI_TRAMPOLINE_CODELOC_CONFIG (codeloc);
+#ifdef __MACH__
+  void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE);
   config[0] = closure;
   config[1] = start;
+#endif
 #else
   static const unsigned char trampoline[16] = {
     0x90, 0x00, 0x00, 0x58,    /* ldr  x16, tramp+16   */
index 2862ec7..5ded0e4 100644 (file)
@@ -48,14 +48,21 @@ typedef enum ffi_abi
 /* ---- Definitions for closures ----------------------------------------- */
 
 #define FFI_CLOSURES 1
-#if defined (__APPLE__)
-#define FFI_TRAMPOLINE_SIZE 20
+#define FFI_NATIVE_RAW_API 0
+
+#if defined (FFI_EXEC_TRAMPOLINE_TABLE) && FFI_EXEC_TRAMPOLINE_TABLE
+
+#ifdef __MACH__
+#define FFI_TRAMPOLINE_SIZE 16
 #define FFI_TRAMPOLINE_CLOSURE_OFFSET 16
 #else
+#error "No trampoline table implementation"
+#endif
+
+#else
 #define FFI_TRAMPOLINE_SIZE 24
 #define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE
 #endif
-#define FFI_NATIVE_RAW_API 0
 
 /* ---- Internal ---- */
 
index c1bf9b9..7936c0e 100644 (file)
@@ -356,16 +356,18 @@ CNAME(ffi_closure_SYSV):
 #endif
 
 #if FFI_EXEC_TRAMPOLINE_TABLE
-    .align 12
+
+#ifdef __MACH__
+#include <mach/vm_param.h>
+    .align PAGE_MAX_SHIFT
 CNAME(ffi_closure_trampoline_table_page):
-    .rept 16384 / FFI_TRAMPOLINE_SIZE
-    adr        x17, -16384
-    adr        x16, -16380
-    ldr x16, [x16]
-    ldr x17, [x17]
-    br x16
+    .rept PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE
+    adr x16, -PAGE_MAX_SIZE
+    ldp x17, x16, [x16]
+    br x16
+       nop             /* each entry in the trampoline config page is 2*sizeof(void*) so the trampoline itself cannot be smaller that 16 bytes */
     .endr
-    
+
     .globl CNAME(ffi_closure_trampoline_table_page)
     #ifdef __ELF__
        .type   CNAME(ffi_closure_trampoline_table_page), #function
@@ -374,6 +376,8 @@ CNAME(ffi_closure_trampoline_table_page):
     #endif
 #endif
 
+#endif /* FFI_EXEC_TRAMPOLINE_TABLE */
+
 #ifdef FFI_GO_CLOSURES
        .align 4
 CNAME(ffi_go_closure_SYSV_V):
index 9c8732d..c24085d 100644 (file)
    DEALINGS IN THE SOFTWARE.
    ----------------------------------------------------------------------- */
 
+#include <fficonfig.h>
 #include <ffi.h>
 #include <ffi_common.h>
 #include <stdlib.h>
 #include "internal.h"
 
+#if FFI_EXEC_TRAMPOLINE_TABLE
+
+#ifdef __MACH__
+#include <mach/vm_param.h>
+#endif
+
+#else
+extern unsigned int ffi_arm_trampoline[2] FFI_HIDDEN;
+#endif
+
 /* Forward declares. */
 static int vfp_type_p (const ffi_type *);
 static void layout_vfp_args (ffi_cif *);
@@ -530,252 +541,6 @@ void ffi_closure_VFP (void) FFI_HIDDEN;
 void ffi_go_closure_SYSV (void) FFI_HIDDEN;
 void ffi_go_closure_VFP (void) FFI_HIDDEN;
 
-#if FFI_EXEC_TRAMPOLINE_TABLE
-
-#include <mach/mach.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-extern void *ffi_closure_trampoline_table_page;
-
-typedef struct ffi_trampoline_table ffi_trampoline_table;
-typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
-
-struct ffi_trampoline_table
-{
-  /* contiguous writable and executable pages */
-  vm_address_t config_page;
-  vm_address_t trampoline_page;
-
-  /* free list tracking */
-  uint16_t free_count;
-  ffi_trampoline_table_entry *free_list;
-  ffi_trampoline_table_entry *free_list_pool;
-
-  ffi_trampoline_table *prev;
-  ffi_trampoline_table *next;
-};
-
-struct ffi_trampoline_table_entry
-{
-  void *(*trampoline) ();
-  ffi_trampoline_table_entry *next;
-};
-
-/* Override the standard architecture trampoline size */
-// XXX TODO - Fix
-#undef FFI_TRAMPOLINE_SIZE
-#define FFI_TRAMPOLINE_SIZE 12
-
-/* The trampoline configuration is placed at 4080 bytes prior to the trampoline's entry point */
-#define FFI_TRAMPOLINE_CODELOC_CONFIG(codeloc) ((void **) (((uint8_t *) codeloc) - 4080));
-
-/* The first 16 bytes of the config page are unused, as they are unaddressable from the trampoline page. */
-#define FFI_TRAMPOLINE_CONFIG_PAGE_OFFSET 16
-
-/* Total number of trampolines that fit in one trampoline table */
-#define FFI_TRAMPOLINE_COUNT ((PAGE_SIZE - FFI_TRAMPOLINE_CONFIG_PAGE_OFFSET) / FFI_TRAMPOLINE_SIZE)
-
-static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
-static ffi_trampoline_table *ffi_trampoline_tables = NULL;
-
-static ffi_trampoline_table *
-ffi_trampoline_table_alloc ()
-{
-  ffi_trampoline_table *table = NULL;
-
-  /* Loop until we can allocate two contiguous pages */
-  while (table == NULL)
-    {
-      vm_address_t config_page = 0x0;
-      kern_return_t kt;
-
-      /* Try to allocate two pages */
-      kt =
-       vm_allocate (mach_task_self (), &config_page, PAGE_SIZE * 2,
-                    VM_FLAGS_ANYWHERE);
-      if (kt != KERN_SUCCESS)
-       {
-         fprintf (stderr, "vm_allocate() failure: %d at %s:%d\n", kt,
-                  __FILE__, __LINE__);
-         break;
-       }
-
-      /* Now drop the second half of the allocation to make room for the trampoline table */
-      vm_address_t trampoline_page = config_page + PAGE_SIZE;
-      kt = vm_deallocate (mach_task_self (), trampoline_page, PAGE_SIZE);
-      if (kt != KERN_SUCCESS)
-       {
-         fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
-                  __FILE__, __LINE__);
-         break;
-       }
-
-      /* Remap the trampoline table to directly follow the config page */
-      vm_prot_t cur_prot;
-      vm_prot_t max_prot;
-
-      kt =
-       vm_remap (mach_task_self (), &trampoline_page, PAGE_SIZE, 0x0, FALSE,
-                 mach_task_self (),
-                 (vm_address_t) & ffi_closure_trampoline_table_page, FALSE,
-                 &cur_prot, &max_prot, VM_INHERIT_SHARE);
-
-      /* If we lost access to the destination trampoline page, drop our config allocation mapping and retry */
-      if (kt != KERN_SUCCESS)
-       {
-         /* Log unexpected failures */
-         if (kt != KERN_NO_SPACE)
-           {
-             fprintf (stderr, "vm_remap() failure: %d at %s:%d\n", kt,
-                      __FILE__, __LINE__);
-           }
-
-         vm_deallocate (mach_task_self (), config_page, PAGE_SIZE);
-         continue;
-       }
-
-      /* We have valid trampoline and config pages */
-      table = calloc (1, sizeof (ffi_trampoline_table));
-      table->free_count = FFI_TRAMPOLINE_COUNT;
-      table->config_page = config_page;
-      table->trampoline_page = trampoline_page;
-
-      /* Create and initialize the free list */
-      table->free_list_pool =
-       calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
-
-      uint16_t i;
-      for (i = 0; i < table->free_count; i++)
-       {
-         ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
-         entry->trampoline =
-           (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
-
-         if (i < table->free_count - 1)
-           entry->next = &table->free_list_pool[i + 1];
-       }
-
-      table->free_list = table->free_list_pool;
-    }
-
-  return table;
-}
-
-void *
-ffi_closure_alloc (size_t size, void **code)
-{
-  /* Create the closure */
-  ffi_closure *closure = malloc (size);
-  if (closure == NULL)
-    return NULL;
-
-  pthread_mutex_lock (&ffi_trampoline_lock);
-
-  /* Check for an active trampoline table with available entries. */
-  ffi_trampoline_table *table = ffi_trampoline_tables;
-  if (table == NULL || table->free_list == NULL)
-    {
-      table = ffi_trampoline_table_alloc ();
-      if (table == NULL)
-       {
-         free (closure);
-         return NULL;
-       }
-
-      /* Insert the new table at the top of the list */
-      table->next = ffi_trampoline_tables;
-      if (table->next != NULL)
-       table->next->prev = table;
-
-      ffi_trampoline_tables = table;
-    }
-
-  /* Claim the free entry */
-  ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
-  ffi_trampoline_tables->free_list = entry->next;
-  ffi_trampoline_tables->free_count--;
-  entry->next = NULL;
-
-  pthread_mutex_unlock (&ffi_trampoline_lock);
-
-  /* Initialize the return values */
-  *code = entry->trampoline;
-  closure->trampoline_table = table;
-  closure->trampoline_table_entry = entry;
-
-  return closure;
-}
-
-void
-ffi_closure_free (void *ptr)
-{
-  ffi_closure *closure = ptr;
-
-  pthread_mutex_lock (&ffi_trampoline_lock);
-
-  /* Fetch the table and entry references */
-  ffi_trampoline_table *table = closure->trampoline_table;
-  ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
-
-  /* Return the entry to the free list */
-  entry->next = table->free_list;
-  table->free_list = entry;
-  table->free_count++;
-
-  /* If all trampolines within this table are free, and at least one other table exists, deallocate
-   * the table */
-  if (table->free_count == FFI_TRAMPOLINE_COUNT
-      && ffi_trampoline_tables != table)
-    {
-      /* Remove from the list */
-      if (table->prev != NULL)
-       table->prev->next = table->next;
-
-      if (table->next != NULL)
-       table->next->prev = table->prev;
-
-      /* Deallocate pages */
-      kern_return_t kt;
-      kt = vm_deallocate (mach_task_self (), table->config_page, PAGE_SIZE);
-      if (kt != KERN_SUCCESS)
-       fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
-                __FILE__, __LINE__);
-
-      kt =
-       vm_deallocate (mach_task_self (), table->trampoline_page, PAGE_SIZE);
-      if (kt != KERN_SUCCESS)
-       fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
-                __FILE__, __LINE__);
-
-      /* Deallocate free list */
-      free (table->free_list_pool);
-      free (table);
-    }
-  else if (ffi_trampoline_tables != table)
-    {
-      /* Otherwise, bump this table to the top of the list */
-      table->prev = NULL;
-      table->next = ffi_trampoline_tables;
-      if (ffi_trampoline_tables != NULL)
-       ffi_trampoline_tables->prev = table;
-
-      ffi_trampoline_tables = table;
-    }
-
-  pthread_mutex_unlock (&ffi_trampoline_lock);
-
-  /* Free the closure */
-  free (closure);
-}
-
-#else
-
-extern unsigned int ffi_arm_trampoline[2] FFI_HIDDEN;
-
-#endif
-
 /* the cif must already be prep'ed */
 
 ffi_status
@@ -796,7 +561,7 @@ ffi_prep_closure_loc (ffi_closure * closure,
     return FFI_BAD_ABI;
 
 #if FFI_EXEC_TRAMPOLINE_TABLE
-  void **config = FFI_TRAMPOLINE_CODELOC_CONFIG (codeloc);
+  void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE);
   config[0] = closure;
   config[1] = closure_func;
 #else
index 4f473f9..1cf1036 100644 (file)
@@ -63,7 +63,20 @@ typedef enum ffi_abi {
 
 #define FFI_CLOSURES 1
 #define FFI_GO_CLOSURES 1
-#define FFI_TRAMPOLINE_SIZE 12
 #define FFI_NATIVE_RAW_API 0
 
+#if defined (FFI_EXEC_TRAMPOLINE_TABLE) && FFI_EXEC_TRAMPOLINE_TABLE
+
+#ifdef __MACH__
+#define FFI_TRAMPOLINE_SIZE 12
+#define FFI_TRAMPOLINE_CLOSURE_OFFSET 8
+#else
+#error "No trampoline table implementation"
+#endif
+
+#else
+#define FFI_TRAMPOLINE_SIZE 12
+#define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE
+#endif
+
 #endif
index fd16589..379dcdf 100644 (file)
@@ -25,7 +25,7 @@
    DEALINGS IN THE SOFTWARE.
    ----------------------------------------------------------------------- */
 
-#define LIBFFI_ASM     
+#define LIBFFI_ASM
 #include <fficonfig.h>
 #include <ffi.h>
 #include <ffi_cfi.h>
 #endif
 
 /* Conditionally compile unwinder directives.  */
-.macro UNWIND text:vararg
 #ifdef __ARM_EABI__
-       \text
-#endif 
-.endm
+# define UNWIND(...)   __VA_ARGS__
+#else
+# define UNWIND(...)
+#endif
+
 #if defined(HAVE_AS_CFI_PSEUDO_OP) && defined(__ARM_EABI__)
        .cfi_sections   .debug_frame
 #endif
 # define TYPE(X, Y)
 #endif
 
-#define ARM_FUNC_START(name, gl) \
-       .align  3; \
-       .ifne gl; .globl CNAME(name); FFI_HIDDEN(CNAME(name)); .endif; \
-       TYPE(name, %function); \
+#define ARM_FUNC_START_LOCAL(name)     \
+       .align  3;                      \
+       TYPE(CNAME(name), %function);   \
        CNAME(name):
 
+#define ARM_FUNC_START(name)           \
+       .globl CNAME(name);             \
+       FFI_HIDDEN(CNAME(name));        \
+       ARM_FUNC_START_LOCAL(name)
+
 #define ARM_FUNC_END(name) \
        SIZE(name)
 
 /* Aid in defining a jump table with 8 bytes between entries.  */
-.macro E index
-       .if . - 0b - 8*\index
-       .error "type table out of sync"
+/* ??? The clang assembler doesn't handle .if with symbolic expressions.  */
+#ifdef __clang__
+# define E(index)
+#else
+# define E(index)                              \
+       .if . - 0b - 8*index;                   \
+       .error "type table out of sync";        \
        .endif
-.endm
+#endif
 
        .text
        .syntax unified
        .arm
 
+#ifndef __clang__
        /* We require interworking on LDM, which implies ARMv5T,
           which implies the existance of BLX.  */
-       .arch   armv5t
+       .arch   armv5t
+#endif
 
        /* Note that we use STC and LDC to encode VFP instructions,
           so that we do not need ".fpu vfp", nor get that added to
        @ r2:   fn
        @ r3:   vfp_used
 
-ARM_FUNC_START(ffi_call_VFP, 1)
-       UNWIND  .fnstart
+ARM_FUNC_START(ffi_call_VFP)
+       UNWIND(.fnstart)
        cfi_startproc
 
        cmp     r3, #3                  @ load only d0 if possible
+#ifdef __clang__
+       vldrle d0, [sp]
+       vldmgt sp, {d0-d7}
+#else
        ldcle   p11, cr0, [r0]          @ vldrle d0, [sp]
        ldcgt   p11, cr0, [r0], {16}    @ vldmgt sp, {d0-d7}
+#endif
        add     r0, r0, #64             @ discard the vfp register args
        /* FALLTHRU */
 ARM_FUNC_END(ffi_call_VFP)
 
-ARM_FUNC_START(ffi_call_SYSV, 1)
+ARM_FUNC_START(ffi_call_SYSV)
        stm     r1, {fp, lr}
        mov     fp, r1
 
        @ This is a bit of a lie wrt the origin of the unwind info, but
        @ now we've got the usual frame pointer and two saved registers.
-       UNWIND  .save {fp,lr}
-       UNWIND  .setfp fp, sp
+       UNWIND(.save {fp,lr})
+       UNWIND(.setfp fp, sp)
        cfi_def_cfa(fp, 8)
        cfi_rel_offset(fp, 0)
        cfi_rel_offset(lr, 4)
@@ -154,29 +170,41 @@ ARM_FUNC_START(ffi_call_SYSV, 1)
        add     pc, pc, r3, lsl #3
        nop
 0:
-E ARM_TYPE_VFP_S
+E(ARM_TYPE_VFP_S)
+#ifdef __clang__
+       vstr s0, [r2]
+#else
        stc     p10, cr0, [r2]          @ vstr s0, [r2]
+#endif
        pop     {fp,pc}
-E ARM_TYPE_VFP_D
+E(ARM_TYPE_VFP_D)
+#ifdef __clang__
+       vstr d0, [r2]
+#else
        stc     p11, cr0, [r2]          @ vstr d0, [r2]
+#endif
        pop     {fp,pc}
-E ARM_TYPE_VFP_N
+E(ARM_TYPE_VFP_N)
+#ifdef __clang__
+       vstm r2, {d0-d3}
+#else
        stc     p11, cr0, [r2], {8}     @ vstm r2, {d0-d3}
+#endif
        pop     {fp,pc}
-E ARM_TYPE_INT64
+E(ARM_TYPE_INT64)
        str     r1, [r2, #4]
        nop
-E ARM_TYPE_INT
+E(ARM_TYPE_INT)
        str     r0, [r2]
        pop     {fp,pc}
-E ARM_TYPE_VOID
+E(ARM_TYPE_VOID)
        pop     {fp,pc}
        nop
-E ARM_TYPE_STRUCT
+E(ARM_TYPE_STRUCT)
        pop     {fp,pc}
 
        cfi_endproc
-       UNWIND  .fnend
+       UNWIND(.fnend)
 ARM_FUNC_END(ffi_call_SYSV)
 
 
@@ -184,7 +212,7 @@ ARM_FUNC_END(ffi_call_SYSV)
        int ffi_closure_inner_* (cif, fun, user_data, frame)
 */
 
-ARM_FUNC_START(ffi_go_closure_SYSV, 1)
+ARM_FUNC_START(ffi_go_closure_SYSV)
        cfi_startproc
        stmdb   sp!, {r0-r3}                    @ save argument regs
        cfi_adjust_cfa_offset(16)
@@ -195,14 +223,18 @@ ARM_FUNC_START(ffi_go_closure_SYSV, 1)
        cfi_endproc
 ARM_FUNC_END(ffi_go_closure_SYSV)
 
-ARM_FUNC_START(ffi_closure_SYSV, 1)
-       UNWIND  .fnstart
+ARM_FUNC_START(ffi_closure_SYSV)
+       UNWIND(.fnstart)
        cfi_startproc
        stmdb   sp!, {r0-r3}                    @ save argument regs
        cfi_adjust_cfa_offset(16)
-       ldr     r0, [ip, #FFI_TRAMPOLINE_SIZE]    @ load cif
-       ldr     r1, [ip, #FFI_TRAMPOLINE_SIZE+4]  @ load fun
-       ldr     r2, [ip, #FFI_TRAMPOLINE_SIZE+8]  @ load user_data
+
+#if FFI_EXEC_TRAMPOLINE_TABLE
+       ldr ip, [ip]                            @ ip points to the config page, dereference to get the ffi_closure*
+#endif
+       ldr     r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET]        @ load cif
+       ldr     r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4]  @ load fun
+       ldr     r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8]  @ load user_data
 0:
        add     ip, sp, #16                     @ compute entry sp
        sub     sp, sp, #64+32                  @ allocate frame
@@ -212,7 +244,7 @@ ARM_FUNC_START(ffi_closure_SYSV, 1)
        /* Remember that EABI unwind info only applies at call sites.
           We need do nothing except note the save of the stack pointer
           and the link registers.  */
-       UNWIND  .save {sp,lr}
+       UNWIND(.save {sp,lr})
        cfi_adjust_cfa_offset(8)
        cfi_rel_offset(lr, 4)
 
@@ -224,10 +256,10 @@ ARM_FUNC_START(ffi_closure_SYSV, 1)
        adr     r3, CNAME(ffi_closure_ret)
        add     pc, r3, r0, lsl #3
        cfi_endproc
-       UNWIND  .fnend
+       UNWIND(.fnend)
 ARM_FUNC_END(ffi_closure_SYSV)
 
-ARM_FUNC_START(ffi_go_closure_VFP, 1)
+ARM_FUNC_START(ffi_go_closure_VFP)
        cfi_startproc
        stmdb   sp!, {r0-r3}                    @ save argument regs
        cfi_adjust_cfa_offset(16)
@@ -238,23 +270,31 @@ ARM_FUNC_START(ffi_go_closure_VFP, 1)
        cfi_endproc
 ARM_FUNC_END(ffi_go_closure_VFP)
 
-ARM_FUNC_START(ffi_closure_VFP, 1)
-       UNWIND  .fnstart
+ARM_FUNC_START(ffi_closure_VFP)
+       UNWIND(.fnstart)
        cfi_startproc
        stmdb   sp!, {r0-r3}                    @ save argument regs
        cfi_adjust_cfa_offset(16)
-       ldr     r0, [ip, #FFI_TRAMPOLINE_SIZE]    @ load cif
-       ldr     r1, [ip, #FFI_TRAMPOLINE_SIZE+4]  @ load fun
-       ldr     r2, [ip, #FFI_TRAMPOLINE_SIZE+8]  @ load user_data
+
+#if FFI_EXEC_TRAMPOLINE_TABLE
+       ldr ip, [ip]                            @ ip points to the config page, dereference to get the ffi_closure*
+#endif
+       ldr     r0, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET]        @ load cif
+       ldr     r1, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+4]  @ load fun
+       ldr     r2, [ip, #FFI_TRAMPOLINE_CLOSURE_OFFSET+8]  @ load user_data
 0:
        add     ip, sp, #16
        sub     sp, sp, #64+32                  @ allocate frame
        cfi_adjust_cfa_offset(64+32)
+#ifdef __clang__
+       vstm sp, {d0-d7}
+#else
        stc     p11, cr0, [sp], {16}            @ vstm sp, {d0-d7}
+#endif
        stmdb   sp!, {ip,lr}
 
        /* See above.  */
-       UNWIND  .save {sp,lr}
+       UNWIND(.save {sp,lr})
        cfi_adjust_cfa_offset(8)
        cfi_rel_offset(lr, 4)
 
@@ -266,63 +306,71 @@ ARM_FUNC_START(ffi_closure_VFP, 1)
        adr     r3, CNAME(ffi_closure_ret)
        add     pc, r3, r0, lsl #3
        cfi_endproc
-       UNWIND  .fnend
+       UNWIND(.fnend)
 ARM_FUNC_END(ffi_closure_VFP)
 
 /* Load values returned in registers for both closure entry points.
    Note that we use LDM with SP in the register set.  This is deprecated
    by ARM, but not yet unpredictable.  */
 
-ARM_FUNC_START(ffi_closure_ret, 0)
+ARM_FUNC_START_LOCAL(ffi_closure_ret)
        cfi_startproc
        cfi_rel_offset(sp, 0)
        cfi_rel_offset(lr, 4)
 0:
-E ARM_TYPE_VFP_S
+E(ARM_TYPE_VFP_S)
+#ifdef __clang__
+       vldr s0, [r2]
+#else
        ldc     p10, cr0, [r2]                  @ vldr s0, [r2]
+#endif
        ldm     sp, {sp,pc}
-E ARM_TYPE_VFP_D
+E(ARM_TYPE_VFP_D)
+#ifdef __clang__
+       vldr d0, [r2]
+#else
        ldc     p11, cr0, [r2]                  @ vldr d0, [r2]
+#endif
        ldm     sp, {sp,pc}
-E ARM_TYPE_VFP_N
+E(ARM_TYPE_VFP_N)
+#ifdef __clang__
+       vldm r2, {d0-d3}
+#else
        ldc     p11, cr0, [r2], {8}             @ vldm r2, {d0-d3}
+#endif
        ldm     sp, {sp,pc}
-E ARM_TYPE_INT64
+E(ARM_TYPE_INT64)
        ldr     r1, [r2, #4]
        nop
-E ARM_TYPE_INT
+E(ARM_TYPE_INT)
        ldr     r0, [r2]
        ldm     sp, {sp,pc}
-E ARM_TYPE_VOID
+E(ARM_TYPE_VOID)
        ldm     sp, {sp,pc}
        nop
-E ARM_TYPE_STRUCT
+E(ARM_TYPE_STRUCT)
        ldm     sp, {sp,pc}
        cfi_endproc
 ARM_FUNC_END(ffi_closure_ret)
 
 #if FFI_EXEC_TRAMPOLINE_TABLE
 
-/* ??? The iOS support should be updated.  The first insn used to
-   be STMFD, but that's been moved into ffi_closure_SYSV.  If the
-   writable page is put after this one we can make use of the
-   pc+8 feature of the architecture.  We can also reduce the size
-   of the thunk to 8 and pack more of these into the page.
-
-   In the meantime, simply replace the STMFD with a NOP so as to
-   keep all the magic numbers the same within ffi.c.  */
+#ifdef __MACH__
+#include <mach/vm_param.h>
 
-       .align  12
+.align PAGE_MAX_SHIFT
 ARM_FUNC_START(ffi_closure_trampoline_table_page)
-.rept  4096 / 12
-       nop
-       ldr     ip, [pc, #-4092]
-       ldr     pc, [pc, #-4092]
+.rept  PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE
+       adr ip, #-PAGE_MAX_SIZE   @ the config page is PAGE_MAX_SIZE behind the trampoline page
+       sub ip, #8                                @ account for pc bias
+       ldr     pc, [ip, #4]              @ jump to ffi_closure_SYSV or ffi_closure_VFP
 .endr
+ARM_FUNC_END(ffi_closure_trampoline_table_page)
+#endif
 
 #else
 
-ARM_FUNC_START(ffi_arm_trampoline, 1)
+ARM_FUNC_START(ffi_arm_trampoline)
 0:     adr     ip, 0b
        ldr     pc, 1f
 1:     .long   0
index 721ff00..3dec0e3 100644 (file)
@@ -30,6 +30,7 @@
 #define _GNU_SOURCE 1
 #endif
 
+#include <fficonfig.h>
 #include <ffi.h>
 #include <ffi_common.h>
 
 
 #if FFI_CLOSURES
 
-# if FFI_EXEC_TRAMPOLINE_TABLE
+#if FFI_EXEC_TRAMPOLINE_TABLE
+
+#ifdef __MACH__
+
+#include <mach/mach.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void *ffi_closure_trampoline_table_page;
+
+typedef struct ffi_trampoline_table ffi_trampoline_table;
+typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
+
+struct ffi_trampoline_table
+{
+  /* contiguous writable and executable pages */
+  vm_address_t config_page;
+  vm_address_t trampoline_page;
+
+  /* free list tracking */
+  uint16_t free_count;
+  ffi_trampoline_table_entry *free_list;
+  ffi_trampoline_table_entry *free_list_pool;
+
+  ffi_trampoline_table *prev;
+  ffi_trampoline_table *next;
+};
+
+struct ffi_trampoline_table_entry
+{
+  void *(*trampoline) ();
+  ffi_trampoline_table_entry *next;
+};
+
+/* Total number of trampolines that fit in one trampoline table */
+#define FFI_TRAMPOLINE_COUNT (PAGE_MAX_SIZE / FFI_TRAMPOLINE_SIZE)
+
+static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
+static ffi_trampoline_table *ffi_trampoline_tables = NULL;
+
+static ffi_trampoline_table *
+ffi_trampoline_table_alloc ()
+{
+  ffi_trampoline_table *table = NULL;
+
+  /* Loop until we can allocate two contiguous pages */
+  while (table == NULL)
+    {
+      vm_address_t config_page = 0x0;
+      kern_return_t kt;
+
+      /* Try to allocate two pages */
+      kt =
+       vm_allocate (mach_task_self (), &config_page, PAGE_MAX_SIZE * 2,
+                    VM_FLAGS_ANYWHERE);
+      if (kt != KERN_SUCCESS)
+       {
+         fprintf (stderr, "vm_allocate() failure: %d at %s:%d\n", kt,
+                  __FILE__, __LINE__);
+         break;
+       }
+
+      /* Now drop the second half of the allocation to make room for the trampoline table */
+      vm_address_t trampoline_page = config_page + PAGE_MAX_SIZE;
+      kt = vm_deallocate (mach_task_self (), trampoline_page, PAGE_MAX_SIZE);
+      if (kt != KERN_SUCCESS)
+       {
+         fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
+                  __FILE__, __LINE__);
+         break;
+       }
+
+      /* Remap the trampoline table to directly follow the config page */
+      vm_prot_t cur_prot;
+      vm_prot_t max_prot;
+
+         vm_address_t trampoline_page_template = (vm_address_t)&ffi_closure_trampoline_table_page;
+#ifdef __arm__
+         /* ffi_closure_trampoline_table_page can be thumb-biased on some ARM archs */
+         trampoline_page_template &= ~1UL;
+#endif
+
+      kt =
+       vm_remap (mach_task_self (), &trampoline_page, PAGE_MAX_SIZE, 0x0, FALSE,
+                 mach_task_self (), trampoline_page_template, FALSE,
+                 &cur_prot, &max_prot, VM_INHERIT_SHARE);
+
+      /* If we lost access to the destination trampoline page, drop our config allocation mapping and retry */
+      if (kt != KERN_SUCCESS)
+       {
+         /* Log unexpected failures */
+         if (kt != KERN_NO_SPACE)
+           {
+             fprintf (stderr, "vm_remap() failure: %d at %s:%d\n", kt,
+                      __FILE__, __LINE__);
+           }
+
+         vm_deallocate (mach_task_self (), config_page, PAGE_SIZE);
+         continue;
+       }
+
+      /* We have valid trampoline and config pages */
+      table = calloc (1, sizeof (ffi_trampoline_table));
+      table->free_count = FFI_TRAMPOLINE_COUNT;
+      table->config_page = config_page;
+      table->trampoline_page = trampoline_page;
+
+      /* Create and initialize the free list */
+      table->free_list_pool =
+       calloc (FFI_TRAMPOLINE_COUNT, sizeof (ffi_trampoline_table_entry));
+
+      uint16_t i;
+      for (i = 0; i < table->free_count; i++)
+       {
+         ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
+         entry->trampoline =
+           (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
+
+         if (i < table->free_count - 1)
+           entry->next = &table->free_list_pool[i + 1];
+       }
+
+      table->free_list = table->free_list_pool;
+    }
+
+  return table;
+}
+
+void *
+ffi_closure_alloc (size_t size, void **code)
+{
+  /* Create the closure */
+  ffi_closure *closure = malloc (size);
+  if (closure == NULL)
+    return NULL;
+
+  pthread_mutex_lock (&ffi_trampoline_lock);
+
+  /* Check for an active trampoline table with available entries. */
+  ffi_trampoline_table *table = ffi_trampoline_tables;
+  if (table == NULL || table->free_list == NULL)
+    {
+      table = ffi_trampoline_table_alloc ();
+      if (table == NULL)
+       {
+         free (closure);
+         return NULL;
+       }
+
+      /* Insert the new table at the top of the list */
+      table->next = ffi_trampoline_tables;
+      if (table->next != NULL)
+       table->next->prev = table;
+
+      ffi_trampoline_tables = table;
+    }
+
+  /* Claim the free entry */
+  ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
+  ffi_trampoline_tables->free_list = entry->next;
+  ffi_trampoline_tables->free_count--;
+  entry->next = NULL;
+
+  pthread_mutex_unlock (&ffi_trampoline_lock);
+
+  /* Initialize the return values */
+  *code = entry->trampoline;
+  closure->trampoline_table = table;
+  closure->trampoline_table_entry = entry;
+
+  return closure;
+}
+
+void
+ffi_closure_free (void *ptr)
+{
+  ffi_closure *closure = ptr;
+
+  pthread_mutex_lock (&ffi_trampoline_lock);
+
+  /* Fetch the table and entry references */
+  ffi_trampoline_table *table = closure->trampoline_table;
+  ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
+
+  /* Return the entry to the free list */
+  entry->next = table->free_list;
+  table->free_list = entry;
+  table->free_count++;
+
+  /* If all trampolines within this table are free, and at least one other table exists, deallocate
+   * the table */
+  if (table->free_count == FFI_TRAMPOLINE_COUNT
+      && ffi_trampoline_tables != table)
+    {
+      /* Remove from the list */
+      if (table->prev != NULL)
+       table->prev->next = table->next;
+
+      if (table->next != NULL)
+       table->next->prev = table->prev;
+
+      /* Deallocate pages */
+      kern_return_t kt;
+      kt = vm_deallocate (mach_task_self (), table->config_page, PAGE_SIZE);
+      if (kt != KERN_SUCCESS)
+       fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
+                __FILE__, __LINE__);
+
+      kt =
+       vm_deallocate (mach_task_self (), table->trampoline_page, PAGE_SIZE);
+      if (kt != KERN_SUCCESS)
+       fprintf (stderr, "vm_deallocate() failure: %d at %s:%d\n", kt,
+                __FILE__, __LINE__);
+
+      /* Deallocate free list */
+      free (table->free_list_pool);
+      free (table);
+    }
+  else if (ffi_trampoline_tables != table)
+    {
+      /* Otherwise, bump this table to the top of the list */
+      table->prev = NULL;
+      table->next = ffi_trampoline_tables;
+      if (ffi_trampoline_tables != NULL)
+       ffi_trampoline_tables->prev = table;
+
+      ffi_trampoline_tables = table;
+    }
+
+  pthread_mutex_unlock (&ffi_trampoline_lock);
+
+  /* Free the closure */
+  free (closure);
+}
+
+#endif
 
 // Per-target implementation; It's unclear what can reasonable be shared between two OS/architecture implementations.
 
-# elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
+#elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
 
 #define USE_LOCKS 1
 #define USE_DL_PREFIX 1
index 5d0dd70..3ed9b48 100644 (file)
@@ -581,14 +581,15 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
 /* Low level routine for calling O32 functions */
 extern int ffi_call_O32(void (*)(char *, extended_cif *, int, int), 
                        extended_cif *, unsigned, 
-                       unsigned, unsigned *, void (*)(void));
+                       unsigned, unsigned *, void (*)(void), void *closure);
 
 /* Low level routine for calling N32 functions */
 extern int ffi_call_N32(void (*)(char *, extended_cif *, int, int), 
                        extended_cif *, unsigned, 
-                       unsigned, void *, void (*)(void));
+                       unsigned, void *, void (*)(void), void *closure);
 
-void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+void ffi_call_int(ffi_cif *cif, void (*fn)(void), void *rvalue, 
+             void **avalue, void *closure)
 {
   extended_cif ecif;
 
@@ -610,7 +611,7 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
     case FFI_O32:
     case FFI_O32_SOFT_FLOAT:
       ffi_call_O32(ffi_prep_args, &ecif, cif->bytes, 
-                  cif->flags, ecif.rvalue, fn);
+                  cif->flags, ecif.rvalue, fn, closure);
       break;
 #endif
 
@@ -642,7 +643,7 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
 #endif
          }
         ffi_call_N32(ffi_prep_args, &ecif, cif->bytes,
-                     cif->flags, rvalue_copy, fn);
+                     cif->flags, rvalue_copy, fn, closure);
         if (copy_rvalue)
           memcpy(ecif.rvalue, rvalue_copy + copy_offset, cif->rtype->size);
       }
@@ -655,11 +656,27 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
     }
 }
 
+void
+ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+{
+  ffi_call_int (cif, fn, rvalue, avalue, NULL);
+}
+
+void
+ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue,
+            void **avalue, void *closure)
+{
+  ffi_call_int (cif, fn, rvalue, avalue, closure);
+}
+
+
 #if FFI_CLOSURES
 #if defined(FFI_MIPS_O32)
 extern void ffi_closure_O32(void);
+extern void ffi_go_closure_O32(void);
 #else
 extern void ffi_closure_N32(void);
+extern void ffi_go_closure_N32(void);
 #endif /* FFI_MIPS_O32 */
 
 ffi_status
@@ -762,17 +779,17 @@ ffi_prep_closure_loc (ffi_closure *closure,
  * Based on the similar routine for sparc.
  */
 int
-ffi_closure_mips_inner_O32 (ffi_closure *closure,
+ffi_closure_mips_inner_O32 (ffi_cif *cif,
+                            void (*fun)(ffi_cif*, void*, void**, void*),
+                           void *user_data,
                            void *rvalue, ffi_arg *ar,
                            double *fpr)
 {
-  ffi_cif *cif;
   void **avaluep;
   ffi_arg *avalue;
   ffi_type **arg_types;
   int i, avn, argn, seen_int;
 
-  cif = closure->cif;
   avalue = alloca (cif->nargs * sizeof (ffi_arg));
   avaluep = alloca (cif->nargs * sizeof (ffi_arg));
 
@@ -840,7 +857,7 @@ ffi_closure_mips_inner_O32 (ffi_closure *closure,
     }
 
   /* Invoke the closure. */
-  (closure->fun) (cif, rvalue, avaluep, closure->user_data);
+  fun(cif, rvalue, avaluep, user_data);
 
   if (cif->abi == FFI_O32_SOFT_FLOAT)
     {
@@ -916,11 +933,12 @@ copy_struct_N32(char *target, unsigned offset, ffi_abi abi, ffi_type *type,
  *
  */
 int
-ffi_closure_mips_inner_N32 (ffi_closure *closure,
+ffi_closure_mips_inner_N32 (ffi_cif *cif, 
+                           void (*fun)(ffi_cif*, void*, void**, void*),
+                            void *user_data,
                            void *rvalue, ffi_arg *ar,
                            ffi_arg *fpr)
 {
-  ffi_cif *cif;
   void **avaluep;
   ffi_arg *avalue;
   ffi_type **arg_types;
@@ -928,7 +946,6 @@ ffi_closure_mips_inner_N32 (ffi_closure *closure,
   int soft_float;
   ffi_arg *argp;
 
-  cif = closure->cif;
   soft_float = cif->abi == FFI_N64_SOFT_FLOAT
     || cif->abi == FFI_N32_SOFT_FLOAT;
   avalue = alloca (cif->nargs * sizeof (ffi_arg));
@@ -1040,11 +1057,49 @@ ffi_closure_mips_inner_N32 (ffi_closure *closure,
     }
 
   /* Invoke the closure. */
-  (closure->fun) (cif, rvalue, avaluep, closure->user_data);
+  fun (cif, rvalue, avaluep, user_data);
 
   return cif->flags >> (FFI_FLAG_BITS * 8);
 }
 
 #endif /* FFI_MIPS_N32 */
 
+#if defined(FFI_MIPS_O32)
+extern void ffi_closure_O32(void);
+extern void ffi_go_closure_O32(void);
+#else
+extern void ffi_closure_N32(void);
+extern void ffi_go_closure_N32(void);
+#endif /* FFI_MIPS_O32 */
+
+ffi_status
+ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
+                    void (*fun)(ffi_cif*,void*,void**,void*))
+{
+  void * fn;
+
+#if defined(FFI_MIPS_O32)
+  if (cif->abi != FFI_O32 && cif->abi != FFI_O32_SOFT_FLOAT)
+    return FFI_BAD_ABI;
+  fn = ffi_go_closure_O32;
+#else
+#if _MIPS_SIM ==_ABIN32
+  if (cif->abi != FFI_N32
+      && cif->abi != FFI_N32_SOFT_FLOAT)
+    return FFI_BAD_ABI;
+#else
+  if (cif->abi != FFI_N64
+      && cif->abi != FFI_N64_SOFT_FLOAT)
+    return FFI_BAD_ABI;
+#endif
+  fn = ffi_go_closure_N32;
+#endif /* FFI_MIPS_O32 */
+
+  closure->tramp = (void *)fn;
+  closure->cif = cif;
+  closure->fun = fun;
+
+  return FFI_OK;
+}
+
 #endif /* FFI_CLOSURES */
index 717d659..cc89a84 100644 (file)
@@ -231,12 +231,14 @@ typedef enum ffi_abi {
 
 #if defined(FFI_MIPS_O32)
 #define FFI_CLOSURES 1
+#define FFI_GO_CLOSURES 1
 #define FFI_TRAMPOLINE_SIZE 20
 #else
 /* N32/N64. */
 # define FFI_CLOSURES 1
+#define FFI_GO_CLOSURES 1
 #if _MIPS_SIM==_ABI64
-#define FFI_TRAMPOLINE_SIZE 52
+#define FFI_TRAMPOLINE_SIZE 56
 #else
 #define FFI_TRAMPOLINE_SIZE 20
 #endif
index c6985d3..b402c88 100644 (file)
 #define flags   a3
 #define raddr    a4
 #define fn       a5
+#define closure  a6
 
-#define SIZEOF_FRAME   ( 8 * FFI_SIZEOF_ARG )
+/* Note: to keep stack 16 byte aligned we need even number slots 
+   used 9 slots here
+*/
+#define SIZEOF_FRAME   ( 10 * FFI_SIZEOF_ARG )
 
 #ifdef __GNUC__
        .abicalls
        .globl  ffi_call_N32
        .ent    ffi_call_N32
 ffi_call_N32:  
-.LFB3:
+.LFB0:
        .frame  $fp, SIZEOF_FRAME, ra
        .mask   0xc0000000,-FFI_SIZEOF_ARG
        .fmask  0x00000000,0
 
        # Prologue
        SUBU    $sp, SIZEOF_FRAME                       # Frame size
-.LCFI0:
+.LCFI00:
        REG_S   $fp, SIZEOF_FRAME - 2*FFI_SIZEOF_ARG($sp)       # Save frame pointer
        REG_S   ra, SIZEOF_FRAME - 1*FFI_SIZEOF_ARG($sp)        # Save return address
-.LCFI1:
+.LCFI01:
        move    $fp, $sp
-.LCFI3:
+.LCFI02:
        move    t9, callback    # callback function pointer
        REG_S   bytes, 2*FFI_SIZEOF_ARG($fp) # bytes
        REG_S   flags, 3*FFI_SIZEOF_ARG($fp) # flags
        REG_S   raddr, 4*FFI_SIZEOF_ARG($fp) # raddr
        REG_S   fn,    5*FFI_SIZEOF_ARG($fp) # fn
+       REG_S   closure, 6*FFI_SIZEOF_ARG($fp) # closure
 
        # Allocate at least 4 words in the argstack
        move    v0, bytes
@@ -198,6 +203,9 @@ callit:
        # Load the function pointer
        REG_L   t9, 5*FFI_SIZEOF_ARG($fp)
 
+       # install the static chain(t7=$15)
+       REG_L   t7, 6*FFI_SIZEOF_ARG($fp)
+
        # If the return value pointer is NULL, assume no return value.
        REG_L   t5, 4*FFI_SIZEOF_ARG($fp)
        beqz    t5, noretval
@@ -346,7 +354,7 @@ epilogue:
        ADDU    $sp, SIZEOF_FRAME                     # Fix stack pointer
        j       ra
 
-.LFE3:
+.LFE0:
        .end    ffi_call_N32
 
 /* ffi_closure_N32. Expects address of the passed-in ffi_closure in t0
@@ -406,6 +414,41 @@ epilogue:
 #define GP_OFF2                (0  * FFI_SIZEOF_ARG)
 
        .align  2
+       .globl  ffi_go_closure_N32
+       .ent    ffi_go_closure_N32
+ffi_go_closure_N32:
+.LFB1:
+       .frame  $sp, SIZEOF_FRAME2, ra
+       .mask   0x90000000,-(SIZEOF_FRAME2 - RA_OFF2)
+       .fmask  0x00000000,0
+       SUBU    $sp, SIZEOF_FRAME2
+.LCFI10:
+       .cpsetup t9, GP_OFF2, ffi_go_closure_N32
+       REG_S   ra, RA_OFF2($sp)        # Save return address
+.LCFI11:
+
+       REG_S   a0, A0_OFF2($sp)
+       REG_S   a1, A1_OFF2($sp)
+       REG_S   a2, A2_OFF2($sp)
+       REG_S   a3, A3_OFF2($sp)
+       REG_S   a4, A4_OFF2($sp)
+       REG_S   a5, A5_OFF2($sp)
+
+       # Call ffi_closure_mips_inner_N32 to do the real work.
+       LA      t9, ffi_closure_mips_inner_N32
+       REG_L   a0, 8($15)   # cif
+       REG_L   a1, 16($15) # fun
+       move    a2, t7                     # userdata=closure
+       ADDU    a3, $sp, V0_OFF2           # rvalue
+       ADDU    a4, $sp, A0_OFF2           # ar
+       ADDU    a5, $sp, F12_OFF2          # fpr
+
+       b       $do_closure
+
+.LFE1: 
+       .end    ffi_go_closure_N32
+
+       .align  2
        .globl  ffi_closure_N32
        .ent    ffi_closure_N32
 ffi_closure_N32:
@@ -414,18 +457,29 @@ ffi_closure_N32:
        .mask   0x90000000,-(SIZEOF_FRAME2 - RA_OFF2)
        .fmask  0x00000000,0
        SUBU    $sp, SIZEOF_FRAME2
-.LCFI5:
+.LCFI20:
        .cpsetup t9, GP_OFF2, ffi_closure_N32
        REG_S   ra, RA_OFF2($sp)        # Save return address
-.LCFI6:
-       # Store all possible argument registers. If there are more than
-       # fit in registers, then they were stored on the stack.
+.LCFI21:
        REG_S   a0, A0_OFF2($sp)
        REG_S   a1, A1_OFF2($sp)
        REG_S   a2, A2_OFF2($sp)
        REG_S   a3, A3_OFF2($sp)
        REG_S   a4, A4_OFF2($sp)
        REG_S   a5, A5_OFF2($sp)
+
+       # Call ffi_closure_mips_inner_N32 to do the real work.
+       LA      t9, ffi_closure_mips_inner_N32
+       REG_L   a0, 56($12)   # cif
+       REG_L   a1, 64($12)   # fun
+       REG_L   a2, 72($12) # user_data
+       ADDU    a3, $sp, V0_OFF2
+       ADDU    a4, $sp, A0_OFF2
+       ADDU    a5, $sp, F12_OFF2
+
+$do_closure:
+       # Store all possible argument registers. If there are more than
+       # fit in registers, then they were stored on the stack.
        REG_S   a6, A6_OFF2($sp)
        REG_S   a7, A7_OFF2($sp)
 
@@ -439,12 +493,6 @@ ffi_closure_N32:
        s.d     $f18, F18_OFF2($sp)
        s.d     $f19, F19_OFF2($sp)
 
-       # Call ffi_closure_mips_inner_N32 to do the real work.
-       LA      t9, ffi_closure_mips_inner_N32
-       move    a0, $12  # Pointer to the ffi_closure
-       ADDU    a1, $sp, V0_OFF2
-       ADDU    a2, $sp, A0_OFF2
-       ADDU    a3, $sp, F12_OFF2
        jalr    t9
 
        # Return flags are in v0
@@ -531,46 +579,66 @@ cls_epilogue:
         .align  EH_FRAME_ALIGN
 .LECIE1:
 
-.LSFDE1:
-        .4byte  .LEFDE1-.LASFDE1       # length.
-.LASFDE1:
-        .4byte  .LASFDE1-.Lframe1      # CIE_pointer.
-        FDE_ADDR_BYTES  .LFB3          # initial_location.
-        FDE_ADDR_BYTES  .LFE3-.LFB3    # address_range.
+.LSFDE0:
+        .4byte  .LEFDE0-.LASFDE0       # length.
+.LASFDE0:
+        .4byte  .LASFDE0-.Lframe1      # CIE_pointer.
+        FDE_ADDR_BYTES  .LFB0          # initial_location.
+        FDE_ADDR_BYTES  .LFE0-.LFB0    # address_range.
         .byte   0x4                    # DW_CFA_advance_loc4
-        .4byte  .LCFI0-.LFB3           # to .LCFI0
+        .4byte  .LCFI00-.LFB0          # to .LCFI00
         .byte   0xe                    # DW_CFA_def_cfa_offset
         .uleb128 SIZEOF_FRAME          # adjust stack.by SIZEOF_FRAME
         .byte   0x4                    # DW_CFA_advance_loc4
-        .4byte  .LCFI1-.LCFI0          # to .LCFI1
+        .4byte  .LCFI01-.LCFI00                # to .LCFI01
         .byte   0x9e                   # DW_CFA_offset of $fp
         .uleb128 2*FFI_SIZEOF_ARG/4    # 
         .byte   0x9f                   # DW_CFA_offset of ra
         .uleb128 1*FFI_SIZEOF_ARG/4    # 
         .byte   0x4                    # DW_CFA_advance_loc4
-        .4byte  .LCFI3-.LCFI1          # to .LCFI3
+        .4byte  .LCFI02-.LCFI01                # to .LCFI02
         .byte   0xd                    # DW_CFA_def_cfa_register
         .uleb128 0x1e                  # in $fp
         .align  EH_FRAME_ALIGN
+.LEFDE0:
+
+.LSFDE1:
+       .4byte  .LEFDE1-.LASFDE1        # length
+.LASFDE1:
+       .4byte  .LASFDE1-.Lframe1       # CIE_pointer.
+       FDE_ADDR_BYTES  .LFB1           # initial_location.
+       FDE_ADDR_BYTES  .LFE1-.LFB1     # address_range.
+       .byte   0x4                     # DW_CFA_advance_loc4
+       .4byte  .LCFI10-.LFB1           # to .LCFI10
+       .byte   0xe                     # DW_CFA_def_cfa_offset
+       .uleb128 SIZEOF_FRAME2          # adjust stack.by SIZEOF_FRAME
+       .byte   0x4                     # DW_CFA_advance_loc4
+       .4byte  .LCFI11-.LCFI10         # to .LCFI11
+       .byte   0x9c                    # DW_CFA_offset of $gp ($28)
+       .uleb128 (SIZEOF_FRAME2 - GP_OFF2)/4
+       .byte   0x9f                    # DW_CFA_offset of ra ($31)
+       .uleb128 (SIZEOF_FRAME2 - RA_OFF2)/4
+       .align  EH_FRAME_ALIGN
 .LEFDE1:
-.LSFDE3:
-       .4byte  .LEFDE3-.LASFDE3        # length
-.LASFDE3:
-       .4byte  .LASFDE3-.Lframe1       # CIE_pointer.
+
+.LSFDE2:
+       .4byte  .LEFDE2-.LASFDE2        # length
+.LASFDE2:
+       .4byte  .LASFDE2-.Lframe1       # CIE_pointer.
        FDE_ADDR_BYTES  .LFB2           # initial_location.
        FDE_ADDR_BYTES  .LFE2-.LFB2     # address_range.
        .byte   0x4                     # DW_CFA_advance_loc4
-       .4byte  .LCFI5-.LFB2            # to .LCFI5
+       .4byte  .LCFI20-.LFB2           # to .LCFI20
        .byte   0xe                     # DW_CFA_def_cfa_offset
        .uleb128 SIZEOF_FRAME2          # adjust stack.by SIZEOF_FRAME
        .byte   0x4                     # DW_CFA_advance_loc4
-       .4byte  .LCFI6-.LCFI5           # to .LCFI6
+       .4byte  .LCFI21-.LCFI20         # to .LCFI21
        .byte   0x9c                    # DW_CFA_offset of $gp ($28)
        .uleb128 (SIZEOF_FRAME2 - GP_OFF2)/4
        .byte   0x9f                    # DW_CFA_offset of ra ($31)
        .uleb128 (SIZEOF_FRAME2 - RA_OFF2)/4
        .align  EH_FRAME_ALIGN
-.LEFDE3:
+.LEFDE2:
 #endif /* __GNUC__ */  
        
 #endif
index eb27981..69324e6 100644 (file)
@@ -50,14 +50,14 @@ ffi_call_O32:
 $LFB0:
        # Prologue
        SUBU    $sp, SIZEOF_FRAME       # Frame size
-$LCFI0:
+$LCFI00:
        REG_S   $fp, FP_OFF($sp)        # Save frame pointer
-$LCFI1:
+$LCFI01:
        REG_S   ra, RA_OFF($sp)         # Save return address
-$LCFI2:
+$LCFI02:
        move    $fp, $sp
 
-$LCFI3:
+$LCFI03:
        move    t9, callback            # callback function pointer
        REG_S   flags, A3_OFF($fp)      # flags
 
@@ -132,6 +132,9 @@ pass_f_d:
        l.d     $f14, 2*FFI_SIZEOF_ARG($sp)     # passing double and float
 
 call_it:       
+       # Load the static chain pointer
+       REG_L   t7, SIZEOF_FRAME + 6*FFI_SIZEOF_ARG($fp)
+
        # Load the function pointer
        REG_L   t9, SIZEOF_FRAME + 5*FFI_SIZEOF_ARG($fp)
 
@@ -204,13 +207,15 @@ $LFE0:
        -8 - f14 (le low, be high)
        -9 - f12 (le high, be low)
        -10 - f12 (le low, be high)
-       -11 - Called function a3 save
-       -12 - Called function a2 save
-       -13 - Called function a1 save
-       -14 - Called function a0 save, our sp and fp point here
+       -11 - Called function a5 save
+       -12 - Called function a4 save
+       -13 - Called function a3 save
+       -14 - Called function a2 save
+       -15 - Called function a1 save
+       -16 - Called function a0 save, our sp and fp point here
         */
        
-#define SIZEOF_FRAME2  (14 * FFI_SIZEOF_ARG)
+#define SIZEOF_FRAME2  (16 * FFI_SIZEOF_ARG)
 #define A3_OFF2                (SIZEOF_FRAME2 + 3 * FFI_SIZEOF_ARG)
 #define A2_OFF2                (SIZEOF_FRAME2 + 2 * FFI_SIZEOF_ARG)
 #define A1_OFF2                (SIZEOF_FRAME2 + 1 * FFI_SIZEOF_ARG)
@@ -225,13 +230,71 @@ $LFE0:
 #define FA_1_0_OFF2    (SIZEOF_FRAME2 - 8 * FFI_SIZEOF_ARG)
 #define FA_0_1_OFF2    (SIZEOF_FRAME2 - 9 * FFI_SIZEOF_ARG)
 #define FA_0_0_OFF2    (SIZEOF_FRAME2 - 10 * FFI_SIZEOF_ARG)
+#define CALLED_A5_OFF2  (SIZEOF_FRAME2 - 11 * FFI_SIZEOF_ARG)
+#define CALLED_A4_OFF2  (SIZEOF_FRAME2 - 12 * FFI_SIZEOF_ARG)
 
        .text
+
+       .align  2
+       .globl  ffi_go_closure_O32
+       .ent    ffi_go_closure_O32
+ffi_go_closure_O32:
+$LFB1:
+       # Prologue
+       .frame  $fp, SIZEOF_FRAME2, ra
+       .set    noreorder
+       .cpload t9
+       .set    reorder
+       SUBU    $sp, SIZEOF_FRAME2
+       .cprestore GP_OFF2
+$LCFI10:
+
+       REG_S   $16, S0_OFF2($sp)        # Save s0
+       REG_S   $fp, FP_OFF2($sp)        # Save frame pointer
+       REG_S   ra, RA_OFF2($sp)         # Save return address
+$LCFI11:
+
+       move    $fp, $sp
+$LCFI12:
+
+       REG_S   a0, A0_OFF2($fp)
+       REG_S   a1, A1_OFF2($fp)
+       REG_S   a2, A2_OFF2($fp)
+       REG_S   a3, A3_OFF2($fp)
+
+       # Load ABI enum to s0
+       REG_L   $16, 4($15)     # cif 
+       REG_L   $16, 0($16)     # abi is first member.
+
+       li      $13, 1          # FFI_O32
+       bne     $16, $13, 1f    # Skip fp save if FFI_O32_SOFT_FLOAT
+       
+       # Store all possible float/double registers.
+       s.d     $f12, FA_0_0_OFF2($fp)
+       s.d     $f14, FA_1_0_OFF2($fp)
+1:
+       # prepare arguments for ffi_closure_mips_inner_O32
+       REG_L   a0, 4($15)       # cif 
+       REG_L   a1, 8($15)       # fun
+       move    a2, $15          # user_data = go closure
+       addu    a3, $fp, V0_OFF2 # rvalue
+
+       addu    t9, $fp, A0_OFF2 # ar
+       REG_S   t9, CALLED_A4_OFF2($fp)
+
+       addu    t9, $fp, FA_0_0_OFF2 #fpr
+       REG_S   t9, CALLED_A5_OFF2($fp)
+
+       b $do_closure
+
+$LFE1:
+       .end ffi_go_closure_O32
+
        .align  2
        .globl  ffi_closure_O32
        .ent    ffi_closure_O32
 ffi_closure_O32:
-$LFB1:
+$LFB2:
        # Prologue
        .frame  $fp, SIZEOF_FRAME2, ra
        .set    noreorder
@@ -239,14 +302,14 @@ $LFB1:
        .set    reorder
        SUBU    $sp, SIZEOF_FRAME2
        .cprestore GP_OFF2
-$LCFI4:
+$LCFI20:
        REG_S   $16, S0_OFF2($sp)        # Save s0
        REG_S   $fp, FP_OFF2($sp)        # Save frame pointer
        REG_S   ra, RA_OFF2($sp)         # Save return address
-$LCFI6:
+$LCFI21:
        move    $fp, $sp
 
-$LCFI7:
+$LCFI22:
        # Store all possible argument registers. If there are more than
        # four arguments, then they are stored above where we put a3.
        REG_S   a0, A0_OFF2($fp)
@@ -265,12 +328,21 @@ $LCFI7:
        s.d     $f12, FA_0_0_OFF2($fp)
        s.d     $f14, FA_1_0_OFF2($fp)
 1:     
-       # Call ffi_closure_mips_inner_O32 to do the work.
+       # prepare arguments for ffi_closure_mips_inner_O32
+       REG_L   a0, 20($12)      # cif pointer follows tramp.
+       REG_L   a1, 24($12)      # fun
+       REG_L   a2, 28($12)      # user_data
+       addu    a3, $fp, V0_OFF2 # rvalue
+
+       addu    t9, $fp, A0_OFF2 # ar
+       REG_S   t9, CALLED_A4_OFF2($fp)
+
+       addu    t9, $fp, FA_0_0_OFF2 #fpr
+       REG_S   t9, CALLED_A5_OFF2($fp)
+
+$do_closure:
        la      t9, ffi_closure_mips_inner_O32
-       move    a0, $12  # Pointer to the ffi_closure
-       addu    a1, $fp, V0_OFF2
-       addu    a2, $fp, A0_OFF2
-       addu    a3, $fp, FA_0_0_OFF2
+       # Call ffi_closure_mips_inner_O32 to do the work.
        jalr    t9
 
        # Load the return value into the appropriate register.
@@ -300,7 +372,7 @@ closure_done:
        REG_L   ra,  RA_OFF2($sp)        # Restore return address
        ADDU    $sp, SIZEOF_FRAME2
        j       ra
-$LFE1:
+$LFE2:
        .end    ffi_closure_O32
 
 /* DWARF-2 unwind info. */
@@ -322,6 +394,7 @@ $LSCIE0:
        .uleb128 0x0
        .align  2
 $LECIE0:
+
 $LSFDE0:
        .4byte  $LEFDE0-$LASFDE0         # FDE Length
 $LASFDE0:
@@ -330,11 +403,11 @@ $LASFDE0:
        .4byte  $LFE0-$LFB0      # FDE address range
        .uleb128 0x0     # Augmentation size
        .byte   0x4      # DW_CFA_advance_loc4
-       .4byte  $LCFI0-$LFB0
+       .4byte  $LCFI00-$LFB0
        .byte   0xe      # DW_CFA_def_cfa_offset
        .uleb128 0x18
        .byte   0x4      # DW_CFA_advance_loc4
-       .4byte  $LCFI2-$LCFI0
+       .4byte  $LCFI01-$LCFI00
        .byte   0x11     # DW_CFA_offset_extended_sf
        .uleb128 0x1e    # $fp
        .sleb128 -2      # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
@@ -342,12 +415,13 @@ $LASFDE0:
        .uleb128 0x1f    # $ra
        .sleb128 -1      # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
        .byte   0x4      # DW_CFA_advance_loc4
-       .4byte  $LCFI3-$LCFI2
+       .4byte  $LCFI02-$LCFI01
        .byte   0xc      # DW_CFA_def_cfa
        .uleb128 0x1e
        .uleb128 0x18
        .align  2
 $LEFDE0:
+
 $LSFDE1:
        .4byte  $LEFDE1-$LASFDE1         # FDE Length
 $LASFDE1:
@@ -356,11 +430,11 @@ $LASFDE1:
        .4byte  $LFE1-$LFB1      # FDE address range
        .uleb128 0x0     # Augmentation size
        .byte   0x4      # DW_CFA_advance_loc4
-       .4byte  $LCFI4-$LFB1
+       .4byte  $LCFI10-$LFB1
        .byte   0xe      # DW_CFA_def_cfa_offset
-       .uleb128 0x38
+       .uleb128 SIZEOF_FRAME2
        .byte   0x4      # DW_CFA_advance_loc4
-       .4byte  $LCFI6-$LCFI4
+       .4byte  $LCFI11-$LCFI10
        .byte   0x11     # DW_CFA_offset_extended_sf
        .uleb128 0x10    # $16
        .sleb128 -3      # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp)
@@ -371,11 +445,41 @@ $LASFDE1:
        .uleb128 0x1f    # $ra
        .sleb128 -1      # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
        .byte   0x4      # DW_CFA_advance_loc4
-       .4byte  $LCFI7-$LCFI6
+       .4byte  $LCFI12-$LCFI11
        .byte   0xc      # DW_CFA_def_cfa
        .uleb128 0x1e
-       .uleb128 0x38
+       .uleb128 SIZEOF_FRAME2
        .align  2
 $LEFDE1:
 
+$LSFDE2:
+       .4byte  $LEFDE2-$LASFDE2         # FDE Length
+$LASFDE2:
+       .4byte  $LASFDE2-$Lframe0        # FDE CIE offset
+       .4byte  $LFB2    # FDE initial location
+       .4byte  $LFE2-$LFB2      # FDE address range
+       .uleb128 0x0     # Augmentation size
+       .byte   0x4      # DW_CFA_advance_loc4
+       .4byte  $LCFI20-$LFB2
+       .byte   0xe      # DW_CFA_def_cfa_offset
+       .uleb128 SIZEOF_FRAME2
+       .byte   0x4      # DW_CFA_advance_loc4
+       .4byte  $LCFI21-$LCFI20
+       .byte   0x11     # DW_CFA_offset_extended_sf
+       .uleb128 0x10    # $16
+       .sleb128 -3      # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp)
+       .byte   0x11     # DW_CFA_offset_extended_sf
+       .uleb128 0x1e    # $fp
+       .sleb128 -2      # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
+       .byte   0x11     # DW_CFA_offset_extended_sf
+       .uleb128 0x1f    # $ra
+       .sleb128 -1      # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
+       .byte   0x4      # DW_CFA_advance_loc4
+       .4byte  $LCFI22-$LCFI21
+       .byte   0xc      # DW_CFA_def_cfa
+       .uleb128 0x1e
+       .uleb128 SIZEOF_FRAME2
+       .align  2
+$LEFDE2:
+
 #endif
index 1b8b008..0d74627 100644 (file)
@@ -182,7 +182,7 @@ proc libffi_target_compile { source dest type options } {
     # 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];
+       lappend  options "additional_flags=$TOOL_OPTIONS"
     }
 
     # search for ffi_mips.h in srcdir, too
diff --git a/testsuite/libffi.call/align_mixed.c b/testsuite/libffi.call/align_mixed.c
new file mode 100644 (file)
index 0000000..5d4959c
--- /dev/null
@@ -0,0 +1,46 @@
+/* Area:       ffi_call
+   Purpose:    Check for proper argument alignment.
+   Limitations:        none.
+   PR:         none.
+   Originator: <twalljava@java.net> (from many_win32.c) */
+
+/* { dg-do run } */
+
+#include "ffitest.h"
+
+static float ABI_ATTR align_arguments(int i1,
+                                      double f2,
+                                      int i3,
+                                      double f4)
+{
+  return i1+f2+i3+f4;
+}
+
+int main(void)
+{
+  ffi_cif cif;
+  ffi_type *args[4] = {
+    &ffi_type_sint,
+    &ffi_type_double,
+    &ffi_type_sint,
+    &ffi_type_double
+  };
+  double fa[2] = {1,2};
+  int ia[2] = {1,2};
+  void *values[4] = {&ia[0], &fa[0], &ia[1], &fa[1]};
+  float f, ff;
+
+  /* Initialize the cif */
+  CHECK(ffi_prep_cif(&cif, ABI_NUM, 4,
+                    &ffi_type_float, args) == FFI_OK);
+
+  ff = align_arguments(ia[0], fa[0], ia[1], fa[1]);
+
+  ffi_call(&cif, FFI_FN(align_arguments), &f, values);
+
+  if (f == ff)
+    printf("align arguments tests ok!\n");
+  else
+    CHECK(0);
+  exit(0);
+}
index 46fb1eb..bc2bdc3 100644 (file)
@@ -19,7 +19,7 @@ libffi-init
 
 global srcdir subdir
 
-set tlist [lsearch -inline -all -not -glob [lsort [glob -nocomplain -- $srcdir/$subdir/*.{c,cc}]] *complex*]
+set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.{c,cc}]]
 
 run-many-tests $tlist ""