Rework frame handling, correct some ABI problems and optimize memory-to-
[mono.git] / mono / mini / tramp-s390.c
1 /*------------------------------------------------------------------*/
2 /*                                                                  */
3 /* Name        - tramp-s390.c                                       */
4 /*                                                                  */
5 /* Function    - JIT trampoline code for S/390.                     */
6 /*                                                                  */
7 /* Name        - Neale Ferguson (Neale.Ferguson@SoftwareAG-usa.com) */
8 /*                                                                  */
9 /* Date        - January, 2004                                      */
10 /*                                                                  */
11 /* Derivation  - From exceptions-x86 & exceptions-ppc               */
12 /*               Paolo Molaro (lupus@ximian.com)                    */
13 /*               Dietmar Maurer (dietmar@ximian.com)                */
14 /*                                                                  */
15 /* Copyright   - 2001 Ximian, Inc.                                  */
16 /*                                                                  */
17 /*------------------------------------------------------------------*/
18
19 /*------------------------------------------------------------------*/
20 /*                 D e f i n e s                                    */
21 /*------------------------------------------------------------------*/
22
23 #define GR_SAVE_SIZE            4*sizeof(long)
24 #define FP_SAVE_SIZE            16*sizeof(double)
25 #define CREATE_STACK_SIZE       (S390_MINIMAL_STACK_SIZE+GR_SAVE_SIZE+FP_SAVE_SIZE+2*sizeof(long))
26 #define CREATE_GR_OFFSET        S390_MINIMAL_STACK_SIZE
27 #define CREATE_FP_OFFSET        CREATE_GR_OFFSET+GR_SAVE_SIZE
28 #define CREATE_LMF_OFFSET       CREATE_FP_OFFSET+FP_SAVE_SIZE
29 #define METHOD_SAVE_OFFSET      S390_RET_ADDR_OFFSET-4
30
31 /*------------------------------------------------------------------*/
32 /* adapt to mini later...                                           */
33 /*------------------------------------------------------------------*/
34 #define mono_jit_share_code     (1)
35
36 /*------------------------------------------------------------------*/
37 /* Method-specific trampoline code fragment sizes                   */
38 /*------------------------------------------------------------------*/
39 #define METHOD_TRAMPOLINE_SIZE  64
40 #define JUMP_TRAMPOLINE_SIZE    64
41
42 /*========================= End of Defines =========================*/
43
44 /*------------------------------------------------------------------*/
45 /*                 I n c l u d e s                                  */
46 /*------------------------------------------------------------------*/
47
48 #include <config.h>
49 #include <glib.h>
50 #include <string.h>
51
52 #include <mono/metadata/appdomain.h>
53 #include <mono/metadata/marshal.h>
54 #include <mono/metadata/tabledefs.h>
55 #include <mono/arch/s390/s390-codegen.h>
56 #include <mono/metadata/mono-debug-debugger.h>
57
58 #include "mini.h"
59 #include "mini-s390.h"
60
61 /*========================= End of Includes ========================*/
62
63 /*------------------------------------------------------------------*/
64 /*                 T y p e d e f s                                  */
65 /*------------------------------------------------------------------*/
66
67 typedef enum {
68         MONO_TRAMPOLINE_GENERIC,
69         MONO_TRAMPOLINE_JUMP,
70         MONO_TRAMPOLINE_CLASS_INIT
71 } MonoTrampolineType;
72
73 /*========================= End of Typedefs ========================*/
74
75 /*------------------------------------------------------------------*/
76 /*                   P r o t o t y p e s                            */
77 /*------------------------------------------------------------------*/
78
79 /*------------------------------------------------------------------*/
80 /* Address of the generic trampoline code.  This is used by the     */
81 /* debugger to check whether a method is a trampoline.              */
82 /*------------------------------------------------------------------*/
83 guint8 *mono_generic_trampoline_code = NULL;
84
85 /*========================= End of Prototypes ======================*/
86
87 /*------------------------------------------------------------------*/
88 /*                 G l o b a l   V a r i a b l e s                  */
89 /*------------------------------------------------------------------*/
90
91
92 /*====================== End of Global Variables ===================*/
93
94 /*------------------------------------------------------------------*/
95 /*                                                                  */
96 /* Name         - get_unbox_trampoline                              */
97 /*                                                                  */
98 /* Function     - Return a pointer to a trampoline which does the   */
99 /*                unboxing before calling the method.               */
100 /*                                                                  */
101 /*                When value type methods are called through the    */
102 /*                vtable we need to unbox the 'this' argument.      */
103 /*                                                                  */
104 /* Parameters   - method - Methd pointer                            */
105 /*                addr   - Pointer to native code for method        */
106 /*                                                                  */
107 /*------------------------------------------------------------------*/
108
109 static gpointer
110 get_unbox_trampoline (MonoMethod *method, gpointer addr)
111 {
112         guint8 *code, *start;
113         int this_pos = s390_r2;
114
115         start = addr;
116         if ((method->klass->valuetype)) {
117 //      if ((method->klass->valuetype) && 
118 //          (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
119                 if ((!method->signature->ret->byref) && 
120                     (MONO_TYPE_ISSTRUCT (method->signature->ret)))
121                         this_pos = s390_r3;
122             
123                 start = code = g_malloc (28);
124
125                 s390_basr (code, s390_r13, 0);
126                 s390_j    (code, 4);
127                 s390_word (code, addr);
128                 s390_l    (code, s390_r1, 0, s390_r13, 4);
129                 s390_ahi  (code, this_pos, sizeof(MonoObject));
130                 s390_br   (code, s390_r1);
131
132                 g_assert ((code - start) <= 28);
133         }
134
135         return start;
136 }
137
138 /*========================= End of Function ========================*/
139
140 /*------------------------------------------------------------------*/
141 /*                                                                  */
142 /* Name         - s390_magic_trampoline                             */
143 /*                                                                  */
144 /* Function     - This method is called by the function             */
145 /*                "arch_create_jit_trampoline", which in turn is    */
146 /*                called by the trampoline functions for virtual    */
147 /*                methods. After having called the JIT compiler to  */
148 /*                compile the method, it inspects the caller code   */
149 /*                to find the address of the method-specific part   */
150 /*                of the trampoline vtable slot for this method,    */
151 /*                updates it with a fragment that calls the newly   */
152 /*                compiled code and returns this address. The calls */
153 /*                generated by mono for S/390 will look like either:*/
154 /*                1. l     %r1,xxx(%rx)                             */
155 /*                   bras  %r14,%r1                                 */
156 /*                2. brasl %r14,xxxxxx                              */
157 /*                                                                  */
158 /* Parameters   - code   - Pointer into caller code                 */
159 /*                method - The method to compile                    */
160 /*                sp     - Stack pointer                            */
161 /*                                                                  */
162 /*------------------------------------------------------------------*/
163
164 static gpointer
165 s390_magic_trampoline (MonoMethod *method, guchar *code, char *sp)
166 {
167         gpointer addr;
168         gint32 displace;
169         int reg, base;
170         unsigned short opcode;
171         char *fname;
172
173         addr = mono_compile_method(method);
174         g_assert(addr);
175
176
177         if (code) {
178
179                 /* The top bit needs to be ignored on S/390 */
180                 (guint32) code &= 0x7fffffff;
181
182                 fname = mono_method_full_name (method, TRUE);
183
184                 opcode = *((unsigned short *) (code - 6));
185                 switch (opcode) {
186                         case 0x5810 :
187                                 /* This is a bras r14,r1 instruction */
188                                 code    -= 4;
189                                 reg      = *code >> 4;
190                                 displace = *((short *)code) & 0x0fff;
191                                 if (reg > 5) 
192                                         base = *((int *) (sp + S390_REG_SAVE_OFFSET+
193                                                                sizeof(int)*(reg-6)));
194                                 else
195                                         base = *((int *) (sp + CREATE_GR_OFFSET+
196                                                                sizeof(int)*(reg-2)));
197                                 addr = get_unbox_trampoline(method, addr);
198                                 code = base + displace;
199                                 s390_patch(code, addr);
200                                 break;
201                         case 0xc0e5 :
202                                 /* This is the 'brasl' instruction */
203                                 code    -= 4;
204                                 displace = ((gint32) addr - (gint32) (code - 2)) / 2;
205                                 s390_patch (code, displace);
206                                 mono_arch_flush_icache (code, 4);
207                                 break;
208                         default :
209                                 g_error("Unable to patch instruction prior to %p",code);
210                 }
211         }
212
213
214         return addr;
215 }
216
217 /*========================= End of Function ========================*/
218
219 /*------------------------------------------------------------------*/
220 /*                                                                  */
221 /* Name         - s390_class_init_trampoline                        */
222 /*                                                                  */
223 /* Function     - Initialize a class and then no-op the call to     */
224 /*                the trampoline.                                   */
225 /*                                                                  */
226 /*------------------------------------------------------------------*/
227
228 static void
229 s390_class_init_trampoline (void *vtable, guchar *code, char *sp)
230 {
231         char patch[6] = {0x47, 0x00, 0x00, 0x00, 0x07, 0x00};
232
233         mono_runtime_class_init (vtable);
234
235         code = code - 6;
236
237         memcpy(code, patch, sizeof(patch));
238 }
239
240 /*========================= End of Function ========================*/
241
242 /*------------------------------------------------------------------*/
243 /*                                                                  */
244 /* Name         - create_trampoline_code                            */
245 /*                                                                  */
246 /* Function     - Create the designated type of trampoline according*/
247 /*                to the 'tramp_type' parameter.                    */
248 /*                                                                  */
249 /*------------------------------------------------------------------*/
250
251 static guchar*
252 create_trampoline_code (MonoTrampolineType tramp_type)
253 {
254
255         guint8 *buf, *code = NULL;
256         static guint8* generic_jump_trampoline = NULL;
257         static guint8 *generic_class_init_trampoline = NULL;
258         int i, offset;
259
260         switch (tramp_type) {
261         case MONO_TRAMPOLINE_GENERIC:
262                 if (mono_generic_trampoline_code)
263                         return mono_generic_trampoline_code;
264                 break;
265         case MONO_TRAMPOLINE_JUMP:
266                 if (generic_jump_trampoline)
267                         return generic_jump_trampoline;
268                 break;
269         case MONO_TRAMPOLINE_CLASS_INIT:
270                 if (generic_class_init_trampoline)
271                         return generic_class_init_trampoline;
272                 break;
273         }
274
275         if(!code) {
276                 /* Now we'll create in 'buf' the S/390 trampoline code. This
277                  is the trampoline code common to all methods  */
278                 
279                 code = buf = g_malloc(512);
280                 
281                 /*-----------------------------------------------------------
282                 STEP 0: First create a non-standard function prologue with a
283                 stack size big enough to save our registers.
284                 -----------------------------------------------------------*/
285                 
286                 s390_stm  (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
287                 s390_lr   (buf, s390_r11, s390_r15);
288                 s390_ahi  (buf, STK_BASE, -CREATE_STACK_SIZE);
289                 s390_st   (buf, s390_r11, 0, STK_BASE, 0);
290                 s390_stm  (buf, s390_r2, s390_r5, STK_BASE, CREATE_GR_OFFSET);
291
292                 /* Save the FP registers */
293                 offset = CREATE_FP_OFFSET;
294                 for (i = s390_f0; i <= s390_f15; ++i) {
295                         s390_std  (buf, i, 0, STK_BASE, offset);
296                         offset += 8;
297                 }
298
299                 /*----------------------------------------------------------
300                 STEP 1: call 'mono_get_lmf_addr()' to get the address of our
301                 LMF. We'll need to restore it after the call to
302                 's390_magic_trampoline' and before the call to the native
303                 method.
304                 ----------------------------------------------------------*/
305                                 
306                 s390_basr (buf, s390_r13, 0);
307                 s390_j    (buf, 4);
308                 s390_word (buf, mono_get_lmf_addr);
309                 s390_l    (buf, s390_r1, 0, s390_r13, 4);
310                 s390_basr (buf, s390_r14, s390_r1);
311
312                 /* XXX Update LMF !!! */
313                 
314                 /*----------------------------------------------------------
315                 STEP 2: call 's390_magic_trampoline()', who will compile the
316                 code and fix the method vtable entry for us
317                 ----------------------------------------------------------*/
318                                 
319                 /* Set arguments */
320                 
321                 /* Arg 1: MonoMethod *method. It was put in r11 by the
322                 method-specific trampoline code, and then saved before the call
323                 to mono_get_lmf_addr()'. Restore r13, by the way :-) */
324                 s390_l  (buf, s390_r2, 0, s390_r11, METHOD_SAVE_OFFSET);
325                 
326                 /* Arg 2: code (next address to the instruction that called us) */
327                 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
328                         s390_lhi (buf, s390_r3, 0);
329                 } else {
330                         s390_l  (buf, s390_r3, 0, s390_r11, S390_RET_ADDR_OFFSET);
331                 }
332                 
333                 /* Arg 3: stack pointer */
334                 s390_lr   (buf, s390_r4, STK_BASE);
335                 
336                 /* Calculate call address and call
337                 's390_magic_trampoline'. Return value will be in r2 */
338                 s390_basr (buf, s390_r13, 0);
339                 s390_j    (buf, 4);
340                 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
341                         s390_word (buf, s390_class_init_trampoline);
342                 } else {
343                         s390_word (buf, s390_magic_trampoline);
344                 }
345                 s390_l    (buf, s390_r1, 0, s390_r13, 4);
346                 s390_basr (buf, s390_r14, s390_r1);
347                 
348                 /* OK, code address is now on r2. Move it to r1, so that we
349                 can restore r2 and use it from r1 later */
350                 s390_lr   (buf, s390_r1, s390_r2);
351                 
352
353                 /*----------------------------------------------------------
354                 STEP 3: Restore the LMF
355                 ----------------------------------------------------------*/
356                 
357                 /* XXX Do it !!! */
358                 
359                 /*----------------------------------------------------------
360                 STEP 4: call the compiled method
361                 ----------------------------------------------------------*/
362                 
363                 /* Restore registers */
364
365                 s390_lm   (buf, s390_r2, s390_r5, STK_BASE, CREATE_GR_OFFSET);
366                 
367                 /* Restore the FP registers */
368                 offset = CREATE_FP_OFFSET;
369                 for (i = s390_f0; i <= s390_f15; ++i) {
370                         s390_ld  (buf, i, 0, STK_BASE, offset);
371                         offset += 8;
372                 }
373
374                 /* Restore stack pointer and jump to the code - 
375                    R14 contains the return address to our caller */
376                 s390_lr   (buf, STK_BASE, s390_r11);
377                 s390_lm   (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
378                 s390_br   (buf, s390_r1);
379
380                 /* Flush instruction cache, since we've generated code */
381                 mono_arch_flush_icache (code, buf - code);
382         
383                 /* Sanity check */
384                 g_assert ((buf - code) <= 512);
385         }
386
387         switch (tramp_type) {
388         case MONO_TRAMPOLINE_GENERIC:
389                 mono_generic_trampoline_code = code;
390                 break;
391         case MONO_TRAMPOLINE_JUMP:
392                 generic_jump_trampoline = code;
393                 break;
394         case MONO_TRAMPOLINE_CLASS_INIT:
395                 generic_class_init_trampoline = code;
396                 break;
397         }
398
399         return code;
400 }
401
402 /*========================= End of Function ========================*/
403
404 /*------------------------------------------------------------------*/
405 /*                                                                  */
406 /* Name         - mono_arch_create_jump_trampoline                  */
407 /*                                                                  */
408 /* Function     - Create the designated type of trampoline according*/
409 /*                to the 'tramp_type' parameter.                    */
410 /*                                                                  */
411 /*------------------------------------------------------------------*/
412
413 MonoJitInfo *
414 mono_arch_create_jump_trampoline (MonoMethod *method)
415 {
416         guint8 *code, *buf, *tramp = NULL;
417         MonoJitInfo *ji;
418         MonoDomain *domain = mono_domain_get();
419         gint32 displace;
420
421         tramp = create_trampoline_code (MONO_TRAMPOLINE_JUMP);
422
423         mono_domain_lock (domain);
424         code = buf = mono_code_manager_reserve (domain->code_mp, METHOD_TRAMPOLINE_SIZE);
425         mono_domain_unlock (domain);
426
427         s390_basr (buf, s390_r13, 0);
428         s390_j    (buf, 4);
429         s390_word (buf, method);
430         s390_l    (buf, s390_r13, 0, s390_r13, 4);
431         displace = (tramp - buf) / 2;
432         s390_jcl  (buf, S390_CC_UN, displace);
433
434         mono_arch_flush_icache (code, buf-code);
435
436         g_assert ((buf - code) <= JUMP_TRAMPOLINE_SIZE);
437         
438         ji              = g_new0 (MonoJitInfo, 1);
439         ji->method      = method;
440         ji->code_start  = code;
441         ji->code_size   = buf - code;
442         
443         mono_jit_stats.method_trampolines++;
444
445         return ji;
446 }
447
448 /*========================= End of Function ========================*/
449
450 /*------------------------------------------------------------------*/
451 /*                                                                  */
452 /* Name         - mono_arch_create_jit_trampoline                   */
453 /*                                                                  */
454 /* Function     - Creates a trampoline function for virtual methods.*/
455 /*                If the created code is called it first starts JIT */
456 /*                compilation and then calls the newly created      */
457 /*                method. It also replaces the corresponding vtable */
458 /*                entry (see s390_magic_trampoline).                */
459 /*                                                                  */
460 /*                A trampoline consists of two parts: a main        */
461 /*                fragment, shared by all method trampolines, and   */
462 /*                and some code specific to each method, which      */
463 /*                hard-codes a reference to that method and then    */
464 /*                calls the main fragment.                          */
465 /*                                                                  */
466 /*                The main fragment contains a call to              */
467 /*                's390_magic_trampoline', which performs a call    */
468 /*                to the JIT compiler and substitutes the method-   */
469 /*                specific fragment with some code that directly    */
470 /*                calls the JIT-compiled method.                    */
471 /*                                                                  */
472 /* Parameter    - method - Pointer to the method information        */
473 /*                                                                  */
474 /* Returns      - A pointer to the newly created code               */
475 /*                                                                  */
476 /*------------------------------------------------------------------*/
477
478 gpointer
479 mono_arch_create_jit_trampoline (MonoMethod *method)
480 {
481         guint8 *code, *buf;
482         static guint8 *vc = NULL;
483         gint32 displace;
484
485         /* previously created trampoline code */
486         if (method->info)
487                 return method->info;
488
489         if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
490                 return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
491
492         vc = create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
493
494         /* This is the method-specific part of the trampoline. Its purpose is
495         to provide the generic part with the MonoMethod *method pointer. We'll
496         use r13 to keep that value, for instance. However, the generic part of
497         the trampoline relies on r11 having the same value it had before coming
498         here, so we must save it before. */
499         code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
500
501         s390_basr (buf, s390_r13, 0);
502         s390_j    (buf, 4);
503         s390_word (buf, method);
504         s390_l    (buf, s390_r13, 0, s390_r13, 4);
505         displace = (vc - buf) / 2;
506         s390_jcl  (buf, S390_CC_UN, displace);
507
508         /* Flush instruction cache, since we've generated code */
509         mono_arch_flush_icache (code, buf - code);
510                 
511         /* Sanity check */
512         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
513         
514         /* Store trampoline address */
515         method->info = code;
516
517         mono_jit_stats.method_trampolines++;
518
519         return code;
520 }
521
522 /*========================= End of Function ========================*/
523
524 /*------------------------------------------------------------------*/
525 /*                                                                  */
526 /* Name         - mono_arch_create_class_init_trampoline            */
527 /*                                                                  */
528 /* Function     - Creates a trampoline function to run a type init- */
529 /*                ializer. If the trampoline is called, it calls    */
530 /*                mono_runtime_class_init with the given vtable,    */
531 /*                then patches the caller code so it does not get   */
532 /*                called any more.                                  */
533 /*                                                                  */
534 /* Parameter    - vtable - The type to initialize                   */
535 /*                                                                  */
536 /* Returns      - A pointer to the newly created code               */
537 /*                                                                  */
538 /*------------------------------------------------------------------*/
539
540 gpointer
541 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
542 {
543         guint8 *code, *buf, *tramp;
544
545         tramp = create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
546
547         /* This is the method-specific part of the trampoline. Its purpose is
548         to provide the generic part with the MonoMethod *method pointer. We'll
549         use r11 to keep that value, for instance. However, the generic part of
550         the trampoline relies on r11 having the same value it had before coming
551         here, so we must save it before. */
552         code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
553
554         s390_st   (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
555         s390_ahi  (buf, STK_BASE, -S390_MINIMAL_STACK_SIZE);
556
557         s390_basr (buf, s390_r1, 0);
558         s390_j    (buf, 6);
559         s390_word (buf, vtable);
560         s390_word (buf, s390_class_init_trampoline);
561         s390_lr   (buf, s390_r3, s390_r14);
562         s390_l    (buf, s390_r2, 0, s390_r1, 4);
563         s390_lhi  (buf, s390_r4, 0);
564         s390_l    (buf, s390_r1, 0, s390_r1, 8);
565         s390_basr (buf, s390_r14, s390_r1);
566
567         s390_ahi  (buf, STK_BASE, S390_MINIMAL_STACK_SIZE);
568         s390_l    (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
569         s390_br   (buf, s390_r14);
570
571         /* Flush instruction cache, since we've generated code */
572         mono_arch_flush_icache (code, buf - code);
573                 
574         /* Sanity check */
575         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
576
577         mono_jit_stats.method_trampolines++;
578
579         return code;
580 }
581
582 /*========================= End of Function ========================*/
583
584 /*------------------------------------------------------------------*/
585 /*                                                                  */
586 /* Name         - mono_debuger_create_notification_function         */
587 /*                                                                  */
588 /* Function     - This method is only called when running in the    */
589 /*                Mono debugger. It returns a pointer to the        */
590 /*                arch specific notification function.              */
591 /*                                                                  */
592 /*------------------------------------------------------------------*/
593
594 gpointer
595 mono_debugger_create_notification_function (gpointer *notification_address)
596 {
597         guint8 *ptr, *buf;
598
599         ptr = buf = g_malloc0 (16);
600         s390_break (buf);
601         if (notification_address)
602                 *notification_address = buf;
603         s390_br (buf, s390_r14);
604
605         return ptr;
606 }
607
608 /*========================= End of Function ========================*/