2 * mini-arm64-gsharedvt.c: gsharedvt support code for arm64
5 * Zoltan Varga <vargaz@gmail.com>
7 * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 #include "mini-arm64.h"
12 #include "mini-arm64-gsharedvt.h"
17 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
19 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
22 mono_arm_gsharedvt_init (void)
24 mono_aot_register_jit_icall ("mono_arm_start_gsharedvt_call", mono_arm_start_gsharedvt_call
);
28 mono_arch_gsharedvt_sig_supported (MonoMethodSignature
*sig
)
31 if (sig->ret && is_variable_size (sig->ret))
38 add_to_map (GPtrArray
*map
, int src
, int dst
)
40 g_ptr_array_add (map
, GUINT_TO_POINTER (src
));
41 g_ptr_array_add (map
, GUINT_TO_POINTER (dst
));
60 return reg
+ NUM_GSHAREDVT_ARG_GREGS
;
64 map_stack_slot (int slot
)
66 return slot
+ NUM_GSHAREDVT_ARG_GREGS
+ NUM_GSHAREDVT_ARG_FREGS
;
70 get_arg_slots (ArgInfo
*ainfo
, int **out_slots
)
72 int sreg
= ainfo
->reg
;
73 int sslot
= ainfo
->offset
/ 8;
77 switch (ainfo
->storage
) {
81 src
= g_malloc (nsrc
* sizeof (int));
82 src
[0] = map_reg (sreg
);
84 case ArgVtypeByRefOnStack
:
86 src
= g_malloc (nsrc
* sizeof (int));
87 src
[0] = map_stack_slot (sslot
);
92 src
= g_malloc (nsrc
* sizeof (int));
93 src
[0] = map_freg (sreg
);
97 src
= g_malloc (nsrc
* sizeof (int));
98 for (i
= 0; i
< ainfo
->nregs
; ++i
)
99 src
[i
] = map_freg (sreg
+ i
);
101 case ArgVtypeInIRegs
:
103 src
= g_malloc (nsrc
* sizeof (int));
104 for (i
= 0; i
< ainfo
->nregs
; ++i
)
105 src
[i
] = map_reg (sreg
+ i
);
109 src
= g_malloc (nsrc
* sizeof (int));
110 src
[0] = map_stack_slot (sslot
);
112 case ArgVtypeOnStack
:
113 nsrc
= ainfo
->size
/ 8;
114 src
= g_malloc (nsrc
* sizeof (int));
115 for (i
= 0; i
< nsrc
; ++i
)
116 src
[i
] = map_stack_slot (sslot
+ i
);
128 * mono_arch_get_gsharedvt_call_info:
130 * See mini-x86.c for documentation.
133 mono_arch_get_gsharedvt_call_info (gpointer addr
, MonoMethodSignature
*normal_sig
, MonoMethodSignature
*gsharedvt_sig
, gboolean gsharedvt_in
, gint32 vcall_offset
, gboolean calli
)
135 GSharedVtCallInfo
*info
;
136 CallInfo
*caller_cinfo
, *callee_cinfo
;
137 MonoMethodSignature
*caller_sig
, *callee_sig
;
139 gboolean var_ret
= FALSE
;
140 CallInfo
*cinfo
, *gcinfo
;
141 MonoMethodSignature
*sig
, *gsig
;
145 caller_sig
= normal_sig
;
146 callee_sig
= gsharedvt_sig
;
147 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
148 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
150 callee_sig
= normal_sig
;
151 caller_sig
= gsharedvt_sig
;
152 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
153 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
157 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
158 * normal call signature, while the callee uses the gsharedvt signature.
159 * If GSHAREDVT_IN is false, its the other way around.
162 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
166 cinfo
= caller_cinfo
;
167 gcinfo
= callee_cinfo
;
171 cinfo
= callee_cinfo
;
172 gcinfo
= caller_cinfo
;
175 if (gcinfo
->ret
.gsharedvt
) {
177 * The return type is gsharedvt
183 * The stack looks like this:
187 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
189 map
= g_ptr_array_new ();
191 for (aindex
= 0; aindex
< cinfo
->nargs
; ++aindex
) {
192 ArgInfo
*ainfo
= &caller_cinfo
->args
[aindex
];
193 ArgInfo
*ainfo2
= &callee_cinfo
->args
[aindex
];
194 int *src
= NULL
, *dst
= NULL
;
195 int nsrc
, ndst
, nslots
, src_slot
, arg_marshal
;
198 * The src descriptor looks like this:
200 * - 12 bits number of slots
201 * - 4 bits marshal type (GSHAREDVT_ARG_...)
202 * - 4 bits size/sign descriptor (GSHAREDVT_ARG_SIZE)
203 * - 4 bits offset inside stack slots
205 arg_marshal
= GSHAREDVT_ARG_NONE
;
207 if (ainfo
->gsharedvt
) {
208 /* Pass the value whose address is received in a reg by value */
209 g_assert (!ainfo2
->gsharedvt
);
210 ndst
= get_arg_slots (ainfo2
, &dst
);
212 src
= g_new0 (int, 1);
213 if (ainfo
->storage
== ArgVtypeByRef
)
214 src_slot
= map_reg (ainfo
->reg
);
216 src_slot
= map_stack_slot (ainfo
->offset
/ 8);
217 g_assert (ndst
< 256);
218 g_assert (src_slot
< 64);
219 src
[0] = (ndst
<< 6) | src_slot
;
220 if (ainfo2
->storage
== ArgHFA
&& ainfo2
->esize
== 4)
221 arg_marshal
= GSHAREDVT_ARG_BYREF_TO_BYVAL_HFAR4
;
222 else if (ainfo2
->storage
== ArgVtypeByRef
|| ainfo2
->storage
== ArgVtypeByRefOnStack
)
223 arg_marshal
= GSHAREDVT_ARG_BYREF_TO_BYREF
;
225 arg_marshal
= GSHAREDVT_ARG_BYREF_TO_BYVAL
;
227 nsrc
= get_arg_slots (ainfo
, &src
);
229 if (ainfo2
->storage
== ArgVtypeByRef
&& ainfo2
->gsharedvt
) {
230 /* Pass the address of the first src slot in a reg */
231 if (ainfo
->storage
!= ArgVtypeByRef
) {
232 if (ainfo
->storage
== ArgHFA
&& ainfo
->esize
== 4) {
233 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF_HFAR4
;
234 g_assert (src
[0] < 64);
235 g_assert (nsrc
< 256);
236 src
[0] |= (nsrc
<< 6);
238 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF
;
242 dst
= g_new0 (int, 1);
243 dst
[0] = map_reg (ainfo2
->reg
);
244 } else if (ainfo2
->storage
== ArgVtypeByRefOnStack
&& ainfo2
->gsharedvt
) {
245 /* Pass the address of the first src slot in a stack slot */
246 if (ainfo
->storage
!= ArgVtypeByRef
)
247 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF
;
249 dst
= g_new0 (int, 1);
250 dst
[0] = map_stack_slot (ainfo2
->offset
/ 8);
252 ndst
= get_arg_slots (ainfo2
, &dst
);
255 src
[0] |= (arg_marshal
<< 18);
256 if (ainfo
->storage
== ArgOnStack
&& ainfo
->slot_size
!= 8) {
257 GSharedVtArgSize arg_size
= GSHAREDVT_ARG_SIZE_NONE
;
260 * On IOS, stack arguments smaller than 8 bytes can
261 * share a stack slot. Encode this information into
264 switch (ainfo
->slot_size
) {
266 arg_size
= ainfo
->sign
? GSHAREDVT_ARG_SIZE_I1
: GSHAREDVT_ARG_SIZE_U1
;
269 arg_size
= ainfo
->sign
? GSHAREDVT_ARG_SIZE_I2
: GSHAREDVT_ARG_SIZE_U2
;
272 arg_size
= ainfo
->sign
? GSHAREDVT_ARG_SIZE_I4
: GSHAREDVT_ARG_SIZE_U4
;
278 /* Encode the size/sign */
279 src
[0] |= (arg_size
<< 22);
280 /* Encode the offset inside the stack slot */
281 src
[0] |= ((ainfo
->offset
% 8) << 26);
282 if (ainfo2
->storage
== ArgOnStack
)
283 dst
[0] |= ((ainfo2
->offset
% 8) << 26);
284 } else if (ainfo2
->storage
== ArgOnStack
&& ainfo2
->slot_size
!= 8) {
285 /* The caller passes in an address, need to store it into a stack slot */
287 GSharedVtArgSize arg_size
= GSHAREDVT_ARG_SIZE_NONE
;
288 switch (ainfo2
->slot_size
) {
290 arg_size
= ainfo2
->sign
? GSHAREDVT_ARG_SIZE_I1
: GSHAREDVT_ARG_SIZE_U1
;
293 arg_size
= ainfo2
->sign
? GSHAREDVT_ARG_SIZE_I2
: GSHAREDVT_ARG_SIZE_U2
;
296 arg_size
= ainfo2
->sign
? GSHAREDVT_ARG_SIZE_I4
: GSHAREDVT_ARG_SIZE_U4
;
302 /* Encode the size/sign */
303 src
[0] |= (arg_size
<< 22);
304 /* Encode the offset inside the stack slot */
305 dst
[0] |= ((ainfo2
->offset
% 8) << 26);
307 nslots
= MIN (nsrc
, ndst
);
309 for (i
= 0; i
< nslots
; ++i
)
310 add_to_map (map
, src
[i
], dst
[i
]);
316 if (cinfo
->ret
.storage
== ArgVtypeByRef
) {
317 /* Both the caller and the callee pass the vtype ret address in r8 */
318 g_assert (cinfo
->ret
.storage
== gcinfo
->ret
.storage
);
319 add_to_map (map
, map_reg (ARMREG_R8
), map_reg (ARMREG_R8
));
322 info
= mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo
) + (map
->len
* sizeof (int)));
324 info
->stack_usage
= callee_cinfo
->stack_usage
;
325 info
->ret_marshal
= GSHAREDVT_RET_NONE
;
326 info
->gsharedvt_in
= gsharedvt_in
? 1 : 0;
327 info
->vret_slot
= -1;
331 g_assert (gcinfo
->ret
.gsharedvt
);
332 info
->vret_arg_reg
= map_reg (ARMREG_R8
);
334 info
->vret_arg_reg
= -1;
337 info
->vcall_offset
= vcall_offset
;
338 info
->map_count
= map
->len
/ 2;
339 for (i
= 0; i
< map
->len
; ++i
)
340 info
->map
[i
] = GPOINTER_TO_UINT (g_ptr_array_index (map
, i
));
341 g_ptr_array_free (map
, TRUE
);
343 /* Compute return value marshalling */
345 switch (cinfo
->ret
.storage
) {
347 if (!gsharedvt_in
|| sig
->ret
->byref
) {
348 info
->ret_marshal
= GSHAREDVT_RET_I8
;
350 switch (sig
->ret
->type
) {
352 info
->ret_marshal
= GSHAREDVT_RET_I1
;
355 case MONO_TYPE_BOOLEAN
:
356 info
->ret_marshal
= GSHAREDVT_RET_U1
;
359 info
->ret_marshal
= GSHAREDVT_RET_I2
;
363 info
->ret_marshal
= GSHAREDVT_RET_U2
;
366 info
->ret_marshal
= GSHAREDVT_RET_I4
;
369 info
->ret_marshal
= GSHAREDVT_RET_U4
;
372 info
->ret_marshal
= GSHAREDVT_RET_I8
;
378 info
->ret_marshal
= GSHAREDVT_RET_R8
;
381 info
->ret_marshal
= GSHAREDVT_RET_R4
;
383 case ArgVtypeInIRegs
:
384 info
->ret_marshal
= GSHAREDVT_RET_IREGS_1
- 1 + cinfo
->ret
.nregs
;
387 if (cinfo
->ret
.esize
== 4)
388 info
->ret_marshal
= GSHAREDVT_RET_HFAR4_1
- 1 + cinfo
->ret
.nregs
;
390 info
->ret_marshal
= GSHAREDVT_RET_HFAR8_1
- 1 + cinfo
->ret
.nregs
;
393 /* No conversion needed */
396 g_assert_not_reached ();
400 if (gsharedvt_in
&& var_ret
&& cinfo
->ret
.storage
!= ArgVtypeByRef
) {
401 /* Allocate stack space for the return value */
402 info
->vret_slot
= map_stack_slot (info
->stack_usage
/ sizeof (gpointer
));
403 info
->stack_usage
+= mono_type_stack_size_internal (normal_sig
->ret
, NULL
, FALSE
) + sizeof (gpointer
);
406 info
->stack_usage
= ALIGN_TO (info
->stack_usage
, MONO_ARCH_FRAME_ALIGNMENT
);
414 mono_arm_gsharedvt_init (void)
418 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */