1/* -----------------------------------------------------------------------
2   ffi.c - Copyright (c) 1998, 2001, 2007, 2008  Red Hat, Inc.
3
4   Alpha Foreign Function Interface
5
6   Permission is hereby granted, free of charge, to any person obtaining
7   a copy of this software and associated documentation files (the
8   ``Software''), to deal in the Software without restriction, including
9   without limitation the rights to use, copy, modify, merge, publish,
10   distribute, sublicense, and/or sell copies of the Software, and to
11   permit persons to whom the Software is furnished to do so, subject to
12   the following conditions:
13
14   The above copyright notice and this permission notice shall be included
15   in all copies or substantial portions of the Software.
16
17   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24   DEALINGS IN THE SOFTWARE.
25   ----------------------------------------------------------------------- */
26
27#include <ffi.h>
28#include <ffi_common.h>
29#include <stdlib.h>
30
31/* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE;
32   all further uses in this file will refer to the 128-bit type.  */
33#if defined(__LONG_DOUBLE_128__)
34# if FFI_TYPE_LONGDOUBLE != 4
35#  error FFI_TYPE_LONGDOUBLE out of date
36# endif
37#else
38# undef FFI_TYPE_LONGDOUBLE
39# define FFI_TYPE_LONGDOUBLE 4
40#endif
41
42extern void ffi_call_osf(void *, unsigned long, unsigned, void *, void (*)(void))
43  FFI_HIDDEN;
44extern void ffi_closure_osf(void) FFI_HIDDEN;
45
46
47ffi_status
48ffi_prep_cif_machdep(ffi_cif *cif)
49{
50  /* Adjust cif->bytes to represent a minimum 6 words for the temporary
51     register argument loading area.  */
52  if (cif->bytes < 6*FFI_SIZEOF_ARG)
53    cif->bytes = 6*FFI_SIZEOF_ARG;
54
55  /* Set the return type flag */
56  switch (cif->rtype->type)
57    {
58    case FFI_TYPE_STRUCT:
59    case FFI_TYPE_FLOAT:
60    case FFI_TYPE_DOUBLE:
61      cif->flags = cif->rtype->type;
62      break;
63
64    case FFI_TYPE_LONGDOUBLE:
65      /* 128-bit long double is returned in memory, like a struct.  */
66      cif->flags = FFI_TYPE_STRUCT;
67      break;
68
69    default:
70      cif->flags = FFI_TYPE_INT;
71      break;
72    }
73
74  return FFI_OK;
75}
76
77
78void
79ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
80{
81  unsigned long *stack, *argp;
82  long i, avn;
83  ffi_type **arg_types;
84
85  /* If the return value is a struct and we don't have a return
86     value address then we need to make one.  */
87  if (rvalue == NULL && cif->flags == FFI_TYPE_STRUCT)
88    rvalue = alloca(cif->rtype->size);
89
90  /* Allocate the space for the arguments, plus 4 words of temp
91     space for ffi_call_osf.  */
92  argp = stack = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
93
94  if (cif->flags == FFI_TYPE_STRUCT)
95    *(void **) argp++ = rvalue;
96
97  i = 0;
98  avn = cif->nargs;
99  arg_types = cif->arg_types;
100
101  while (i < avn)
102    {
103      size_t size = (*arg_types)->size;
104
105      switch ((*arg_types)->type)
106	{
107	case FFI_TYPE_SINT8:
108	  *(SINT64 *) argp = *(SINT8 *)(* avalue);
109	  break;
110
111	case FFI_TYPE_UINT8:
112	  *(SINT64 *) argp = *(UINT8 *)(* avalue);
113	  break;
114
115	case FFI_TYPE_SINT16:
116	  *(SINT64 *) argp = *(SINT16 *)(* avalue);
117	  break;
118
119	case FFI_TYPE_UINT16:
120	  *(SINT64 *) argp = *(UINT16 *)(* avalue);
121	  break;
122
123	case FFI_TYPE_SINT32:
124	case FFI_TYPE_UINT32:
125	  /* Note that unsigned 32-bit quantities are sign extended.  */
126	  *(SINT64 *) argp = *(SINT32 *)(* avalue);
127	  break;
128
129	case FFI_TYPE_SINT64:
130	case FFI_TYPE_UINT64:
131	case FFI_TYPE_POINTER:
132	  *(UINT64 *) argp = *(UINT64 *)(* avalue);
133	  break;
134
135	case FFI_TYPE_FLOAT:
136	  if (argp - stack < 6)
137	    {
138	      /* Note the conversion -- all the fp regs are loaded as
139		 doubles.  The in-register format is the same.  */
140	      *(double *) argp = *(float *)(* avalue);
141	    }
142	  else
143	    *(float *) argp = *(float *)(* avalue);
144	  break;
145
146	case FFI_TYPE_DOUBLE:
147	  *(double *) argp = *(double *)(* avalue);
148	  break;
149
150	case FFI_TYPE_LONGDOUBLE:
151	  /* 128-bit long double is passed by reference.  */
152	  *(long double **) argp = (long double *)(* avalue);
153	  size = sizeof (long double *);
154	  break;
155
156	case FFI_TYPE_STRUCT:
157	  memcpy(argp, *avalue, (*arg_types)->size);
158	  break;
159
160	default:
161	  FFI_ASSERT(0);
162	}
163
164      argp += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
165      i++, arg_types++, avalue++;
166    }
167
168  ffi_call_osf(stack, cif->bytes, cif->flags, rvalue, fn);
169}
170
171
172ffi_status
173ffi_prep_closure_loc (ffi_closure* closure,
174		      ffi_cif* cif,
175		      void (*fun)(ffi_cif*, void*, void**, void*),
176		      void *user_data,
177		      void *codeloc)
178{
179  unsigned int *tramp;
180
181  tramp = (unsigned int *) &closure->tramp[0];
182  tramp[0] = 0x47fb0401;	/* mov $27,$1		*/
183  tramp[1] = 0xa77b0010;	/* ldq $27,16($27)	*/
184  tramp[2] = 0x6bfb0000;	/* jmp $31,($27),0	*/
185  tramp[3] = 0x47ff041f;	/* nop			*/
186  *(void **) &tramp[4] = ffi_closure_osf;
187
188  closure->cif = cif;
189  closure->fun = fun;
190  closure->user_data = user_data;
191
192  /* Flush the Icache.
193
194     Tru64 UNIX as doesn't understand the imb mnemonic, so use call_pal
195     instead, since both Compaq as and gas can handle it.
196
197     0x86 is PAL_imb in Tru64 UNIX <alpha/pal.h>.  */
198  asm volatile ("call_pal 0x86" : : : "memory");
199
200  return FFI_OK;
201}
202
203
204long FFI_HIDDEN
205ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
206{
207  ffi_cif *cif;
208  void **avalue;
209  ffi_type **arg_types;
210  long i, avn, argn;
211
212  cif = closure->cif;
213  avalue = alloca(cif->nargs * sizeof(void *));
214
215  argn = 0;
216
217  /* Copy the caller's structure return address to that the closure
218     returns the data directly to the caller.  */
219  if (cif->flags == FFI_TYPE_STRUCT)
220    {
221      rvalue = (void *) argp[0];
222      argn = 1;
223    }
224
225  i = 0;
226  avn = cif->nargs;
227  arg_types = cif->arg_types;
228
229  /* Grab the addresses of the arguments from the stack frame.  */
230  while (i < avn)
231    {
232      size_t size = arg_types[i]->size;
233
234      switch (arg_types[i]->type)
235	{
236	case FFI_TYPE_SINT8:
237	case FFI_TYPE_UINT8:
238	case FFI_TYPE_SINT16:
239	case FFI_TYPE_UINT16:
240	case FFI_TYPE_SINT32:
241	case FFI_TYPE_UINT32:
242	case FFI_TYPE_SINT64:
243	case FFI_TYPE_UINT64:
244	case FFI_TYPE_POINTER:
245	case FFI_TYPE_STRUCT:
246	  avalue[i] = &argp[argn];
247	  break;
248
249	case FFI_TYPE_FLOAT:
250	  if (argn < 6)
251	    {
252	      /* Floats coming from registers need conversion from double
253	         back to float format.  */
254	      *(float *)&argp[argn - 6] = *(double *)&argp[argn - 6];
255	      avalue[i] = &argp[argn - 6];
256	    }
257	  else
258	    avalue[i] = &argp[argn];
259	  break;
260
261	case FFI_TYPE_DOUBLE:
262	  avalue[i] = &argp[argn - (argn < 6 ? 6 : 0)];
263	  break;
264
265	case FFI_TYPE_LONGDOUBLE:
266	  /* 128-bit long double is passed by reference.  */
267	  avalue[i] = (long double *) argp[argn];
268	  size = sizeof (long double *);
269	  break;
270
271	default:
272	  abort ();
273	}
274
275      argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
276      i++;
277    }
278
279  /* Invoke the closure.  */
280  closure->fun (cif, rvalue, avalue, closure->user_data);
281
282  /* Tell ffi_closure_osf how to perform return type promotions.  */
283  return cif->rtype->type;
284}
285