Back to index

plt-scheme  4.2.1
ffi.c
Go to the documentation of this file.
00001 /* -----------------------------------------------------------------------
00002    ffi.c - Copyright (c) 1998, 2007 Red Hat, Inc.
00003           Copyright (c) 2000 Hewlett Packard Company
00004    
00005    IA64 Foreign Function Interface 
00006 
00007    Permission is hereby granted, free of charge, to any person obtaining
00008    a copy of this software and associated documentation files (the
00009    ``Software''), to deal in the Software without restriction, including
00010    without limitation the rights to use, copy, modify, merge, publish,
00011    distribute, sublicense, and/or sell copies of the Software, and to
00012    permit persons to whom the Software is furnished to do so, subject to
00013    the following conditions:
00014 
00015    The above copyright notice and this permission notice shall be included
00016    in all copies or substantial portions of the Software.
00017 
00018    THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
00019    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00020    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00021    IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
00022    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00023    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00024    OTHER DEALINGS IN THE SOFTWARE.
00025    ----------------------------------------------------------------------- */
00026 
00027 #include <ffi.h>
00028 #include <ffi_common.h>
00029 
00030 #include <stdlib.h>
00031 #include <stdbool.h>
00032 #include <float.h>
00033 
00034 #include "ia64_flags.h"
00035 
00036 /* A 64-bit pointer value.  In LP64 mode, this is effectively a plain
00037    pointer.  In ILP32 mode, it's a pointer that's been extended to 
00038    64 bits by "addp4".  */
00039 typedef void *PTR64 __attribute__((mode(DI)));
00040 
00041 /* Memory image of fp register contents.  This is the implementation
00042    specific format used by ldf.fill/stf.spill.  All we care about is
00043    that it wants a 16 byte aligned slot.  */
00044 typedef struct
00045 {
00046   UINT64 x[2] __attribute__((aligned(16)));
00047 } fpreg;
00048 
00049 
00050 /* The stack layout given to ffi_call_unix and ffi_closure_unix_inner.  */
00051 
00052 struct ia64_args
00053 {
00054   fpreg fp_regs[8];  /* Contents of 8 fp arg registers.  */
00055   UINT64 gp_regs[8]; /* Contents of 8 gp arg registers.  */
00056   UINT64 other_args[];      /* Arguments passed on stack, variable size.  */
00057 };
00058 
00059 
00060 /* Adjust ADDR, a pointer to an 8 byte slot, to point to the low LEN bytes.  */
00061 
00062 static inline void *
00063 endian_adjust (void *addr, size_t len)
00064 {
00065 #ifdef __BIG_ENDIAN__
00066   return addr + (8 - len);
00067 #else
00068   return addr;
00069 #endif
00070 }
00071 
00072 /* Store VALUE to ADDR in the current cpu implementation's fp spill format.
00073    This is a macro instead of a function, so that it works for all 3 floating
00074    point types without type conversions.  Type conversion to long double breaks
00075    the denorm support.  */
00076 
00077 #define stf_spill(addr, value)     \
00078   asm ("stf.spill %0 = %1%P0" : "=m" (*addr) : "f"(value));
00079 
00080 /* Load a value from ADDR, which is in the current cpu implementation's
00081    fp spill format.  As above, this must also be a macro.  */
00082 
00083 #define ldf_fill(result, addr)     \
00084   asm ("ldf.fill %0 = %1%P1" : "=f"(result) : "m"(*addr));
00085 
00086 /* Return the size of the C type associated with with TYPE.  Which will
00087    be one of the FFI_IA64_TYPE_HFA_* values.  */
00088 
00089 static size_t
00090 hfa_type_size (int type)
00091 {
00092   switch (type)
00093     {
00094     case FFI_IA64_TYPE_HFA_FLOAT:
00095       return sizeof(float);
00096     case FFI_IA64_TYPE_HFA_DOUBLE:
00097       return sizeof(double);
00098     case FFI_IA64_TYPE_HFA_LDOUBLE:
00099       return sizeof(__float80);
00100     default:
00101       abort ();
00102     }
00103 }
00104 
00105 /* Load from ADDR a value indicated by TYPE.  Which will be one of
00106    the FFI_IA64_TYPE_HFA_* values.  */
00107 
00108 static void
00109 hfa_type_load (fpreg *fpaddr, int type, void *addr)
00110 {
00111   switch (type)
00112     {
00113     case FFI_IA64_TYPE_HFA_FLOAT:
00114       stf_spill (fpaddr, *(float *) addr);
00115       return;
00116     case FFI_IA64_TYPE_HFA_DOUBLE:
00117       stf_spill (fpaddr, *(double *) addr);
00118       return;
00119     case FFI_IA64_TYPE_HFA_LDOUBLE:
00120       stf_spill (fpaddr, *(__float80 *) addr);
00121       return;
00122     default:
00123       abort ();
00124     }
00125 }
00126 
00127 /* Load VALUE into ADDR as indicated by TYPE.  Which will be one of
00128    the FFI_IA64_TYPE_HFA_* values.  */
00129 
00130 static void
00131 hfa_type_store (int type, void *addr, fpreg *fpaddr)
00132 {
00133   switch (type)
00134     {
00135     case FFI_IA64_TYPE_HFA_FLOAT:
00136       {
00137        float result;
00138        ldf_fill (result, fpaddr);
00139        *(float *) addr = result;
00140        break;
00141       }
00142     case FFI_IA64_TYPE_HFA_DOUBLE:
00143       {
00144        double result;
00145        ldf_fill (result, fpaddr);
00146        *(double *) addr = result;
00147        break;
00148       }
00149     case FFI_IA64_TYPE_HFA_LDOUBLE:
00150       {
00151        __float80 result;
00152        ldf_fill (result, fpaddr);
00153        *(__float80 *) addr = result;
00154        break;
00155       }
00156     default:
00157       abort ();
00158     }
00159 }
00160 
00161 /* Is TYPE a struct containing floats, doubles, or extended doubles,
00162    all of the same fp type?  If so, return the element type.  Return
00163    FFI_TYPE_VOID if not.  */
00164 
00165 static int
00166 hfa_element_type (ffi_type *type, int nested)
00167 {
00168   int element = FFI_TYPE_VOID;
00169 
00170   switch (type->type)
00171     {
00172     case FFI_TYPE_FLOAT:
00173       /* We want to return VOID for raw floating-point types, but the
00174         synthetic HFA type if we're nested within an aggregate.  */
00175       if (nested)
00176        element = FFI_IA64_TYPE_HFA_FLOAT;
00177       break;
00178 
00179     case FFI_TYPE_DOUBLE:
00180       /* Similarly.  */
00181       if (nested)
00182        element = FFI_IA64_TYPE_HFA_DOUBLE;
00183       break;
00184 
00185     case FFI_TYPE_LONGDOUBLE:
00186       /* Similarly, except that that HFA is true for double extended,
00187         but not quad precision.  Both have sizeof == 16, so tell the
00188         difference based on the precision.  */
00189       if (LDBL_MANT_DIG == 64 && nested)
00190        element = FFI_IA64_TYPE_HFA_LDOUBLE;
00191       break;
00192 
00193     case FFI_TYPE_STRUCT:
00194       {
00195        ffi_type **ptr = &type->elements[0];
00196 
00197        for (ptr = &type->elements[0]; *ptr ; ptr++)
00198          {
00199            int sub_element = hfa_element_type (*ptr, 1);
00200            if (sub_element == FFI_TYPE_VOID)
00201              return FFI_TYPE_VOID;
00202 
00203            if (element == FFI_TYPE_VOID)
00204              element = sub_element;
00205            else if (element != sub_element)
00206              return FFI_TYPE_VOID;
00207          }
00208       }
00209       break;
00210 
00211     default:
00212       return FFI_TYPE_VOID;
00213     }
00214 
00215   return element;
00216 }
00217 
00218 
00219 /* Perform machine dependent cif processing. */
00220 
00221 ffi_status
00222 ffi_prep_cif_machdep(ffi_cif *cif)
00223 {
00224   int flags;
00225 
00226   /* Adjust cif->bytes to include space for the bits of the ia64_args frame
00227      that preceeds the integer register portion.  The estimate that the 
00228      generic bits did for the argument space required is good enough for the
00229      integer component.  */
00230   cif->bytes += offsetof(struct ia64_args, gp_regs[0]);
00231   if (cif->bytes < sizeof(struct ia64_args))
00232     cif->bytes = sizeof(struct ia64_args);
00233 
00234   /* Set the return type flag. */
00235   flags = cif->rtype->type;
00236   switch (cif->rtype->type)
00237     {
00238     case FFI_TYPE_LONGDOUBLE:
00239       /* Leave FFI_TYPE_LONGDOUBLE as meaning double extended precision,
00240         and encode quad precision as a two-word integer structure.  */
00241       if (LDBL_MANT_DIG != 64)
00242        flags = FFI_IA64_TYPE_SMALL_STRUCT | (16 << 8);
00243       break;
00244 
00245     case FFI_TYPE_STRUCT:
00246       {
00247         size_t size = cif->rtype->size;
00248        int hfa_type = hfa_element_type (cif->rtype, 0);
00249 
00250        if (hfa_type != FFI_TYPE_VOID)
00251          {
00252            size_t nelts = size / hfa_type_size (hfa_type);
00253            if (nelts <= 8)
00254              flags = hfa_type | (size << 8);
00255          }
00256        else
00257          {
00258            if (size <= 32)
00259              flags = FFI_IA64_TYPE_SMALL_STRUCT | (size << 8);
00260          }
00261       }
00262       break;
00263 
00264     default:
00265       break;
00266     }
00267   cif->flags = flags;
00268 
00269   return FFI_OK;
00270 }
00271 
00272 extern int ffi_call_unix (struct ia64_args *, PTR64, void (*)(), UINT64);
00273 
00274 void
00275 ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
00276 {
00277   struct ia64_args *stack;
00278   long i, avn, gpcount, fpcount;
00279   ffi_type **p_arg;
00280 
00281   FFI_ASSERT (cif->abi == FFI_UNIX);
00282 
00283   /* If we have no spot for a return value, make one.  */
00284   if (rvalue == NULL && cif->rtype->type != FFI_TYPE_VOID)
00285     rvalue = alloca (cif->rtype->size);
00286     
00287   /* Allocate the stack frame.  */
00288   stack = alloca (cif->bytes);
00289 
00290   gpcount = fpcount = 0;
00291   avn = cif->nargs;
00292   for (i = 0, p_arg = cif->arg_types; i < avn; i++, p_arg++)
00293     {
00294       switch ((*p_arg)->type)
00295        {
00296        case FFI_TYPE_SINT8:
00297          stack->gp_regs[gpcount++] = *(SINT8 *)avalue[i];
00298          break;
00299        case FFI_TYPE_UINT8:
00300          stack->gp_regs[gpcount++] = *(UINT8 *)avalue[i];
00301          break;
00302        case FFI_TYPE_SINT16:
00303          stack->gp_regs[gpcount++] = *(SINT16 *)avalue[i];
00304          break;
00305        case FFI_TYPE_UINT16:
00306          stack->gp_regs[gpcount++] = *(UINT16 *)avalue[i];
00307          break;
00308        case FFI_TYPE_SINT32:
00309          stack->gp_regs[gpcount++] = *(SINT32 *)avalue[i];
00310          break;
00311        case FFI_TYPE_UINT32:
00312          stack->gp_regs[gpcount++] = *(UINT32 *)avalue[i];
00313          break;
00314        case FFI_TYPE_SINT64:
00315        case FFI_TYPE_UINT64:
00316          stack->gp_regs[gpcount++] = *(UINT64 *)avalue[i];
00317          break;
00318 
00319        case FFI_TYPE_POINTER:
00320          stack->gp_regs[gpcount++] = (UINT64)(PTR64) *(void **)avalue[i];
00321          break;
00322 
00323        case FFI_TYPE_FLOAT:
00324          if (gpcount < 8 && fpcount < 8)
00325            stf_spill (&stack->fp_regs[fpcount++], *(float *)avalue[i]);
00326          stack->gp_regs[gpcount++] = *(UINT32 *)avalue[i];
00327          break;
00328 
00329        case FFI_TYPE_DOUBLE:
00330          if (gpcount < 8 && fpcount < 8)
00331            stf_spill (&stack->fp_regs[fpcount++], *(double *)avalue[i]);
00332          stack->gp_regs[gpcount++] = *(UINT64 *)avalue[i];
00333          break;
00334 
00335        case FFI_TYPE_LONGDOUBLE:
00336          if (gpcount & 1)
00337            gpcount++;
00338          if (LDBL_MANT_DIG == 64 && gpcount < 8 && fpcount < 8)
00339            stf_spill (&stack->fp_regs[fpcount++], *(__float80 *)avalue[i]);
00340          memcpy (&stack->gp_regs[gpcount], avalue[i], 16);
00341          gpcount += 2;
00342          break;
00343 
00344        case FFI_TYPE_STRUCT:
00345          {
00346            size_t size = (*p_arg)->size;
00347            size_t align = (*p_arg)->alignment;
00348            int hfa_type = hfa_element_type (*p_arg, 0);
00349 
00350            FFI_ASSERT (align <= 16);
00351            if (align == 16 && (gpcount & 1))
00352              gpcount++;
00353 
00354            if (hfa_type != FFI_TYPE_VOID)
00355              {
00356               size_t hfa_size = hfa_type_size (hfa_type);
00357               size_t offset = 0;
00358               size_t gp_offset = gpcount * 8;
00359 
00360               while (fpcount < 8
00361                      && offset < size
00362                      && gp_offset < 8 * 8)
00363                 {
00364                   hfa_type_load (&stack->fp_regs[fpcount], hfa_type,
00365                                avalue[i] + offset);
00366                   offset += hfa_size;
00367                   gp_offset += hfa_size;
00368                   fpcount += 1;
00369                 }
00370              }
00371 
00372            memcpy (&stack->gp_regs[gpcount], avalue[i], size);
00373            gpcount += (size + 7) / 8;
00374          }
00375          break;
00376 
00377        default:
00378          abort ();
00379        }
00380     }
00381 
00382   ffi_call_unix (stack, rvalue, fn, cif->flags);
00383 }
00384 
00385 /* Closures represent a pair consisting of a function pointer, and
00386    some user data.  A closure is invoked by reinterpreting the closure
00387    as a function pointer, and branching to it.  Thus we can make an
00388    interpreted function callable as a C function: We turn the
00389    interpreter itself, together with a pointer specifying the
00390    interpreted procedure, into a closure.
00391 
00392    For IA64, function pointer are already pairs consisting of a code
00393    pointer, and a gp pointer.  The latter is needed to access global
00394    variables.  Here we set up such a pair as the first two words of
00395    the closure (in the "trampoline" area), but we replace the gp
00396    pointer with a pointer to the closure itself.  We also add the real
00397    gp pointer to the closure.  This allows the function entry code to
00398    both retrieve the user data, and to restire the correct gp pointer.  */
00399 
00400 extern void ffi_closure_unix ();
00401 
00402 ffi_status
00403 ffi_prep_closure_loc (ffi_closure* closure,
00404                     ffi_cif* cif,
00405                     void (*fun)(ffi_cif*,void*,void**,void*),
00406                     void *user_data,
00407                     void *codeloc)
00408 {
00409   /* The layout of a function descriptor.  A C function pointer really 
00410      points to one of these.  */
00411   struct ia64_fd
00412   {
00413     UINT64 code_pointer;
00414     UINT64 gp;
00415   };
00416 
00417   struct ffi_ia64_trampoline_struct
00418   {
00419     UINT64 code_pointer;    /* Pointer to ffi_closure_unix.  */
00420     UINT64 fake_gp;         /* Pointer to closure, installed as gp.  */
00421     UINT64 real_gp;         /* Real gp value.  */
00422   };
00423 
00424   struct ffi_ia64_trampoline_struct *tramp;
00425   struct ia64_fd *fd;
00426 
00427   FFI_ASSERT (cif->abi == FFI_UNIX);
00428 
00429   tramp = (struct ffi_ia64_trampoline_struct *)closure->tramp;
00430   fd = (struct ia64_fd *)(void *)ffi_closure_unix;
00431 
00432   tramp->code_pointer = fd->code_pointer;
00433   tramp->real_gp = fd->gp;
00434   tramp->fake_gp = (UINT64)(PTR64)codeloc;
00435   closure->cif = cif;
00436   closure->user_data = user_data;
00437   closure->fun = fun;
00438 
00439   return FFI_OK;
00440 }
00441 
00442 
00443 UINT64
00444 ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
00445                      void *rvalue, void *r8)
00446 {
00447   ffi_cif *cif;
00448   void **avalue;
00449   ffi_type **p_arg;
00450   long i, avn, gpcount, fpcount;
00451 
00452   cif = closure->cif;
00453   avn = cif->nargs;
00454   avalue = alloca (avn * sizeof (void *));
00455 
00456   /* If the structure return value is passed in memory get that location
00457      from r8 so as to pass the value directly back to the caller.  */
00458   if (cif->flags == FFI_TYPE_STRUCT)
00459     rvalue = r8;
00460 
00461   gpcount = fpcount = 0;
00462   for (i = 0, p_arg = cif->arg_types; i < avn; i++, p_arg++)
00463     {
00464       switch ((*p_arg)->type)
00465        {
00466        case FFI_TYPE_SINT8:
00467        case FFI_TYPE_UINT8:
00468          avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], 1);
00469          break;
00470        case FFI_TYPE_SINT16:
00471        case FFI_TYPE_UINT16:
00472          avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], 2);
00473          break;
00474        case FFI_TYPE_SINT32:
00475        case FFI_TYPE_UINT32:
00476          avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], 4);
00477          break;
00478        case FFI_TYPE_SINT64:
00479        case FFI_TYPE_UINT64:
00480          avalue[i] = &stack->gp_regs[gpcount++];
00481          break;
00482        case FFI_TYPE_POINTER:
00483          avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], sizeof(void*));
00484          break;
00485 
00486        case FFI_TYPE_FLOAT:
00487          if (gpcount < 8 && fpcount < 8)
00488            {
00489              fpreg *addr = &stack->fp_regs[fpcount++];
00490              float result;
00491              avalue[i] = addr;
00492              ldf_fill (result, addr);
00493              *(float *)addr = result;
00494            }
00495          else
00496            avalue[i] = endian_adjust(&stack->gp_regs[gpcount], 4);
00497          gpcount++;
00498          break;
00499 
00500        case FFI_TYPE_DOUBLE:
00501          if (gpcount < 8 && fpcount < 8)
00502            {
00503              fpreg *addr = &stack->fp_regs[fpcount++];
00504              double result;
00505              avalue[i] = addr;
00506              ldf_fill (result, addr);
00507              *(double *)addr = result;
00508            }
00509          else
00510            avalue[i] = &stack->gp_regs[gpcount];
00511          gpcount++;
00512          break;
00513 
00514        case FFI_TYPE_LONGDOUBLE:
00515          if (gpcount & 1)
00516            gpcount++;
00517          if (LDBL_MANT_DIG == 64 && gpcount < 8 && fpcount < 8)
00518            {
00519              fpreg *addr = &stack->fp_regs[fpcount++];
00520              __float80 result;
00521              avalue[i] = addr;
00522              ldf_fill (result, addr);
00523              *(__float80 *)addr = result;
00524            }
00525          else
00526            avalue[i] = &stack->gp_regs[gpcount];
00527          gpcount += 2;
00528          break;
00529 
00530        case FFI_TYPE_STRUCT:
00531          {
00532            size_t size = (*p_arg)->size;
00533            size_t align = (*p_arg)->alignment;
00534            int hfa_type = hfa_element_type (*p_arg, 0);
00535 
00536            FFI_ASSERT (align <= 16);
00537            if (align == 16 && (gpcount & 1))
00538              gpcount++;
00539 
00540            if (hfa_type != FFI_TYPE_VOID)
00541              {
00542               size_t hfa_size = hfa_type_size (hfa_type);
00543               size_t offset = 0;
00544               size_t gp_offset = gpcount * 8;
00545               void *addr = alloca (size);
00546 
00547               avalue[i] = addr;
00548 
00549               while (fpcount < 8
00550                      && offset < size
00551                      && gp_offset < 8 * 8)
00552                 {
00553                   hfa_type_store (hfa_type, addr + offset,
00554                                 &stack->fp_regs[fpcount]);
00555                   offset += hfa_size;
00556                   gp_offset += hfa_size;
00557                   fpcount += 1;
00558                 }
00559 
00560               if (offset < size)
00561                 memcpy (addr + offset, (char *)stack->gp_regs + gp_offset,
00562                        size - offset);
00563              }
00564            else
00565              avalue[i] = &stack->gp_regs[gpcount];
00566 
00567            gpcount += (size + 7) / 8;
00568          }
00569          break;
00570 
00571        default:
00572          abort ();
00573        }
00574     }
00575 
00576   closure->fun (cif, rvalue, avalue, closure->user_data);
00577 
00578   return cif->flags;
00579 }