1/* -----------------------------------------------------------------------
2   ffi.c - Copyright (c) 2012 Tilera Corp.
3
4   TILE 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#include <stdint.h>
31#include <unistd.h>
32#include <arch/abi.h>
33#include <arch/icache.h>
34#include <arch/opcode.h>
35
36
37/* The first 10 registers are used to pass arguments and return values. */
38#define NUM_ARG_REGS 10
39
40/* Performs a raw function call with the given NUM_ARG_REGS register arguments
41   and the specified additional stack arguments (if any). */
42extern void ffi_call_tile(ffi_sarg reg_args[NUM_ARG_REGS],
43                          const ffi_sarg *stack_args,
44                          size_t stack_args_bytes,
45                          void (*fnaddr)(void))
46  FFI_HIDDEN;
47
48/* This handles the raw call from the closure stub, cleaning up the
49   parameters and delegating to ffi_closure_tile_inner. */
50extern void ffi_closure_tile(void) FFI_HIDDEN;
51
52
53ffi_status
54ffi_prep_cif_machdep(ffi_cif *cif)
55{
56  /* We always allocate room for all registers. Even if we don't
57     use them as parameters, they get returned in the same array
58     as struct return values so we need to make room. */
59  if (cif->bytes < NUM_ARG_REGS * FFI_SIZEOF_ARG)
60    cif->bytes = NUM_ARG_REGS * FFI_SIZEOF_ARG;
61
62  if (cif->rtype->size > NUM_ARG_REGS * FFI_SIZEOF_ARG)
63    cif->flags = FFI_TYPE_STRUCT;
64  else
65    cif->flags = FFI_TYPE_INT;
66
67  /* Nothing to do. */
68  return FFI_OK;
69}
70
71
72static long
73assign_to_ffi_arg(ffi_sarg *out, void *in, const ffi_type *type,
74                  int write_to_reg)
75{
76  switch (type->type)
77    {
78    case FFI_TYPE_SINT8:
79      *out = *(SINT8 *)in;
80      return 1;
81
82    case FFI_TYPE_UINT8:
83      *out = *(UINT8 *)in;
84      return 1;
85
86    case FFI_TYPE_SINT16:
87      *out = *(SINT16 *)in;
88      return 1;
89
90    case FFI_TYPE_UINT16:
91      *out = *(UINT16 *)in;
92      return 1;
93
94    case FFI_TYPE_SINT32:
95    case FFI_TYPE_UINT32:
96#ifndef __LP64__
97    case FFI_TYPE_POINTER:
98#endif
99      /* Note that even unsigned 32-bit quantities are sign extended
100         on tilegx when stored in a register.  */
101      *out = *(SINT32 *)in;
102      return 1;
103
104    case FFI_TYPE_FLOAT:
105#ifdef __tilegx__
106      if (write_to_reg)
107        {
108          /* Properly sign extend the value.  */
109          union { float f; SINT32 s32; } val;
110          val.f = *(float *)in;
111          *out = val.s32;
112        }
113      else
114#endif
115        {
116          *(float *)out = *(float *)in;
117        }
118      return 1;
119
120    case FFI_TYPE_SINT64:
121    case FFI_TYPE_UINT64:
122    case FFI_TYPE_DOUBLE:
123#ifdef __LP64__
124    case FFI_TYPE_POINTER:
125#endif
126      *(UINT64 *)out = *(UINT64 *)in;
127      return sizeof(UINT64) / FFI_SIZEOF_ARG;
128
129    case FFI_TYPE_STRUCT:
130      memcpy(out, in, type->size);
131      return (type->size + FFI_SIZEOF_ARG - 1) / FFI_SIZEOF_ARG;
132
133    case FFI_TYPE_VOID:
134      /* Must be a return type. Nothing to do. */
135      return 0;
136
137    default:
138      FFI_ASSERT(0);
139      return -1;
140    }
141}
142
143
144void
145ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
146{
147  ffi_sarg * const arg_mem = alloca(cif->bytes);
148  ffi_sarg * const reg_args = arg_mem;
149  ffi_sarg * const stack_args = &reg_args[NUM_ARG_REGS];
150  ffi_sarg *argp = arg_mem;
151  ffi_type ** const arg_types = cif->arg_types;
152  const long num_args = cif->nargs;
153  long i;
154
155  if (cif->flags == FFI_TYPE_STRUCT)
156    {
157      /* Pass a hidden pointer to the return value. We make sure there
158         is scratch space for the callee to store the return value even if
159         our caller doesn't care about it. */
160      *argp++ = (intptr_t)(rvalue ? rvalue : alloca(cif->rtype->size));
161
162      /* No more work needed to return anything. */
163      rvalue = NULL;
164    }
165
166  for (i = 0; i < num_args; i++)
167    {
168      ffi_type *type = arg_types[i];
169      void * const arg_in = avalue[i];
170      ptrdiff_t arg_word = argp - arg_mem;
171
172#ifndef __tilegx__
173      /* Doubleword-aligned values are always in an even-number register
174         pair, or doubleword-aligned stack slot if out of registers. */
175      long align = arg_word & (type->alignment > FFI_SIZEOF_ARG);
176      argp += align;
177      arg_word += align;
178#endif
179
180      if (type->type == FFI_TYPE_STRUCT)
181        {
182          const size_t arg_size_in_words =
183            (type->size + FFI_SIZEOF_ARG - 1) / FFI_SIZEOF_ARG;
184
185          if (arg_word < NUM_ARG_REGS &&
186              arg_word + arg_size_in_words > NUM_ARG_REGS)
187            {
188              /* Args are not allowed to span registers and the stack. */
189              argp = stack_args;
190            }
191
192          memcpy(argp, arg_in, type->size);
193          argp += arg_size_in_words;
194        }
195      else
196        {
197          argp += assign_to_ffi_arg(argp, arg_in, arg_types[i], 1);
198        }
199    }
200
201  /* Actually do the call. */
202  ffi_call_tile(reg_args, stack_args,
203                cif->bytes - (NUM_ARG_REGS * FFI_SIZEOF_ARG), fn);
204
205  if (rvalue != NULL)
206    assign_to_ffi_arg(rvalue, reg_args, cif->rtype, 0);
207}
208
209
210/* Template code for closure. */
211extern const UINT64 ffi_template_tramp_tile[] FFI_HIDDEN;
212
213
214ffi_status
215ffi_prep_closure_loc (ffi_closure *closure,
216                      ffi_cif *cif,
217                      void (*fun)(ffi_cif*, void*, void**, void*),
218                      void *user_data,
219                      void *codeloc)
220{
221#ifdef __tilegx__
222  /* TILE-Gx */
223  SINT64 c;
224  SINT64 h;
225  int s;
226  UINT64 *out;
227
228  if (cif->abi != FFI_UNIX)
229    return FFI_BAD_ABI;
230
231  out = (UINT64 *)closure->tramp;
232
233  c = (intptr_t)closure;
234  h = (intptr_t)ffi_closure_tile;
235  s = 0;
236
237  /* Find the smallest shift count that doesn't lose information
238     (i.e. no need to explicitly insert high bits of the address that
239     are just the sign extension of the low bits). */
240  while ((c >> s) != (SINT16)(c >> s) || (h >> s) != (SINT16)(h >> s))
241    s += 16;
242
243#define OPS(a, b, shift) \
244  (create_Imm16_X0((a) >> (shift)) | create_Imm16_X1((b) >> (shift)))
245
246  /* Emit the moveli. */
247  *out++ = ffi_template_tramp_tile[0] | OPS(c, h, s);
248  for (s -= 16; s >= 0; s -= 16)
249    *out++ = ffi_template_tramp_tile[1] | OPS(c, h, s);
250
251#undef OPS
252
253  *out++ = ffi_template_tramp_tile[2];
254
255#else
256  /* TILEPro */
257  UINT64 *out;
258  intptr_t delta;
259
260  if (cif->abi != FFI_UNIX)
261    return FFI_BAD_ABI;
262
263  out = (UINT64 *)closure->tramp;
264  delta = (intptr_t)ffi_closure_tile - (intptr_t)codeloc;
265
266  *out++ = ffi_template_tramp_tile[0] | create_JOffLong_X1(delta >> 3);
267#endif
268
269  closure->cif = cif;
270  closure->fun = fun;
271  closure->user_data = user_data;
272
273  invalidate_icache(closure->tramp, (char *)out - closure->tramp,
274                    getpagesize());
275
276  return FFI_OK;
277}
278
279
280/* This is called by the assembly wrapper for closures. This does
281   all of the work. On entry reg_args[0] holds the values the registers
282   had when the closure was invoked. On return reg_args[1] holds the register
283   values to be returned to the caller (many of which may be garbage). */
284void FFI_HIDDEN
285ffi_closure_tile_inner(ffi_closure *closure,
286                       ffi_sarg reg_args[2][NUM_ARG_REGS],
287                       ffi_sarg *stack_args)
288{
289  ffi_cif * const cif = closure->cif;
290  void ** const avalue = alloca(cif->nargs * sizeof(void *));
291  void *rvalue;
292  ffi_type ** const arg_types = cif->arg_types;
293  ffi_sarg * const reg_args_in = reg_args[0];
294  ffi_sarg * const reg_args_out = reg_args[1];
295  ffi_sarg * argp;
296  long i, arg_word, nargs = cif->nargs;
297  /* Use a union to guarantee proper alignment for double. */
298  union { ffi_sarg arg[NUM_ARG_REGS]; double d; UINT64 u64; } closure_ret;
299
300  /* Start out reading register arguments. */
301  argp = reg_args_in;
302
303  /* Copy the caller's structure return address to that the closure
304     returns the data directly to the caller.  */
305  if (cif->flags == FFI_TYPE_STRUCT)
306    {
307      /* Return by reference via hidden pointer. */
308      rvalue = (void *)(intptr_t)*argp++;
309      arg_word = 1;
310    }
311  else
312    {
313      /* Return the value in registers. */
314      rvalue = &closure_ret;
315      arg_word = 0;
316    }
317
318  /* Grab the addresses of the arguments. */
319  for (i = 0; i < nargs; i++)
320    {
321      ffi_type * const type = arg_types[i];
322      const size_t arg_size_in_words =
323        (type->size + FFI_SIZEOF_ARG - 1) / FFI_SIZEOF_ARG;
324
325#ifndef __tilegx__
326      /* Doubleword-aligned values are always in an even-number register
327         pair, or doubleword-aligned stack slot if out of registers. */
328      long align = arg_word & (type->alignment > FFI_SIZEOF_ARG);
329      argp += align;
330      arg_word += align;
331#endif
332
333      if (arg_word == NUM_ARG_REGS ||
334          (arg_word < NUM_ARG_REGS &&
335           arg_word + arg_size_in_words > NUM_ARG_REGS))
336        {
337          /* Switch to reading arguments from the stack. */
338          argp = stack_args;
339          arg_word = NUM_ARG_REGS;
340        }
341
342      avalue[i] = argp;
343      argp += arg_size_in_words;
344      arg_word += arg_size_in_words;
345    }
346
347  /* Invoke the closure.  */
348  closure->fun(cif, rvalue, avalue, closure->user_data);
349
350  if (cif->flags != FFI_TYPE_STRUCT)
351    {
352      /* Canonicalize for register representation. */
353      assign_to_ffi_arg(reg_args_out, &closure_ret, cif->rtype, 1);
354    }
355}
356