3 * gsharedvt support code for arm64
6 * Zoltan Varga <vargaz@gmail.com>
8 * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include "mini-arm64.h"
13 #include "mini-arm64-gsharedvt.h"
14 #include "aot-runtime.h"
19 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
22 mono_arm_gsharedvt_init (void)
27 mono_arch_gsharedvt_sig_supported (MonoMethodSignature
*sig
)
30 if (sig->ret && is_variable_size (sig->ret))
37 add_to_map (GPtrArray
*map
, int src
, int dst
)
39 g_ptr_array_add (map
, GUINT_TO_POINTER (src
));
40 g_ptr_array_add (map
, GUINT_TO_POINTER (dst
));
59 return reg
+ NUM_GSHAREDVT_ARG_GREGS
;
63 map_stack_slot (int slot
)
65 return slot
+ NUM_GSHAREDVT_ARG_GREGS
+ NUM_GSHAREDVT_ARG_FREGS
;
69 get_arg_slots (ArgInfo
*ainfo
, int **out_slots
)
71 int sreg
= ainfo
->reg
;
72 int sslot
= ainfo
->offset
/ 8;
76 switch (ainfo
->storage
) {
80 src
= g_malloc (nsrc
* sizeof (int));
81 src
[0] = map_reg (sreg
);
83 case ArgVtypeByRefOnStack
:
85 src
= g_malloc (nsrc
* sizeof (int));
86 src
[0] = map_stack_slot (sslot
);
91 src
= g_malloc (nsrc
* sizeof (int));
92 src
[0] = map_freg (sreg
);
96 src
= g_malloc (nsrc
* sizeof (int));
97 for (i
= 0; i
< ainfo
->nregs
; ++i
)
98 src
[i
] = map_freg (sreg
+ i
);
100 case ArgVtypeInIRegs
:
102 src
= g_malloc (nsrc
* sizeof (int));
103 for (i
= 0; i
< ainfo
->nregs
; ++i
)
104 src
[i
] = map_reg (sreg
+ i
);
110 src
= g_malloc (nsrc
* sizeof (int));
111 src
[0] = map_stack_slot (sslot
);
113 case ArgVtypeOnStack
:
114 nsrc
= ainfo
->size
/ 8;
115 src
= g_malloc (nsrc
* sizeof (int));
116 for (i
= 0; i
< nsrc
; ++i
)
117 src
[i
] = map_stack_slot (sslot
+ i
);
129 * mono_arch_get_gsharedvt_call_info:
131 * See mini-x86.c for documentation.
134 mono_arch_get_gsharedvt_call_info (gpointer addr
, MonoMethodSignature
*normal_sig
, MonoMethodSignature
*gsharedvt_sig
, gboolean gsharedvt_in
, gint32 vcall_offset
, gboolean calli
)
136 GSharedVtCallInfo
*info
;
137 CallInfo
*caller_cinfo
, *callee_cinfo
;
138 MonoMethodSignature
*caller_sig
, *callee_sig
;
140 gboolean var_ret
= FALSE
;
141 CallInfo
*cinfo
, *gcinfo
;
142 MonoMethodSignature
*sig
, *gsig
;
146 caller_sig
= normal_sig
;
147 callee_sig
= gsharedvt_sig
;
148 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
149 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
151 callee_sig
= normal_sig
;
152 caller_sig
= gsharedvt_sig
;
153 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
154 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
158 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
159 * normal call signature, while the callee uses the gsharedvt signature.
160 * If GSHAREDVT_IN is false, its the other way around.
163 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
167 cinfo
= caller_cinfo
;
168 gcinfo
= callee_cinfo
;
172 cinfo
= callee_cinfo
;
173 gcinfo
= caller_cinfo
;
176 if (gcinfo
->ret
.gsharedvt
) {
178 * The return type is gsharedvt
184 * The stack looks like this:
188 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
190 map
= g_ptr_array_new ();
192 for (aindex
= 0; aindex
< cinfo
->nargs
; ++aindex
) {
193 ArgInfo
*ainfo
= &caller_cinfo
->args
[aindex
];
194 ArgInfo
*ainfo2
= &callee_cinfo
->args
[aindex
];
195 int *src
= NULL
, *dst
= NULL
;
196 int nsrc
, ndst
, nslots
, src_slot
, arg_marshal
;
199 * The src descriptor looks like this:
201 * - 12 bits number of slots
202 * - 4 bits marshal type (GSHAREDVT_ARG_...)
203 * - 4 bits size/sign descriptor (GSHAREDVT_ARG_SIZE)
204 * - 4 bits offset inside stack slots
206 arg_marshal
= GSHAREDVT_ARG_NONE
;
208 if (ainfo
->gsharedvt
) {
209 /* Pass the value whose address is received in a reg by value */
210 g_assert (!ainfo2
->gsharedvt
);
211 ndst
= get_arg_slots (ainfo2
, &dst
);
213 src
= g_new0 (int, 1);
214 if (ainfo
->storage
== ArgVtypeByRef
)
215 src_slot
= map_reg (ainfo
->reg
);
217 src_slot
= map_stack_slot (ainfo
->offset
/ sizeof (target_mgreg_t
));
218 g_assert (ndst
< 256);
219 g_assert (src_slot
< 64);
220 src
[0] = (ndst
<< 6) | src_slot
;
221 if (ainfo2
->storage
== ArgHFA
&& ainfo2
->esize
== 4)
222 arg_marshal
= GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4
;
223 else if (ainfo2
->storage
== ArgVtypeByRef
|| ainfo2
->storage
== ArgVtypeByRefOnStack
)
224 arg_marshal
= GSHAREDVT_ARG_BYREF_TO_BYREF
;
226 arg_marshal
= GSHAREDVT_ARG_BYREF_TO_BYVAL
;
228 nsrc
= get_arg_slots (ainfo
, &src
);
230 if (ainfo2
->storage
== ArgVtypeByRef
&& ainfo2
->gsharedvt
) {
231 /* Pass the address of the first src slot in a reg */
232 if (ainfo
->storage
!= ArgVtypeByRef
) {
233 if (ainfo
->storage
== ArgHFA
&& ainfo
->esize
== 4) {
234 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4
;
235 g_assert (src
[0] < 64);
236 g_assert (nsrc
< 256);
237 src
[0] |= (nsrc
<< 6);
239 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF
;
243 dst
= g_new0 (int, 1);
244 dst
[0] = map_reg (ainfo2
->reg
);
245 } else if (ainfo2
->storage
== ArgVtypeByRefOnStack
&& ainfo2
->gsharedvt
) {
246 /* Pass the address of the first src slot in a stack slot */
247 if (ainfo
->storage
!= ArgVtypeByRef
)
248 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF
;
250 dst
= g_new0 (int, 1);
251 dst
[0] = map_stack_slot (ainfo2
->offset
/ sizeof (target_mgreg_t
));
253 ndst
= get_arg_slots (ainfo2
, &dst
);
256 src
[0] |= (arg_marshal
<< 18);
257 if ((ainfo
->storage
== ArgOnStack
|| ainfo
->storage
== ArgOnStackR4
) && ainfo
->slot_size
!= 8) {
258 GSharedVtArgSize arg_size
= GSHAREDVT_ARG_SIZE_NONE
;
261 * On IOS, stack arguments smaller than 8 bytes can
262 * share a stack slot. Encode this information into
265 switch (ainfo
->slot_size
) {
267 arg_size
= ainfo
->sign
? GSHAREDVT_ARG_SIZE_I1
: GSHAREDVT_ARG_SIZE_U1
;
270 arg_size
= ainfo
->sign
? GSHAREDVT_ARG_SIZE_I2
: GSHAREDVT_ARG_SIZE_U2
;
273 arg_size
= ainfo
->sign
? GSHAREDVT_ARG_SIZE_I4
: GSHAREDVT_ARG_SIZE_U4
;
279 /* Encode the size/sign */
280 src
[0] |= (arg_size
<< 22);
281 /* Encode the offset inside the stack slot */
282 src
[0] |= ((ainfo
->offset
% 8) << 26);
283 if (ainfo2
->storage
== ArgOnStack
|| ainfo2
->storage
== ArgOnStackR4
)
284 dst
[0] |= ((ainfo2
->offset
% 8) << 26);
285 } else if (ainfo2
->storage
== ArgOnStack
&& ainfo2
->slot_size
!= 8) {
286 /* The caller passes in an address, need to store it into a stack slot */
288 GSharedVtArgSize arg_size
= GSHAREDVT_ARG_SIZE_NONE
;
289 switch (ainfo2
->slot_size
) {
291 arg_size
= ainfo2
->sign
? GSHAREDVT_ARG_SIZE_I1
: GSHAREDVT_ARG_SIZE_U1
;
294 arg_size
= ainfo2
->sign
? GSHAREDVT_ARG_SIZE_I2
: GSHAREDVT_ARG_SIZE_U2
;
297 arg_size
= ainfo2
->sign
? GSHAREDVT_ARG_SIZE_I4
: GSHAREDVT_ARG_SIZE_U4
;
303 /* Encode the size/sign */
304 src
[0] |= (arg_size
<< 22);
305 /* Encode the offset inside the stack slot */
306 dst
[0] |= ((ainfo2
->offset
% 8) << 26);
308 nslots
= MIN (nsrc
, ndst
);
310 for (i
= 0; i
< nslots
; ++i
)
311 add_to_map (map
, src
[i
], dst
[i
]);
317 if (cinfo
->ret
.storage
== ArgVtypeByRef
) {
318 /* Both the caller and the callee pass the vtype ret address in r8 */
319 g_assert (cinfo
->ret
.storage
== gcinfo
->ret
.storage
);
320 add_to_map (map
, map_reg (ARMREG_R8
), map_reg (ARMREG_R8
));
323 info
= mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo
) + (map
->len
* sizeof (int)));
325 info
->stack_usage
= callee_cinfo
->stack_usage
;
326 info
->ret_marshal
= GSHAREDVT_RET_NONE
;
327 info
->gsharedvt_in
= gsharedvt_in
? 1 : 0;
328 info
->vret_slot
= -1;
332 g_assert (gcinfo
->ret
.gsharedvt
);
333 info
->vret_arg_reg
= map_reg (ARMREG_R8
);
335 info
->vret_arg_reg
= -1;
338 info
->vcall_offset
= vcall_offset
;
339 info
->map_count
= map
->len
/ 2;
340 for (i
= 0; i
< map
->len
; ++i
)
341 info
->map
[i
] = GPOINTER_TO_UINT (g_ptr_array_index (map
, i
));
342 g_ptr_array_free (map
, TRUE
);
344 /* Compute return value marshalling */
346 switch (cinfo
->ret
.storage
) {
348 if (!gsharedvt_in
|| sig
->ret
->byref
) {
349 info
->ret_marshal
= GSHAREDVT_RET_I8
;
351 MonoType
*rtype
= mini_get_underlying_type (sig
->ret
);
353 switch (rtype
->type
) {
355 info
->ret_marshal
= GSHAREDVT_RET_I1
;
358 info
->ret_marshal
= GSHAREDVT_RET_U1
;
361 info
->ret_marshal
= GSHAREDVT_RET_I2
;
364 info
->ret_marshal
= GSHAREDVT_RET_U2
;
367 info
->ret_marshal
= GSHAREDVT_RET_I4
;
370 info
->ret_marshal
= GSHAREDVT_RET_U4
;
373 info
->ret_marshal
= GSHAREDVT_RET_I8
;
379 info
->ret_marshal
= GSHAREDVT_RET_R8
;
382 info
->ret_marshal
= GSHAREDVT_RET_R4
;
384 case ArgVtypeInIRegs
:
385 info
->ret_marshal
= GSHAREDVT_RET_IREGS_1
- 1 + cinfo
->ret
.nregs
;
388 if (cinfo
->ret
.esize
== 4)
389 info
->ret_marshal
= GSHAREDVT_RET_HFAR4_1
- 1 + cinfo
->ret
.nregs
;
391 info
->ret_marshal
= GSHAREDVT_RET_HFAR8_1
- 1 + cinfo
->ret
.nregs
;
394 /* No conversion needed */
397 g_assert_not_reached ();
401 if (gsharedvt_in
&& var_ret
&& cinfo
->ret
.storage
!= ArgVtypeByRef
) {
402 /* Allocate stack space for the return value */
403 info
->vret_slot
= map_stack_slot (info
->stack_usage
/ sizeof (target_mgreg_t
));
404 info
->stack_usage
+= mono_type_stack_size_internal (normal_sig
->ret
, NULL
, FALSE
) + sizeof (target_mgreg_t
);
407 info
->stack_usage
= ALIGN_TO (info
->stack_usage
, MONO_ARCH_FRAME_ALIGNMENT
);
415 mono_arm_gsharedvt_init (void)
419 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */