1*1fd5a2e1SPrashanth Swaminathan /* -----------------------------------------------------------------------
2*1fd5a2e1SPrashanth Swaminathan ffi.c - Copyright (c) 2015 Michael Knyszek <[email protected]>
3*1fd5a2e1SPrashanth Swaminathan 2015 Andrew Waterman <[email protected]>
4*1fd5a2e1SPrashanth Swaminathan 2018 Stef O'Rear <[email protected]>
5*1fd5a2e1SPrashanth Swaminathan Based on MIPS N32/64 port
6*1fd5a2e1SPrashanth Swaminathan
7*1fd5a2e1SPrashanth Swaminathan RISC-V Foreign Function Interface
8*1fd5a2e1SPrashanth Swaminathan
9*1fd5a2e1SPrashanth Swaminathan Permission is hereby granted, free of charge, to any person obtaining
10*1fd5a2e1SPrashanth Swaminathan a copy of this software and associated documentation files (the
11*1fd5a2e1SPrashanth Swaminathan ``Software''), to deal in the Software without restriction, including
12*1fd5a2e1SPrashanth Swaminathan without limitation the rights to use, copy, modify, merge, publish,
13*1fd5a2e1SPrashanth Swaminathan distribute, sublicense, and/or sell copies of the Software, and to
14*1fd5a2e1SPrashanth Swaminathan permit persons to whom the Software is furnished to do so, subject to
15*1fd5a2e1SPrashanth Swaminathan the following conditions:
16*1fd5a2e1SPrashanth Swaminathan
17*1fd5a2e1SPrashanth Swaminathan The above copyright notice and this permission notice shall be included
18*1fd5a2e1SPrashanth Swaminathan in all copies or substantial portions of the Software.
19*1fd5a2e1SPrashanth Swaminathan
20*1fd5a2e1SPrashanth Swaminathan THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
21*1fd5a2e1SPrashanth Swaminathan EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22*1fd5a2e1SPrashanth Swaminathan MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23*1fd5a2e1SPrashanth Swaminathan NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24*1fd5a2e1SPrashanth Swaminathan HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25*1fd5a2e1SPrashanth Swaminathan WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26*1fd5a2e1SPrashanth Swaminathan OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27*1fd5a2e1SPrashanth Swaminathan DEALINGS IN THE SOFTWARE.
28*1fd5a2e1SPrashanth Swaminathan ----------------------------------------------------------------------- */
29*1fd5a2e1SPrashanth Swaminathan
30*1fd5a2e1SPrashanth Swaminathan #include <ffi.h>
31*1fd5a2e1SPrashanth Swaminathan #include <ffi_common.h>
32*1fd5a2e1SPrashanth Swaminathan
33*1fd5a2e1SPrashanth Swaminathan #include <stdlib.h>
34*1fd5a2e1SPrashanth Swaminathan #include <stdint.h>
35*1fd5a2e1SPrashanth Swaminathan
36*1fd5a2e1SPrashanth Swaminathan #if __riscv_float_abi_double
37*1fd5a2e1SPrashanth Swaminathan #define ABI_FLEN 64
38*1fd5a2e1SPrashanth Swaminathan #define ABI_FLOAT double
39*1fd5a2e1SPrashanth Swaminathan #elif __riscv_float_abi_single
40*1fd5a2e1SPrashanth Swaminathan #define ABI_FLEN 32
41*1fd5a2e1SPrashanth Swaminathan #define ABI_FLOAT float
42*1fd5a2e1SPrashanth Swaminathan #endif
43*1fd5a2e1SPrashanth Swaminathan
44*1fd5a2e1SPrashanth Swaminathan #define NARGREG 8
45*1fd5a2e1SPrashanth Swaminathan #define STKALIGN 16
46*1fd5a2e1SPrashanth Swaminathan #define MAXCOPYARG (2 * sizeof(double))
47*1fd5a2e1SPrashanth Swaminathan
48*1fd5a2e1SPrashanth Swaminathan typedef struct call_context
49*1fd5a2e1SPrashanth Swaminathan {
50*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN
51*1fd5a2e1SPrashanth Swaminathan ABI_FLOAT fa[8];
52*1fd5a2e1SPrashanth Swaminathan #endif
53*1fd5a2e1SPrashanth Swaminathan size_t a[8];
54*1fd5a2e1SPrashanth Swaminathan /* used by the assembly code to in-place construct its own stack frame */
55*1fd5a2e1SPrashanth Swaminathan char frame[16];
56*1fd5a2e1SPrashanth Swaminathan } call_context;
57*1fd5a2e1SPrashanth Swaminathan
58*1fd5a2e1SPrashanth Swaminathan typedef struct call_builder
59*1fd5a2e1SPrashanth Swaminathan {
60*1fd5a2e1SPrashanth Swaminathan call_context *aregs;
61*1fd5a2e1SPrashanth Swaminathan int used_integer;
62*1fd5a2e1SPrashanth Swaminathan int used_float;
63*1fd5a2e1SPrashanth Swaminathan size_t *used_stack;
64*1fd5a2e1SPrashanth Swaminathan } call_builder;
65*1fd5a2e1SPrashanth Swaminathan
66*1fd5a2e1SPrashanth Swaminathan /* integer (not pointer) less than ABI XLEN */
67*1fd5a2e1SPrashanth Swaminathan /* FFI_TYPE_INT does not appear to be used */
68*1fd5a2e1SPrashanth Swaminathan #if __SIZEOF_POINTER__ == 8
69*1fd5a2e1SPrashanth Swaminathan #define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT64)
70*1fd5a2e1SPrashanth Swaminathan #else
71*1fd5a2e1SPrashanth Swaminathan #define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT32)
72*1fd5a2e1SPrashanth Swaminathan #endif
73*1fd5a2e1SPrashanth Swaminathan
74*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN
75*1fd5a2e1SPrashanth Swaminathan typedef struct {
76*1fd5a2e1SPrashanth Swaminathan char as_elements, type1, offset2, type2;
77*1fd5a2e1SPrashanth Swaminathan } float_struct_info;
78*1fd5a2e1SPrashanth Swaminathan
79*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN >= 64
80*1fd5a2e1SPrashanth Swaminathan #define IS_FLOAT(type) ((type) >= FFI_TYPE_FLOAT && (type) <= FFI_TYPE_DOUBLE)
81*1fd5a2e1SPrashanth Swaminathan #else
82*1fd5a2e1SPrashanth Swaminathan #define IS_FLOAT(type) ((type) == FFI_TYPE_FLOAT)
83*1fd5a2e1SPrashanth Swaminathan #endif
84*1fd5a2e1SPrashanth Swaminathan
flatten_struct(ffi_type * in,ffi_type ** out,ffi_type ** out_end)85*1fd5a2e1SPrashanth Swaminathan static ffi_type **flatten_struct(ffi_type *in, ffi_type **out, ffi_type **out_end) {
86*1fd5a2e1SPrashanth Swaminathan int i;
87*1fd5a2e1SPrashanth Swaminathan if (out == out_end) return out;
88*1fd5a2e1SPrashanth Swaminathan if (in->type != FFI_TYPE_STRUCT) {
89*1fd5a2e1SPrashanth Swaminathan *(out++) = in;
90*1fd5a2e1SPrashanth Swaminathan } else {
91*1fd5a2e1SPrashanth Swaminathan for (i = 0; in->elements[i]; i++)
92*1fd5a2e1SPrashanth Swaminathan out = flatten_struct(in->elements[i], out, out_end);
93*1fd5a2e1SPrashanth Swaminathan }
94*1fd5a2e1SPrashanth Swaminathan return out;
95*1fd5a2e1SPrashanth Swaminathan }
96*1fd5a2e1SPrashanth Swaminathan
97*1fd5a2e1SPrashanth Swaminathan /* Structs with at most two fields after flattening, one of which is of
98*1fd5a2e1SPrashanth Swaminathan floating point type, are passed in multiple registers if sufficient
99*1fd5a2e1SPrashanth Swaminathan registers are available. */
struct_passed_as_elements(call_builder * cb,ffi_type * top)100*1fd5a2e1SPrashanth Swaminathan static float_struct_info struct_passed_as_elements(call_builder *cb, ffi_type *top) {
101*1fd5a2e1SPrashanth Swaminathan float_struct_info ret = {0, 0, 0, 0};
102*1fd5a2e1SPrashanth Swaminathan ffi_type *fields[3];
103*1fd5a2e1SPrashanth Swaminathan int num_floats, num_ints;
104*1fd5a2e1SPrashanth Swaminathan int num_fields = flatten_struct(top, fields, fields + 3) - fields;
105*1fd5a2e1SPrashanth Swaminathan
106*1fd5a2e1SPrashanth Swaminathan if (num_fields == 1) {
107*1fd5a2e1SPrashanth Swaminathan if (IS_FLOAT(fields[0]->type)) {
108*1fd5a2e1SPrashanth Swaminathan ret.as_elements = 1;
109*1fd5a2e1SPrashanth Swaminathan ret.type1 = fields[0]->type;
110*1fd5a2e1SPrashanth Swaminathan }
111*1fd5a2e1SPrashanth Swaminathan } else if (num_fields == 2) {
112*1fd5a2e1SPrashanth Swaminathan num_floats = IS_FLOAT(fields[0]->type) + IS_FLOAT(fields[1]->type);
113*1fd5a2e1SPrashanth Swaminathan num_ints = IS_INT(fields[0]->type) + IS_INT(fields[1]->type);
114*1fd5a2e1SPrashanth Swaminathan if (num_floats == 0 || num_floats + num_ints != 2)
115*1fd5a2e1SPrashanth Swaminathan return ret;
116*1fd5a2e1SPrashanth Swaminathan if (cb->used_float + num_floats > NARGREG || cb->used_integer + (2 - num_floats) > NARGREG)
117*1fd5a2e1SPrashanth Swaminathan return ret;
118*1fd5a2e1SPrashanth Swaminathan if (!IS_FLOAT(fields[0]->type) && !IS_FLOAT(fields[1]->type))
119*1fd5a2e1SPrashanth Swaminathan return ret;
120*1fd5a2e1SPrashanth Swaminathan
121*1fd5a2e1SPrashanth Swaminathan ret.type1 = fields[0]->type;
122*1fd5a2e1SPrashanth Swaminathan ret.type2 = fields[1]->type;
123*1fd5a2e1SPrashanth Swaminathan ret.offset2 = FFI_ALIGN(fields[0]->size, fields[1]->alignment);
124*1fd5a2e1SPrashanth Swaminathan ret.as_elements = 1;
125*1fd5a2e1SPrashanth Swaminathan }
126*1fd5a2e1SPrashanth Swaminathan
127*1fd5a2e1SPrashanth Swaminathan return ret;
128*1fd5a2e1SPrashanth Swaminathan }
129*1fd5a2e1SPrashanth Swaminathan #endif
130*1fd5a2e1SPrashanth Swaminathan
131*1fd5a2e1SPrashanth Swaminathan /* allocates a single register, float register, or XLEN-sized stack slot to a datum */
marshal_atom(call_builder * cb,int type,void * data)132*1fd5a2e1SPrashanth Swaminathan static void marshal_atom(call_builder *cb, int type, void *data) {
133*1fd5a2e1SPrashanth Swaminathan size_t value = 0;
134*1fd5a2e1SPrashanth Swaminathan switch (type) {
135*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT8: value = *(uint8_t *)data; break;
136*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT8: value = *(int8_t *)data; break;
137*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT16: value = *(uint16_t *)data; break;
138*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT16: value = *(int16_t *)data; break;
139*1fd5a2e1SPrashanth Swaminathan /* 32-bit quantities are always sign-extended in the ABI */
140*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT32: value = *(int32_t *)data; break;
141*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT32: value = *(int32_t *)data; break;
142*1fd5a2e1SPrashanth Swaminathan #if __SIZEOF_POINTER__ == 8
143*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT64: value = *(uint64_t *)data; break;
144*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT64: value = *(int64_t *)data; break;
145*1fd5a2e1SPrashanth Swaminathan #endif
146*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_POINTER: value = *(size_t *)data; break;
147*1fd5a2e1SPrashanth Swaminathan
148*1fd5a2e1SPrashanth Swaminathan /* float values may be recoded in an implementation-defined way
149*1fd5a2e1SPrashanth Swaminathan by hardware conforming to 2.1 or earlier, so use asm to
150*1fd5a2e1SPrashanth Swaminathan reinterpret floats as doubles */
151*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN >= 32
152*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_FLOAT:
153*1fd5a2e1SPrashanth Swaminathan asm("" : "=f"(cb->aregs->fa[cb->used_float++]) : "0"(*(float *)data));
154*1fd5a2e1SPrashanth Swaminathan return;
155*1fd5a2e1SPrashanth Swaminathan #endif
156*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN >= 64
157*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_DOUBLE:
158*1fd5a2e1SPrashanth Swaminathan asm("" : "=f"(cb->aregs->fa[cb->used_float++]) : "0"(*(double *)data));
159*1fd5a2e1SPrashanth Swaminathan return;
160*1fd5a2e1SPrashanth Swaminathan #endif
161*1fd5a2e1SPrashanth Swaminathan default: FFI_ASSERT(0); break;
162*1fd5a2e1SPrashanth Swaminathan }
163*1fd5a2e1SPrashanth Swaminathan
164*1fd5a2e1SPrashanth Swaminathan if (cb->used_integer == NARGREG) {
165*1fd5a2e1SPrashanth Swaminathan *cb->used_stack++ = value;
166*1fd5a2e1SPrashanth Swaminathan } else {
167*1fd5a2e1SPrashanth Swaminathan cb->aregs->a[cb->used_integer++] = value;
168*1fd5a2e1SPrashanth Swaminathan }
169*1fd5a2e1SPrashanth Swaminathan }
170*1fd5a2e1SPrashanth Swaminathan
unmarshal_atom(call_builder * cb,int type,void * data)171*1fd5a2e1SPrashanth Swaminathan static void unmarshal_atom(call_builder *cb, int type, void *data) {
172*1fd5a2e1SPrashanth Swaminathan size_t value;
173*1fd5a2e1SPrashanth Swaminathan switch (type) {
174*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN >= 32
175*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_FLOAT:
176*1fd5a2e1SPrashanth Swaminathan asm("" : "=f"(*(float *)data) : "0"(cb->aregs->fa[cb->used_float++]));
177*1fd5a2e1SPrashanth Swaminathan return;
178*1fd5a2e1SPrashanth Swaminathan #endif
179*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN >= 64
180*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_DOUBLE:
181*1fd5a2e1SPrashanth Swaminathan asm("" : "=f"(*(double *)data) : "0"(cb->aregs->fa[cb->used_float++]));
182*1fd5a2e1SPrashanth Swaminathan return;
183*1fd5a2e1SPrashanth Swaminathan #endif
184*1fd5a2e1SPrashanth Swaminathan }
185*1fd5a2e1SPrashanth Swaminathan
186*1fd5a2e1SPrashanth Swaminathan if (cb->used_integer == NARGREG) {
187*1fd5a2e1SPrashanth Swaminathan value = *cb->used_stack++;
188*1fd5a2e1SPrashanth Swaminathan } else {
189*1fd5a2e1SPrashanth Swaminathan value = cb->aregs->a[cb->used_integer++];
190*1fd5a2e1SPrashanth Swaminathan }
191*1fd5a2e1SPrashanth Swaminathan
192*1fd5a2e1SPrashanth Swaminathan switch (type) {
193*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT8: *(uint8_t *)data = value; break;
194*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT8: *(uint8_t *)data = value; break;
195*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT16: *(uint16_t *)data = value; break;
196*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT16: *(uint16_t *)data = value; break;
197*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT32: *(uint32_t *)data = value; break;
198*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT32: *(uint32_t *)data = value; break;
199*1fd5a2e1SPrashanth Swaminathan #if __SIZEOF_POINTER__ == 8
200*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_UINT64: *(uint64_t *)data = value; break;
201*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_SINT64: *(uint64_t *)data = value; break;
202*1fd5a2e1SPrashanth Swaminathan #endif
203*1fd5a2e1SPrashanth Swaminathan case FFI_TYPE_POINTER: *(size_t *)data = value; break;
204*1fd5a2e1SPrashanth Swaminathan default: FFI_ASSERT(0); break;
205*1fd5a2e1SPrashanth Swaminathan }
206*1fd5a2e1SPrashanth Swaminathan }
207*1fd5a2e1SPrashanth Swaminathan
208*1fd5a2e1SPrashanth Swaminathan /* adds an argument to a call, or a not by reference return value */
marshal(call_builder * cb,ffi_type * type,int var,void * data)209*1fd5a2e1SPrashanth Swaminathan static void marshal(call_builder *cb, ffi_type *type, int var, void *data) {
210*1fd5a2e1SPrashanth Swaminathan size_t realign[2];
211*1fd5a2e1SPrashanth Swaminathan
212*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN
213*1fd5a2e1SPrashanth Swaminathan if (!var && type->type == FFI_TYPE_STRUCT) {
214*1fd5a2e1SPrashanth Swaminathan float_struct_info fsi = struct_passed_as_elements(cb, type);
215*1fd5a2e1SPrashanth Swaminathan if (fsi.as_elements) {
216*1fd5a2e1SPrashanth Swaminathan marshal_atom(cb, fsi.type1, data);
217*1fd5a2e1SPrashanth Swaminathan if (fsi.offset2)
218*1fd5a2e1SPrashanth Swaminathan marshal_atom(cb, fsi.type2, ((char*)data) + fsi.offset2);
219*1fd5a2e1SPrashanth Swaminathan return;
220*1fd5a2e1SPrashanth Swaminathan }
221*1fd5a2e1SPrashanth Swaminathan }
222*1fd5a2e1SPrashanth Swaminathan
223*1fd5a2e1SPrashanth Swaminathan if (!var && cb->used_float < NARGREG && IS_FLOAT(type->type)) {
224*1fd5a2e1SPrashanth Swaminathan marshal_atom(cb, type->type, data);
225*1fd5a2e1SPrashanth Swaminathan return;
226*1fd5a2e1SPrashanth Swaminathan }
227*1fd5a2e1SPrashanth Swaminathan #endif
228*1fd5a2e1SPrashanth Swaminathan
229*1fd5a2e1SPrashanth Swaminathan if (type->size > 2 * __SIZEOF_POINTER__) {
230*1fd5a2e1SPrashanth Swaminathan /* pass by reference */
231*1fd5a2e1SPrashanth Swaminathan marshal_atom(cb, FFI_TYPE_POINTER, &data);
232*1fd5a2e1SPrashanth Swaminathan } else if (IS_INT(type->type) || type->type == FFI_TYPE_POINTER) {
233*1fd5a2e1SPrashanth Swaminathan marshal_atom(cb, type->type, data);
234*1fd5a2e1SPrashanth Swaminathan } else {
235*1fd5a2e1SPrashanth Swaminathan /* overlong integers, soft-float floats, and structs without special
236*1fd5a2e1SPrashanth Swaminathan float handling are treated identically from this point on */
237*1fd5a2e1SPrashanth Swaminathan
238*1fd5a2e1SPrashanth Swaminathan /* variadics are aligned even in registers */
239*1fd5a2e1SPrashanth Swaminathan if (type->alignment > __SIZEOF_POINTER__) {
240*1fd5a2e1SPrashanth Swaminathan if (var)
241*1fd5a2e1SPrashanth Swaminathan cb->used_integer = FFI_ALIGN(cb->used_integer, 2);
242*1fd5a2e1SPrashanth Swaminathan cb->used_stack = (size_t *)FFI_ALIGN(cb->used_stack, 2*__SIZEOF_POINTER__);
243*1fd5a2e1SPrashanth Swaminathan }
244*1fd5a2e1SPrashanth Swaminathan
245*1fd5a2e1SPrashanth Swaminathan memcpy(realign, data, type->size);
246*1fd5a2e1SPrashanth Swaminathan if (type->size > 0)
247*1fd5a2e1SPrashanth Swaminathan marshal_atom(cb, FFI_TYPE_POINTER, realign);
248*1fd5a2e1SPrashanth Swaminathan if (type->size > __SIZEOF_POINTER__)
249*1fd5a2e1SPrashanth Swaminathan marshal_atom(cb, FFI_TYPE_POINTER, realign + 1);
250*1fd5a2e1SPrashanth Swaminathan }
251*1fd5a2e1SPrashanth Swaminathan }
252*1fd5a2e1SPrashanth Swaminathan
253*1fd5a2e1SPrashanth Swaminathan /* for arguments passed by reference returns the pointer, otherwise the arg is copied (up to MAXCOPYARG bytes) */
unmarshal(call_builder * cb,ffi_type * type,int var,void * data)254*1fd5a2e1SPrashanth Swaminathan static void *unmarshal(call_builder *cb, ffi_type *type, int var, void *data) {
255*1fd5a2e1SPrashanth Swaminathan size_t realign[2];
256*1fd5a2e1SPrashanth Swaminathan void *pointer;
257*1fd5a2e1SPrashanth Swaminathan
258*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN
259*1fd5a2e1SPrashanth Swaminathan if (!var && type->type == FFI_TYPE_STRUCT) {
260*1fd5a2e1SPrashanth Swaminathan float_struct_info fsi = struct_passed_as_elements(cb, type);
261*1fd5a2e1SPrashanth Swaminathan if (fsi.as_elements) {
262*1fd5a2e1SPrashanth Swaminathan unmarshal_atom(cb, fsi.type1, data);
263*1fd5a2e1SPrashanth Swaminathan if (fsi.offset2)
264*1fd5a2e1SPrashanth Swaminathan unmarshal_atom(cb, fsi.type2, ((char*)data) + fsi.offset2);
265*1fd5a2e1SPrashanth Swaminathan return data;
266*1fd5a2e1SPrashanth Swaminathan }
267*1fd5a2e1SPrashanth Swaminathan }
268*1fd5a2e1SPrashanth Swaminathan
269*1fd5a2e1SPrashanth Swaminathan if (!var && cb->used_float < NARGREG && IS_FLOAT(type->type)) {
270*1fd5a2e1SPrashanth Swaminathan unmarshal_atom(cb, type->type, data);
271*1fd5a2e1SPrashanth Swaminathan return data;
272*1fd5a2e1SPrashanth Swaminathan }
273*1fd5a2e1SPrashanth Swaminathan #endif
274*1fd5a2e1SPrashanth Swaminathan
275*1fd5a2e1SPrashanth Swaminathan if (type->size > 2 * __SIZEOF_POINTER__) {
276*1fd5a2e1SPrashanth Swaminathan /* pass by reference */
277*1fd5a2e1SPrashanth Swaminathan unmarshal_atom(cb, FFI_TYPE_POINTER, (char*)&pointer);
278*1fd5a2e1SPrashanth Swaminathan return pointer;
279*1fd5a2e1SPrashanth Swaminathan } else if (IS_INT(type->type) || type->type == FFI_TYPE_POINTER) {
280*1fd5a2e1SPrashanth Swaminathan unmarshal_atom(cb, type->type, data);
281*1fd5a2e1SPrashanth Swaminathan return data;
282*1fd5a2e1SPrashanth Swaminathan } else {
283*1fd5a2e1SPrashanth Swaminathan /* overlong integers, soft-float floats, and structs without special
284*1fd5a2e1SPrashanth Swaminathan float handling are treated identically from this point on */
285*1fd5a2e1SPrashanth Swaminathan
286*1fd5a2e1SPrashanth Swaminathan /* variadics are aligned even in registers */
287*1fd5a2e1SPrashanth Swaminathan if (type->alignment > __SIZEOF_POINTER__) {
288*1fd5a2e1SPrashanth Swaminathan if (var)
289*1fd5a2e1SPrashanth Swaminathan cb->used_integer = FFI_ALIGN(cb->used_integer, 2);
290*1fd5a2e1SPrashanth Swaminathan cb->used_stack = (size_t *)FFI_ALIGN(cb->used_stack, 2*__SIZEOF_POINTER__);
291*1fd5a2e1SPrashanth Swaminathan }
292*1fd5a2e1SPrashanth Swaminathan
293*1fd5a2e1SPrashanth Swaminathan if (type->size > 0)
294*1fd5a2e1SPrashanth Swaminathan unmarshal_atom(cb, FFI_TYPE_POINTER, realign);
295*1fd5a2e1SPrashanth Swaminathan if (type->size > __SIZEOF_POINTER__)
296*1fd5a2e1SPrashanth Swaminathan unmarshal_atom(cb, FFI_TYPE_POINTER, realign + 1);
297*1fd5a2e1SPrashanth Swaminathan memcpy(data, realign, type->size);
298*1fd5a2e1SPrashanth Swaminathan return data;
299*1fd5a2e1SPrashanth Swaminathan }
300*1fd5a2e1SPrashanth Swaminathan }
301*1fd5a2e1SPrashanth Swaminathan
passed_by_ref(call_builder * cb,ffi_type * type,int var)302*1fd5a2e1SPrashanth Swaminathan static int passed_by_ref(call_builder *cb, ffi_type *type, int var) {
303*1fd5a2e1SPrashanth Swaminathan #if ABI_FLEN
304*1fd5a2e1SPrashanth Swaminathan if (!var && type->type == FFI_TYPE_STRUCT) {
305*1fd5a2e1SPrashanth Swaminathan float_struct_info fsi = struct_passed_as_elements(cb, type);
306*1fd5a2e1SPrashanth Swaminathan if (fsi.as_elements) return 0;
307*1fd5a2e1SPrashanth Swaminathan }
308*1fd5a2e1SPrashanth Swaminathan #endif
309*1fd5a2e1SPrashanth Swaminathan
310*1fd5a2e1SPrashanth Swaminathan return type->size > 2 * __SIZEOF_POINTER__;
311*1fd5a2e1SPrashanth Swaminathan }
312*1fd5a2e1SPrashanth Swaminathan
313*1fd5a2e1SPrashanth Swaminathan /* Perform machine dependent cif processing */
ffi_prep_cif_machdep(ffi_cif * cif)314*1fd5a2e1SPrashanth Swaminathan ffi_status ffi_prep_cif_machdep(ffi_cif *cif) {
315*1fd5a2e1SPrashanth Swaminathan cif->riscv_nfixedargs = cif->nargs;
316*1fd5a2e1SPrashanth Swaminathan return FFI_OK;
317*1fd5a2e1SPrashanth Swaminathan }
318*1fd5a2e1SPrashanth Swaminathan
319*1fd5a2e1SPrashanth Swaminathan /* Perform machine dependent cif processing when we have a variadic function */
320*1fd5a2e1SPrashanth Swaminathan
ffi_prep_cif_machdep_var(ffi_cif * cif,unsigned int nfixedargs,unsigned int ntotalargs)321*1fd5a2e1SPrashanth Swaminathan ffi_status ffi_prep_cif_machdep_var(ffi_cif *cif, unsigned int nfixedargs, unsigned int ntotalargs) {
322*1fd5a2e1SPrashanth Swaminathan cif->riscv_nfixedargs = nfixedargs;
323*1fd5a2e1SPrashanth Swaminathan return FFI_OK;
324*1fd5a2e1SPrashanth Swaminathan }
325*1fd5a2e1SPrashanth Swaminathan
326*1fd5a2e1SPrashanth Swaminathan /* Low level routine for calling functions */
327*1fd5a2e1SPrashanth Swaminathan extern void ffi_call_asm (void *stack, struct call_context *regs,
328*1fd5a2e1SPrashanth Swaminathan void (*fn) (void), void *closure) FFI_HIDDEN;
329*1fd5a2e1SPrashanth Swaminathan
330*1fd5a2e1SPrashanth Swaminathan static void
ffi_call_int(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue,void * closure)331*1fd5a2e1SPrashanth Swaminathan ffi_call_int (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue,
332*1fd5a2e1SPrashanth Swaminathan void *closure)
333*1fd5a2e1SPrashanth Swaminathan {
334*1fd5a2e1SPrashanth Swaminathan /* this is a conservative estimate, assuming a complex return value and
335*1fd5a2e1SPrashanth Swaminathan that all remaining arguments are long long / __int128 */
336*1fd5a2e1SPrashanth Swaminathan size_t arg_bytes = cif->nargs <= 3 ? 0 :
337*1fd5a2e1SPrashanth Swaminathan FFI_ALIGN(2 * sizeof(size_t) * (cif->nargs - 3), STKALIGN);
338*1fd5a2e1SPrashanth Swaminathan size_t rval_bytes = 0;
339*1fd5a2e1SPrashanth Swaminathan if (rvalue == NULL && cif->rtype->size > 2*__SIZEOF_POINTER__)
340*1fd5a2e1SPrashanth Swaminathan rval_bytes = FFI_ALIGN(cif->rtype->size, STKALIGN);
341*1fd5a2e1SPrashanth Swaminathan size_t alloc_size = arg_bytes + rval_bytes + sizeof(call_context);
342*1fd5a2e1SPrashanth Swaminathan
343*1fd5a2e1SPrashanth Swaminathan /* the assembly code will deallocate all stack data at lower addresses
344*1fd5a2e1SPrashanth Swaminathan than the argument region, so we need to allocate the frame and the
345*1fd5a2e1SPrashanth Swaminathan return value after the arguments in a single allocation */
346*1fd5a2e1SPrashanth Swaminathan size_t alloc_base;
347*1fd5a2e1SPrashanth Swaminathan /* Argument region must be 16-byte aligned */
348*1fd5a2e1SPrashanth Swaminathan if (_Alignof(max_align_t) >= STKALIGN) {
349*1fd5a2e1SPrashanth Swaminathan /* since sizeof long double is normally 16, the compiler will
350*1fd5a2e1SPrashanth Swaminathan guarantee alloca alignment to at least that much */
351*1fd5a2e1SPrashanth Swaminathan alloc_base = (size_t)alloca(alloc_size);
352*1fd5a2e1SPrashanth Swaminathan } else {
353*1fd5a2e1SPrashanth Swaminathan alloc_base = FFI_ALIGN(alloca(alloc_size + STKALIGN - 1), STKALIGN);
354*1fd5a2e1SPrashanth Swaminathan }
355*1fd5a2e1SPrashanth Swaminathan
356*1fd5a2e1SPrashanth Swaminathan if (rval_bytes)
357*1fd5a2e1SPrashanth Swaminathan rvalue = (void*)(alloc_base + arg_bytes);
358*1fd5a2e1SPrashanth Swaminathan
359*1fd5a2e1SPrashanth Swaminathan call_builder cb;
360*1fd5a2e1SPrashanth Swaminathan cb.used_float = cb.used_integer = 0;
361*1fd5a2e1SPrashanth Swaminathan cb.aregs = (call_context*)(alloc_base + arg_bytes + rval_bytes);
362*1fd5a2e1SPrashanth Swaminathan cb.used_stack = (void*)alloc_base;
363*1fd5a2e1SPrashanth Swaminathan
364*1fd5a2e1SPrashanth Swaminathan int return_by_ref = passed_by_ref(&cb, cif->rtype, 0);
365*1fd5a2e1SPrashanth Swaminathan if (return_by_ref)
366*1fd5a2e1SPrashanth Swaminathan marshal(&cb, &ffi_type_pointer, 0, &rvalue);
367*1fd5a2e1SPrashanth Swaminathan
368*1fd5a2e1SPrashanth Swaminathan int i;
369*1fd5a2e1SPrashanth Swaminathan for (i = 0; i < cif->nargs; i++)
370*1fd5a2e1SPrashanth Swaminathan marshal(&cb, cif->arg_types[i], i >= cif->riscv_nfixedargs, avalue[i]);
371*1fd5a2e1SPrashanth Swaminathan
372*1fd5a2e1SPrashanth Swaminathan ffi_call_asm ((void *) alloc_base, cb.aregs, fn, closure);
373*1fd5a2e1SPrashanth Swaminathan
374*1fd5a2e1SPrashanth Swaminathan cb.used_float = cb.used_integer = 0;
375*1fd5a2e1SPrashanth Swaminathan if (!return_by_ref && rvalue)
376*1fd5a2e1SPrashanth Swaminathan unmarshal(&cb, cif->rtype, 0, rvalue);
377*1fd5a2e1SPrashanth Swaminathan }
378*1fd5a2e1SPrashanth Swaminathan
379*1fd5a2e1SPrashanth Swaminathan void
ffi_call(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue)380*1fd5a2e1SPrashanth Swaminathan ffi_call (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue)
381*1fd5a2e1SPrashanth Swaminathan {
382*1fd5a2e1SPrashanth Swaminathan ffi_call_int(cif, fn, rvalue, avalue, NULL);
383*1fd5a2e1SPrashanth Swaminathan }
384*1fd5a2e1SPrashanth Swaminathan
385*1fd5a2e1SPrashanth Swaminathan void
ffi_call_go(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue,void * closure)386*1fd5a2e1SPrashanth Swaminathan ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue,
387*1fd5a2e1SPrashanth Swaminathan void **avalue, void *closure)
388*1fd5a2e1SPrashanth Swaminathan {
389*1fd5a2e1SPrashanth Swaminathan ffi_call_int(cif, fn, rvalue, avalue, closure);
390*1fd5a2e1SPrashanth Swaminathan }
391*1fd5a2e1SPrashanth Swaminathan
392*1fd5a2e1SPrashanth Swaminathan extern void ffi_closure_asm(void) FFI_HIDDEN;
393*1fd5a2e1SPrashanth Swaminathan
ffi_prep_closure_loc(ffi_closure * closure,ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *),void * user_data,void * codeloc)394*1fd5a2e1SPrashanth Swaminathan ffi_status ffi_prep_closure_loc(ffi_closure *closure, ffi_cif *cif, void (*fun)(ffi_cif*,void*,void**,void*), void *user_data, void *codeloc)
395*1fd5a2e1SPrashanth Swaminathan {
396*1fd5a2e1SPrashanth Swaminathan uint32_t *tramp = (uint32_t *) &closure->tramp[0];
397*1fd5a2e1SPrashanth Swaminathan uint64_t fn = (uint64_t) (uintptr_t) ffi_closure_asm;
398*1fd5a2e1SPrashanth Swaminathan
399*1fd5a2e1SPrashanth Swaminathan if (cif->abi <= FFI_FIRST_ABI || cif->abi >= FFI_LAST_ABI)
400*1fd5a2e1SPrashanth Swaminathan return FFI_BAD_ABI;
401*1fd5a2e1SPrashanth Swaminathan
402*1fd5a2e1SPrashanth Swaminathan /* we will call ffi_closure_inner with codeloc, not closure, but as long
403*1fd5a2e1SPrashanth Swaminathan as the memory is readable it should work */
404*1fd5a2e1SPrashanth Swaminathan
405*1fd5a2e1SPrashanth Swaminathan tramp[0] = 0x00000317; /* auipc t1, 0 (i.e. t0 <- codeloc) */
406*1fd5a2e1SPrashanth Swaminathan #if __SIZEOF_POINTER__ == 8
407*1fd5a2e1SPrashanth Swaminathan tramp[1] = 0x01033383; /* ld t2, 16(t1) */
408*1fd5a2e1SPrashanth Swaminathan #else
409*1fd5a2e1SPrashanth Swaminathan tramp[1] = 0x01032383; /* lw t2, 16(t1) */
410*1fd5a2e1SPrashanth Swaminathan #endif
411*1fd5a2e1SPrashanth Swaminathan tramp[2] = 0x00038067; /* jr t2 */
412*1fd5a2e1SPrashanth Swaminathan tramp[3] = 0x00000013; /* nop */
413*1fd5a2e1SPrashanth Swaminathan tramp[4] = fn;
414*1fd5a2e1SPrashanth Swaminathan tramp[5] = fn >> 32;
415*1fd5a2e1SPrashanth Swaminathan
416*1fd5a2e1SPrashanth Swaminathan closure->cif = cif;
417*1fd5a2e1SPrashanth Swaminathan closure->fun = fun;
418*1fd5a2e1SPrashanth Swaminathan closure->user_data = user_data;
419*1fd5a2e1SPrashanth Swaminathan
420*1fd5a2e1SPrashanth Swaminathan __builtin___clear_cache(codeloc, codeloc + FFI_TRAMPOLINE_SIZE);
421*1fd5a2e1SPrashanth Swaminathan
422*1fd5a2e1SPrashanth Swaminathan return FFI_OK;
423*1fd5a2e1SPrashanth Swaminathan }
424*1fd5a2e1SPrashanth Swaminathan
425*1fd5a2e1SPrashanth Swaminathan extern void ffi_go_closure_asm (void) FFI_HIDDEN;
426*1fd5a2e1SPrashanth Swaminathan
427*1fd5a2e1SPrashanth Swaminathan ffi_status
ffi_prep_go_closure(ffi_go_closure * closure,ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *))428*1fd5a2e1SPrashanth Swaminathan ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif,
429*1fd5a2e1SPrashanth Swaminathan void (*fun) (ffi_cif *, void *, void **, void *))
430*1fd5a2e1SPrashanth Swaminathan {
431*1fd5a2e1SPrashanth Swaminathan if (cif->abi <= FFI_FIRST_ABI || cif->abi >= FFI_LAST_ABI)
432*1fd5a2e1SPrashanth Swaminathan return FFI_BAD_ABI;
433*1fd5a2e1SPrashanth Swaminathan
434*1fd5a2e1SPrashanth Swaminathan closure->tramp = (void *) ffi_go_closure_asm;
435*1fd5a2e1SPrashanth Swaminathan closure->cif = cif;
436*1fd5a2e1SPrashanth Swaminathan closure->fun = fun;
437*1fd5a2e1SPrashanth Swaminathan
438*1fd5a2e1SPrashanth Swaminathan return FFI_OK;
439*1fd5a2e1SPrashanth Swaminathan }
440*1fd5a2e1SPrashanth Swaminathan
441*1fd5a2e1SPrashanth Swaminathan /* Called by the assembly code with aregs pointing to saved argument registers
442*1fd5a2e1SPrashanth Swaminathan and stack pointing to the stacked arguments. Return values passed in
443*1fd5a2e1SPrashanth Swaminathan registers will be reloaded from aregs. */
444*1fd5a2e1SPrashanth Swaminathan void FFI_HIDDEN
ffi_closure_inner(ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *),void * user_data,size_t * stack,call_context * aregs)445*1fd5a2e1SPrashanth Swaminathan ffi_closure_inner (ffi_cif *cif,
446*1fd5a2e1SPrashanth Swaminathan void (*fun) (ffi_cif *, void *, void **, void *),
447*1fd5a2e1SPrashanth Swaminathan void *user_data,
448*1fd5a2e1SPrashanth Swaminathan size_t *stack, call_context *aregs)
449*1fd5a2e1SPrashanth Swaminathan {
450*1fd5a2e1SPrashanth Swaminathan void **avalue = alloca(cif->nargs * sizeof(void*));
451*1fd5a2e1SPrashanth Swaminathan /* storage for arguments which will be copied by unmarshal(). We could
452*1fd5a2e1SPrashanth Swaminathan theoretically avoid the copies in many cases and use at most 128 bytes
453*1fd5a2e1SPrashanth Swaminathan of memory, but allocating disjoint storage for each argument is
454*1fd5a2e1SPrashanth Swaminathan simpler. */
455*1fd5a2e1SPrashanth Swaminathan char *astorage = alloca(cif->nargs * MAXCOPYARG);
456*1fd5a2e1SPrashanth Swaminathan void *rvalue;
457*1fd5a2e1SPrashanth Swaminathan call_builder cb;
458*1fd5a2e1SPrashanth Swaminathan int return_by_ref;
459*1fd5a2e1SPrashanth Swaminathan int i;
460*1fd5a2e1SPrashanth Swaminathan
461*1fd5a2e1SPrashanth Swaminathan cb.aregs = aregs;
462*1fd5a2e1SPrashanth Swaminathan cb.used_integer = cb.used_float = 0;
463*1fd5a2e1SPrashanth Swaminathan cb.used_stack = stack;
464*1fd5a2e1SPrashanth Swaminathan
465*1fd5a2e1SPrashanth Swaminathan return_by_ref = passed_by_ref(&cb, cif->rtype, 0);
466*1fd5a2e1SPrashanth Swaminathan if (return_by_ref)
467*1fd5a2e1SPrashanth Swaminathan unmarshal(&cb, &ffi_type_pointer, 0, &rvalue);
468*1fd5a2e1SPrashanth Swaminathan else
469*1fd5a2e1SPrashanth Swaminathan rvalue = alloca(cif->rtype->size);
470*1fd5a2e1SPrashanth Swaminathan
471*1fd5a2e1SPrashanth Swaminathan for (i = 0; i < cif->nargs; i++)
472*1fd5a2e1SPrashanth Swaminathan avalue[i] = unmarshal(&cb, cif->arg_types[i],
473*1fd5a2e1SPrashanth Swaminathan i >= cif->riscv_nfixedargs, astorage + i*MAXCOPYARG);
474*1fd5a2e1SPrashanth Swaminathan
475*1fd5a2e1SPrashanth Swaminathan fun (cif, rvalue, avalue, user_data);
476*1fd5a2e1SPrashanth Swaminathan
477*1fd5a2e1SPrashanth Swaminathan if (!return_by_ref && cif->rtype->type != FFI_TYPE_VOID) {
478*1fd5a2e1SPrashanth Swaminathan cb.used_integer = cb.used_float = 0;
479*1fd5a2e1SPrashanth Swaminathan marshal(&cb, cif->rtype, 0, rvalue);
480*1fd5a2e1SPrashanth Swaminathan }
481*1fd5a2e1SPrashanth Swaminathan }
482