2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / arch / amd64 / tramp.c
1 /*
2  * Create trampolines to invoke arbitrary functions.
3  * 
4  * Copyright (C) Ximian Inc.
5  * 
6  * Author: 
7  *   Zalman Stern
8  * Based on code by:
9  *   Paolo Molaro (lupus@ximian.com)
10  *   Dietmar Maurer (dietmar@ximian.com)
11  * 
12  * To understand this code, one will want to the calling convention section of the ABI sepc at:
13  *     http://x86-64.org/abi.pdf
14  * and the AMD64 architecture docs found at amd.com .
15  */
16
17 #include "config.h"
18 #include <stdlib.h>
19 #include <string.h>
20 #include "amd64-codegen.h"
21 #include "mono/metadata/class.h"
22 #include "mono/metadata/tabledefs.h"
23 #include "mono/interpreter/interp.h"
24 #include "mono/metadata/appdomain.h"
25 #include "mono/metadata/marshal.h"
26
27 /*
28  * The resulting function takes the form:
29  * void func (void (*callme)(), void *retval, void *this_obj, stackval *arguments);
30  */
31 #define FUNC_ADDR_POS   8
32 #define RETVAL_POS      12
33 #define THIS_POS        16
34 #define ARGP_POS        20
35 #define LOC_POS -4
36
37 #define ARG_SIZE        sizeof (stackval)
38
39 #define MAX_INT_ARG_REGS        6
40 #define MAX_FLOAT_ARG_REGS      8
41
42 // TODO get these right. They are upper bounds anyway, so it doesn't much matter.
43 #define PUSH_INT_STACK_ARG_SIZE         16
44 #define MOVE_INT_REG_ARG_SIZE           16
45 #define PUSH_FLOAT_STACK_ARG_SIZE       16
46 #define MOVE_FLOAT_REG_ARG_SIZE         16
47 #define COPY_STRUCT_STACK_ARG_SIZE      16
48
49 /* Maps an argument number (starting at 0) to the register it is passed in (if it fits).
50  * E.g. int foo(int bar, int quux) has the foo arg in RDI and the quux arg in RSI
51  * There is no such map for floating point args as they go in XMM0-XMM7 in order and thus the
52  * index is the register number.
53  */
54 static int int_arg_regs[] = { AMD64_RDI, AMD64_RSI, AMD64_RDX, AMD64_RCX, AMD64_R8, AMD64_R9 };
55
56 /* This next block of code resolves the ABI rules for passing structures in the argument registers.
57  * These basically amount to "Use up to two registers if they are all integer or all floating point.
58  * If the structure is bigger than two registers or would be in one integer register and one floating point,
59  * it is passed in memory instead.
60  *
61  * It is possible this code needs to be recursive to be correct in the case when one of the structure members
62  * is itself a structure.
63  *
64  * The 80-bit floating point stuff is ignored.
65  */
66 typedef enum {
67         ARG_IN_MEMORY,
68         ARG_IN_INT_REGS,
69         ARG_IN_FLOAT_REGS
70 } struct_arg_type;
71
72 static struct_arg_type compute_arg_type(MonoType *type)
73 {
74         guint32 simpletype = type->type;
75
76         switch (simpletype) {
77                 case MONO_TYPE_BOOLEAN:
78                 case MONO_TYPE_CHAR:
79                 case MONO_TYPE_I1:
80                 case MONO_TYPE_U1:
81                 case MONO_TYPE_I2:
82                 case MONO_TYPE_U2:
83                 case MONO_TYPE_I4:
84                 case MONO_TYPE_U4:
85                 case MONO_TYPE_I:
86                 case MONO_TYPE_U:
87                 case MONO_TYPE_PTR:
88                 case MONO_TYPE_SZARRAY:
89                 case MONO_TYPE_CLASS:
90                 case MONO_TYPE_OBJECT:
91                 case MONO_TYPE_STRING:
92                 case MONO_TYPE_I8:
93                         return ARG_IN_INT_REGS;
94                         break;
95                 case MONO_TYPE_VALUETYPE: {
96                         if (type->data.klass->enumtype)
97                                 return ARG_IN_INT_REGS;
98                         return ARG_IN_MEMORY;
99                         break;
100                 }
101                 case MONO_TYPE_R4:
102                 case MONO_TYPE_R8:
103                         return ARG_IN_FLOAT_REGS;
104                         break;
105                 default:
106                         g_error ("Can't trampoline 0x%x", type->type);
107         }
108
109         return ARG_IN_MEMORY;
110 }
111
112 static struct_arg_type value_type_info(MonoClass *klass, int *native_size, int *regs_used, int *offset1, int *size1, int *offset2, int *size2)
113 {
114         MonoMarshalType *info = mono_marshal_load_type_info (klass);
115
116         *native_size = info->native_size;
117
118         if (info->native_size > 8 || info->num_fields > 2)
119         {
120                 *regs_used = 0;
121                 *offset1 = -1;
122                 *offset2 = -1;
123                 return ARG_IN_MEMORY;
124         }
125
126         if (info->num_fields == 1)
127         {
128                 struct_arg_type result = compute_arg_type(info->fields[0].field->type);
129                 if (result != ARG_IN_MEMORY)
130                 {
131                         *regs_used = 1;
132                         *offset1 = info->fields[0].offset;
133                         *size1 = mono_marshal_type_size (info->fields[0].field->type, info->fields[0].mspec, NULL, 1, 1);
134                 } 
135                 else
136                 {
137                         *regs_used = 0;
138                         *offset1 = -1;
139                 }
140
141                 *offset2 = -1;
142                 return result;
143         }
144
145         struct_arg_type result1 = compute_arg_type(info->fields[0].field->type);
146         struct_arg_type result2 = compute_arg_type(info->fields[0].field->type);
147
148         if (result1 == result2 && result1 != ARG_IN_MEMORY)
149         {
150                 *regs_used = 2;
151                 *offset1 = info->fields[0].offset;
152                 *size1 = mono_marshal_type_size (info->fields[0].field->type, info->fields[0].mspec, NULL, 1, 1);
153                 *offset2 = info->fields[1].offset;
154                 *size2 = mono_marshal_type_size (info->fields[1].field->type, info->fields[1].mspec, NULL, 1, 1);
155                 return result1;
156         }
157
158         return ARG_IN_MEMORY;
159 }
160
161 MonoPIFunc
162 mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
163 {
164         unsigned char *p, *code_buffer;
165         guint32 stack_size = 0, code_size = 50;
166         guint32 arg_pos, simpletype;
167         int i;
168         static GHashTable *cache = NULL;
169         MonoPIFunc res;
170
171         guint32 int_arg_regs_used = 0;
172         guint32 float_arg_regs_used = 0;
173         guint32 next_int_arg_reg = 0;
174         guint32 next_float_arg_reg = 0;
175         /* Indicates that the return value is filled in inside the called function. */
176         int retval_implicit = 0;
177         char *arg_in_reg_bitvector; /* A set index by argument number saying if it is in a register
178                                        (integer or floating point according to type) */
179
180         if (!cache) 
181                 cache = g_hash_table_new ((GHashFunc)mono_signature_hash, 
182                                           (GCompareFunc)mono_metadata_signature_equal);
183
184         if ((res = (MonoPIFunc)g_hash_table_lookup (cache, sig)))
185                 return res;
186
187         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) {
188                 int_arg_regs_used++;
189                 code_size += MOVE_INT_REG_ARG_SIZE;
190         }
191
192         if (sig->hasthis) {
193                 int_arg_regs_used++;
194                 code_size += MOVE_INT_REG_ARG_SIZE;
195         }
196         
197         /* Run through stuff to calculate code size and argument bytes that will be pushed on stack (stack_size). */
198         for (i = 0; i < sig->param_count; ++i) {
199                 if (sig->params [i]->byref)
200                         simpletype = MONO_TYPE_PTR;
201                 else
202                         simpletype = sig->params [i]->type;
203 enum_calc_size:
204                 switch (simpletype) {
205                 case MONO_TYPE_BOOLEAN:
206                 case MONO_TYPE_CHAR:
207                 case MONO_TYPE_I1:
208                 case MONO_TYPE_U1:
209                 case MONO_TYPE_I2:
210                 case MONO_TYPE_U2:
211                 case MONO_TYPE_I4:
212                 case MONO_TYPE_U4:
213                 case MONO_TYPE_I:
214                 case MONO_TYPE_U:
215                 case MONO_TYPE_PTR:
216                 case MONO_TYPE_SZARRAY:
217                 case MONO_TYPE_CLASS:
218                 case MONO_TYPE_OBJECT:
219                 case MONO_TYPE_STRING:
220                 case MONO_TYPE_I8:
221                         if (int_arg_regs_used++ > MAX_INT_ARG_REGS) {
222                                 stack_size += 8;
223                                 code_size += PUSH_INT_STACK_ARG_SIZE;
224                         }
225                         else
226                                 code_size += MOVE_INT_REG_ARG_SIZE;
227                         break;
228                 case MONO_TYPE_VALUETYPE: {
229                         int size;
230                         int arg_type;
231                         int regs_used;
232                         int offset1;
233                         int size1;
234                         int offset2;
235                         int size2;
236
237                         if (sig->params [i]->data.klass->enumtype) {
238                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
239                                 goto enum_calc_size;
240                         }
241
242                         arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
243                         if (arg_type == ARG_IN_INT_REGS &&
244                             (int_arg_regs_used + regs_used) <= MAX_INT_ARG_REGS)
245                         {
246                                 code_size += MOVE_INT_REG_ARG_SIZE;
247                                 int_arg_regs_used += regs_used;
248                                 break;
249                         }
250
251                         if (arg_type == ARG_IN_FLOAT_REGS &&
252                             (float_arg_regs_used + regs_used) <= MAX_FLOAT_ARG_REGS)
253                         {
254                                 code_size += MOVE_FLOAT_REG_ARG_SIZE;
255                                 float_arg_regs_used += regs_used;
256                                 break;
257                         }
258
259                         /* Else item is in memory. */
260
261                         stack_size += size + 7;
262                         stack_size &= ~7;
263                         code_size += COPY_STRUCT_STACK_ARG_SIZE;
264
265                         break;
266                 }
267                 case MONO_TYPE_R4:
268                 case MONO_TYPE_R8:
269                         if (float_arg_regs_used++ > MAX_FLOAT_ARG_REGS) {
270                                 stack_size += 8;
271                                 code_size += PUSH_FLOAT_STACK_ARG_SIZE;
272                         }
273                         else
274                                 code_size += MOVE_FLOAT_REG_ARG_SIZE;
275                         break;
276                 default:
277                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
278                 }
279         }
280         /*
281          * FIXME: take into account large return values.
282          * (Comment carried over from IA32 code. Not sure what it means :-)
283          */
284
285         code_buffer = p = alloca (code_size);
286
287         /*
288          * Standard function prolog.
289          */
290         amd64_push_reg (p, AMD64_RBP);
291         amd64_mov_reg_reg (p, AMD64_RBP, AMD64_RSP, 8);
292         /*
293          * and align to 16 byte boundary...
294          */
295
296         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
297                 MonoClass *klass = sig->ret->data.klass;
298                 if (!klass->enumtype) {
299                         retval_implicit = 1;
300                 }
301         }
302
303         if (sig->ret->byref || string_ctor || !(retval_implicit || sig->ret->type == MONO_TYPE_VOID)) {
304                 /* Push the retval register so it is saved across the call. It will be addressed via RBP later. */
305                 amd64_push_reg (p, AMD64_RSI);
306                 stack_size += 8;
307         }
308
309         /* Ensure stack is 16 byte aligned when entering called function as required by calling convention. 
310          * Getting this wrong results in a general protection fault on an SSE load or store somewhere in the
311          * code called under the trampoline.
312          */
313         if ((stack_size & 15) != 0)
314                 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, 16 - (stack_size & 15));
315
316         /*
317          * On entry to generated function:
318          *     RDI has target function address
319          *     RSI has return value location address
320          *     RDX has this pointer address
321          *     RCX has the pointer to the args array.
322          *
323          * Inside the stub function:
324          *     R10 holds the pointer to the args 
325          *     R11 holds the target function address.
326          *     The return value address is pushed on the stack.
327          *     The this pointer is moved into the first arg register at the start.
328          *
329          * Optimization note: we could keep the args pointer in RCX and then
330          * load over itself at the end. Ditto the callee addres could be left in RDI in some cases.
331          */
332
333         /* Move args pointer to temp register. */
334         amd64_mov_reg_reg (p, AMD64_R10, AMD64_RCX, 8);
335         amd64_mov_reg_reg (p, AMD64_R11, AMD64_RDI, 8);
336
337         /* First args register gets return value pointer, if need be.
338          * Note that "byref" equal true means the called function returns a pointer.
339          */
340         if (retval_implicit) {
341                 amd64_mov_reg_reg (p, int_arg_regs[next_int_arg_reg], AMD64_RSI, 8);
342                 next_int_arg_reg++;
343         }
344
345         /* this pointer goes in next args register. */
346         if (sig->hasthis) {
347                 amd64_mov_reg_reg (p, int_arg_regs[next_int_arg_reg], AMD64_RDX, 8);
348                 next_int_arg_reg++;
349         }
350
351         /*
352          * Generate code to handle arguments in registers. Stack arguments will happen in a loop after this.
353          */
354         arg_in_reg_bitvector = (char *)alloca((sig->param_count + 7) / 8);
355         memset(arg_in_reg_bitvector, 0, (sig->param_count + 7) / 8);
356
357         /* First, load all the arguments that are passed in registers into the appropriate registers.
358          * Below there is another loop to handle arguments passed on the stack.
359          */
360         for (i = 0; i < sig->param_count; i++) {
361                 arg_pos = ARG_SIZE * i;
362
363                 if (sig->params [i]->byref)
364                         simpletype = MONO_TYPE_PTR;
365                 else
366                         simpletype = sig->params [i]->type;
367 enum_marshal:
368                 switch (simpletype) {
369                 case MONO_TYPE_BOOLEAN:
370                 case MONO_TYPE_I1:
371                 case MONO_TYPE_U1:
372                 case MONO_TYPE_I2:
373                 case MONO_TYPE_U2:
374                 case MONO_TYPE_CHAR:
375                 case MONO_TYPE_I4:
376                 case MONO_TYPE_U4:
377                 case MONO_TYPE_I:
378                 case MONO_TYPE_U:
379                 case MONO_TYPE_PTR:
380                 case MONO_TYPE_OBJECT:
381                 case MONO_TYPE_STRING:
382                 case MONO_TYPE_SZARRAY:
383                 case MONO_TYPE_I8:
384                 case MONO_TYPE_U8:
385                 case MONO_TYPE_CLASS:
386                         if (next_int_arg_reg < MAX_INT_ARG_REGS) {
387                                 amd64_mov_reg_membase (p, int_arg_regs[next_int_arg_reg], AMD64_R10, arg_pos, 8);
388                                 next_int_arg_reg++;
389                                 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
390                         }
391                         break;
392                 case MONO_TYPE_R4:
393                         if (next_float_arg_reg < MAX_FLOAT_ARG_REGS) {
394                                 amd64_movss_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos);
395                                 next_float_arg_reg++;
396                                 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
397                         }
398                         break;
399                 case MONO_TYPE_R8:
400                         if (next_float_arg_reg < MAX_FLOAT_ARG_REGS) {
401                                 amd64_movsd_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos);
402                                 next_float_arg_reg++;
403                                 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
404                         }
405                         break;
406                 case MONO_TYPE_VALUETYPE: {
407                         if (!sig->params [i]->data.klass->enumtype) {
408                                 int size;
409                                 int arg_type;
410                                 int regs_used;
411                                 int offset1;
412                                 int size1;
413                                 int offset2;
414                                 int size2;
415
416                                 arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
417
418                                 if (arg_type == ARG_IN_INT_REGS &&
419                                     (next_int_arg_reg + regs_used) <= MAX_INT_ARG_REGS)
420                                 {
421                                         amd64_mov_reg_membase (p, int_arg_regs[next_int_arg_reg], AMD64_R10, arg_pos + offset1, size1);
422                                         next_int_arg_reg++;
423                                         if (regs_used > 1)
424                                         {
425                                                 amd64_mov_reg_membase (p, int_arg_regs[next_int_arg_reg], AMD64_R10, arg_pos + offset2, size2);
426                                                 next_int_arg_reg++;
427                                         }
428                                         arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
429                                         break;
430                                 }
431
432                                 if (arg_type == ARG_IN_FLOAT_REGS &&
433                                     (next_float_arg_reg + regs_used) <= MAX_FLOAT_ARG_REGS)
434                                 {
435                                         if (size1 == 4)
436                                                 amd64_movss_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset1);
437                                         else
438                                                 amd64_movsd_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset1);
439                                         next_float_arg_reg++;
440
441                                         if (regs_used > 1)
442                                         {
443                                                 if (size2 == 4)
444                                                         amd64_movss_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset2);
445                                                 else
446                                                         amd64_movsd_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset2);
447                                                 next_float_arg_reg++;
448                                         }
449                                         arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
450                                         break;
451                                 }
452
453                                 /* Structs in memory are handled in the next loop. */
454                         } else {
455                                 /* it's an enum value */
456                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
457                                 goto enum_marshal;
458                         }
459                         break;
460                 }
461                 default:
462                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
463                 }
464         }
465
466         /* Handle stack arguments, pushing the rightmost argument first. */
467         for (i = sig->param_count; i > 0; --i) {
468                 arg_pos = ARG_SIZE * (i - 1);
469                 if (sig->params [i - 1]->byref)
470                         simpletype = MONO_TYPE_PTR;
471                 else
472                         simpletype = sig->params [i - 1]->type;
473 enum_marshal2:
474                 switch (simpletype) {
475                 case MONO_TYPE_BOOLEAN:
476                 case MONO_TYPE_I1:
477                 case MONO_TYPE_U1:
478                 case MONO_TYPE_I2:
479                 case MONO_TYPE_U2:
480                 case MONO_TYPE_CHAR:
481                 case MONO_TYPE_I4:
482                 case MONO_TYPE_U4:
483                 case MONO_TYPE_I:
484                 case MONO_TYPE_U:
485                 case MONO_TYPE_PTR:
486                 case MONO_TYPE_OBJECT:
487                 case MONO_TYPE_STRING:
488                 case MONO_TYPE_SZARRAY:
489                 case MONO_TYPE_I8:
490                 case MONO_TYPE_U8:
491                 case MONO_TYPE_CLASS:
492                         if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0) {
493                                 amd64_push_membase (p, AMD64_R10, arg_pos);
494                         }
495                         break;
496                 case MONO_TYPE_R4:
497                         if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0) {
498                                 amd64_push_membase (p, AMD64_R10, arg_pos);
499                         }
500                         break;
501                 case MONO_TYPE_R8:
502                         if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0) {
503                                 amd64_push_membase (p, AMD64_R10, arg_pos);
504                         }
505                         break;
506                 case MONO_TYPE_VALUETYPE:
507                         if (!sig->params [i - 1]->data.klass->enumtype) {
508                                 if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0)
509                                 {
510                                         int ss = mono_class_native_size (sig->params [i - 1]->data.klass, NULL);
511                                         ss += 7;
512                                         ss &= ~7;
513
514                                         amd64_alu_reg_imm(p, X86_SUB, AMD64_RSP, ss);
515                                         /* Count register */
516                                         amd64_mov_reg_imm(p, AMD64_RCX, ss);
517                                         /* Source register */
518                                         amd64_lea_membase(p, AMD64_RSI, AMD64_R10, arg_pos);
519                                         /* Dest register */
520                                         amd64_mov_reg_reg(p, AMD64_RDI, AMD64_RSP, 8);
521
522                                         /* AMD64 calling convention guarantees direction flag is clear at call boundary. */
523                                         x86_prefix(p, AMD64_REX(AMD64_REX_W));
524                                         x86_prefix(p, X86_REP_PREFIX);
525                                         x86_movsb(p);
526                                 }
527                         } else {
528                                 /* it's an enum value */
529                                 simpletype = sig->params [i - 1]->data.klass->enum_basetype->type;
530                                 goto enum_marshal2;
531                         }
532                         break;
533                 default:
534                         g_error ("Can't trampoline 0x%x", sig->params [i - 1]->type);
535                 }
536         }
537
538         /* TODO: Set RAL to number of XMM registers used in case this is a varags function? */
539  
540         /* 
541          * Insert call to function 
542          */
543         amd64_call_reg (p, AMD64_R11);
544
545         if (sig->ret->byref || string_ctor || !(retval_implicit || sig->ret->type == MONO_TYPE_VOID)) {
546                 amd64_mov_reg_membase(p, AMD64_RSI, AMD64_RBP, -8, 8);
547         }
548         /*
549          * Handle retval.
550          * Small integer and pointer values are in EAX.
551          * Long integers are in EAX:EDX.
552          * FP values are on the FP stack.
553          */
554
555         if (sig->ret->byref || string_ctor) {
556                 simpletype = MONO_TYPE_PTR;
557         } else {
558                 simpletype = sig->ret->type;
559         }
560         enum_retvalue:
561         switch (simpletype) {
562                 case MONO_TYPE_BOOLEAN:
563                 case MONO_TYPE_I1:
564                 case MONO_TYPE_U1:
565                         amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 1);
566                         break;
567                 case MONO_TYPE_CHAR:
568                 case MONO_TYPE_I2:
569                 case MONO_TYPE_U2:
570                         amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 2);
571                         break;
572                 case MONO_TYPE_I4:
573                 case MONO_TYPE_U4:
574                 case MONO_TYPE_I:
575                 case MONO_TYPE_U:
576                 case MONO_TYPE_CLASS:
577                 case MONO_TYPE_OBJECT:
578                 case MONO_TYPE_SZARRAY:
579                 case MONO_TYPE_ARRAY:
580                 case MONO_TYPE_STRING: 
581                 case MONO_TYPE_PTR:
582                         amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 8);
583                         break;
584                 case MONO_TYPE_R4:
585                         amd64_movss_regp_reg (p, AMD64_RSI, AMD64_XMM0);
586                         break;
587                 case MONO_TYPE_R8:
588                         amd64_movsd_regp_reg (p, AMD64_RSI, AMD64_XMM0);
589                         break;
590                 case MONO_TYPE_I8:
591                         amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 8);
592                         break;
593                 case MONO_TYPE_VALUETYPE: {
594                         int size;
595                         int arg_type;
596                         int regs_used;
597                         int offset1;
598                         int size1;
599                         int offset2;
600                         int size2;
601
602                         if (sig->ret->data.klass->enumtype) {
603                                 simpletype = sig->ret->data.klass->enum_basetype->type;
604                                 goto enum_retvalue;
605                         }
606
607                         arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
608
609                         if (arg_type == ARG_IN_INT_REGS)
610                         {
611                                 amd64_mov_membase_reg (p, AMD64_RSI, offset1, AMD64_RAX, size1);
612                                 if (regs_used > 1)
613                                         amd64_mov_membase_reg (p, AMD64_RSI, offset2, AMD64_RDX, size2);
614                                 break;
615                         }
616
617                         if (arg_type == ARG_IN_FLOAT_REGS)
618                         {
619                                 if (size1 == 4)
620                                         amd64_movss_membase_reg (p, AMD64_RSI, offset1, AMD64_XMM0);
621                                 else
622                                         amd64_movsd_membase_reg (p, AMD64_RSI, offset1, AMD64_XMM0);
623
624                                 if (regs_used > 1)
625                                 {
626                                         if (size2 == 4)
627                                                 amd64_movss_membase_reg (p, AMD64_RSI, offset2, AMD64_XMM1);
628                                         else
629                                                 amd64_movsd_membase_reg (p, AMD64_RSI, offset2, AMD64_XMM1);
630                                 }
631                                 break;
632                         }
633
634                         /* Else result should have been stored in place already. */
635                         break;
636                 }
637                 case MONO_TYPE_VOID:
638                         break;
639                 default:
640                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
641         }
642
643         /*
644          * Standard epilog.
645          */
646         amd64_leave (p);
647         amd64_ret (p);
648
649         g_assert (p - code_buffer < code_size);
650         res = (MonoPIFunc)g_memdup (code_buffer, p - code_buffer);
651
652         g_hash_table_insert (cache, sig, res);
653
654         return res;
655 }
656
657 /*
658  * Returns a pointer to a native function that can be used to
659  * call the specified method.
660  * The function created will receive the arguments according
661  * to the call convention specified in the method.
662  * This function works by creating a MonoInvocation structure,
663  * filling the fields in and calling ves_exec_method on it.
664  * Still need to figure out how to handle the exception stuff
665  * across the managed/unmanaged boundary.
666  */
667 void *
668 mono_arch_create_method_pointer (MonoMethod *method)
669 {
670         MonoMethodSignature *sig;
671         MonoJitInfo *ji;
672         unsigned char *p, *code_buffer;
673         guint32 simpletype;
674         gint32 local_size;
675         gint32 stackval_pos;
676         gint32 mono_invocation_pos;
677         int i, cpos;
678         int *vtbuf;
679         int *rbpoffsets;
680         int int_arg_regs_used = 0;
681         int float_arg_regs_used = 0;
682         int stacked_args_size = 0; /* bytes of register passed arguments pushed on stack for safe keeping. Used to get alignment right. */
683         int next_stack_arg_rbp_offset = 16;
684         int retval_ptr_rbp_offset = 0;
685         int this_reg = -1; /* Remember register this ptr is in. */
686
687         /*
688          * If it is a static P/Invoke method, we can just return the pointer
689          * to the method implementation.
690          */
691         if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL && ((MonoMethodPInvoke*) method)->addr) {
692                 ji = g_new0 (MonoJitInfo, 1);
693                 ji->method = method;
694                 ji->code_size = 1;
695                 ji->code_start = ((MonoMethodPInvoke*) method)->addr;
696
697                 mono_jit_info_table_add (mono_get_root_domain (), ji);
698                 return ((MonoMethodPInvoke*) method)->addr;
699         }
700
701         sig = method->signature;
702
703         code_buffer = p = alloca (512); /* FIXME: check for overflows... */
704         vtbuf = alloca (sizeof(int)*sig->param_count);
705         rbpoffsets = alloca (sizeof(int)*sig->param_count);
706
707
708         /*
709          * Standard function prolog.
710          */
711         amd64_push_reg (p, AMD64_RBP);
712         amd64_mov_reg_reg (p, AMD64_RBP, AMD64_RSP, 8);
713
714         /* If there is an implicit return value pointer in the first args reg, save it now so
715          * the result can be stored through the pointer at the end.
716          */
717         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) 
718         {
719                 amd64_push_reg (p, int_arg_regs[int_arg_regs_used]);
720                 int_arg_regs_used++;
721                 stacked_args_size += 8;
722                 retval_ptr_rbp_offset = -stacked_args_size;
723         }
724
725         /*
726          * If there is a this pointer, remember the number of the register it is in.
727          */
728         if (sig->hasthis) {
729                 this_reg = int_arg_regs[int_arg_regs_used++];
730         }
731
732         /* Put all arguments passed in registers on the stack.
733          * Record offsets from RBP to each argument.
734          */
735         cpos = 0;
736
737         for (i = 0; i < sig->param_count; i++) {
738                 if (sig->params [i]->byref)
739                         simpletype = MONO_TYPE_PTR;
740                 else
741                         simpletype = sig->params [i]->type;
742 enum_calc_size:
743                 switch (simpletype) {
744                 case MONO_TYPE_BOOLEAN:
745                 case MONO_TYPE_CHAR:
746                 case MONO_TYPE_I1:
747                 case MONO_TYPE_U1:
748                 case MONO_TYPE_I2:
749                 case MONO_TYPE_U2:
750                 case MONO_TYPE_I4:
751                 case MONO_TYPE_U4:
752                 case MONO_TYPE_I:
753                 case MONO_TYPE_U:
754                 case MONO_TYPE_PTR:
755                 case MONO_TYPE_SZARRAY:
756                 case MONO_TYPE_CLASS:
757                 case MONO_TYPE_OBJECT:
758                 case MONO_TYPE_STRING:
759                 case MONO_TYPE_I8:
760                         if (int_arg_regs_used < MAX_INT_ARG_REGS) {
761                                 amd64_push_reg (p, int_arg_regs[int_arg_regs_used]);
762                                 int_arg_regs_used++;
763                                 stacked_args_size += 8;
764                                 rbpoffsets[i] = -stacked_args_size;
765                         }
766                         else
767                         {
768                                 rbpoffsets[i] = next_stack_arg_rbp_offset;
769                                 next_stack_arg_rbp_offset += 8;
770                         }
771                         break;
772                 case MONO_TYPE_VALUETYPE: {
773                         if (sig->params [i]->data.klass->enumtype) {
774                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
775                                 goto enum_calc_size;
776                         }
777                         else
778                         {
779                                 int size;
780                                 int arg_type;
781                                 int regs_used;
782                                 int offset1;
783                                 int size1;
784                                 int offset2;
785                                 int size2;
786
787                                 arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
788
789                                 if (arg_type == ARG_IN_INT_REGS &&
790                                     (int_arg_regs_used + regs_used) <= MAX_INT_ARG_REGS)
791                                 {
792                                         amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, size);
793                                         stacked_args_size += size;
794                                         rbpoffsets[i] = stacked_args_size;
795
796                                         amd64_mov_reg_membase (p, int_arg_regs[int_arg_regs_used], AMD64_RSP, offset1, size1);
797                                         int_arg_regs_used++;
798                                         if (regs_used > 1)
799                                         {
800                                                 amd64_mov_reg_membase (p, int_arg_regs[int_arg_regs_used], AMD64_RSP, offset2, size2);
801                                                 int_arg_regs_used++;
802                                         }
803                                         break;
804                                 }
805
806                                 if (arg_type == ARG_IN_FLOAT_REGS &&
807                                     (float_arg_regs_used + regs_used) <= MAX_FLOAT_ARG_REGS)
808                                 {
809                                         amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, size);
810                                         stacked_args_size += size;
811                                         rbpoffsets[i] = stacked_args_size;
812
813                                         if (size1 == 4)
814                                                 amd64_movss_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset1);
815                                         else
816                                                 amd64_movsd_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset1);
817                                         float_arg_regs_used++;
818
819                                         if (regs_used > 1)
820                                         {
821                                                 if (size2 == 4)
822                                                         amd64_movss_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset2);
823                                                 else
824                                                         amd64_movsd_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset2);
825                                                 float_arg_regs_used++;
826                                         }
827                                         break;
828                                 }
829
830                                 rbpoffsets[i] = next_stack_arg_rbp_offset;
831                                 next_stack_arg_rbp_offset += size;
832                         }
833                         break;
834                 }
835                 case MONO_TYPE_R4:
836                         if (float_arg_regs_used < MAX_FLOAT_ARG_REGS) {
837                                 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, 8);
838                                 amd64_movss_regp_reg (p, AMD64_RSP, float_arg_regs_used);
839                                 float_arg_regs_used++;
840                                 stacked_args_size += 8;
841                                 rbpoffsets[i] = -stacked_args_size;
842                         }
843                         else
844                         {
845                                 rbpoffsets[i] = next_stack_arg_rbp_offset;
846                                 next_stack_arg_rbp_offset += 8;
847                         }
848                         break;
849                 case MONO_TYPE_R8:
850                         stacked_args_size += 8;
851                         if (float_arg_regs_used < MAX_FLOAT_ARG_REGS) {
852                                 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, 8);
853                                 amd64_movsd_regp_reg (p, AMD64_RSP, float_arg_regs_used);
854                                 float_arg_regs_used++;
855                                 stacked_args_size += 8;
856                                 rbpoffsets[i] = -stacked_args_size;
857                         }
858                         else
859                         {
860                                 rbpoffsets[i] = next_stack_arg_rbp_offset;
861                                 next_stack_arg_rbp_offset += 8;
862                         }
863                         break;
864                 default:
865                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
866                 }
867         }
868
869         local_size = sizeof (MonoInvocation) + sizeof (stackval) * (sig->param_count + 1) + stacked_args_size;
870
871         local_size += 15;
872         local_size &= ~15;
873
874         stackval_pos = -local_size;
875         mono_invocation_pos = stackval_pos + sizeof (stackval) * (sig->param_count + 1);
876
877         /* stacked_args_size has already been pushed onto the stack. Make room for the rest of it. */
878         amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, local_size - stacked_args_size);
879
880         /* Be careful not to trash any arg regs before saving this_reg to MonoInvocation structure below. */
881
882         /*
883          * Initialize MonoInvocation fields, first the ones known now.
884          */
885         amd64_alu_reg_reg (p, X86_XOR, AMD64_RAX, AMD64_RAX);
886         amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, ex)), AMD64_RAX, 8);
887         amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), AMD64_RAX, 8);
888         amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, parent)), AMD64_RAX, 8);
889         /*
890          * Set the method pointer.
891          */
892         amd64_mov_membase_imm (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, method)), (long)method, 8);
893
894         /*
895          * Handle this.
896          */
897         if (sig->hasthis)
898                 amd64_mov_membase_reg(p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, obj)), this_reg, 8);
899
900         /*
901          * Handle the arguments. stackval_pos is the offset from RBP of the stackval in the MonoInvocation args array .
902          * arg_pos is the offset from RBP to the incoming arg on the stack.
903          * We just call stackval_from_data to handle all the (nasty) issues....
904          */
905         amd64_lea_membase (p, AMD64_RAX, AMD64_RBP, stackval_pos);
906         amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, stack_args)), AMD64_RAX, 8);
907         for (i = 0; i < sig->param_count; ++i) {
908 /* Need to call stackval_from_data (MonoType *type, stackval *result, char *data, gboolean pinvoke); */
909                 amd64_mov_reg_imm (p, AMD64_R11, stackval_from_data);
910                 amd64_mov_reg_imm (p, int_arg_regs[0], sig->params[i]);
911                 amd64_lea_membase (p, int_arg_regs[1], AMD64_RBP, stackval_pos);
912                 amd64_lea_membase (p, int_arg_regs[2], AMD64_RBP, rbpoffsets[i]);
913                 amd64_mov_reg_imm (p, int_arg_regs[3], sig->pinvoke);
914                 amd64_call_reg (p, AMD64_R11);
915                 stackval_pos += sizeof (stackval);
916 #if 0
917                 /* fixme: alignment */
918                 if (sig->pinvoke)
919                         arg_pos += mono_type_native_stack_size (sig->params [i], &align);
920                 else
921                         arg_pos += mono_type_stack_size (sig->params [i], &align);
922 #endif
923         }
924
925         /*
926          * Handle the return value storage area.
927          */
928         amd64_lea_membase (p, AMD64_RAX, AMD64_RBP, stackval_pos);
929         amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, retval)), AMD64_RAX, 8);
930         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
931                 MonoClass *klass  = sig->ret->data.klass;
932                 if (!klass->enumtype) {
933                         amd64_mov_reg_membase (p, AMD64_RCX, AMD64_RBP, retval_ptr_rbp_offset, 8);
934                         amd64_mov_membase_reg (p, AMD64_RBP, stackval_pos, AMD64_RCX, 8);
935                 }
936         }
937
938         /*
939          * Call the method.
940          */
941         amd64_lea_membase (p, int_arg_regs[0], AMD64_RBP, mono_invocation_pos);
942         amd64_mov_reg_imm (p, AMD64_R11, ves_exec_method);
943         amd64_call_reg (p, AMD64_R11);
944         
945         /*
946          * Move the return value to the proper place.
947          */
948         amd64_lea_membase (p, AMD64_RAX, AMD64_RBP, stackval_pos);
949         if (sig->ret->byref) {
950                 amd64_mov_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 8);
951         } else {
952                 int simpletype = sig->ret->type;        
953         enum_retvalue:
954                 switch (sig->ret->type) {
955                 case MONO_TYPE_VOID:
956                         break;
957                 case MONO_TYPE_BOOLEAN:
958                 case MONO_TYPE_I1:
959                 case MONO_TYPE_U1:
960                         amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 1);
961                         break;
962                 case MONO_TYPE_CHAR:
963                 case MONO_TYPE_I2:
964                 case MONO_TYPE_U2:
965                         amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 2);
966                         break;
967                 case MONO_TYPE_I4:
968                 case MONO_TYPE_U4:
969                 case MONO_TYPE_I:
970                 case MONO_TYPE_U:
971                 case MONO_TYPE_OBJECT:
972                 case MONO_TYPE_STRING:
973                 case MONO_TYPE_CLASS:
974                         amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 4);
975                         break;
976                 case MONO_TYPE_I8:
977                         amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 8);
978                         break;
979                 case MONO_TYPE_R4:
980                         amd64_movss_regp_reg (p, AMD64_RAX, AMD64_XMM0);
981                         break;
982                 case MONO_TYPE_R8:
983                         amd64_movsd_regp_reg (p, AMD64_RAX, AMD64_XMM0);
984                         break;
985                 case MONO_TYPE_VALUETYPE: {
986                         int size;
987                         int arg_type;
988                         int regs_used;
989                         int offset1;
990                         int size1;
991                         int offset2;
992                         int size2;
993
994                         if (sig->ret->data.klass->enumtype) {
995                                 simpletype = sig->ret->data.klass->enum_basetype->type;
996                                 goto enum_retvalue;
997                         }
998
999                         arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
1000
1001                         if (arg_type == ARG_IN_INT_REGS)
1002                         {
1003                                 if (regs_used > 1)
1004                                         amd64_mov_membase_reg (p, AMD64_RAX, offset2, AMD64_RDX, size2);
1005                                 amd64_mov_membase_reg (p, AMD64_RAX, offset1, AMD64_RAX, size1);
1006                                 break;
1007                         }
1008
1009                         if (arg_type == ARG_IN_FLOAT_REGS)
1010                         {
1011                                 if (size1 == 4)
1012                                         amd64_movss_membase_reg (p, AMD64_RAX, offset1, AMD64_XMM0);
1013                                 else
1014                                         amd64_movsd_membase_reg (p, AMD64_RAX, offset1, AMD64_XMM0);
1015
1016                                 if (regs_used > 1)
1017                                 {
1018                                         if (size2 == 4)
1019                                                 amd64_movss_membase_reg (p, AMD64_RAX, offset2, AMD64_XMM1);
1020                                         else
1021                                                 amd64_movsd_membase_reg (p, AMD64_RAX, offset2, AMD64_XMM1);
1022                                 }
1023                                 break;
1024                         }
1025
1026                         /* Else result should have been stored in place already. IA32 code has a stackval_to_data call here, which
1027                          * looks wrong to me as the pointer in the stack val being converted is setup to point to the output area anyway.
1028                          * It all looks a bit suspect anyway.
1029                          */
1030                         break;
1031                 }
1032                 default:
1033                         g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
1034                         break;
1035                 }
1036         }
1037         
1038         /*
1039          * Standard epilog.
1040          */
1041         amd64_leave (p);
1042         amd64_ret (p);
1043
1044         g_assert (p - code_buffer < 512);
1045
1046         ji = g_new0 (MonoJitInfo, 1);
1047         ji->method = method;
1048         ji->code_size = p - code_buffer;
1049         ji->code_start = g_memdup (code_buffer, p - code_buffer);
1050
1051         mono_jit_info_table_add (mono_get_root_domain (), ji);
1052
1053         return ji->code_start;
1054 }