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