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