Merge pull request #2816 from xmcclure/profile-clean-0
[mono.git] / mono / mini / tramp-x86-gsharedvt.c
1 /*
2  * tramp-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 #include <mono/metadata/abi-details.h>
12
13 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
14
15 gpointer
16 mono_x86_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
17 {
18         int i;
19         int *map = info->map;
20
21         /* Set vtype ret arg */
22         if (info->vret_arg_slot != -1) {
23                 callee [info->vret_arg_slot] = &callee [info->vret_slot];
24         }
25         /* Copy data from the caller argument area to the callee */
26         for (i = 0; i < info->map_count; ++i) {
27                 int src = map [i * 2];
28                 int dst = map [i * 2 + 1];
29
30                 switch ((src >> 16) & 0x3) {
31                 case 0:
32                         callee [dst] = caller [src];
33                         break;
34                 case 1: {
35                         int j, nslots;
36                         gpointer *arg;
37
38                         /* gsharedvt->normal */
39                         nslots = src >> 18;
40                         arg = (gpointer*)caller [src & 0xffff];
41                         for (j = 0; j < nslots; ++j)
42                                 callee [dst + j] = arg [j];
43                         break;
44                 }
45                 case 2:
46                         /* gsharedvt arg, have to take its address */
47                         callee [dst] = caller + (src & 0xffff);
48                         break;
49 #if 0
50                 int dst = map [i * 2 + 1];
51                 if (dst >= 0xffff) {
52                         /* gsharedvt arg, have to take its address */
53                         callee [dst - 0xffff] = caller + map [i * 2];
54                 } else {
55                         callee [dst] = caller [map [i * 2]];
56                 }
57 #endif
58                 }
59         }
60
61         if (info->vcall_offset != -1) {
62                 MonoObject *this_obj = caller [0];
63
64                 if (G_UNLIKELY (!this_obj))
65                         return NULL;
66                 if (info->vcall_offset == MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET)
67                         /* delegate invoke */
68                         return ((MonoDelegate*)this_obj)->invoke_impl;
69                 else
70                         return *(gpointer*)((char*)this_obj->vtable + info->vcall_offset);
71         } else if (info->calli) {
72                 /* The address to call is passed in the mrgctx reg */
73                 return mrgctx_reg;
74         } else {
75                 return info->addr;
76         }
77
78 }
79
80 gpointer
81 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
82 {
83         guint8 *code, *buf;
84         int buf_len, cfa_offset;
85         GSList *unwind_ops = NULL;
86         MonoJumpInfo *ji = NULL;
87         guint8 *br_out, *br [16];
88         int info_offset, mrgctx_offset;
89
90         buf_len = 320;
91         buf = code = mono_global_codeman_reserve (buf_len);
92
93         /*
94          * This trampoline is responsible for marshalling calls between normal code and gsharedvt code. The
95          * caller is a normal or gshared method which uses the signature of the inflated method to make the call, while
96          * the callee is a gsharedvt method which has a signature which uses valuetypes in place of type parameters, i.e.
97          * caller:
98          * foo<bool> (bool b)
99          * callee:
100          * T=<type used to represent vtype type arguments, currently TypedByRef>
101          * foo<T> (T b)
102          * The trampoline is responsible for marshalling the arguments and marshalling the result back. To simplify
103          * things, we create our own stack frame, and do most of the work in a C function, which receives a
104          * GSharedVtCallInfo structure as an argument. The structure should contain information to execute the C function to
105          * be as fast as possible. The argument is received in EAX from a gsharedvt trampoline. So the real
106          * call sequence looks like this:
107          * caller -> gsharedvt trampoline -> gsharevt in trampoline -> start_gsharedvt_call
108          * FIXME: Optimize this.
109          */
110
111         cfa_offset = sizeof (gpointer);
112         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, cfa_offset);
113         mono_add_unwind_op_offset (unwind_ops, code, buf, X86_NREG, -cfa_offset);
114         x86_push_reg (code, X86_EBP);
115         cfa_offset += sizeof (gpointer);
116         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
117         mono_add_unwind_op_offset (unwind_ops, code, buf, X86_EBP, - cfa_offset);
118         x86_mov_reg_reg (code, X86_EBP, X86_ESP, sizeof (gpointer));
119         mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, X86_EBP);
120         /* Alloc stack frame/align stack */
121         x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
122         info_offset = -4;
123         mrgctx_offset = - 8;
124         /* The info struct is put into EAX by the gsharedvt trampoline */
125         /* Save info struct addr */
126         x86_mov_membase_reg (code, X86_EBP, info_offset, X86_EAX, 4);
127         /* Save rgctx */
128         x86_mov_membase_reg (code, X86_EBP, mrgctx_offset, MONO_ARCH_RGCTX_REG, 4);
129
130         /* Allocate stack area used to pass arguments to the method */
131         x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage), sizeof (gpointer));
132         x86_alu_reg_reg (code, X86_SUB, X86_ESP, X86_EAX);
133
134 #if 0
135         /* Stack alignment check */
136         x86_mov_reg_reg (code, X86_ECX, X86_ESP, 4);
137         x86_alu_reg_imm (code, X86_AND, X86_ECX, MONO_ARCH_FRAME_ALIGNMENT - 1);
138         x86_alu_reg_imm (code, X86_CMP, X86_ECX, 0);
139         x86_branch_disp (code, X86_CC_EQ, 3, FALSE);
140         x86_breakpoint (code);
141 #endif
142
143         /* ecx = caller argument area */
144         x86_mov_reg_reg (code, X86_ECX, X86_EBP, 4);
145         x86_alu_reg_imm (code, X86_ADD, X86_ECX, 8);
146         /* eax = callee argument area */
147         x86_mov_reg_reg (code, X86_EAX, X86_ESP, 4);
148
149         /* Call start_gsharedvt_call */
150         /* Arg 4 */
151         x86_push_membase (code, X86_EBP, mrgctx_offset);
152         /* Arg3 */
153         x86_push_reg (code, X86_EAX);
154         /* Arg2 */
155         x86_push_reg (code, X86_ECX);
156         /* Arg1 */
157         x86_push_membase (code, X86_EBP, info_offset);
158         if (aot) {
159                 code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_x86_start_gsharedvt_call");
160                 x86_call_reg (code, X86_EAX);
161         } else {
162                 x86_call_code (code, mono_x86_start_gsharedvt_call);
163         }
164         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4 * 4);
165         /* The address to call is in eax */
166         /* The stack is now setup for the real call */
167         /* Load info struct */
168         x86_mov_reg_membase (code, X86_ECX, X86_EBP, info_offset, 4);
169         /* Load rgctx */
170         x86_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, X86_EBP, mrgctx_offset, sizeof (gpointer));
171         /* Make the call */
172         x86_call_reg (code, X86_EAX);
173         /* The return value is either in registers, or stored to an area beginning at sp [info->vret_slot] */
174         /* EAX/EDX might contain the return value, only ECX is free */
175         /* Load info struct */
176         x86_mov_reg_membase (code, X86_ECX, X86_EBP, info_offset, 4);
177
178         /* Branch to the in/out handling code */
179         x86_alu_membase_imm (code, X86_CMP, X86_ECX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1);  
180         br_out = code;
181         x86_branch32 (code, X86_CC_NE, 0, TRUE);
182
183         /*
184          * IN CASE
185          */
186
187         /* Load ret marshal type */
188         x86_mov_reg_membase (code, X86_ECX, X86_ECX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
189         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_NONE);
190         br [0] = code;
191         x86_branch8 (code, X86_CC_NE, 0, TRUE);
192
193         /* Normal return, no marshalling required */
194         x86_leave (code);
195         x86_ret (code);
196
197         /* Return value marshalling */
198         x86_patch (br [0], code);
199         /* Load info struct */
200         x86_mov_reg_membase (code, X86_EAX, X86_EBP, info_offset, 4);
201         /* Load 'vret_slot' */
202         x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
203         /* Compute ret area address */
204         x86_shift_reg_imm (code, X86_SHL, X86_EAX, 2);
205         x86_alu_reg_reg (code, X86_ADD, X86_EAX, X86_ESP);
206         /* The callee does a ret $4, so sp is off by 4 */
207         x86_alu_reg_imm (code, X86_SUB, X86_EAX, sizeof (gpointer));
208
209         /* Branch to specific marshalling code */
210         // FIXME: Move the I4 case to the top */
211         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_DOUBLE_FPSTACK);
212         br [1] = code;
213         x86_branch8 (code, X86_CC_E, 0, TRUE);
214         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_FLOAT_FPSTACK);
215         br [2] = code;
216         x86_branch8 (code, X86_CC_E, 0, TRUE);
217         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_STACK_POP);
218         br [3] = code;
219         x86_branch8 (code, X86_CC_E, 0, TRUE);
220         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_I1);
221         br [4] = code;
222         x86_branch8 (code, X86_CC_E, 0, TRUE);
223         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_U1);
224         br [5] = code;
225         x86_branch8 (code, X86_CC_E, 0, TRUE);
226         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_I2);
227         br [6] = code;
228         x86_branch8 (code, X86_CC_E, 0, TRUE);
229         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_U2);
230         br [7] = code;
231         x86_branch8 (code, X86_CC_E, 0, TRUE);
232         /* IREGS case */
233         /* Load both eax and edx for simplicity */
234         x86_mov_reg_membase (code, X86_EDX, X86_EAX, sizeof (gpointer), sizeof (gpointer));
235         x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, sizeof (gpointer));
236         x86_leave (code);
237         x86_ret (code);
238         /* DOUBLE_FPSTACK case */
239         x86_patch (br [1], code);
240         x86_fld_membase (code, X86_EAX, 0, TRUE);
241         x86_jump8 (code, 0);
242         x86_leave (code);
243         x86_ret (code);
244         /* FLOAT_FPSTACK case */
245         x86_patch (br [2], code);
246         x86_fld_membase (code, X86_EAX, 0, FALSE);
247         x86_leave (code);
248         x86_ret (code);
249         /* STACK_POP case */
250         x86_patch (br [3], code);
251         x86_leave (code);
252         x86_ret_imm (code, 4);
253         /* I1 case */
254         x86_patch (br [4], code);
255         x86_widen_membase (code, X86_EAX, X86_EAX, 0, TRUE, FALSE);
256         x86_leave (code);
257         x86_ret (code);
258         /* U1 case */
259         x86_patch (br [5], code);
260         x86_widen_membase (code, X86_EAX, X86_EAX, 0, FALSE, FALSE);
261         x86_leave (code);
262         x86_ret (code);
263         /* I2 case */
264         x86_patch (br [6], code);
265         x86_widen_membase (code, X86_EAX, X86_EAX, 0, TRUE, TRUE);
266         x86_leave (code);
267         x86_ret (code);
268         /* U2 case */
269         x86_patch (br [7], code);
270         x86_widen_membase (code, X86_EAX, X86_EAX, 0, FALSE, TRUE);
271         x86_leave (code);
272         x86_ret (code);
273
274         /*
275          * OUT CASE
276          */
277
278         x86_patch (br_out, code);
279         /* Load ret marshal type into ECX */
280         x86_mov_reg_membase (code, X86_ECX, X86_ECX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
281         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_NONE);
282         br [0] = code;
283         x86_branch8 (code, X86_CC_NE, 0, TRUE);
284
285         /* Normal return, no marshalling required */
286         x86_leave (code);
287         x86_ret (code);
288
289         /* Return value marshalling */
290         x86_patch (br [0], code);
291
292         /* EAX might contain the return value */
293         // FIXME: Use moves
294         x86_push_reg (code, X86_EAX);
295
296         /* Load info struct */
297         x86_mov_reg_membase (code, X86_EAX, X86_EBP, info_offset, 4);
298         /* Load 'vret_arg_slot' */
299         x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_slot), 4);
300         /* Compute ret area address in the caller frame in EAX */
301         x86_shift_reg_imm (code, X86_SHL, X86_EAX, 2);
302         x86_alu_reg_reg (code, X86_ADD, X86_EAX, X86_EBP);
303         x86_alu_reg_imm (code, X86_ADD, X86_EAX, 8);
304         x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, sizeof (gpointer));
305
306         /* Branch to specific marshalling code */
307         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_DOUBLE_FPSTACK);
308         br [1] = code;
309         x86_branch8 (code, X86_CC_E, 0, TRUE);
310         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_FLOAT_FPSTACK);
311         br [2] = code;
312         x86_branch8 (code, X86_CC_E, 0, TRUE);
313         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_STACK_POP);
314         br [3] = code;
315         x86_branch8 (code, X86_CC_E, 0, TRUE);
316         x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_IREGS);
317         br [4] = code;
318         x86_branch8 (code, X86_CC_E, 0, TRUE);
319         /* IREG case */
320         x86_mov_reg_reg (code, X86_ECX, X86_EAX, sizeof (gpointer));
321         x86_pop_reg (code, X86_EAX);
322         x86_mov_membase_reg (code, X86_ECX, 0, X86_EAX, sizeof (gpointer));
323         x86_leave (code);
324         x86_ret_imm (code, 4);
325         /* IREGS case */
326         x86_patch (br [4], code);
327         x86_mov_reg_reg (code, X86_ECX, X86_EAX, sizeof (gpointer));
328         x86_pop_reg (code, X86_EAX);
329         x86_mov_membase_reg (code, X86_ECX, sizeof (gpointer), X86_EDX, sizeof (gpointer));
330         x86_mov_membase_reg (code, X86_ECX, 0, X86_EAX, sizeof (gpointer));
331         x86_leave (code);
332         x86_ret_imm (code, 4);
333         /* DOUBLE_FPSTACK case */
334         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
335         x86_patch (br [1], code);
336         x86_fst_membase (code, X86_EAX, 0, TRUE, TRUE);
337         x86_jump8 (code, 0);
338         x86_leave (code);
339         x86_ret_imm (code, 4);
340         /* FLOAT_FPSTACK case */
341         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
342         x86_patch (br [2], code);
343         x86_fst_membase (code, X86_EAX, 0, FALSE, TRUE);
344         x86_leave (code);
345         x86_ret_imm (code, 4);
346         /* STACK_POP case */
347         x86_patch (br [3], code);
348         x86_leave (code);
349         x86_ret_imm (code, 4);
350
351         g_assert ((code - buf) < buf_len);
352
353         if (info)
354                 *info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);
355
356         mono_arch_flush_icache (buf, code - buf);
357         return buf;
358 }
359
360 #else
361
362 gpointer
363 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
364 {
365         *info = NULL;
366         return NULL;
367 }
368
369 #endif /* MONO_ARCH_GSHAREDVT_SUPPORTED */