Moved ia64 files
authorgreen <green>
Sun, 22 Apr 2001 18:10:20 +0000 (18:10 +0000)
committergreen <green>
Sun, 22 Apr 2001 18:10:20 +0000 (18:10 +0000)
libffi/ia64/ffi.c [new file with mode: 0644]
libffi/ia64/ia64_flags.h [new file with mode: 0644]
libffi/ia64/unix.S [new file with mode: 0644]

diff --git a/libffi/ia64/ffi.c b/libffi/ia64/ffi.c
new file mode 100644 (file)
index 0000000..bb4fbb7
--- /dev/null
@@ -0,0 +1,670 @@
+/* -----------------------------------------------------------------------
+   ffi.c - Copyright (c) 1998 Cygnus Solutions
+          Copyright (c) 2000 Hewlett Packard Company
+   
+   IA64 Foreign Function Interface 
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   ``Software''), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be included
+   in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+   ----------------------------------------------------------------------- */
+
+#include <ffi.h>
+#include <ffi_common.h>
+
+#include <stdlib.h>
+
+#include "ia64_flags.h"
+
+/* Memory image of fp register contents.  Should eventually be an fp   */
+/* type long enough to hold an entire register.  For now we use double.        */
+typedef double float80;
+
+/* The stack layout at call to ffi_prep_regs.  Other_args will remain  */
+/* on the stack for the actual call.  Everything else we be transferred        */
+/* to registers and popped by the assembly code.                       */
+
+struct ia64_args {
+    long scratch[2];   /* Two scratch words at top of stack.           */
+                       /* Allows sp to passed as arg pointer.          */
+    void * r8_contents;        /* Value to be passed in r8                     */
+    long spare;                /* Not used.                                    */
+    float80 fp_regs[8]; /* Contents of 8 floating point argument       */
+                       /* registers.                                   */
+    long out_regs[8];  /* Contents of the 8 out registers used         */
+                       /* for integer parameters.                      */
+    long other_args[0]; /* Arguments passed on stack, variable size    */
+                       /* Treated as continuation of out_regs.         */
+};
+
+static size_t float_type_size(unsigned short tp)
+{
+  switch(tp) {
+    case FFI_TYPE_FLOAT:
+      return sizeof(float);
+    case FFI_TYPE_DOUBLE:
+      return sizeof(double);
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+    case FFI_TYPE_LONGDOUBLE:
+      return sizeof(long double);
+#endif
+    default:
+      FFI_ASSERT(0);
+  }
+}
+
+/*
+ * Is type a struct containing at most n floats, doubles, or extended
+ * doubles, all of the same fp type?
+ * If so, set *element_type to the fp type.
+ */
+static bool is_homogeneous_fp_aggregate(ffi_type * type, int n,
+                                       unsigned short * element_type)
+{
+  ffi_type **ptr; 
+  unsigned short element, struct_element;
+
+  int type_set = 0;
+
+  FFI_ASSERT(type != NULL);
+
+  FFI_ASSERT(type->elements != NULL);
+
+  ptr = &(type->elements[0]);
+
+  while ((*ptr) != NULL)
+    {
+      switch((*ptr) -> type) {
+       case FFI_TYPE_FLOAT:
+         if (type_set && element != FFI_TYPE_FLOAT) return 0;
+         if (--n < 0) return FALSE;
+         type_set = 1;
+         element = FFI_TYPE_FLOAT;
+         break;
+       case FFI_TYPE_DOUBLE:
+         if (type_set && element != FFI_TYPE_DOUBLE) return 0;
+         if (--n < 0) return FALSE;
+         type_set = 1;
+         element = FFI_TYPE_DOUBLE;
+         break;
+       case FFI_TYPE_STRUCT:
+         if (!is_homogeneous_fp_aggregate(type, n, &struct_element))
+             return FALSE;
+         if (type_set && struct_element != element) return FALSE;
+         n -= (type -> size)/float_type_size(element);
+         element = struct_element;
+         if (n < 0) return FALSE;
+         break;
+       /* case FFI_TYPE_LONGDOUBLE:
+         Not yet implemented.  */
+       default:
+         return FALSE;
+      }
+      ptr++;
+    }
+  *element_type = element;
+  return TRUE;
+   
+} 
+
+/* ffi_prep_args is called by the assembly routine once stack space
+   has been allocated for the function's arguments.  Returns nonzero
+   if fp registers are used for arguments. */
+
+static bool
+ffi_prep_args(struct ia64_args *stack, extended_cif *ecif, int bytes)
+{
+  register long i, avn;
+  register void **p_argv;
+  register long *argp = stack -> out_regs;
+  register float80 *fp_argp = stack -> fp_regs;
+  register ffi_type **p_arg;
+
+  /* For big return structs, r8 needs to contain the target address.   */
+  /* Since r8 is otherwise dead, we set it unconditionally.            */
+  stack -> r8_contents = ecif -> rvalue;
+  i = 0;
+  avn = ecif->cif->nargs;
+  p_arg = ecif->cif->arg_types;
+  p_argv = ecif->avalue;
+  while (i < avn)
+    {
+      size_t z; /* z is in units of arg slots or words, not bytes.     */
+
+      switch ((*p_arg)->type)
+       {
+       case FFI_TYPE_SINT8:
+         z = 1;
+         *(SINT64 *) argp = *(SINT8 *)(* p_argv);
+         break;
+                 
+       case FFI_TYPE_UINT8:
+         z = 1;
+         *(UINT64 *) argp = *(UINT8 *)(* p_argv);
+         break;
+                 
+       case FFI_TYPE_SINT16:
+         z = 1;
+         *(SINT64 *) argp = *(SINT16 *)(* p_argv);
+         break;
+                 
+       case FFI_TYPE_UINT16:
+         z = 1;
+         *(UINT64 *) argp = *(UINT16 *)(* p_argv);
+         break;
+                 
+       case FFI_TYPE_SINT32:
+         z = 1;
+         *(SINT64 *) argp = *(SINT32 *)(* p_argv);
+         break;
+                 
+       case FFI_TYPE_UINT32:
+         z = 1;
+         *(UINT64 *) argp = *(UINT32 *)(* p_argv);
+         break;
+
+       case FFI_TYPE_SINT64:
+       case FFI_TYPE_UINT64:
+       case FFI_TYPE_POINTER:
+         z = 1;
+         *(UINT64 *) argp = *(UINT64 *)(* p_argv);
+         break;
+
+       case FFI_TYPE_FLOAT:
+         z = 1;
+         if (fp_argp - stack->fp_regs < 8)
+           {
+             /* Note the conversion -- all the fp regs are loaded as
+                doubles.  */
+             *fp_argp++ = *(float *)(* p_argv);
+           }
+         /* Also put it into the integer registers or memory: */
+           *(UINT64 *) argp = *(UINT32 *)(* p_argv);
+         break;
+
+       case FFI_TYPE_DOUBLE:
+         z = 1;
+         if (fp_argp - stack->fp_regs < 8)
+           *fp_argp++ = *(double *)(* p_argv);
+         /* Also put it into the integer registers or memory: */
+           *(double *) argp = *(double *)(* p_argv);
+         break;
+
+       case FFI_TYPE_STRUCT:
+         {
+             size_t sz = (*p_arg)->size;
+             unsigned short element_type;
+              z = ((*p_arg)->size + SIZEOF_ARG - 1)/SIZEOF_ARG;
+             if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) {
+               int i;
+               int nelements = sz/float_type_size(element_type);
+               for (i = 0; i < nelements; ++i) {
+                 switch (element_type) {
+                   case FFI_TYPE_FLOAT:
+                     if (fp_argp - stack->fp_regs < 8)
+                       *fp_argp++ = ((float *)(* p_argv))[i];
+                     break;
+                   case FFI_TYPE_DOUBLE:
+                     if (fp_argp - stack->fp_regs < 8)
+                       *fp_argp++ = ((double *)(* p_argv))[i];
+                     break;
+                   default:
+                       /* Extended precision not yet implemented. */
+                       abort();
+                 }
+               }
+             }
+             /* And pass it in integer registers as a struct, with     */
+             /* its actual field sizes packed into registers.          */
+             memcpy(argp, *p_argv, (*p_arg)->size);
+         }
+         break;
+
+       default:
+         FFI_ASSERT(0);
+       }
+
+      argp += z;
+      i++, p_arg++, p_argv++;
+    }
+  return (fp_argp != stack -> fp_regs);
+}
+
+/* Perform machine dependent cif processing */
+ffi_status
+ffi_prep_cif_machdep(ffi_cif *cif)
+{
+  long i, avn;
+  bool is_simple = TRUE;
+  long simple_flag = FFI_SIMPLE_V;
+  /* Adjust cif->bytes to include space for the 2 scratch words,
+     r8 register contents, spare word,
+     the 8 fp register contents, and all 8 integer register contents.
+     This will be removed before the call, though 2 scratch words must
+     remain.  */
+
+  cif->bytes += 4*sizeof(long) + 8 *sizeof(float80);
+  if (cif->bytes < sizeof(struct ia64_args))
+    cif->bytes = sizeof(struct ia64_args);
+
+  /* The stack must be double word aligned, so round bytes up
+     appropriately. */
+
+  cif->bytes = ALIGN(cif->bytes, 2*sizeof(void*));
+
+  avn = cif->nargs;
+  if (avn <= 2) {
+    for (i = 0; i < avn; ++i) {
+      switch(cif -> arg_types[i] -> type) {
+       case FFI_TYPE_SINT32:
+         simple_flag = FFI_ADD_INT_ARG(simple_flag);
+         break;
+       case FFI_TYPE_SINT64:
+       case FFI_TYPE_UINT64:
+       case FFI_TYPE_POINTER:
+         simple_flag = FFI_ADD_LONG_ARG(simple_flag);
+         break;
+       default:
+         is_simple = FALSE;
+      }
+    }
+  } else {
+    is_simple = FALSE;
+  }
+
+  /* Set the return type flag */
+  switch (cif->rtype->type)
+    {
+    case FFI_TYPE_VOID:
+      cif->flags = FFI_TYPE_VOID;
+      break;
+
+    case FFI_TYPE_STRUCT:
+      {
+        size_t sz = cif -> rtype -> size;
+       unsigned short element_type;
+
+       is_simple = FALSE;
+       if (is_homogeneous_fp_aggregate(cif -> rtype, 8, &element_type)) {
+         int nelements = sz/float_type_size(element_type);
+         if (nelements <= 1) {
+           if (0 == nelements) {
+             cif -> flags = FFI_TYPE_VOID;
+           } else {
+             cif -> flags = element_type;
+           }
+         } else {
+           switch(element_type) {
+             case FFI_TYPE_FLOAT:
+               cif -> flags = FFI_IS_FLOAT_FP_AGGREGATE | nelements;
+               break;
+             case FFI_TYPE_DOUBLE:
+               cif -> flags = FFI_IS_DOUBLE_FP_AGGREGATE | nelements;
+               break;
+             default:
+               /* long double NYI */
+               abort();
+           }
+         }
+         break;
+        }
+        if (sz <= 32) {
+         if (sz <= 8) {
+              cif->flags = FFI_TYPE_INT;
+         } else if (sz <= 16) {
+              cif->flags = FFI_IS_SMALL_STRUCT2;
+         } else if (sz <= 24) {
+              cif->flags = FFI_IS_SMALL_STRUCT3;
+         } else {
+              cif->flags = FFI_IS_SMALL_STRUCT4;
+         }
+        } else {
+          cif->flags = FFI_TYPE_STRUCT;
+       }
+      }
+      break;
+
+    case FFI_TYPE_FLOAT:
+      is_simple = FALSE;
+      cif->flags = FFI_TYPE_FLOAT;
+      break;
+
+    case FFI_TYPE_DOUBLE:
+      is_simple = FALSE;
+      cif->flags = FFI_TYPE_DOUBLE;
+      break;
+
+    default:
+      cif->flags = FFI_TYPE_INT;
+      /* This seems to depend on little endian mode, and the fact that */
+      /* the return pointer always points to at least 8 bytes.  But    */
+      /* that also seems to be true for other platforms.               */
+      break;
+    }
+  
+  if (is_simple) cif -> flags |= simple_flag;
+  return FFI_OK;
+}
+
+extern int ffi_call_unix(bool (*)(struct ia64_args *, extended_cif *, int), 
+                        extended_cif *, unsigned, 
+                        unsigned, unsigned *, void (*)());
+
+void
+ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
+{
+  extended_cif ecif;
+  long simple = cif -> flags & FFI_SIMPLE;
+
+  /* Should this also check for Unix ABI? */
+  /* This is almost, but not quite, machine independent.  Note that    */
+  /* we can get away with not caring about length of the result because        */
+  /* we assume we are little endian, and the result buffer is large    */
+  /* enough.                                                           */
+  /* This needs work for HP/UX.                                                */
+  if (simple) {
+    long (*lfn)() = (long (*)())fn;
+    long result;
+    switch(simple) {
+      case FFI_SIMPLE_V:
+       result = lfn();
+       break;
+      case FFI_SIMPLE_I:
+       result = lfn(*(int *)avalue[0]);
+       break;
+      case FFI_SIMPLE_L:
+       result = lfn(*(long *)avalue[0]);
+       break;
+      case FFI_SIMPLE_II:
+       result = lfn(*(int *)avalue[0], *(int *)avalue[1]);
+       break;
+      case FFI_SIMPLE_IL:
+       result = lfn(*(int *)avalue[0], *(long *)avalue[1]);
+       break;
+      case FFI_SIMPLE_LI:
+       result = lfn(*(long *)avalue[0], *(int *)avalue[1]);
+       break;
+      case FFI_SIMPLE_LL:
+       result = lfn(*(long *)avalue[0], *(long *)avalue[1]);
+       break;
+    }
+    if ((cif->flags & ~FFI_SIMPLE) != FFI_TYPE_VOID && 0 != rvalue) {
+      * (long *)rvalue = result;
+    }
+    return;
+  }
+  ecif.cif = cif;
+  ecif.avalue = avalue;
+  
+  /* If the return value is a struct and we don't have a return
+     value address then we need to make one.  */
+  
+  if (rvalue == NULL && cif->rtype->type == FFI_TYPE_STRUCT)
+    ecif.rvalue = alloca(cif->rtype->size);
+  else
+    ecif.rvalue = rvalue;
+    
+  switch (cif->abi) 
+    {
+    case FFI_UNIX:
+      ffi_call_unix(ffi_prep_args, &ecif, cif->bytes,
+                   cif->flags, rvalue, fn);
+      break;
+
+    default:
+      FFI_ASSERT(0);
+      break;
+    }
+}
+
+/*
+ * Closures represent a pair consisting of a function pointer, and
+ * some user data.  A closure is invoked by reinterpreting the closure
+ * as a function pointer, and branching to it.  Thus we can make an
+ * interpreted function callable as a C function:  We turn the interpreter
+ * itself, together with a pointer specifying the interpreted procedure,
+ * into a closure.
+ * On X86, the first few words of the closure structure actually contain code,
+ * which will do the right thing.  On most other architectures, this
+ * would raise some Icache/Dcache coherence issues (which can be solved, but
+ * often not cheaply).
+ * For IA64, function pointer are already pairs consisting of a code
+ * pointer, and a gp pointer.  The latter is needed to access global variables.
+ * Here we set up such a pair as the first two words of the closure (in
+ * the "trampoline" area), but we replace the gp pointer with a pointer
+ * to the closure itself.  We also add the real gp pointer to the
+ * closure.  This allows the function entry code to both retrieve the
+ * user data, and to restire the correct gp pointer.
+ */
+
+static void 
+ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue,
+                           void **avalue, ffi_cif *cif);
+
+/* This function is entered with the doctored gp (r1) value.
+ * This code is extremely gcc specific.  There is some argument that
+ * it should really be written in assembly code, since it depends on
+ * gcc properties that might change over time.
+ */
+
+/* ffi_closure_UNIX is an assembly routine, which copies the register  */
+/* state into s struct ia64_args, and the invokes                      */
+/* ffi_closure_UNIX_inner.  It also recovers the closure pointer       */
+/* from its fake gp pointer.                                           */
+void ffi_closure_UNIX();
+
+#ifndef __GNUC__
+#   error This requires gcc
+#endif
+void
+ffi_closure_UNIX_inner (ffi_closure *closure, struct ia64_args * args)
+/* Hopefully declarint this as a varargs function will force all args  */
+/* to memory.                                                          */
+{
+  // this is our return value storage
+  long double    res;
+
+  // our various things...
+  ffi_cif       *cif;
+  unsigned short rtype;
+  void          *resp;
+  void         **arg_area;
+
+  resp = (void*)&res;
+  cif         = closure->cif;
+  arg_area    = (void**) alloca (cif->nargs * sizeof (void*));  
+
+  /* this call will initialize ARG_AREA, such that each
+   * element in that array points to the corresponding 
+   * value on the stack; and if the function returns
+   * a structure, it will re-set RESP to point to the
+   * structure return address.  */
+
+  ffi_prep_incoming_args_UNIX(args, (void**)&resp, arg_area, cif);
+  
+  (closure->fun) (cif, resp, arg_area, closure->user_data);
+
+  rtype = cif->flags;
+
+  /* now, do a generic return based on the value of rtype */
+  if (rtype == FFI_TYPE_INT)
+    {
+      asm volatile ("ld8 r8=[%0]" : : "r" (resp) : "r8");
+    }
+  else if (rtype == FFI_TYPE_FLOAT)
+    {
+      asm volatile ("ldfs f8=[%0]" : : "r" (resp) : "f8");
+    }
+  else if (rtype == FFI_TYPE_DOUBLE)
+    {
+      asm volatile ("ldfd f8=[%0]" : : "r" (resp) : "f8");
+    }
+  else if (rtype == FFI_IS_SMALL_STRUCT2)
+    {
+      asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]"
+                   : : "r" (resp), "r" (resp+8) : "r8","r9");
+    }
+  else if (rtype == FFI_IS_SMALL_STRUCT3)
+    {
+      asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]"
+                   : : "r" (resp), "r" (resp+8), "r" (resp+16)
+                   : "r8","r9","r10");
+    }
+  else if (rtype == FFI_IS_SMALL_STRUCT4)
+    {
+      asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]; ld8 r11=[%3]"
+                   : : "r" (resp), "r" (resp+8), "r" (resp+16), "r" (resp+24)
+                   : "r8","r9","r10","r11");
+    }
+  else if (rtype != FFI_TYPE_VOID && rtype != FFI_TYPE_STRUCT)
+    {
+      /* Can only happen for homogeneous FP aggregates?        */
+      abort();
+    }
+}
+
+static void 
+ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue,
+                           void **avalue, ffi_cif *cif)
+{
+  register unsigned int i;
+  register unsigned int avn;
+  register void **p_argv;
+  register unsigned long *argp = args -> out_regs;
+  unsigned fp_reg_num = 0;
+  register ffi_type **p_arg;
+
+  avn = cif->nargs;
+  p_argv = avalue;
+
+  for (i = cif->nargs, p_arg = cif->arg_types; i != 0; i--, p_arg++)
+    {
+      size_t z; /* In units of words or argument slots.        */
+
+      switch ((*p_arg)->type)
+       {
+       case FFI_TYPE_SINT8:
+       case FFI_TYPE_UINT8:
+       case FFI_TYPE_SINT16:
+       case FFI_TYPE_UINT16:
+       case FFI_TYPE_SINT32:
+       case FFI_TYPE_UINT32:
+       case FFI_TYPE_SINT64:
+       case FFI_TYPE_UINT64:
+       case FFI_TYPE_POINTER:
+         z = 1;
+         *p_argv = (void *)argp;
+         break;
+                 
+       case FFI_TYPE_FLOAT:
+         z = 1;
+         /* Convert argument back to float in place from the saved value */
+         if (fp_reg_num < 8) {
+             *(float *)argp = args -> fp_regs[fp_reg_num++];
+         } else {
+             *(float *)argp = *(double *)argp;
+         }
+         *p_argv = (void *)argp;
+         break;
+
+       case FFI_TYPE_DOUBLE:
+         z = 1;
+         if (fp_reg_num < 8) {
+             *p_argv = args -> fp_regs + fp_reg_num++;
+         } else {
+             *p_argv = (void *)argp;
+         }
+         break;
+
+       case FFI_TYPE_STRUCT:
+         {
+             size_t sz = (*p_arg)->size;
+             unsigned short element_type;
+              z = ((*p_arg)->size + SIZEOF_ARG - 1)/SIZEOF_ARG;
+             if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) {
+               int nelements = sz/float_type_size(element_type);
+               if (nelements + fp_reg_num >= 8) {
+                 /* hard case NYI.     */
+                 abort();
+               }
+               if (element_type == FFI_TYPE_DOUBLE) {
+                 *p_argv = args -> fp_regs + fp_reg_num;
+                 fp_reg_num += nelements;
+                 break;
+               }
+               if (element_type == FFI_TYPE_FLOAT) {
+                 int j;
+                 for (j = 0; j < nelements; ++ j) {
+                    ((float *)argp)[j] = args -> fp_regs[fp_reg_num + j];
+                 }
+                 *p_argv = (void *)argp;
+                 fp_reg_num += nelements;
+                 break;
+               }
+               abort();  /* Other fp types NYI */
+             }
+         }
+         break;
+
+       default:
+         FFI_ASSERT(0);
+       }
+
+      argp += z;
+      p_argv++;
+
+    }
+  
+  return;
+}
+
+
+/* Fill in a closure to refer to the specified fun and user_data.      */
+/* cif specifies the argument and result types for fun.                        */
+/* the cif must already be prep'ed */
+
+/* The layout of a function descriptor.  A C function pointer really   */
+/* points to one of these.                                             */
+typedef struct ia64_fd_struct {
+    void *code_pointer;
+    void *gp;
+} ia64_fd;
+
+ffi_status
+ffi_prep_closure (ffi_closure* closure,
+                 ffi_cif* cif,
+                 void (*fun)(ffi_cif*,void*,void**,void*),
+                 void *user_data)
+{
+  struct ffi_ia64_trampoline_struct *tramp =
+    (struct ffi_ia64_trampoline_struct *) (closure -> tramp);
+  ia64_fd *fd = (ia64_fd *)(void *)ffi_closure_UNIX;
+
+  FFI_ASSERT (cif->abi == FFI_UNIX);
+
+  tramp -> code_pointer = fd -> code_pointer;
+  tramp -> real_gp = fd -> gp;
+  tramp -> fake_gp = closure;
+  closure->cif  = cif;
+  closure->user_data = user_data;
+  closure->fun  = fun;
+
+  return FFI_OK;
+}
+
+
diff --git a/libffi/ia64/ia64_flags.h b/libffi/ia64/ia64_flags.h
new file mode 100644 (file)
index 0000000..23dbd3e
--- /dev/null
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------
+   ia64_flags.h - Copyright (c) 2000 Hewlett Packard Company
+   
+   IA64/unix Foreign Function Interface 
+
+   Original author: Hans Boehm, HP Labs
+
+   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 CYGNUS SOLUTIONS 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.
+   ----------------------------------------------------------------------- */
+
+
+/* Homogeneous Floating Point Aggregates (HFAs) which are returned     */
+/* in FP registers.  The least significant bits specify the size in    */
+/* words.                                                              */
+#define FFI_IS_FLOAT_FP_AGGREGATE 0x1000
+#define FFI_IS_DOUBLE_FP_AGGREGATE 0x0800
+#define FLOAT_FP_AGGREGATE_BIT 12
+#define DOUBLE_FP_AGGREGATE_BIT 11
+
+/* Small structures containing N words.  If N=1, they are returned     */
+/* as though they were integers.                                       */
+#define FFI_IS_SMALL_STRUCT2   0x40 /* Struct > 8, <=16 bytes  */
+#define FFI_IS_SMALL_STRUCT3   0x41 /* Struct > 16 <= 24 bytes */
+#define FFI_IS_SMALL_STRUCT4   0x42 /* Struct > 24, <=32 bytes */
+
+/* Flag values identifying particularly simple cases, which are        */
+/* handled specially.  We treat functions as simple if they take all   */
+/* arguments can be passed as 32 or 64 bit integer quantities, there is        */
+/* either no return value or it can be treated as a 64bit integer, and */
+/* if there are at most 2 arguments.                                   */
+/* This is OR'ed with the normal flag values.                          */
+#define FFI_SIMPLE_V 0x10000   /* () -> X      */
+#define FFI_SIMPLE_I 0x20000   /* (int) -> X   */
+#define FFI_SIMPLE_L 0x30000   /* (long) -> X  */
+#define FFI_SIMPLE_II 0x40000  /* (int,int) -> X       */
+#define FFI_SIMPLE_IL 0x50000  /* (int,long) -> X      */
+#define FFI_SIMPLE_LI 0x60000  /* (long,int) -> X      */
+#define FFI_SIMPLE_LL 0x70000  /* (long,long) -> X     */
+
+/* Mask for all of the FFI_SIMPLE bits:        */
+#define FFI_SIMPLE 0xf0000
+
+/* An easy way to build FFI_SIMPLE flags from FFI_SIMPLE_V:    */
+#define FFI_ADD_LONG_ARG(flag) (((flag) << 1) | 0x10000)
+#define FFI_ADD_INT_ARG(flag) ((flag) << 1)
diff --git a/libffi/ia64/unix.S b/libffi/ia64/unix.S
new file mode 100644 (file)
index 0000000..fdaf8be
--- /dev/null
@@ -0,0 +1,301 @@
+/* -----------------------------------------------------------------------
+   unix.S - Copyright (c) 1998 Cygnus Solutions
+            Copyright (c) 2000 Hewlett Packard Company
+   
+   IA64/unix Foreign Function Interface 
+
+   Primary author: Hans Boehm, HP Labs
+
+   Loosely modeled on Cygnus code for other platforms.
+
+   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 CYGNUS SOLUTIONS 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 <ffi.h>
+#include "ia64_flags.h"
+
+/* parameters: */
+#define callback       in0
+#define ecifp          in1
+#define bytes          in2
+#define flags          in3
+#define raddr          in4
+#define fn             in5
+
+#define FLOAT_SZ       8 /* in-memory size of fp operands      */
+
+.text
+        .align 16
+        .global ffi_call_unix#
+        .proc ffi_call_unix#
+ffi_call_unix:
+       alloc   loc0=ar.pfs,6,5,8,0
+       mov     loc1=b0;
+       sub     sp=sp,bytes
+       mov     loc4=r1         /* Save gp      */
+       ld8     r8=[callback],8 /* code address of callback     */
+       ;;
+       mov     out0=sp
+       mov     out1=ecifp
+       mov     out2=bytes
+       ld8     r1=[callback]   /* Set up gp for callback.  Unnecessary? */
+       mov     b6=r8
+       ;;
+       br.call.sptk.many b0 = b6       /* call ffi_prep_args           */
+       cmp.eq  p6,p0=0,r8              /* r8 nonzero ==> need fp regs  */
+       ;;
+(p6)   add     loc2=32+8*FLOAT_SZ,sp
+(p6)   br.cond.dptk.many       fp_done
+       ;;      /* Quiets warning; needed?      */
+       add     loc2=32,sp
+       add     loc3=32+FLOAT_SZ,sp
+       ;;
+       ldfd    f8=[loc2],2*FLOAT_SZ
+       ldfd    f9=[loc3],2*FLOAT_SZ
+       ;;
+       ldfd    f10=[loc2],2*FLOAT_SZ
+       ldfd    f11=[loc3],2*FLOAT_SZ
+       ;;
+       ldfd    f12=[loc2],2*FLOAT_SZ
+       ldfd    f13=[loc3],2*FLOAT_SZ
+       ;;
+       ldfd    f14=[loc2],2*FLOAT_SZ
+       ldfd    f15=[loc3]
+fp_done:
+       add     r9=16,sp        /* Pointer to r8_contents       */
+       /* loc2 points at first integer register value.  */
+       add     loc3=8,loc2
+       ;;
+       ld8     r8=[r9]         /* Just in case we return large struct */
+       ld8     out0=[loc2],16
+       ld8     out1=[loc3],16
+       ;;
+       ld8     out2=[loc2],16
+       ld8     out3=[loc3],16
+       ;;
+       ld8     out4=[loc2],16
+       ld8     out5=[loc3],16
+       ;;
+       ld8     out6=[loc2],16
+       ld8     out7=[loc3]
+       /* loc2 points at first stack parameter.  Set sp to 16 bytes    */
+       /* below that.                                                  */
+       add     sp=-16,loc2
+       
+       ld8     r8=[fn],8
+       ;;
+       ld8     r1=[fn]         /* Set up gp */
+       mov     b6=r8;;
+       br.call.sptk.many b0 = b6       /* call ffi_prep_args   */
+       
+       /* Handle return value. */
+       cmp.eq  p6,p0=0,raddr
+       cmp.eq  p7,p0=FFI_TYPE_INT,flags
+       cmp.eq  p10,p0=FFI_IS_SMALL_STRUCT2,flags
+       cmp.eq  p11,p0=FFI_IS_SMALL_STRUCT3,flags
+       cmp.eq  p12,p0=FFI_IS_SMALL_STRUCT4,flags
+       ;;
+(p6)   br.cond.dpnt.few done           /* Dont copy ret values if raddr = 0 */
+(p7)   br.cond.dptk.few copy1
+(p10)  br.cond.dpnt.few copy2
+(p11)  br.cond.dpnt.few copy3
+(p12)  br.cond.dpnt.few copy4
+       cmp.eq  p8,p0=FFI_TYPE_FLOAT,flags
+       cmp.eq  p9,p0=FFI_TYPE_DOUBLE,flags
+       tbit.nz p6,p0=flags,FLOAT_FP_AGGREGATE_BIT
+       tbit.nz p7,p0=flags,DOUBLE_FP_AGGREGATE_BIT
+       ;;
+(p8)   stfs    [raddr]=f8
+(p9)   stfd    [raddr]=f8
+       ;;
+(p6)   br.cond.dpnt.few handle_float_hfa
+(p7)   br.cond.dpnt.few handle_double_hfa
+       br done
+
+copy4:
+       add     loc3=24,raddr
+       ;;
+       st8     [loc3]=r11
+copy3:
+       add     loc3=16,raddr
+       ;;
+       st8     [loc3]=r10
+copy2:
+       add     loc3=8,raddr
+       ;;
+       st8     [loc3]=r9
+copy1:
+       st8     [raddr]=r8
+       /* In the big struct case, raddr was passed as an argument.     */
+       /* In the void case there was nothing to do.                    */
+
+done:
+       mov     r1=loc4         /* Restore gp   */
+       mov     ar.pfs = loc0
+       mov     b0 = loc1
+       br.ret.sptk.many b0
+
+handle_double_hfa:
+       /* Homogeneous floating point array of doubles is returned in   */
+       /* registers f8-f15.  Save one at a time to return area.        */
+       and     flags=0xf,flags /* Retrieve size        */
+       ;;
+       cmp.eq  p6,p0=2,flags
+       cmp.eq  p7,p0=3,flags
+       cmp.eq  p8,p0=4,flags
+       cmp.eq  p9,p0=5,flags
+       cmp.eq  p10,p0=6,flags
+       cmp.eq  p11,p0=7,flags
+       cmp.eq  p12,p0=8,flags
+       ;;
+(p6)   br.cond.dptk.few        dhfa2
+(p7)   br.cond.dptk.few        dhfa3
+(p8)   br.cond.dptk.few        dhfa4
+(p9)   br.cond.dptk.few        dhfa5
+(p10)  br.cond.dptk.few        dhfa6
+(p11)  br.cond.dptk.few        dhfa7
+dhfa8: add     loc3=7*8,raddr
+       ;;
+       stfd    [loc3]=f15
+dhfa7: add     loc3=6*8,raddr
+       ;;
+       stfd    [loc3]=f14
+dhfa6: add     loc3=5*8,raddr
+       ;;
+       stfd    [loc3]=f13
+dhfa5: add     loc3=4*8,raddr
+       ;;
+       stfd    [loc3]=f12
+dhfa4: add     loc3=3*8,raddr
+       ;;
+       stfd    [loc3]=f11
+dhfa3: add     loc3=2*8,raddr
+       ;;
+       stfd    [loc3]=f10
+dhfa2: add     loc3=1*8,raddr
+       ;;
+       stfd    [loc3]=f9
+       stfd    [raddr]=f8
+       br      done
+
+handle_float_hfa:
+       /* Homogeneous floating point array of floats is returned in    */
+       /* registers f8-f15.  Save one at a time to return area.        */
+       and     flags=0xf,flags /* Retrieve size        */
+       ;;
+       cmp.eq  p6,p0=2,flags
+       cmp.eq  p7,p0=3,flags
+       cmp.eq  p8,p0=4,flags
+       cmp.eq  p9,p0=5,flags
+       cmp.eq  p10,p0=6,flags
+       cmp.eq  p11,p0=7,flags
+       cmp.eq  p12,p0=8,flags
+       ;;
+(p6)   br.cond.dptk.few        shfa2
+(p7)   br.cond.dptk.few        shfa3
+(p8)   br.cond.dptk.few        shfa4
+(p9)   br.cond.dptk.few        shfa5
+(p10)  br.cond.dptk.few        shfa6
+(p11)  br.cond.dptk.few        shfa7
+shfa8: add     loc3=7*4,raddr
+       ;;
+       stfd    [loc3]=f15
+shfa7: add     loc3=6*4,raddr
+       ;;
+       stfd    [loc3]=f14
+shfa6: add     loc3=5*4,raddr
+       ;;
+       stfd    [loc3]=f13
+shfa5: add     loc3=4*4,raddr
+       ;;
+       stfd    [loc3]=f12
+shfa4: add     loc3=3*4,raddr
+       ;;
+       stfd    [loc3]=f11
+shfa3: add     loc3=2*4,raddr
+       ;;
+       stfd    [loc3]=f10
+shfa2: add     loc3=1*4,raddr
+       ;;
+       stfd    [loc3]=f9
+       stfd    [raddr]=f8
+       br      done
+
+        .endp ffi_call_unix
+
+
+.text
+        .align 16
+        .global ffi_closure_UNIX
+        .proc ffi_closure_UNIX
+ffi_closure_UNIX:
+       alloc   loc0=ar.pfs,8,2,2,0
+       mov     loc1=b0
+       /* Retrieve closure pointer and real gp.        */
+       mov     out0=gp
+       add     gp=16,gp
+       ;;
+       ld8     gp=[gp]
+       /* Reserve a structia64_args on the stack such that arguments   */
+       /* past the first 8 are automatically placed in the right       */
+       /* slot.  Note that when we start the sp points at 2 8-byte     */
+       /* scratch words, followed by the extra arguments.              */
+#      define BASIC_ARGS_SZ (8*FLOAT_SZ+8*8+2*8)
+#      define FIRST_FP_OFFSET (4*8)
+       add     r14=-(BASIC_ARGS_SZ-FIRST_FP_OFFSET),sp
+       add     r15=-(BASIC_ARGS_SZ-FIRST_FP_OFFSET-FLOAT_SZ),sp
+       add     sp=-BASIC_ARGS_SZ,sp
+       /* r14 points to fp_regs[0], r15 points to fp_regs[1]   */
+       ;;
+       stfd    [r14]=f8,2*FLOAT_SZ
+       stfd    [r15]=f9,2*FLOAT_SZ
+       ;;
+       stfd    [r14]=f10,2*FLOAT_SZ
+       stfd    [r15]=f11,2*FLOAT_SZ
+       ;;
+       stfd    [r14]=f12,2*FLOAT_SZ
+       stfd    [r15]=f13,2*FLOAT_SZ
+       ;;
+       stfd    [r14]=f14,FLOAT_SZ+8
+       stfd    [r15]=f15,2*8
+       ;;
+       /* r14 points to first parameter register area, r15 to second. */
+       st8     [r14]=in0,2*8
+       st8     [r15]=in1,2*8
+       ;;
+       st8     [r14]=in2,2*8
+       st8     [r15]=in3,2*8
+       ;;
+       st8     [r14]=in4,2*8
+       st8     [r15]=in5,2*8
+       ;;
+       st8     [r14]=in6,2*8
+       st8     [r15]=in7,2*8
+       /* Call ffi_closure_UNIX_inner */
+       mov     out1=sp
+       br.call.sptk.many b0=ffi_closure_UNIX_inner
+       ;;
+       mov     b0=loc1
+       mov     ar.pfs=loc0
+       br.ret.sptk.many b0
+       .endp ffi_closure_UNIX
+       
+