3 * gsharedvt support code for arm
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.
14 #include <mono/metadata/abi-details.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/marshal.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/arch/arm/arm-codegen.h>
20 #include <mono/arch/arm/arm-vfp-codegen.h>
25 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
32 mono_arch_gsharedvt_sig_supported (MonoMethodSignature
*sig
)
35 if (sig->ret && is_variable_size (sig->ret))
42 add_to_map (GPtrArray
*map
, int src
, int dst
)
44 g_ptr_array_add (map
, GUINT_TO_POINTER (src
));
45 g_ptr_array_add (map
, GUINT_TO_POINTER (dst
));
55 map_stack_slot (int slot
)
61 get_arg_slots (ArgInfo
*ainfo
, int **out_slots
)
63 int sreg
= ainfo
->reg
;
64 int sslot
= ainfo
->offset
/ 4;
68 switch (ainfo
->storage
) {
71 src
= g_malloc (nsrc
* sizeof (int));
72 src
[0] = map_reg (sreg
);
76 src
= g_malloc (nsrc
* sizeof (int));
77 src
[0] = map_reg (sreg
);
78 src
[1] = map_reg (sreg
+ 1);
80 case RegTypeStructByVal
:
81 nsrc
= ainfo
->struct_size
/ 4;
82 src
= g_malloc (nsrc
* sizeof (int));
83 g_assert (ainfo
->size
<= nsrc
);
84 for (i
= 0; i
< ainfo
->size
; ++i
)
85 src
[i
] = map_reg (sreg
+ i
);
86 for (i
= ainfo
->size
; i
< nsrc
; ++i
)
87 src
[i
] = map_stack_slot (sslot
+ (i
- ainfo
->size
));
90 nsrc
= ainfo
->size
/ 4;
91 src
= g_malloc (nsrc
* sizeof (int));
92 for (i
= 0; i
< nsrc
; ++i
)
93 src
[i
] = map_stack_slot (sslot
+ i
);
97 src
= g_malloc (nsrc
* sizeof (int));
98 src
[0] = map_reg (ARMREG_R3
);
99 src
[1] = map_stack_slot (sslot
);
102 g_assert_not_reached ();
111 * mono_arch_get_gsharedvt_call_info:
113 * See mini-x86.c for documentation.
116 mono_arch_get_gsharedvt_call_info (gpointer addr
, MonoMethodSignature
*normal_sig
, MonoMethodSignature
*gsharedvt_sig
, gboolean gsharedvt_in
, gint32 vcall_offset
, gboolean calli
)
118 GSharedVtCallInfo
*info
;
119 CallInfo
*caller_cinfo
, *callee_cinfo
;
120 MonoMethodSignature
*caller_sig
, *callee_sig
;
122 gboolean var_ret
= FALSE
;
123 gboolean have_fregs
= FALSE
;
124 CallInfo
*cinfo
, *gcinfo
;
125 MonoMethodSignature
*sig
, *gsig
;
129 caller_sig
= normal_sig
;
130 callee_sig
= gsharedvt_sig
;
131 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
132 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
134 callee_sig
= normal_sig
;
135 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
136 caller_sig
= gsharedvt_sig
;
137 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
141 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
142 * normal call signature, while the callee uses the gsharedvt signature.
143 * If GSHAREDVT_IN is false, its the other way around.
146 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
150 cinfo
= caller_cinfo
;
151 gcinfo
= callee_cinfo
;
155 cinfo
= callee_cinfo
;
156 gcinfo
= caller_cinfo
;
159 if (gcinfo
->ret
.storage
== RegTypeStructByAddr
&& gsig
->ret
&& mini_is_gsharedvt_type (gsig
->ret
)) {
161 * The return type is gsharedvt
167 * The stack looks like this:
172 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
173 * The argument registers are mapped to slot 0..3, stack slot 0 is mapped to slot 4, etc.
175 map
= g_ptr_array_new ();
177 if (cinfo
->ret
.storage
== RegTypeStructByAddr
) {
180 * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
183 g_assert (caller_cinfo
->ret
.storage
== RegTypeStructByAddr
);
184 g_assert (callee_cinfo
->ret
.storage
== RegTypeStructByAddr
);
185 add_to_map (map
, map_reg (caller_cinfo
->ret
.reg
), map_reg (callee_cinfo
->ret
.reg
));
188 for (aindex
= 0; aindex
< cinfo
->nargs
; ++aindex
) {
189 ArgInfo
*ainfo
= &caller_cinfo
->args
[aindex
];
190 ArgInfo
*ainfo2
= &callee_cinfo
->args
[aindex
];
191 int *src
= NULL
, *dst
= NULL
;
192 int nsrc
, ndst
, nslots
, src_slot
, arg_marshal
;
194 if (ainfo
->storage
== RegTypeFP
|| ainfo2
->storage
== RegTypeFP
) {
200 * The src descriptor looks like this:
202 * - 12 bits number of slots
203 * - 8 bits marshal type (GSHAREDVT_ARG_...)
206 arg_marshal
= GSHAREDVT_ARG_NONE
;
208 if (ainfo
->storage
== RegTypeGSharedVtInReg
|| ainfo
->storage
== RegTypeGSharedVtOnStack
) {
209 /* Pass the value whose address is received in a reg by value */
210 g_assert (ainfo2
->storage
!= RegTypeGSharedVtInReg
);
211 ndst
= get_arg_slots (ainfo2
, &dst
);
213 src
= g_new0 (int, 1);
214 if (ainfo
->storage
== RegTypeGSharedVtInReg
)
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
< 256);
220 src
[0] = (ndst
<< 8) | src_slot
;
222 if (ainfo2
->storage
== RegTypeGeneral
&& ainfo2
->size
!= 0 && ainfo2
->size
!= sizeof (target_mgreg_t
)) {
223 /* Have to load less than 4 bytes */
224 switch (ainfo2
->size
) {
226 arg_marshal
= ainfo2
->is_signed
? GSHAREDVT_ARG_BYREF_TO_BYVAL_I1
: GSHAREDVT_ARG_BYREF_TO_BYVAL_U1
;
229 arg_marshal
= ainfo2
->is_signed
? GSHAREDVT_ARG_BYREF_TO_BYVAL_I2
: GSHAREDVT_ARG_BYREF_TO_BYVAL_U2
;
232 g_assert_not_reached ();
236 arg_marshal
= GSHAREDVT_ARG_BYREF_TO_BYVAL
;
239 nsrc
= get_arg_slots (ainfo
, &src
);
241 if (ainfo2
->storage
== RegTypeGSharedVtInReg
) {
242 /* Pass the address of the first src slot in a reg */
243 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF
;
245 dst
= g_new0 (int, 1);
246 dst
[0] = map_reg (ainfo2
->reg
);
247 } else if (ainfo2
->storage
== RegTypeGSharedVtOnStack
) {
248 /* Pass the address of the first src slot in a stack slot */
249 arg_marshal
= GSHAREDVT_ARG_BYVAL_TO_BYREF
;
251 dst
= g_new0 (int, 1);
252 dst
[0] = map_stack_slot (ainfo2
->offset
/ sizeof (target_mgreg_t
));
254 ndst
= get_arg_slots (ainfo2
, &dst
);
257 src
[0] |= (arg_marshal
<< 24);
258 nslots
= MIN (nsrc
, ndst
);
260 for (i
= 0; i
< nslots
; ++i
)
261 add_to_map (map
, src
[i
], dst
[i
]);
267 info
= mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo
) + (map
->len
* sizeof (int)));
269 info
->stack_usage
= callee_cinfo
->stack_usage
;
270 info
->ret_marshal
= GSHAREDVT_RET_NONE
;
271 info
->gsharedvt_in
= gsharedvt_in
? 1 : 0;
272 info
->vret_slot
= -1;
275 g_assert (gcinfo
->ret
.storage
== RegTypeStructByAddr
);
276 info
->vret_arg_reg
= gcinfo
->ret
.reg
;
278 info
->vret_arg_reg
= -1;
280 info
->vcall_offset
= vcall_offset
;
281 info
->map_count
= map
->len
/ 2;
282 for (i
= 0; i
< map
->len
; ++i
)
283 info
->map
[i
] = GPOINTER_TO_UINT (g_ptr_array_index (map
, i
));
284 g_ptr_array_free (map
, TRUE
);
286 /* Compute return value marshalling */
288 switch (cinfo
->ret
.storage
) {
290 if (gsharedvt_in
&& !sig
->ret
->byref
&& sig
->ret
->type
== MONO_TYPE_I1
)
291 info
->ret_marshal
= GSHAREDVT_RET_I1
;
292 else if (gsharedvt_in
&& !sig
->ret
->byref
&& (sig
->ret
->type
== MONO_TYPE_U1
|| sig
->ret
->type
== MONO_TYPE_BOOLEAN
))
293 info
->ret_marshal
= GSHAREDVT_RET_U1
;
294 else if (gsharedvt_in
&& !sig
->ret
->byref
&& sig
->ret
->type
== MONO_TYPE_I2
)
295 info
->ret_marshal
= GSHAREDVT_RET_I2
;
296 else if (gsharedvt_in
&& !sig
->ret
->byref
&& (sig
->ret
->type
== MONO_TYPE_U2
|| sig
->ret
->type
== MONO_TYPE_CHAR
))
297 info
->ret_marshal
= GSHAREDVT_RET_U2
;
299 info
->ret_marshal
= GSHAREDVT_RET_IREG
;
301 case RegTypeIRegPair
:
302 info
->ret_marshal
= GSHAREDVT_RET_IREGS
;
305 if (mono_arm_is_hard_float ()) {
306 if (cinfo
->ret
.size
== 4)
307 info
->ret_marshal
= GSHAREDVT_RET_VFP_R4
;
309 info
->ret_marshal
= GSHAREDVT_RET_VFP_R8
;
311 if (cinfo
->ret
.size
== sizeof (target_mgreg_t
))
312 info
->ret_marshal
= GSHAREDVT_RET_IREG
;
314 info
->ret_marshal
= GSHAREDVT_RET_IREGS
;
317 case RegTypeStructByAddr
:
318 info
->ret_marshal
= GSHAREDVT_RET_NONE
;
321 g_assert_not_reached ();
325 if (gsharedvt_in
&& var_ret
&& caller_cinfo
->ret
.storage
!= RegTypeStructByAddr
) {
326 /* Allocate stack space for the return value */
327 info
->vret_slot
= map_stack_slot (info
->stack_usage
/ sizeof (target_mgreg_t
));
328 info
->stack_usage
+= mono_type_stack_size_internal (normal_sig
->ret
, NULL
, FALSE
) + sizeof (target_mgreg_t
);
331 info
->stack_usage
= ALIGN_TO (info
->stack_usage
, MONO_ARCH_FRAME_ALIGNMENT
);
332 info
->caller_cinfo
= caller_cinfo
;
333 info
->callee_cinfo
= callee_cinfo
;
334 info
->have_fregs
= have_fregs
;