Merge pull request #3140 from esdrubal/syscall_details
[mono-project.git] / mono / mini / mini-x86-gsharedvt.c
blob130d48e7f56d7acbdf1cd9422eaf3610b1f0d1fd
1 /*
2 * mini-x86-gsharedvt.c: gsharedvt support code for x86
4 * Authors:
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.
9 */
10 #include "mini.h"
12 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
14 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
17 * GSHAREDVT
20 gboolean
21 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
24 if (sig->ret && is_variable_size (sig->ret))
25 return FALSE;
27 return TRUE;
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.
37 gpointer
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;
43 int i, j;
44 gboolean var_ret = FALSE;
45 CallInfo *cinfo, *gcinfo;
46 MonoMethodSignature *sig, *gsig;
47 GPtrArray *map;
49 if (gsharedvt_in) {
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);
54 } else {
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 */
68 if (gsharedvt_in) {
69 sig = caller_sig;
70 gsig = callee_sig;
71 cinfo = caller_cinfo;
72 gcinfo = callee_cinfo;
73 } else {
74 sig = callee_sig;
75 gsig = caller_sig;
76 cinfo = 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
84 var_ret = TRUE;
88 * The stack looks like this:
89 * <arguments>
90 * <ret addr>
91 * <saved ebp>
92 * <call area>
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) {
99 * Map ret arg.
100 * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
101 * with a vtype.
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];
110 int nslots;
112 switch (ainfo->storage) {
113 case ArgGSharedVt:
114 if (ainfo2->storage == ArgOnStack) {
115 nslots = callee_cinfo->args [i].nslots;
116 if (!nslots)
117 nslots = 1;
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))));
120 } else {
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))));
124 break;
125 default:
126 if (ainfo2->storage == ArgOnStack) {
127 nslots = cinfo->args [i].nslots;
128 if (!nslots)
129 nslots = 1;
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));
134 } else {
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))));
139 break;
143 info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
144 info->addr = addr;
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;
150 if (var_ret)
151 info->vret_arg_slot = gcinfo->vret_arg_offset / sizeof (gpointer);
152 else
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 */
161 if (var_ret) {
162 switch (cinfo->ret.storage) {
163 case ArgInIReg:
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;
174 else
175 info->ret_marshal = GSHAREDVT_RET_IREG;
176 break;
177 case ArgOnDoubleFpStack:
178 info->ret_marshal = GSHAREDVT_RET_DOUBLE_FPSTACK;
179 break;
180 case ArgOnFloatFpStack:
181 info->ret_marshal = GSHAREDVT_RET_FLOAT_FPSTACK;
182 break;
183 case ArgOnStack:
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;
188 break;
189 default:
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);
199 // FIXME:
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);
208 return info;
210 #endif