2005-01-25 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mono / mini / tramp-s390x.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-8
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/s390x/s390x-codegen.h>
56 #include <mono/metadata/mono-debug-debugger.h>
57
58 #include "mini.h"
59 #include "mini-s390x.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->signature->ret->byref) && 
118                     (MONO_TYPE_ISSTRUCT (method->signature->ret)))
119                         this_pos = s390_r3;
120             
121                 start = code = g_malloc (28);
122
123                 s390_basr (code, s390_r13, 0);
124                 s390_j    (code, 6);
125                 s390_llong(code, addr);
126                 s390_lg   (code, s390_r1, 0, s390_r13, 4);
127                 s390_aghi (code, this_pos, sizeof(MonoObject));
128                 s390_br   (code, s390_r1);
129
130                 g_assert ((code - start) <= 28);
131         }
132
133         return start;
134 }
135
136 /*========================= End of Function ========================*/
137
138 /*------------------------------------------------------------------*/
139 /*                                                                  */
140 /* Name         - s390_magic_trampoline                             */
141 /*                                                                  */
142 /* Function     - This method is called by the function             */
143 /*                "arch_create_jit_trampoline", which in turn is    */
144 /*                called by the trampoline functions for virtual    */
145 /*                methods. After having called the JIT compiler to  */
146 /*                compile the method, it inspects the caller code   */
147 /*                to find the address of the method-specific part   */
148 /*                of the trampoline vtable slot for this method,    */
149 /*                updates it with a fragment that calls the newly   */
150 /*                compiled code and returns this address. The calls */
151 /*                generated by mono for S/390 will look like either:*/
152 /*                1. l     %r1,xxx(%rx)                             */
153 /*                   bras  %r14,%r1                                 */
154 /*                2. brasl %r14,xxxxxx                              */
155 /*                                                                  */
156 /* Parameters   - code   - Pointer into caller code                 */
157 /*                method - The method to compile                    */
158 /*                sp     - Stack pointer                            */
159 /*                                                                  */
160 /*------------------------------------------------------------------*/
161
162 static gpointer
163 s390_magic_trampoline (MonoMethod *method, guchar *code, char *sp)
164 {
165         gpointer addr;
166         guint32 displace;
167         gint64 base;
168         int reg;
169         unsigned short opcode;
170         char *fname;
171
172         addr = mono_compile_method(method);
173         g_assert(addr);
174
175         if (code) {
176
177                 fname = mono_method_full_name (method, TRUE);
178
179                 opcode = *((unsigned short *) (code - 6));
180                 if (opcode == 0xc0e5) {
181                         /* This is the 'brasl' instruction */
182                         code    -= 4;
183                         displace = ((gint64) addr - (gint64) (code - 2)) / 2;
184                         s390_patch (code, displace);
185                         mono_arch_flush_icache (code, 4);
186                 } else {
187                         /* This is a bras rx,r1 instruction */
188                         code    -= 6;
189                         reg      = *code >> 4;
190                         displace = ((*(char *)(code+2) << 8) | 
191                                     (*((short *)code) & 0x0fff));
192                         if (reg > 5) 
193                                 base = *((gint64 *) (sp + S390_REG_SAVE_OFFSET+
194                                                      sizeof(gint64)*(reg-6)));
195                         else
196                                 base = *((gint64 *) (sp + CREATE_GR_OFFSET+
197                                                      sizeof(gint64)*(reg-2)));
198                         addr = get_unbox_trampoline(method, addr);
199                         code = base + displace;
200                         *((guint64 *) (code)) = addr;
201                 }
202         }
203
204
205         return addr;
206 }
207
208 /*========================= End of Function ========================*/
209
210 /*------------------------------------------------------------------*/
211 /*                                                                  */
212 /* Name         - s390_class_init_trampoline                        */
213 /*                                                                  */
214 /* Function     - Initialize a class and then no-op the call to     */
215 /*                the trampoline.                                   */
216 /*                                                                  */
217 /*------------------------------------------------------------------*/
218
219 static void
220 s390_class_init_trampoline (void *vtable, guchar *code, char *sp)
221 {
222         char patch[6] = {0x47, 0x00, 0x00, 0x00, 0x07, 0x00};
223
224         mono_runtime_class_init (vtable);
225
226         code = code - 6;
227
228         memcpy(code, patch, sizeof(patch));
229 }
230
231 /*========================= End of Function ========================*/
232
233 /*------------------------------------------------------------------*/
234 /*                                                                  */
235 /* Name         - create_trampoline_code                            */
236 /*                                                                  */
237 /* Function     - Create the designated type of trampoline according*/
238 /*                to the 'tramp_type' parameter.                    */
239 /*                                                                  */
240 /*------------------------------------------------------------------*/
241
242 static guchar*
243 create_trampoline_code (MonoTrampolineType tramp_type)
244 {
245
246         guint8 *buf, *code = NULL;
247         static guint8* generic_jump_trampoline = NULL;
248         static guint8 *generic_class_init_trampoline = NULL;
249         int i, offset;
250
251         switch (tramp_type) {
252         case MONO_TRAMPOLINE_GENERIC:
253                 if (mono_generic_trampoline_code)
254                         return mono_generic_trampoline_code;
255                 break;
256         case MONO_TRAMPOLINE_JUMP:
257                 if (generic_jump_trampoline)
258                         return generic_jump_trampoline;
259                 break;
260         case MONO_TRAMPOLINE_CLASS_INIT:
261                 if (generic_class_init_trampoline)
262                         return generic_class_init_trampoline;
263                 break;
264         }
265
266         if(!code) {
267                 /* Now we'll create in 'buf' the S/390 trampoline code. This
268                  is the trampoline code common to all methods  */
269                 
270                 code = buf = g_malloc(512);
271                 
272                 /*-----------------------------------------------------------
273                 STEP 0: First create a non-standard function prologue with a
274                 stack size big enough to save our registers.
275                 -----------------------------------------------------------*/
276                 
277                 s390_stmg (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
278                 s390_lgr  (buf, s390_r11, s390_r15);
279                 s390_aghi (buf, STK_BASE, -CREATE_STACK_SIZE);
280                 s390_stg  (buf, s390_r11, 0, STK_BASE, 0);
281                 s390_stmg (buf, s390_r2, s390_r5, STK_BASE, CREATE_GR_OFFSET);
282
283                 /* Save the FP registers */
284                 offset = CREATE_FP_OFFSET;
285                 for (i = s390_f0; i <= s390_f15; ++i) {
286                         s390_std  (buf, i, 0, STK_BASE, offset);
287                         offset += 8;
288                 }
289
290                 /*----------------------------------------------------------
291                 STEP 1: call 'mono_get_lmf_addr()' to get the address of our
292                 LMF. We'll need to restore it after the call to
293                 's390_magic_trampoline' and before the call to the native
294                 method.
295                 ----------------------------------------------------------*/
296                                 
297                 s390_basr (buf, s390_r13, 0);
298                 s390_j    (buf, 6);
299                 s390_llong(buf, mono_get_lmf_addr);
300                 s390_lg   (buf, s390_r1, 0, s390_r13, 4);
301                 s390_basr (buf, s390_r14, s390_r1);
302
303                 /*----------------------------------------------------------
304                 STEP 2: call 's390_magic_trampoline()', who will compile the
305                 code and fix the method vtable entry for us
306                 ----------------------------------------------------------*/
307                                 
308                 /* Set arguments */
309                 
310                 /* Arg 1: MonoMethod *method. It was put in r11 by the
311                 method-specific trampoline code, and then saved before the call
312                 to mono_get_lmf_addr()'. Restore r13, by the way :-) */
313                 s390_lg (buf, s390_r2, 0, s390_r11, METHOD_SAVE_OFFSET);
314                 
315                 /* Arg 2: code (next address to the instruction that called us) */
316                 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
317                         s390_lghi (buf, s390_r3, 0);
318                 } else {
319                         s390_lg  (buf, s390_r3, 0, s390_r11, S390_RET_ADDR_OFFSET);
320                 }
321                 
322                 /* Arg 3: stack pointer */
323                 s390_lgr  (buf, s390_r4, STK_BASE);
324                 
325                 /* Calculate call address and call
326                 's390_magic_trampoline'. Return value will be in r2 */
327                 s390_basr (buf, s390_r13, 0);
328                 s390_j    (buf, 6);
329                 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
330                         s390_llong(buf, s390_class_init_trampoline);
331                 } else {
332                         s390_llong(buf, s390_magic_trampoline);
333                 }
334                 s390_lg   (buf, s390_r1, 0, s390_r13, 4);
335                 s390_basr (buf, s390_r14, s390_r1);
336                 
337                 /* OK, code address is now on r2. Move it to r1, so that we
338                 can restore r2 and use it from r1 later */
339                 s390_lgr  (buf, s390_r1, s390_r2);
340                 
341
342                 /*----------------------------------------------------------
343                 STEP 3: Restore the LMF
344                 ----------------------------------------------------------*/
345                 
346                 /* XXX Do it !!! */
347                 
348                 /*----------------------------------------------------------
349                 STEP 4: call the compiled method
350                 ----------------------------------------------------------*/
351                 
352                 /* Restore registers */
353
354                 s390_lmg  (buf, s390_r2, s390_r5, STK_BASE, CREATE_GR_OFFSET);
355                 
356                 /* Restore the FP registers */
357                 offset = CREATE_FP_OFFSET;
358                 for (i = s390_f0; i <= s390_f15; ++i) {
359                         s390_ld  (buf, i, 0, STK_BASE, offset);
360                         offset += 8;
361                 }
362
363                 /* Restore stack pointer and jump to the code - 
364                    R14 contains the return address to our caller */
365                 s390_lgr  (buf, STK_BASE, s390_r11);
366                 s390_lmg  (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
367                 s390_br   (buf, s390_r1);
368
369                 /* Flush instruction cache, since we've generated code */
370                 mono_arch_flush_icache (code, buf - code);
371         
372                 /* Sanity check */
373                 g_assert ((buf - code) <= 512);
374         }
375
376         switch (tramp_type) {
377         case MONO_TRAMPOLINE_GENERIC:
378                 mono_generic_trampoline_code = code;
379                 break;
380         case MONO_TRAMPOLINE_JUMP:
381                 generic_jump_trampoline = code;
382                 break;
383         case MONO_TRAMPOLINE_CLASS_INIT:
384                 generic_class_init_trampoline = code;
385                 break;
386         }
387
388         return code;
389 }
390
391 /*========================= End of Function ========================*/
392
393 /*------------------------------------------------------------------*/
394 /*                                                                  */
395 /* Name         - mono_arch_create_jump_trampoline                  */
396 /*                                                                  */
397 /* Function     - Create the designated type of trampoline according*/
398 /*                to the 'tramp_type' parameter.                    */
399 /*                                                                  */
400 /*------------------------------------------------------------------*/
401
402 MonoJitInfo *
403 mono_arch_create_jump_trampoline (MonoMethod *method)
404 {
405         guint8 *code, *buf, *tramp = NULL;
406         MonoJitInfo *ji;
407         MonoDomain *domain = mono_domain_get();
408         gint32 displace;
409
410         tramp = create_trampoline_code (MONO_TRAMPOLINE_JUMP);
411
412         mono_domain_lock (domain);
413         code = buf = mono_code_manager_reserve (domain->code_mp, METHOD_TRAMPOLINE_SIZE);
414         mono_domain_unlock (domain);
415
416         s390_basr (buf, s390_r13, 0);
417         s390_j    (buf, 6);
418         s390_llong(buf, method);
419         s390_lg   (buf, s390_r13, 0, s390_r13, 4);
420         displace = (tramp - buf) / 2;
421         s390_jcl  (buf, S390_CC_UN, displace);
422
423         mono_arch_flush_icache (code, buf-code);
424
425         g_assert ((buf - code) <= JUMP_TRAMPOLINE_SIZE);
426         
427         ji              = g_new0 (MonoJitInfo, 1);
428         ji->method      = method;
429         ji->code_start  = code;
430         ji->code_size   = buf - code;
431         
432         mono_jit_stats.method_trampolines++;
433
434         return ji;
435 }
436
437 /*========================= End of Function ========================*/
438
439 /*------------------------------------------------------------------*/
440 /*                                                                  */
441 /* Name         - mono_arch_create_jit_trampoline                   */
442 /*                                                                  */
443 /* Function     - Creates a trampoline function for virtual methods.*/
444 /*                If the created code is called it first starts JIT */
445 /*                compilation and then calls the newly created      */
446 /*                method. It also replaces the corresponding vtable */
447 /*                entry (see s390_magic_trampoline).                */
448 /*                                                                  */
449 /*                A trampoline consists of two parts: a main        */
450 /*                fragment, shared by all method trampolines, and   */
451 /*                and some code specific to each method, which      */
452 /*                hard-codes a reference to that method and then    */
453 /*                calls the main fragment.                          */
454 /*                                                                  */
455 /*                The main fragment contains a call to              */
456 /*                's390_magic_trampoline', which performs a call    */
457 /*                to the JIT compiler and substitutes the method-   */
458 /*                specific fragment with some code that directly    */
459 /*                calls the JIT-compiled method.                    */
460 /*                                                                  */
461 /* Parameter    - method - Pointer to the method information        */
462 /*                                                                  */
463 /* Returns      - A pointer to the newly created code               */
464 /*                                                                  */
465 /*------------------------------------------------------------------*/
466
467 gpointer
468 mono_arch_create_jit_trampoline (MonoMethod *method)
469 {
470         guint8 *code, *buf;
471         static guint8 *vc = NULL;
472         gint32 displace;
473
474         vc = create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
475
476         /* This is the method-specific part of the trampoline. Its purpose is
477         to provide the generic part with the MonoMethod *method pointer. We'll
478         use r13 to keep that value, for instance. However, the generic part of
479         the trampoline relies on r11 having the same value it had before coming
480         here, so we must save it before. */
481         code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
482
483         s390_basr (buf, s390_r13, 0);
484         s390_j    (buf, 6);
485         s390_llong(buf, method);
486         s390_lg   (buf, s390_r13, 0, s390_r13, 4);
487         displace = (vc - buf) / 2;
488         s390_jcl  (buf, S390_CC_UN, displace);
489
490         /* Flush instruction cache, since we've generated code */
491         mono_arch_flush_icache (code, buf - code);
492                 
493         /* Sanity check */
494         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
495         
496         mono_jit_stats.method_trampolines++;
497
498         return code;
499 }
500
501 /*========================= End of Function ========================*/
502
503 /*------------------------------------------------------------------*/
504 /*                                                                  */
505 /* Name         - mono_arch_create_class_init_trampoline            */
506 /*                                                                  */
507 /* Function     - Creates a trampoline function to run a type init- */
508 /*                ializer. If the trampoline is called, it calls    */
509 /*                mono_runtime_class_init with the given vtable,    */
510 /*                then patches the caller code so it does not get   */
511 /*                called any more.                                  */
512 /*                                                                  */
513 /* Parameter    - vtable - The type to initialize                   */
514 /*                                                                  */
515 /* Returns      - A pointer to the newly created code               */
516 /*                                                                  */
517 /*------------------------------------------------------------------*/
518
519 gpointer
520 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
521 {
522         guint8 *code, *buf, *tramp;
523
524         tramp = create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
525
526         /* This is the method-specific part of the trampoline. Its purpose is
527         to provide the generic part with the MonoMethod *method pointer. We'll
528         use r11 to keep that value, for instance. However, the generic part of
529         the trampoline relies on r11 having the same value it had before coming
530         here, so we must save it before. */
531         code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
532
533         s390_stg  (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
534         s390_aghi (buf, STK_BASE, -S390_MINIMAL_STACK_SIZE);
535
536         s390_basr (buf, s390_r1, 0);
537         s390_j    (buf, 6);
538         s390_llong(buf, vtable);
539         s390_llong(buf, s390_class_init_trampoline);
540         s390_lgr  (buf, s390_r3, s390_r14);
541         s390_lg   (buf, s390_r2, 0, s390_r1, 4);
542         s390_lghi (buf, s390_r4, 0);
543         s390_lg   (buf, s390_r1, 0, s390_r1, 8);
544         s390_basr (buf, s390_r14, s390_r1);
545
546         s390_aghi (buf, STK_BASE, S390_MINIMAL_STACK_SIZE);
547         s390_lg   (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
548         s390_br   (buf, s390_r14);
549
550         /* Flush instruction cache, since we've generated code */
551         mono_arch_flush_icache (code, buf - code);
552                 
553         /* Sanity check */
554         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
555
556         mono_jit_stats.method_trampolines++;
557
558         return code;
559 }
560
561 /*========================= End of Function ========================*/
562
563 /*------------------------------------------------------------------*/
564 /*                                                                  */
565 /* Name         - mono_debuger_create_notification_function         */
566 /*                                                                  */
567 /* Function     - This method is only called when running in the    */
568 /*                Mono debugger. It returns a pointer to the        */
569 /*                arch specific notification function.              */
570 /*                                                                  */
571 /*------------------------------------------------------------------*/
572
573 gpointer
574 mono_debugger_create_notification_function (gpointer *notification_address)
575 {
576         guint8 *ptr, *buf;
577
578         ptr = buf = g_malloc0 (16);
579         s390_break (buf);
580         if (notification_address)
581                 *notification_address = buf;
582         s390_br (buf, s390_r14);
583
584         return ptr;
585 }
586
587 /*========================= End of Function ========================*/