3 * gsharedvt support code for x86
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.
13 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
15 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
22 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
25 if (sig->ret && is_variable_size (sig->ret))
32 * mono_arch_get_gsharedvt_call_info:
34 * Compute calling convention information for marshalling a call between NORMAL_SIG and GSHAREDVT_SIG.
35 * If GSHAREDVT_IN is TRUE, then the caller calls using the signature NORMAL_SIG but the call is received by
36 * a method with signature GSHAREDVT_SIG, otherwise its the other way around.
39 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
41 GSharedVtCallInfo *info;
42 CallInfo *caller_cinfo, *callee_cinfo;
43 MonoMethodSignature *caller_sig, *callee_sig;
45 gboolean var_ret = FALSE;
46 CallInfo *cinfo, *gcinfo;
47 MonoMethodSignature *sig, *gsig;
51 caller_sig = normal_sig;
52 callee_sig = gsharedvt_sig;
53 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
54 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
56 callee_sig = normal_sig;
57 callee_cinfo = mono_arch_get_call_info (NULL, callee_sig);
58 caller_sig = gsharedvt_sig;
59 caller_cinfo = mono_arch_get_call_info (NULL, caller_sig);
63 * If GSHAREDVT_IN is true, this means we are transitioning from normal to gsharedvt code. The caller uses the
64 * normal call signature, while the callee uses the gsharedvt signature.
65 * If GSHAREDVT_IN is false, its the other way around.
68 /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
73 gcinfo = callee_cinfo;
78 gcinfo = caller_cinfo;
81 if (gcinfo->vtype_retaddr && gsig->ret && mini_is_gsharedvt_type (gsig->ret)) {
83 * The return type is gsharedvt
89 * The stack looks like this:
94 * We have to map the stack slots in <arguments> to the stack slots in <call area>.
96 map = g_ptr_array_new ();
98 if (cinfo->vtype_retaddr) {
101 * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
104 g_ptr_array_add (map, GUINT_TO_POINTER (caller_cinfo->vret_arg_offset / sizeof (gpointer)));
105 g_ptr_array_add (map, GUINT_TO_POINTER (callee_cinfo->vret_arg_offset / sizeof (gpointer)));
108 for (i = 0; i < cinfo->nargs; ++i) {
109 ArgInfo *ainfo = &caller_cinfo->args [i];
110 ArgInfo *ainfo2 = &callee_cinfo->args [i];
113 switch (ainfo->storage) {
115 if (ainfo2->storage == ArgOnStack) {
116 nslots = callee_cinfo->args [i].nslots;
119 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo->offset / sizeof (gpointer)) + (1 << 16) + (nslots << 18)));
120 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo2->offset / sizeof (gpointer))));
122 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo->offset / sizeof (gpointer))));
123 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo2->offset / sizeof (gpointer))));
127 if (ainfo2->storage == ArgOnStack) {
128 nslots = cinfo->args [i].nslots;
131 for (j = 0; j < nslots; ++j) {
132 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo->offset / sizeof (gpointer)) + j));
133 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo2->offset / sizeof (gpointer)) + j));
136 g_assert (ainfo2->storage == ArgGSharedVt);
137 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo->offset / sizeof (gpointer)) + (2 << 16)));
138 g_ptr_array_add (map, GUINT_TO_POINTER ((ainfo2->offset / sizeof (gpointer))));
144 info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
146 info->stack_usage = callee_cinfo->stack_usage;
147 info->ret_marshal = GSHAREDVT_RET_NONE;
148 info->gsharedvt_in = gsharedvt_in ? 1 : 0;
149 info->vret_slot = -1;
150 info->calli = calli ? 1 : 0;
152 info->vret_arg_slot = gcinfo->vret_arg_offset / sizeof (gpointer);
154 info->vret_arg_slot = -1;
155 info->vcall_offset = vcall_offset;
156 info->map_count = map->len / 2;
157 for (i = 0; i < map->len; ++i)
158 info->map [i] = GPOINTER_TO_UINT (g_ptr_array_index (map, i));
159 g_ptr_array_free (map, TRUE);
161 /* Compute return value marshalling */
163 switch (cinfo->ret.storage) {
165 if (gsharedvt_in && !sig->ret->byref && sig->ret->type == MONO_TYPE_I1)
166 info->ret_marshal = GSHAREDVT_RET_I1;
167 else if (gsharedvt_in && !sig->ret->byref && (sig->ret->type == MONO_TYPE_U1 || sig->ret->type == MONO_TYPE_BOOLEAN))
168 info->ret_marshal = GSHAREDVT_RET_U1;
169 else if (gsharedvt_in && !sig->ret->byref && sig->ret->type == MONO_TYPE_I2)
170 info->ret_marshal = GSHAREDVT_RET_I2;
171 else if (gsharedvt_in && !sig->ret->byref && (sig->ret->type == MONO_TYPE_U2 || sig->ret->type == MONO_TYPE_CHAR))
172 info->ret_marshal = GSHAREDVT_RET_U2;
173 else if (cinfo->ret.is_pair)
174 info->ret_marshal = GSHAREDVT_RET_IREGS;
176 info->ret_marshal = GSHAREDVT_RET_IREG;
178 case ArgOnDoubleFpStack:
179 info->ret_marshal = GSHAREDVT_RET_DOUBLE_FPSTACK;
181 case ArgOnFloatFpStack:
182 info->ret_marshal = GSHAREDVT_RET_FLOAT_FPSTACK;
185 /* The caller passes in a vtype ret arg as well */
186 g_assert (gcinfo->vtype_retaddr);
187 /* Just have to pop the arg, as done by normal methods in their epilog */
188 info->ret_marshal = GSHAREDVT_RET_STACK_POP;
191 g_assert_not_reached ();
193 } else if (gsharedvt_in && cinfo->vtype_retaddr) {
194 info->ret_marshal = GSHAREDVT_RET_STACK_POP;
197 if (gsharedvt_in && var_ret && !caller_cinfo->vtype_retaddr) {
198 /* Allocate stack space for the return value */
199 info->vret_slot = info->stack_usage / sizeof (gpointer);
201 info->stack_usage += sizeof (gpointer) * 3;
204 info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
206 g_free (caller_cinfo);
207 g_free (callee_cinfo);