Merge pull request #2780 from alexanderkyte/seq_point_optimize
[mono.git] / mono / mini / mini-x86-gsharedvt.c
1 /*
2  * mini-x86-gsharedvt.c: gsharedvt support code for x86
3  *
4  * Authors:
5  *   Zoltan Varga <vargaz@gmail.com>
6  *
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"
11
12 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
13
14 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
15
16 /*
17  * GSHAREDVT
18  */
19
20 gboolean
21 mono_arch_gsharedvt_sig_supported (MonoMethodSignature *sig)
22 {
23         /*
24         if (sig->ret && is_variable_size (sig->ret))
25                 return FALSE;
26         */
27         return TRUE;
28 }
29
30 /*
31  * mono_arch_get_gsharedvt_call_info:
32  *
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.
36  */
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)
39 {
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;
48
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);
59         }
60
61         /*
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.
65          */
66
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;
78         }
79
80         if (gcinfo->vtype_retaddr && gsig->ret && mini_is_gsharedvt_type (gsig->ret)) {
81                 /*
82                  * The return type is gsharedvt
83                  */
84                 var_ret = TRUE;
85         }
86
87         /*
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>.
94          */
95         map = g_ptr_array_new ();
96
97         if (cinfo->vtype_retaddr) {
98                 /*
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.
102                  */             
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)));
105         }
106
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;
111
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))));
123                         }
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));
133                                 }
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))));
138                         }
139                         break;
140                 }
141         }
142
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);
159
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 ();
191                 }
192         } else if (gsharedvt_in && cinfo->vtype_retaddr) {
193                 info->ret_marshal = GSHAREDVT_RET_STACK_POP;
194         }
195
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;
201         }
202
203         info->stack_usage = ALIGN_TO (info->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
204
205         g_free (caller_cinfo);
206         g_free (callee_cinfo);
207
208         return info;
209 }
210 #endif