retval value type fixed
[mono.git] / mono / arch / ppc / tramp.c
1 /*
2  * Create trampolines to invoke arbitrary functions.
3  * 
4  * Copyright (C) Radek Doulik
5  * 
6  */
7
8 #include "config.h"
9 #include <stdlib.h>
10 #include <string.h>
11 #include "ppc-codegen.h"
12 #include "mono/metadata/class.h"
13 #include "mono/metadata/tabledefs.h"
14 #include "mono/interpreter/interp.h"
15 #include "mono/metadata/appdomain.h"
16
17 #ifdef NEED_MPROTECT
18 #include <sys/mman.h>
19 #include <limits.h>    /* for PAGESIZE */
20 #ifndef PAGESIZE
21 #define PAGESIZE 4096
22 #endif
23 #endif
24
25 #define DEBUG(x) x
26
27 /* gpointer
28 fake_func (gpointer (*callme)(gpointer), stackval *retval, void *this_obj, stackval *arguments)
29 {
30         guint32 i = 0xc002becd;
31
32         callme = (gpointer) 0x100fabcd;
33
34         *(gpointer*)retval = (gpointer)(*callme) (arguments [0].data.p, arguments [1].data.p, arguments [2].data.p);
35         *(gdouble*) retval = (gdouble)(*callme) (arguments [0].data.f);
36
37         return (gpointer) (*callme) (((MonoType *)arguments [0]. data.p)->data.klass);
38 } */
39
40 #define MIN_CACHE_LINE 8
41
42 static void inline
43 flush_icache (guint8 *code, guint size)
44 {
45         guint i;
46         guint8 *p;
47
48         p = code;
49         for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) {
50                 asm ("dcbst 0,%0;" : : "r"(p) : "memory");
51         }
52         asm ("sync");
53         p = code;
54         for (i = 0; i < size; i += MIN_CACHE_LINE, p += MIN_CACHE_LINE) {
55                 asm ("icbi 0,%0; sync;" : : "r"(p) : "memory");
56         }
57         asm ("sync");
58         asm ("isync");
59 }
60
61 #define NOT_IMPLEMENTED(x) \
62                 g_error ("FIXME: %s is not yet implemented. (trampoline)", x);
63
64 #define PROLOG_INS 8
65 #define CALL_INS   2
66 #define EPILOG_INS 6
67 #define MINIMAL_STACK_SIZE 5
68 #define FLOAT_REGS 8
69 #define GENERAL_REGS 8
70
71 static void inline
72 add_general (guint *gr, guint *stack_size, guint *code_size, gboolean simple)
73 {
74         if (simple) {
75                 if (*gr >= GENERAL_REGS) {
76                         *stack_size += 4;
77                         *code_size += 8;    /* load from stack, save on stack */
78                 } else {
79                         *code_size += 4;    /* load from stack */
80                 }
81         } else {
82                 if (*gr >= GENERAL_REGS - 1) {
83                         *stack_size += 8 + (*stack_size % 8);
84                         *code_size += 16;   /* 2x load from stack, 2x save to stack */
85                 } else {
86                         *code_size += 16;   /* 2x load from stack */
87                 }
88                 if ((*gr) && 1)
89                         (*gr) ++;
90                 (*gr) ++;
91         }
92         (*gr) ++;
93 }
94
95 static void inline
96 calculate_sizes (MonoMethodSignature *sig, guint *stack_size, guint *code_size, gboolean string_ctor, gboolean *use_memcpy)
97 {
98         guint i, fr, gr;
99         guint32 simpletype;
100
101         fr = gr = 0;
102         *stack_size = MINIMAL_STACK_SIZE*4;
103         *code_size  = (PROLOG_INS + CALL_INS + EPILOG_INS)*4;
104
105         if (sig->hasthis) {
106                 add_general (&gr, stack_size, code_size, TRUE);
107         }
108
109         for (i = 0; i < sig->param_count; ++i) {
110                 if (sig->params [i]->byref) {
111                         add_general (&gr, stack_size, code_size, TRUE);
112                         continue;
113                 }
114                 simpletype = sig->params [i]->type;
115         enum_calc_size:
116                 switch (simpletype) {
117                 case MONO_TYPE_BOOLEAN:
118                 case MONO_TYPE_CHAR:
119                 case MONO_TYPE_I1:
120                 case MONO_TYPE_U1:
121                 case MONO_TYPE_I2:
122                 case MONO_TYPE_U2:
123                 case MONO_TYPE_I4:
124                 case MONO_TYPE_U4:
125                 case MONO_TYPE_I:
126                 case MONO_TYPE_U:
127                 case MONO_TYPE_PTR:
128                 case MONO_TYPE_CLASS:
129                 case MONO_TYPE_OBJECT:
130                 case MONO_TYPE_STRING:
131                         add_general (&gr, stack_size, code_size, TRUE);
132                         break;
133                 case MONO_TYPE_SZARRAY:
134                         add_general (&gr, stack_size, code_size, TRUE);
135                         *code_size += 4;
136                         break;
137                 case MONO_TYPE_VALUETYPE: {
138                         gint size;
139                         if (sig->params [i]->data.klass->enumtype) {
140                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
141                                 goto enum_calc_size;
142                         }
143                         size = mono_class_value_size (sig->params [i]->data.klass, NULL);
144                         if (size != 4) {
145                                 DEBUG(printf ("copy %d bytes struct on stack\n",
146                                               mono_class_value_size (sig->params [i]->data.klass, NULL)));
147                                 *use_memcpy = TRUE;
148                                 *code_size += 8*4;
149                                 *stack_size += (size + 3) & (~3);
150                                 if (gr > GENERAL_REGS) {
151                                         *code_size += 4;
152                                         *stack_size += 4;
153                                 }
154                         } else {
155                                 DEBUG(printf ("load %d bytes struct\n",
156                                               mono_class_value_size (sig->params [i]->data.klass, NULL)));
157                                 add_general (&gr, stack_size, code_size, TRUE);
158                                 *code_size += 4;
159                         }
160                         break;
161                 }
162                 case MONO_TYPE_I8:
163                         add_general (&gr, stack_size, code_size, FALSE);
164                         break;
165                 case MONO_TYPE_R4:
166                 case MONO_TYPE_R8:
167                         if (fr < 7) {
168                                 *code_size += 4;
169                                 fr ++;
170                         } else {
171                                 NOT_IMPLEMENTED ("R8 arg");
172                         }
173                         break;
174                 default:
175                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
176                 }
177         }
178
179         if (sig->ret->byref || string_ctor) {
180                 *code_size += 8;
181         } else {
182                 simpletype = sig->ret->type;
183 enum_retvalue:
184                 switch (simpletype) {
185                 case MONO_TYPE_BOOLEAN:
186                 case MONO_TYPE_I1:
187                 case MONO_TYPE_U1:
188                 case MONO_TYPE_I2:
189                 case MONO_TYPE_U2:
190                 case MONO_TYPE_CHAR:
191                 case MONO_TYPE_I4:
192                 case MONO_TYPE_U4:
193                 case MONO_TYPE_I:
194                 case MONO_TYPE_U:
195                 case MONO_TYPE_CLASS:
196                 case MONO_TYPE_OBJECT:
197                 case MONO_TYPE_R4:
198                 case MONO_TYPE_R8:
199                 case MONO_TYPE_SZARRAY:
200                 case MONO_TYPE_ARRAY:
201                 case MONO_TYPE_STRING:
202                         *code_size += 8;
203                         break;
204                 case MONO_TYPE_I8:
205                         *code_size += 12;
206                         break;
207                 case MONO_TYPE_VALUETYPE:
208                         if (sig->ret->data.klass->enumtype) {
209                                 simpletype = sig->ret->data.klass->enum_basetype->type;
210                                 goto enum_retvalue;
211                         }
212                         *code_size += 2*4;
213                         break;
214                 case MONO_TYPE_VOID:
215                         break;
216                 default:
217                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
218                 }
219         }
220
221         if (*use_memcpy) {
222                 *stack_size += 2*4;           /* for r14, r15 */
223                 *code_size += 6*4;
224                 if (sig->hasthis) {
225                         *stack_size += 4;     /* for r16 */
226                         *code_size += 4;
227                 }
228         }
229
230         /* align stack size to 16 */
231         DEBUG (printf ("      stack size: %d (%d)\n       code size: %d\n", (*stack_size + 15) & ~15, *stack_size, *code_size));
232         *stack_size = (*stack_size + 15) & ~15;
233 }
234
235 static inline guint8 *
236 emit_prolog (guint8 *p, MonoMethodSignature *sig, guint stack_size)
237 {
238         /* function prolog */
239         ppc_stwu (p, ppc_r1, -stack_size, ppc_r1);     /* sp      <--- sp - stack_size, sp[0] <---- sp save sp, alloc stack */
240         ppc_mflr (p, ppc_r0);                          /* r0      <--- LR */
241         ppc_stw  (p, ppc_r31, stack_size - 4, ppc_r1); /* sp[+4]  <--- r31     save r31 */
242         ppc_stw  (p, ppc_r0, stack_size + 4, ppc_r1);  /* sp[-4]  <--- LR      save return address for "callme" */
243         ppc_mr   (p, ppc_r31, ppc_r1);                 /* r31     <--- sp */
244
245         return p;
246 }
247
248 #define ARG_BASE ppc_r12
249 #define SAVE_4_IN_GENERIC_REGISTER \
250                         if (gr < GENERAL_REGS) { \
251                                 ppc_lwz  (p, ppc_r3 + gr, i*16, ARG_BASE); \
252                                 gr ++; \
253                         } else { \
254                                 ppc_lwz  (p, ppc_r11, i*16, ARG_BASE); \
255                                 ppc_stw  (p, ppc_r11, stack_par_pos, ppc_r1); \
256                                 stack_par_pos += 4; \
257                         }
258 #define SAVE_4_VAL_IN_GENERIC_REGISTER \
259                         if (gr < GENERAL_REGS) { \
260                                 ppc_lwz  (p, ppc_r3 + gr, i*16, ARG_BASE); \
261                                 ppc_lwz  (p, ppc_r3 + gr, 0, ppc_r3 + gr); \
262                                 gr ++; \
263                         } else { \
264                                 ppc_lwz  (p, ppc_r11, i*16, ARG_BASE); \
265                                 ppc_lwz  (p, ppc_r11, 0, ppc_r11); \
266                                 ppc_stw  (p, ppc_r11, stack_par_pos, ppc_r1); \
267                                 stack_par_pos += 4; \
268                         }
269
270 inline static guint8*
271 emit_save_parameters (guint8 *p, MonoMethodSignature *sig, guint stack_size, gboolean use_memcpy)
272 {
273         guint i, fr, gr, stack_par_pos, struct_pos, cur_struct_pos;
274         guint32 simpletype;
275
276         fr = gr = 0;
277         stack_par_pos = 8;
278
279         ppc_stw  (p, ppc_r4, stack_size - 12, ppc_r31);               /* preserve "retval", sp[+8] */
280
281         if (use_memcpy) {
282                 ppc_stw  (p, ppc_r14, stack_size - 16, ppc_r31);      /* save r14 */
283                 ppc_stw  (p, ppc_r15, stack_size - 20, ppc_r31);      /* save r15 */
284                 ppc_mr   (p, ppc_r14, ppc_r3);                        /* keep "callme" in register */
285                 ppc_mr   (p, ppc_r15, ppc_r6);                        /* keep "arguments" in register */
286         } else {
287                 ppc_mr   (p, ppc_r12, ppc_r6);                        /* keep "arguments" in register */
288                 ppc_mr   (p, ppc_r0, ppc_r3);                         /* keep "callme" in register */
289         }
290
291         if (sig->hasthis) {
292                 if (use_memcpy) {
293                         ppc_stw  (p, ppc_r16, stack_size - 24, ppc_r31);      /* save r16 */
294                         ppc_mr   (p, ppc_r16, ppc_r5);
295                 } else
296                         ppc_mr (p, ppc_r3, ppc_r5);
297                 gr ++;
298         }
299
300         if (use_memcpy) {
301                 cur_struct_pos = struct_pos = stack_par_pos;
302                 for (i = 0; i < sig->param_count; ++i) {
303                         if (sig->params [i]->byref)
304                                 continue;
305                         if (sig->params [i]->type == MONO_TYPE_VALUETYPE && !sig->params [i]->data.klass->enumtype) {
306                                 gint size;
307
308                                 size = mono_class_value_size (sig->params [i]->data.klass, NULL);
309                                 if (size != 4) {
310                                         /* call memcpy */
311                                         ppc_addi (p, ppc_r3, ppc_r1, stack_par_pos);
312                                         ppc_lwz  (p, ppc_r4, i*16, ppc_r15);
313                                         /* FIXME check if size > 0xffff */
314                                         ppc_li   (p, ppc_r5, size & 0xffff);
315                                         ppc_lis  (p, ppc_r0, (guint32) memcpy >> 16);
316                                         ppc_ori  (p, ppc_r0, ppc_r0, (guint32) memcpy & 0xffff);
317                                         ppc_mtlr (p, ppc_r0);
318                                         ppc_blrl (p);
319                                         stack_par_pos += (size + 3) & (~3);
320                                 }
321                         }
322                 }
323
324                 if (sig->hasthis) {
325                         ppc_mr   (p, ppc_r3, ppc_r16);
326                         ppc_lwz  (p, ppc_r16, stack_size - 24, ppc_r31);      /* restore r16 */
327                 }
328                 ppc_mr   (p, ppc_r0,  ppc_r14);
329                 ppc_mr   (p, ppc_r12, ppc_r15);
330                 ppc_lwz  (p, ppc_r14, stack_size - 16, ppc_r31);      /* restore r14 */
331                 ppc_lwz  (p, ppc_r15, stack_size - 20, ppc_r31);      /* restore r15 */
332         }
333
334         if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
335                 MonoClass *klass = sig->ret->data.klass;
336                 if (!klass->enumtype) {
337                         gint size = mono_class_native_size (klass, NULL);
338
339                         DEBUG(printf ("retval value type size: %d\n", size));
340                         if (size > 8) {
341                                 ppc_lwz (p, ppc_r3, stack_size - 12, ppc_r31);
342                                 ppc_lwz (p, ppc_r3, 0, ppc_r3);
343                                 gr ++;
344                         } else {
345                                 NOT_IMPLEMENTED ("retval valuetype <= 8 bytes");
346                         }
347                 }
348         }
349
350         for (i = 0; i < sig->param_count; ++i) {
351                 if (sig->params [i]->byref) {
352                         SAVE_4_IN_GENERIC_REGISTER;
353                         continue;
354                 }
355                 simpletype = sig->params [i]->type;
356         enum_calc_size:
357                 switch (simpletype) {
358                 case MONO_TYPE_BOOLEAN:
359                 case MONO_TYPE_I1:
360                 case MONO_TYPE_U1:
361                 case MONO_TYPE_I2:
362                 case MONO_TYPE_U2:
363                 case MONO_TYPE_CHAR:
364                 case MONO_TYPE_I4:
365                 case MONO_TYPE_U4:
366                 case MONO_TYPE_I:
367                 case MONO_TYPE_U:
368                 case MONO_TYPE_PTR:
369                 case MONO_TYPE_CLASS:
370                 case MONO_TYPE_OBJECT:
371                 case MONO_TYPE_STRING:
372                 case MONO_TYPE_SZARRAY:
373                         SAVE_4_IN_GENERIC_REGISTER;
374                         break;
375                 case MONO_TYPE_VALUETYPE: {
376                         gint size;
377                         if (sig->params [i]->data.klass->enumtype) {
378                                 simpletype = sig->params [i]->data.klass->enum_basetype->type;
379                                 goto enum_calc_size;
380                         }
381                         size = mono_class_value_size (sig->params [i]->data.klass, NULL);
382                         if (size == 4) {
383                                 SAVE_4_VAL_IN_GENERIC_REGISTER;
384                         } else {
385                                 if (gr < GENERAL_REGS) {
386                                         ppc_addi (p, ppc_r3 + gr, ppc_r1, cur_struct_pos);
387                                         gr ++;
388                                 } else {
389                                         ppc_lwz  (p, ppc_r11, cur_struct_pos, ppc_r1);
390                                         ppc_stw  (p, ppc_r11, stack_par_pos, ppc_r1);
391                                         stack_par_pos += 4;
392                                 }
393                                 cur_struct_pos += (size + 3) & (~3);
394                         }
395                         break;
396                 }
397                 case MONO_TYPE_I8:
398                         if (gr < 7) {
399                                 if (gr & 1)
400                                         gr ++;
401                                 ppc_lwz  (p, ppc_r3 + gr, i*16, ARG_BASE);
402                                 gr ++;
403                                 ppc_lwz  (p, ppc_r3 + gr, i*16 + 4, ARG_BASE);
404                                 gr ++;
405                         } else {
406                                 NOT_IMPLEMENTED ("i8 on stack");
407                         }
408                         break;
409                 case MONO_TYPE_R4:
410                         if (fr < 7) {
411                                 ppc_lfs  (p, ppc_f1 + fr, i*16, ARG_BASE);
412                                 fr ++;
413                         } else {
414                                 NOT_IMPLEMENTED ("r4 on stack");
415                         }
416                         break;
417                 case MONO_TYPE_R8:
418                         if (fr < 7) {
419                                 ppc_lfd  (p, ppc_f1 + fr, i*16, ARG_BASE);
420                                 fr ++;
421                         } else {
422                                 NOT_IMPLEMENTED ("r8 on stack");
423                         }
424                         break;
425                 default:
426                         g_error ("Can't trampoline 0x%x", sig->params [i]->type);
427                 }
428         }
429
430         return p;
431 }
432
433 static inline guint8 *
434 alloc_code_memory (guint code_size)
435 {
436         guint8 *p;
437
438 #ifdef NEED_MPROTECT
439         p = g_malloc (code_size + PAGESIZE - 1);
440
441         /* Align to a multiple of PAGESIZE, assumed to be a power of two */
442         p = (char *)(((int) p + PAGESIZE-1) & ~(PAGESIZE-1));
443 #else
444         p = g_malloc (code_size);
445 #endif
446         DEBUG (printf ("           align: %p (%d)\n", p, (guint)p % 4));
447
448         return p;
449 }
450
451 /* static MonoString*
452 mono_string_new_wrapper (const char *text)
453 {
454         return text ? mono_string_new (mono_domain_get (), text) : NULL;
455 } */
456
457 static inline guint8 *
458 emit_call_and_store_retval (guint8 *p, MonoMethodSignature *sig, guint stack_size, gboolean string_ctor)
459 {
460         guint32 simpletype;
461
462         /* call "callme" */
463         ppc_mtlr (p, ppc_r0);
464         ppc_blrl (p);
465
466         /* get return value */
467         if (sig->ret->byref || string_ctor) {
468                 ppc_lwz  (p, ppc_r9, stack_size - 12, ppc_r31);        /* load "retval" address */
469                 ppc_stw  (p, ppc_r3, 0, ppc_r9);                       /* save return value (r3) to "retval" */
470         } else {
471                 simpletype = sig->ret->type;
472 enum_retvalue:
473                 switch (simpletype) {
474                 case MONO_TYPE_BOOLEAN:
475                 case MONO_TYPE_I1:
476                 case MONO_TYPE_U1:
477                         ppc_lwz  (p, ppc_r9, stack_size - 12, ppc_r31);        /* load "retval" address */
478                         ppc_stb  (p, ppc_r3, 0, ppc_r9);                       /* save return value (r3) to "retval" */
479                         break;
480                 case MONO_TYPE_I2:
481                 case MONO_TYPE_U2:
482                 case MONO_TYPE_CHAR:
483                         ppc_lwz  (p, ppc_r9, stack_size - 12, ppc_r31);        /* load "retval" address */
484                         ppc_sth  (p, ppc_r3, 0, ppc_r9);                       /* save return value (r3) to "retval" */
485                         break;
486                 case MONO_TYPE_I4:
487                 case MONO_TYPE_U4:
488                 case MONO_TYPE_I:
489                 case MONO_TYPE_U:
490                 case MONO_TYPE_CLASS:
491                 case MONO_TYPE_OBJECT:
492                 case MONO_TYPE_SZARRAY:
493                 case MONO_TYPE_ARRAY:
494                 case MONO_TYPE_STRING:
495                         ppc_lwz  (p, ppc_r9, stack_size - 12, ppc_r31);        /* load "retval" address */
496                         ppc_stw  (p, ppc_r3, 0, ppc_r9);                       /* save return value (r3) to "retval" */
497                         break;
498                 case MONO_TYPE_R4:
499                         ppc_lwz  (p, ppc_r9, stack_size - 12, ppc_r31);        /* load "retval" address */
500                         ppc_stfs (p, ppc_f1, 0, ppc_r9);                       /* save return value (f1) to "retval" */
501                         break;
502                 case MONO_TYPE_R8:
503                         ppc_lwz  (p, ppc_r9, stack_size - 12, ppc_r31);        /* load "retval" address */
504                         ppc_stfd (p, ppc_f1, 0, ppc_r9);                       /* save return value (f1) to "retval" */
505                         break;
506                 case MONO_TYPE_I8:
507                         ppc_lwz  (p, ppc_r9, stack_size - 12, ppc_r31);        /* load "retval" address */
508                         ppc_stw  (p, ppc_r3, 0, ppc_r9);                       /* save return value (r3) to "retval" */
509                         ppc_stw  (p, ppc_r4, 4, ppc_r9);                       /* save return value (r3) to "retval" */
510                         break;
511                 case MONO_TYPE_VALUETYPE:
512                         if (sig->ret->data.klass->enumtype) {
513                                 simpletype = sig->ret->data.klass->enum_basetype->type;
514                                 goto enum_retvalue;
515                         }
516                         break;
517                 case MONO_TYPE_VOID:
518                         break;
519                 default:
520                         g_error ("Can't handle as return value 0x%x", sig->ret->type);
521                 }
522         }
523
524         return p;
525 }
526
527 static inline guint8 *
528 emit_epilog (guint8 *p, MonoMethodSignature *sig, guint stack_size)
529 {
530         /* function epilog */
531         ppc_lwz  (p, ppc_r11, 0,  ppc_r1);        /* r11     <--- sp[0]   load backchain from caller's function */
532         ppc_lwz  (p, ppc_r0, 4, ppc_r11);         /* r0      <--- r11[4]  load return address */
533         ppc_mtlr (p, ppc_r0);                     /* LR      <--- r0      set return address */
534         ppc_lwz  (p, ppc_r31, -4, ppc_r11);       /* r31     <--- r11[-4] restore r31 */
535         ppc_mr   (p, ppc_r1, ppc_r11);            /* sp      <--- r11     restore stack */
536         ppc_blr  (p);                             /* return */
537
538         return p;
539 }
540
541 MonoPIFunc
542 mono_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
543 {
544         guint8 *p, *code_buffer;
545         guint stack_size, code_size;
546         gboolean use_memcpy = FALSE;
547
548         DEBUG (printf ("\nPInvoke [start emiting]\n"));
549         calculate_sizes (sig, &stack_size, &code_size, string_ctor, &use_memcpy);
550
551         p = code_buffer = alloc_code_memory (code_size);
552         p = emit_prolog (p, sig, stack_size);
553         p = emit_save_parameters (p, sig, stack_size, use_memcpy);
554         p = emit_call_and_store_retval (p, sig, stack_size, string_ctor);
555         p = emit_epilog (p, sig, stack_size);
556
557         /* {
558                 guchar *cp;
559                 printf (".text\n.align 4\n.globl main\n.type main,@function\nmain:\n");
560                 for (cp = code_buffer; cp < p; cp++) {
561                         printf (".byte 0x%x\n", *cp);
562                 }
563                 } */
564
565 #ifdef NEED_MPROTECT
566         if (mprotect (code_buffer, 1024, PROT_READ | PROT_WRITE | PROT_EXEC)) {
567                 g_error ("Cannot mprotect trampoline\n");
568         }
569 #endif
570
571         DEBUG (printf ("emited code size: %d\n", p - code_buffer));
572         flush_icache (code_buffer, p - code_buffer);
573
574         DEBUG (printf ("PInvoke [end emiting]\n"));
575
576         return (MonoPIFunc) code_buffer;
577         /* return fake_func; */
578 }
579
580
581 #define MINV_POS  8   /* MonoInvocation structure offset on stack */
582 #define STACK_POS (MINV_POS - sizeof (stackval) * sig->param_count)
583 #define OBJ_POS   8
584 #define TYPE_OFFSET (G_STRUCT_OFFSET (stackval, type))
585
586 /*
587  * Returns a pointer to a native function that can be used to
588  * call the specified method.
589  * The function created will receive the arguments according
590  * to the call convention specified in the method.
591  * This function works by creating a MonoInvocation structure,
592  * filling the fields in and calling ves_exec_method on it.
593  * Still need to figure out how to handle the exception stuff
594  * across the managed/unmanaged boundary.
595  */
596 void *
597 mono_create_method_pointer (MonoMethod *method)
598 {
599         MonoMethodSignature *sig;
600         MonoJitInfo *ji;
601         guint8 *p, *code_buffer;
602         guint i, align = 0, code_size, stack_size, stackval_arg_pos, local_pos, local_start, reg_param, stack_param,
603                 this_flag, cpos, vt_cur;
604         gint *vtbuf;
605         guint32 simpletype;
606
607         /*
608          * If it is a static P/Invoke method, we can just return the pointer
609          * to the method implementation.
610          */
611         if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL && method->addr) {
612                 ji = g_new0 (MonoJitInfo, 1);
613                 ji->method = method;
614                 ji->code_size = 1;
615                 ji->code_start = method->addr;
616
617                 mono_jit_info_table_add (mono_root_domain, ji);
618                 return method->addr;
619         }
620
621         code_size = 1024;
622         stack_size = 1024;
623         stack_param = 0;
624
625         sig = method->signature;
626
627         p = code_buffer = g_malloc (code_size);
628
629         DEBUG (printf ("\nDelegate [start emiting] %s\n", method->name));
630
631         /* prolog */
632         ppc_stwu (p, ppc_r1, -stack_size, ppc_r1);     /* sp      <--- sp - stack_size, sp[0] <---- sp save sp, alloc stack */
633         ppc_mflr (p, ppc_r0);                          /* r0      <--- LR */
634         ppc_stw  (p, ppc_r31, stack_size - 4, ppc_r1); /* sp[+4]  <--- r31     save r31 */
635         ppc_stw  (p, ppc_r0, stack_size + 4, ppc_r1);  /* sp[-4]  <--- LR      save return address for "callme" */
636         ppc_mr   (p, ppc_r31, ppc_r1);                 /* r31     <--- sp */
637
638         /* let's fill MonoInvocation */
639         /* first zero some fields */
640         ppc_li   (p, ppc_r0, 0);
641         ppc_stw  (p, ppc_r0, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex)), ppc_r31);
642         ppc_stw  (p, ppc_r0, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), ppc_r31);
643         ppc_stw  (p, ppc_r0, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, child)), ppc_r31);
644         ppc_stw  (p, ppc_r0, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, parent)), ppc_r31);
645
646         /* set method pointer */
647         ppc_lis  (p, ppc_r0,     (guint32) method >> 16);
648         ppc_ori  (p, ppc_r0, ppc_r0, (guint32) method & 0xffff);
649         ppc_stw  (p, ppc_r0, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, method)), ppc_r31);
650
651         local_start = local_pos = MINV_POS + sizeof (MonoInvocation) + (sig->param_count + 1) * sizeof (stackval);
652
653         if (sig->hasthis) {
654                 ppc_stw  (p, ppc_r3, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, obj)), ppc_r31);
655                 reg_param = 1;
656         } else if (sig->param_count) {
657                 DEBUG (printf ("save r%d\n", 3));
658                 ppc_stw  (p, ppc_r3, local_pos, ppc_r31);
659                 local_pos += 4;
660                 reg_param = 0;
661         }
662
663         this_flag = (sig->hasthis ? 1 : 0);
664         if (sig->param_count) {
665                 gint save_count = MIN (8, sig->param_count - 1);
666                 for (i = reg_param; i < save_count; i ++) {
667                         ppc_stw (p, ppc_r4 + i, local_pos, ppc_r31);
668                         local_pos += 4;
669                         DEBUG (printf ("save r%d\n", 4 + i));
670                 }
671         }
672
673         /* prepare space for valuetypes */
674         vt_cur = local_pos;
675         vtbuf = alloca (sizeof(int)*sig->param_count);
676         cpos = 0;
677         for (i = 0; i < sig->param_count; i++) {
678                 MonoType *type = sig->params [i];
679                 vtbuf [i] = -1;
680                 if (type->type == MONO_TYPE_VALUETYPE) {
681                         MonoClass *klass = type->data.klass;
682                         gint size;
683
684                         if (klass->enumtype)
685                                 continue;
686                         size = mono_class_native_size (klass, &align);
687                         cpos += align - 1;
688                         cpos &= ~(align - 1);
689                         vtbuf [i] = cpos;
690                         cpos += size;
691                 }       
692         }
693         cpos += 3;
694         cpos &= ~3;
695
696         local_pos += cpos;
697
698         /* set MonoInvocation::stack_args */
699         stackval_arg_pos = MINV_POS + sizeof (MonoInvocation);
700         ppc_addi (p, ppc_r0, ppc_r31, stackval_arg_pos);
701         ppc_stw  (p, ppc_r0, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, stack_args)), ppc_r31);
702
703         /* add stackval arguments */
704         for (i = 0; i < sig->param_count; ++i) {
705                 if (reg_param < 8) {
706                         ppc_addi (p, ppc_r5, ppc_r31, local_start + (reg_param - this_flag)*4);
707                         reg_param ++;
708                 } else {
709                         ppc_addi (p, ppc_r5, stack_size + 8 + stack_param, ppc_r31);
710                         stack_param ++;
711                 }
712                 ppc_lis  (p, ppc_r3, (guint32) sig->params [i] >> 16);
713
714                 if (vtbuf [i] >= 0) {
715                         ppc_addi (p, ppc_r4, ppc_r31, vt_cur);
716                         ppc_stw  (p, ppc_r4, stackval_arg_pos, ppc_r31);
717                         ppc_addi (p, ppc_r4, ppc_r31, stackval_arg_pos);
718                         ppc_lwz  (p, ppc_r5, 0, ppc_r5);
719                         vt_cur += vtbuf [i];
720                 } else {
721                         ppc_addi (p, ppc_r4, ppc_r31, stackval_arg_pos);
722                 }
723                 ppc_ori  (p, ppc_r3, ppc_r3, (guint32) sig->params [i] & 0xffff);
724                 ppc_lis  (p, ppc_r0, (guint32) stackval_from_data >> 16);
725                 ppc_li   (p, ppc_r6, sig->pinvoke);
726                 ppc_ori  (p, ppc_r0, ppc_r0, (guint32) stackval_from_data & 0xffff);
727                 ppc_mtlr (p, ppc_r0);
728                 ppc_blrl (p);
729
730                 /* fixme: alignment */
731                 DEBUG (printf ("arg_pos %d --> ", stackval_arg_pos));
732                 if (sig->pinvoke)
733                         stackval_arg_pos += 4*mono_type_native_stack_size (sig->params [i], &align);
734                 else
735                         stackval_arg_pos += 4*mono_type_stack_size (sig->params [i], &align);
736                 DEBUG (printf ("%d\n", stackval_arg_pos));
737         }
738
739         /* return value storage */
740         if (sig->param_count) {
741                 ppc_addi (p, ppc_r0, ppc_r31, stackval_arg_pos);
742         }
743         ppc_stw  (p, ppc_r0, (MINV_POS + G_STRUCT_OFFSET (MonoInvocation, retval)), ppc_r31);
744
745         /* call ves_exec_method */
746         ppc_lis  (p, ppc_r0,     (guint32) ves_exec_method >> 16);
747         ppc_addi (p, ppc_r3, ppc_r31, MINV_POS);
748         ppc_ori  (p, ppc_r0, ppc_r0, (guint32) ves_exec_method & 0xffff);
749         ppc_mtlr (p, ppc_r0);
750         ppc_blrl (p);
751
752         /* move retval from stackval to proper place (r3/r4/...) */
753         if (sig->ret->byref) {
754                 DEBUG (printf ("ret by ref\n"));
755                 ppc_lwz (p, ppc_r3, stackval_arg_pos, ppc_r31);
756         } else {
757         enum_retvalue:
758                 switch (sig->ret->type) {
759                 case MONO_TYPE_VOID:
760                         break;
761                 case MONO_TYPE_BOOLEAN:
762                 case MONO_TYPE_I1:
763                 case MONO_TYPE_U1:
764                         ppc_lbz (p, ppc_r3, stackval_arg_pos, ppc_r31);
765                         break;
766                 case MONO_TYPE_I2:
767                 case MONO_TYPE_U2:
768                         ppc_lhz (p, ppc_r3, stackval_arg_pos, ppc_r31);
769                         break;
770                 case MONO_TYPE_I4:
771                 case MONO_TYPE_U4:
772                 case MONO_TYPE_I:
773                 case MONO_TYPE_U:
774                 case MONO_TYPE_OBJECT:
775                 case MONO_TYPE_STRING:
776                 case MONO_TYPE_CLASS:
777                         ppc_lwz (p, ppc_r3, stackval_arg_pos, ppc_r31);
778                         break;
779                 case MONO_TYPE_I8:
780                         ppc_lwz (p, ppc_r3, stackval_arg_pos, ppc_r31);
781                         ppc_lwz (p, ppc_r4, stackval_arg_pos + 4, ppc_r31);
782                         break;
783                 case MONO_TYPE_R4:
784                         ppc_lfs (p, ppc_f1, stackval_arg_pos, ppc_r31);
785                         break;
786                 case MONO_TYPE_R8:
787                         ppc_lfd (p, ppc_f1, stackval_arg_pos, ppc_r31);
788                         break;
789                 case MONO_TYPE_VALUETYPE:
790                         if (sig->ret->data.klass->enumtype) {
791                                 simpletype = sig->ret->data.klass->enum_basetype->type;
792                                 goto enum_retvalue;
793                         }
794                         NOT_IMPLEMENTED ("value type as ret val from delegate");
795                         break;
796                 default:
797                         g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
798                         break;
799                 }
800         }
801
802         /* epilog */
803         ppc_lwz  (p, ppc_r11, 0,  ppc_r1);        /* r11     <--- sp[0]   load backchain from caller's function */
804         ppc_lwz  (p, ppc_r0, 4, ppc_r11);         /* r0      <--- r11[4]  load return address */
805         ppc_mtlr (p, ppc_r0);                     /* LR      <--- r0      set return address */
806         ppc_lwz  (p, ppc_r31, -4, ppc_r11);       /* r31     <--- r11[-4] restore r31 */
807         ppc_mr   (p, ppc_r1, ppc_r11);            /* sp      <--- r11     restore stack */
808         ppc_blr  (p);                             /* return */
809
810         DEBUG (printf ("emited code size: %d\n", p - code_buffer));
811         flush_icache (code_buffer, p - code_buffer);
812
813         DEBUG (printf ("Delegate [end emiting]\n"));
814
815         ji = g_new0 (MonoJitInfo, 1);
816         ji->method = method;
817         ji->code_size = p - code_buffer;
818         ji->code_start = code_buffer;
819
820         mono_jit_info_table_add (mono_root_domain, ji);
821
822         return ji->code_start;
823 }