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