2 * mini-x86-gsharedvt.c: gsharedvt support code for x86
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.
12 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
14 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
21 mono_arch_gsharedvt_sig_supported (MonoMethodSignature
*sig
)
24 if (sig->ret && is_variable_size (sig->ret))
31 * mono_arch_get_gsharedvt_call_info:
33 * Compute calling convention information for marshalling a call between NORMAL_SIG and GSHAREDVT_SIG.
34 * If GSHAREDVT_IN is TRUE, then the caller calls using the signature NORMAL_SIG but the call is received by
35 * a method with signature GSHAREDVT_SIG, otherwise its the other way around.
38 mono_arch_get_gsharedvt_call_info (gpointer addr
, MonoMethodSignature
*normal_sig
, MonoMethodSignature
*gsharedvt_sig
, gboolean gsharedvt_in
, gint32 vcall_offset
, gboolean calli
)
40 GSharedVtCallInfo
*info
;
41 CallInfo
*caller_cinfo
, *callee_cinfo
;
42 MonoMethodSignature
*caller_sig
, *callee_sig
;
44 gboolean var_ret
= FALSE
;
45 CallInfo
*cinfo
, *gcinfo
;
46 MonoMethodSignature
*sig
, *gsig
;
50 caller_sig
= normal_sig
;
51 callee_sig
= gsharedvt_sig
;
52 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
53 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
55 callee_sig
= normal_sig
;
56 callee_cinfo
= mono_arch_get_call_info (NULL
, callee_sig
);
57 caller_sig
= gsharedvt_sig
;
58 caller_cinfo
= mono_arch_get_call_info (NULL
, caller_sig
);
62 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
63 * normal call signature, while the callee uses the gsharedvt signature.
64 * If GSHAREDVT_IN is false, its the other way around.
67 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
72 gcinfo
= callee_cinfo
;
77 gcinfo
= caller_cinfo
;
80 if (gcinfo
->vtype_retaddr
&& gsig
->ret
&& mini_is_gsharedvt_type (gsig
->ret
)) {
82 * The return type is gsharedvt
88 * The stack looks like this:
93 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
95 map
= g_ptr_array_new ();
97 if (cinfo
->vtype_retaddr
) {
100 * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
103 g_ptr_array_add (map
, GUINT_TO_POINTER (caller_cinfo
->vret_arg_offset
/ sizeof (gpointer
)));
104 g_ptr_array_add (map
, GUINT_TO_POINTER (callee_cinfo
->vret_arg_offset
/ sizeof (gpointer
)));
107 for (i
= 0; i
< cinfo
->nargs
; ++i
) {
108 ArgInfo
*ainfo
= &caller_cinfo
->args
[i
];
109 ArgInfo
*ainfo2
= &callee_cinfo
->args
[i
];
112 switch (ainfo
->storage
) {
114 if (ainfo2
->storage
== ArgOnStack
) {
115 nslots
= callee_cinfo
->args
[i
].nslots
;
118 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo
->offset
/ sizeof (gpointer
)) + (1 << 16) + (nslots
<< 18)));
119 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo2
->offset
/ sizeof (gpointer
))));
121 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo
->offset
/ sizeof (gpointer
))));
122 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo2
->offset
/ sizeof (gpointer
))));
126 if (ainfo2
->storage
== ArgOnStack
) {
127 nslots
= cinfo
->args
[i
].nslots
;
130 for (j
= 0; j
< nslots
; ++j
) {
131 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo
->offset
/ sizeof (gpointer
)) + j
));
132 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo2
->offset
/ sizeof (gpointer
)) + j
));
135 g_assert (ainfo2
->storage
== ArgGSharedVt
);
136 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo
->offset
/ sizeof (gpointer
)) + (2 << 16)));
137 g_ptr_array_add (map
, GUINT_TO_POINTER ((ainfo2
->offset
/ sizeof (gpointer
))));
143 info
= mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo
) + (map
->len
* sizeof (int)));
145 info
->stack_usage
= callee_cinfo
->stack_usage
;
146 info
->ret_marshal
= GSHAREDVT_RET_NONE
;
147 info
->gsharedvt_in
= gsharedvt_in
? 1 : 0;
148 info
->vret_slot
= -1;
149 info
->calli
= calli
? 1 : 0;
151 info
->vret_arg_slot
= gcinfo
->vret_arg_offset
/ sizeof (gpointer
);
153 info
->vret_arg_slot
= -1;
154 info
->vcall_offset
= vcall_offset
;
155 info
->map_count
= map
->len
/ 2;
156 for (i
= 0; i
< map
->len
; ++i
)
157 info
->map
[i
] = GPOINTER_TO_UINT (g_ptr_array_index (map
, i
));
158 g_ptr_array_free (map
, TRUE
);
160 /* Compute return value marshalling */
162 switch (cinfo
->ret
.storage
) {
164 if (gsharedvt_in
&& !sig
->ret
->byref
&& sig
->ret
->type
== MONO_TYPE_I1
)
165 info
->ret_marshal
= GSHAREDVT_RET_I1
;
166 else if (gsharedvt_in
&& !sig
->ret
->byref
&& (sig
->ret
->type
== MONO_TYPE_U1
|| sig
->ret
->type
== MONO_TYPE_BOOLEAN
))
167 info
->ret_marshal
= GSHAREDVT_RET_U1
;
168 else if (gsharedvt_in
&& !sig
->ret
->byref
&& sig
->ret
->type
== MONO_TYPE_I2
)
169 info
->ret_marshal
= GSHAREDVT_RET_I2
;
170 else if (gsharedvt_in
&& !sig
->ret
->byref
&& (sig
->ret
->type
== MONO_TYPE_U2
|| sig
->ret
->type
== MONO_TYPE_CHAR
))
171 info
->ret_marshal
= GSHAREDVT_RET_U2
;
172 else if (cinfo
->ret
.is_pair
)
173 info
->ret_marshal
= GSHAREDVT_RET_IREGS
;
175 info
->ret_marshal
= GSHAREDVT_RET_IREG
;
177 case ArgOnDoubleFpStack
:
178 info
->ret_marshal
= GSHAREDVT_RET_DOUBLE_FPSTACK
;
180 case ArgOnFloatFpStack
:
181 info
->ret_marshal
= GSHAREDVT_RET_FLOAT_FPSTACK
;
184 /* The caller passes in a vtype ret arg as well */
185 g_assert (gcinfo
->vtype_retaddr
);
186 /* Just have to pop the arg, as done by normal methods in their epilog */
187 info
->ret_marshal
= GSHAREDVT_RET_STACK_POP
;
190 g_assert_not_reached ();
192 } else if (gsharedvt_in
&& cinfo
->vtype_retaddr
) {
193 info
->ret_marshal
= GSHAREDVT_RET_STACK_POP
;
196 if (gsharedvt_in
&& var_ret
&& !caller_cinfo
->vtype_retaddr
) {
197 /* Allocate stack space for the return value */
198 info
->vret_slot
= info
->stack_usage
/ sizeof (gpointer
);
200 info
->stack_usage
+= sizeof (gpointer
) * 3;
203 info
->stack_usage
= ALIGN_TO (info
->stack_usage
, MONO_ARCH_FRAME_ALIGNMENT
);
205 g_free (caller_cinfo
);
206 g_free (callee_cinfo
);