Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / mini-x86-gsharedvt.c
1 /**
2  * \file
3  * gsharedvt support code for x86
4  *
5  * Authors:
6  *   Zoltan Varga <vargaz@gmail.com>
7  *
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.
10  */
11 #include "mini.h"
12
13 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
14
15 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
16
17 /*
18  * GSHAREDVT
19  */
20
21 gboolean
22 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
23 {
24         /*
25         if (sig->ret && is_variable_size (sig->ret))
26                 return FALSE;
27         */
28         return TRUE;
29 }
30
31 /*
32  * mono_arch_get_gsharedvt_call_info:
33  *
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.
37  */
38 gpointer
39 mono_arch_get_gsharedvt_call_info (gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, gboolean gsharedvt_in, gint32 vcall_offset, gboolean calli)
40 {
41         GSharedVtCallInfo *info;
42         CallInfo *caller_cinfo, *callee_cinfo;
43         MonoMethodSignature *caller_sig, *callee_sig;
44         int i, j;
45         gboolean var_ret = FALSE;
46         CallInfo *cinfo, *gcinfo;
47         MonoMethodSignature *sig, *gsig;
48         GPtrArray *map;
49
50         if (gsharedvt_in) {
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);
55         } else {
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);
60         }
61
62         /*
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.
66          */
67
68         /* sig/cinfo describes the normal call, while gsig/gcinfo describes the gsharedvt call */
69         if (gsharedvt_in) {
70                 sig = caller_sig;
71                 gsig = callee_sig;
72                 cinfo = caller_cinfo;
73                 gcinfo = callee_cinfo;
74         } else {
75                 sig = callee_sig;
76                 gsig = caller_sig;
77                 cinfo = callee_cinfo;
78                 gcinfo = caller_cinfo;
79         }
80
81         if (gcinfo->vtype_retaddr && gsig->ret && mini_is_gsharedvt_type (gsig->ret)) {
82                 /*
83                  * The return type is gsharedvt
84                  */
85                 var_ret = TRUE;
86         }
87
88         /*
89          * The stack looks like this:
90          * <arguments>
91          * <ret addr>
92          * <saved ebp>
93          * <call area>
94          * We have to map the stack slots in <arguments> to the stack slots in <call area>.
95          */
96         map = g_ptr_array_new ();
97
98         if (cinfo->vtype_retaddr) {
99                 /*
100                  * Map ret arg.
101                  * This handles the case when the method returns a normal vtype, and when it returns a type arg, and its instantiated
102                  * with a vtype.
103                  */             
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)));
106         }
107
108         for (i = 0; i < cinfo->nargs; ++i) {
109                 ArgInfo *ainfo = &caller_cinfo->args [i];
110                 ArgInfo *ainfo2 = &callee_cinfo->args [i];
111                 int nslots;
112
113                 switch (ainfo->storage) {
114                 case ArgGSharedVt:
115                         if (ainfo2->storage == ArgOnStack) {
116                                 nslots = callee_cinfo->args [i].nslots;
117                                 if (!nslots)
118                                         nslots = 1;
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))));
121                         } else {
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))));
124                         }
125                         break;
126                 default:
127                         if (ainfo2->storage == ArgOnStack) {
128                                 nslots = cinfo->args [i].nslots;
129                                 if (!nslots)
130                                         nslots = 1;
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));
134                                 }
135                         } else {
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))));
139                         }
140                         break;
141                 }
142         }
143
144         info = mono_domain_alloc0 (mono_domain_get (), sizeof (GSharedVtCallInfo) + (map->len * sizeof (int)));
145         info->addr = addr;
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;
151         if (var_ret)
152                 info->vret_arg_slot = gcinfo->vret_arg_offset / sizeof (gpointer);
153         else
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);
160
161         /* Compute return value marshalling */
162         if (var_ret) {
163                 switch (cinfo->ret.storage) {
164                 case ArgInIReg:
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;
175                         else
176                                 info->ret_marshal = GSHAREDVT_RET_IREG;
177                         break;
178                 case ArgOnDoubleFpStack:
179                         info->ret_marshal = GSHAREDVT_RET_DOUBLE_FPSTACK;
180                         break;
181                 case ArgOnFloatFpStack:
182                         info->ret_marshal = GSHAREDVT_RET_FLOAT_FPSTACK;
183                         break;
184                 case ArgOnStack:
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;
189                         break;
190                 default:
191                         g_assert_not_reached ();
192                 }
193         } else if (gsharedvt_in && cinfo->vtype_retaddr) {
194                 info->ret_marshal = GSHAREDVT_RET_STACK_POP;
195         }
196
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);
200                 // FIXME:
201                 info->stack_usage += sizeof (gpointer) * 3;
202         }
203
204         info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
205
206         g_free (caller_cinfo);
207         g_free (callee_cinfo);
208
209         return info;
210 }
211 #endif