Wed Oct 6 12:40:28 CEST 2004 Paolo Molaro <lupus@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         /* previously created trampoline code */
475         if (method->info)
476                 return method->info;
477
478         if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
479                 return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
480
481         vc = create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
482
483         /* This is the method-specific part of the trampoline. Its purpose is
484         to provide the generic part with the MonoMethod *method pointer. We'll
485         use r13 to keep that value, for instance. However, the generic part of
486         the trampoline relies on r11 having the same value it had before coming
487         here, so we must save it before. */
488         code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
489
490         s390_basr (buf, s390_r13, 0);
491         s390_j    (buf, 6);
492         s390_llong(buf, method);
493         s390_lg   (buf, s390_r13, 0, s390_r13, 4);
494         displace = (vc - buf) / 2;
495         s390_jcl  (buf, S390_CC_UN, displace);
496
497         /* Flush instruction cache, since we've generated code */
498         mono_arch_flush_icache (code, buf - code);
499                 
500         /* Sanity check */
501         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
502         
503         /* Store trampoline address */
504         method->info = code;
505
506         mono_jit_stats.method_trampolines++;
507
508         return code;
509 }
510
511 /*========================= End of Function ========================*/
512
513 /*------------------------------------------------------------------*/
514 /*                                                                  */
515 /* Name         - mono_arch_create_class_init_trampoline            */
516 /*                                                                  */
517 /* Function     - Creates a trampoline function to run a type init- */
518 /*                ializer. If the trampoline is called, it calls    */
519 /*                mono_runtime_class_init with the given vtable,    */
520 /*                then patches the caller code so it does not get   */
521 /*                called any more.                                  */
522 /*                                                                  */
523 /* Parameter    - vtable - The type to initialize                   */
524 /*                                                                  */
525 /* Returns      - A pointer to the newly created code               */
526 /*                                                                  */
527 /*------------------------------------------------------------------*/
528
529 gpointer
530 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
531 {
532         guint8 *code, *buf, *tramp;
533
534         tramp = create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
535
536         /* This is the method-specific part of the trampoline. Its purpose is
537         to provide the generic part with the MonoMethod *method pointer. We'll
538         use r11 to keep that value, for instance. However, the generic part of
539         the trampoline relies on r11 having the same value it had before coming
540         here, so we must save it before. */
541         code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
542
543         s390_stg  (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
544         s390_aghi (buf, STK_BASE, -S390_MINIMAL_STACK_SIZE);
545
546         s390_basr (buf, s390_r1, 0);
547         s390_j    (buf, 6);
548         s390_llong(buf, vtable);
549         s390_llong(buf, s390_class_init_trampoline);
550         s390_lgr  (buf, s390_r3, s390_r14);
551         s390_lg   (buf, s390_r2, 0, s390_r1, 4);
552         s390_lghi (buf, s390_r4, 0);
553         s390_lg   (buf, s390_r1, 0, s390_r1, 8);
554         s390_basr (buf, s390_r14, s390_r1);
555
556         s390_aghi (buf, STK_BASE, S390_MINIMAL_STACK_SIZE);
557         s390_lg   (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
558         s390_br   (buf, s390_r14);
559
560         /* Flush instruction cache, since we've generated code */
561         mono_arch_flush_icache (code, buf - code);
562                 
563         /* Sanity check */
564         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
565
566         mono_jit_stats.method_trampolines++;
567
568         return code;
569 }
570
571 /*========================= End of Function ========================*/
572
573 /*------------------------------------------------------------------*/
574 /*                                                                  */
575 /* Name         - mono_debuger_create_notification_function         */
576 /*                                                                  */
577 /* Function     - This method is only called when running in the    */
578 /*                Mono debugger. It returns a pointer to the        */
579 /*                arch specific notification function.              */
580 /*                                                                  */
581 /*------------------------------------------------------------------*/
582
583 gpointer
584 mono_debugger_create_notification_function (gpointer *notification_address)
585 {
586         guint8 *ptr, *buf;
587
588         ptr = buf = g_malloc0 (16);
589         s390_break (buf);
590         if (notification_address)
591                 *notification_address = buf;
592         s390_br (buf, s390_r14);
593
594         return ptr;
595 }
596
597 /*========================= End of Function ========================*/