- Fix atomic ops on s390
[mono.git] / mono / mini / tramp-s390.c
blobc88eefd1be93f89840794d1165e48d72b1114ba8
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 /*------------------------------------------------------------------*/
19 /*------------------------------------------------------------------*/
20 /* D e f i n e s */
21 /*------------------------------------------------------------------*/
23 #define GR_SAVE_SIZE 4*sizeof(long)
24 #define FP_SAVE_SIZE 16*sizeof(double)
25 #define CREATE_GR_OFFSET S390_MINIMAL_STACK_SIZE
26 #define CREATE_FP_OFFSET CREATE_GR_OFFSET+GR_SAVE_SIZE
27 #define CREATE_LMF_OFFSET CREATE_FP_OFFSET+FP_SAVE_SIZE
28 #define CREATE_STACK_SIZE (CREATE_LMF_OFFSET+2*sizeof(long)+sizeof(MonoLMF))
29 #define METHOD_SAVE_OFFSET S390_RET_ADDR_OFFSET-4
31 /*------------------------------------------------------------------*/
32 /* adapt to mini later... */
33 /*------------------------------------------------------------------*/
34 #define mono_jit_share_code (1)
36 /*------------------------------------------------------------------*/
37 /* Method-specific trampoline code fragment sizes */
38 /*------------------------------------------------------------------*/
39 #define METHOD_TRAMPOLINE_SIZE 64
40 #define JUMP_TRAMPOLINE_SIZE 64
42 /*========================= End of Defines =========================*/
44 /*------------------------------------------------------------------*/
45 /* I n c l u d e s */
46 /*------------------------------------------------------------------*/
48 #include <config.h>
49 #include <glib.h>
50 #include <string.h>
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>
58 #include "mini.h"
59 #include "mini-s390.h"
61 /*========================= End of Includes ========================*/
63 /*------------------------------------------------------------------*/
64 /* T y p e d e f s */
65 /*------------------------------------------------------------------*/
67 typedef enum {
68 MONO_TRAMPOLINE_GENERIC,
69 MONO_TRAMPOLINE_JUMP,
70 MONO_TRAMPOLINE_CLASS_INIT
71 } MonoTrampolineType;
73 /*========================= End of Typedefs ========================*/
75 /*------------------------------------------------------------------*/
76 /* P r o t o t y p e s */
77 /*------------------------------------------------------------------*/
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;
85 /*========================= End of Prototypes ======================*/
87 /*------------------------------------------------------------------*/
88 /* G l o b a l V a r i a b l e s */
89 /*------------------------------------------------------------------*/
92 /*====================== End of Global Variables ===================*/
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 /*------------------------------------------------------------------*/
109 static gpointer
110 get_unbox_trampoline (MonoMethod *method, gpointer addr)
112 guint8 *code, *start;
113 int this_pos = s390_r2;
115 start = addr;
116 if ((!method->signature->ret->byref) &&
117 (MONO_TYPE_ISSTRUCT (method->signature->ret)))
118 this_pos = s390_r3;
120 start = code = g_malloc (28);
122 s390_basr (code, s390_r13, 0);
123 s390_j (code, 4);
124 s390_word (code, addr);
125 s390_l (code, s390_r1, 0, s390_r13, 4);
126 s390_ahi (code, this_pos, sizeof(MonoObject));
127 s390_br (code, s390_r1);
129 g_assert ((code - start) <= 28);
131 return start;
134 /*========================= End of Function ========================*/
136 /*------------------------------------------------------------------*/
137 /* */
138 /* Name - s390_magic_trampoline */
139 /* */
140 /* Function - This method is called by the function */
141 /* "arch_create_jit_trampoline", which in turn is */
142 /* called by the trampoline functions for virtual */
143 /* methods. After having called the JIT compiler to */
144 /* compile the method, it inspects the caller code */
145 /* to find the address of the method-specific part */
146 /* of the trampoline vtable slot for this method, */
147 /* updates it with a fragment that calls the newly */
148 /* compiled code and returns this address. The calls */
149 /* generated by mono for S/390 will look like either:*/
150 /* 1. l %r1,xxx(%rx) */
151 /* bras %r14,%r1 */
152 /* 2. brasl %r14,xxxxxx */
153 /* */
154 /* Parameters - code - Pointer into caller code */
155 /* method - The method to compile */
156 /* sp - Stack pointer */
157 /* */
158 /*------------------------------------------------------------------*/
160 static gpointer
161 s390_magic_trampoline (MonoMethod *method, guchar *code, char *sp)
163 gpointer addr;
164 gint32 displace;
165 int reg, base;
166 unsigned short opcode;
167 char *fname;
168 MonoJitInfo *codeJi,
169 *addrJi;
171 addr = mono_compile_method(method);
172 g_assert(addr);
175 if (code) {
177 /* The top bit needs to be ignored on S/390 */
178 (guint32) code &= 0x7fffffff;
180 fname = mono_method_full_name (method, TRUE);
181 codeJi = mono_jit_info_table_find (mono_domain_get(), code);
182 addrJi = mono_jit_info_table_find (mono_domain_get(), addr);
183 if (mono_method_same_domain (codeJi, addrJi)) {
185 opcode = *((unsigned short *) (code - 6));
186 switch (opcode) {
187 case 0x5810 :
188 /* This is a bras r14,r1 instruction */
189 code -= 4;
190 reg = *code >> 4;
191 displace = *((short *)code) & 0x0fff;
192 if (reg > 5)
193 base = *((int *) (sp + S390_REG_SAVE_OFFSET+
194 sizeof(int)*(reg-6)));
195 else
196 base = *((int *) (sp + CREATE_GR_OFFSET+
197 sizeof(int)*(reg-2)));
199 if ((method->klass->valuetype) &&
200 (!mono_aot_is_got_entry(code, base)))
201 addr = get_unbox_trampoline(method, addr);
203 code = base + displace;
204 if (mono_domain_owns_vtable_slot(mono_domain_get(),
205 code))
206 s390_patch(code, addr);
207 break;
208 case 0xc0e5 :
209 /* This is the 'brasl' instruction */
210 code -= 4;
211 displace = ((gint32) addr - (gint32) (code - 2)) / 2;
212 if (mono_method_same_domain (codeJi, addrJi)) {
213 s390_patch (code, displace);
214 mono_arch_flush_icache (code, 4);
216 break;
217 default :
218 g_error("Unable to patch instruction prior to %p",code);
224 return addr;
227 /*========================= End of Function ========================*/
229 /*------------------------------------------------------------------*/
230 /* */
231 /* Name - s390_class_init_trampoline */
232 /* */
233 /* Function - Initialize a class and then no-op the call to */
234 /* the trampoline. */
235 /* */
236 /*------------------------------------------------------------------*/
238 static void
239 s390_class_init_trampoline (void *vtable, guchar *code, char *sp)
241 char patch[6] = {0x47, 0x00, 0x00, 0x00, 0x07, 0x00};
243 mono_runtime_class_init (vtable);
245 code = code - 6;
247 memcpy(code, patch, sizeof(patch));
250 /*========================= End of Function ========================*/
252 /*------------------------------------------------------------------*/
253 /* */
254 /* Name - create_trampoline_code */
255 /* */
256 /* Function - Create the designated type of trampoline according*/
257 /* to the 'tramp_type' parameter. */
258 /* */
259 /*------------------------------------------------------------------*/
261 static guchar*
262 create_trampoline_code (MonoTrampolineType tramp_type)
265 guint8 *buf, *code = NULL;
266 static guint8* generic_jump_trampoline = NULL;
267 static guint8 *generic_class_init_trampoline = NULL;
268 int i, offset, lmfOffset;
270 switch (tramp_type) {
271 case MONO_TRAMPOLINE_GENERIC:
272 if (mono_generic_trampoline_code)
273 return mono_generic_trampoline_code;
274 break;
275 case MONO_TRAMPOLINE_JUMP:
276 if (generic_jump_trampoline)
277 return generic_jump_trampoline;
278 break;
279 case MONO_TRAMPOLINE_CLASS_INIT:
280 if (generic_class_init_trampoline)
281 return generic_class_init_trampoline;
282 break;
285 if(!code) {
286 /* Now we'll create in 'buf' the S/390 trampoline code. This
287 is the trampoline code common to all methods */
289 code = buf = g_malloc(512);
291 /*-----------------------------------------------------------
292 STEP 0: First create a non-standard function prologue with a
293 stack size big enough to save our registers.
294 -----------------------------------------------------------*/
296 s390_stm (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
297 s390_lr (buf, s390_r11, s390_r15);
298 s390_ahi (buf, STK_BASE, -CREATE_STACK_SIZE);
299 s390_st (buf, s390_r11, 0, STK_BASE, 0);
300 s390_stm (buf, s390_r2, s390_r5, STK_BASE, CREATE_GR_OFFSET);
302 /* Save the FP registers */
303 offset = CREATE_FP_OFFSET;
304 for (i = s390_f0; i <= s390_f15; ++i) {
305 s390_std (buf, i, 0, STK_BASE, offset);
306 offset += 8;
309 /*----------------------------------------------------------
310 STEP 1: call 'mono_get_lmf_addr()' to get the address of our
311 LMF. We'll need to restore it after the call to
312 's390_magic_trampoline' and before the call to the native
313 method.
314 ----------------------------------------------------------*/
316 s390_basr (buf, s390_r13, 0);
317 s390_j (buf, 4);
318 s390_word (buf, mono_get_lmf_addr);
319 s390_l (buf, s390_r1, 0, s390_r13, 4);
320 s390_basr (buf, s390_r14, s390_r1);
322 /*---------------------------------------------------------------*/
323 /* we build the MonoLMF structure on the stack - see mini-s390.h */
324 /* Keep in sync with the code in mono_arch_emit_prolog */
325 /*---------------------------------------------------------------*/
326 lmfOffset = CREATE_STACK_SIZE - sizeof(MonoLMF);
328 s390_lr (buf, s390_r13, STK_BASE);
329 s390_ahi (buf, s390_r13, lmfOffset);
331 /*---------------------------------------------------------------*/
332 /* Set lmf.lmf_addr = jit_tls->lmf */
333 /*---------------------------------------------------------------*/
334 s390_st (buf, s390_r2, 0, s390_r13,
335 G_STRUCT_OFFSET(MonoLMF, lmf_addr));
337 /*---------------------------------------------------------------*/
338 /* Get current lmf */
339 /*---------------------------------------------------------------*/
340 s390_l (buf, s390_r0, 0, s390_r2, 0);
342 /*---------------------------------------------------------------*/
343 /* Set our lmf as the current lmf */
344 /*---------------------------------------------------------------*/
345 s390_st (buf, s390_r13, 0, s390_r2, 0);
347 /*---------------------------------------------------------------*/
348 /* Have our lmf.previous_lmf point to the last lmf */
349 /*---------------------------------------------------------------*/
350 s390_st (buf, s390_r0, 0, s390_r13,
351 G_STRUCT_OFFSET(MonoLMF, previous_lmf));
353 /*---------------------------------------------------------------*/
354 /* save method info */
355 /*---------------------------------------------------------------*/
356 s390_l (buf, s390_r1, 0, s390_r11, METHOD_SAVE_OFFSET);
357 s390_st (buf, s390_r1, 0, s390_r13,
358 G_STRUCT_OFFSET(MonoLMF, method));
360 /*---------------------------------------------------------------*/
361 /* save the current SP */
362 /*---------------------------------------------------------------*/
363 s390_l (buf, s390_r1, 0, STK_BASE, 0);
364 s390_st (buf, s390_r1, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, ebp));
366 /*---------------------------------------------------------------*/
367 /* save the current IP */
368 /*---------------------------------------------------------------*/
369 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
370 s390_lhi (buf, s390_r1, 0);
371 } else {
372 s390_l (buf, s390_r1, 0, s390_r1, S390_RET_ADDR_OFFSET);
373 s390_la (buf, s390_r1, 0, s390_r1, 0);
375 s390_st (buf, s390_r1, 0, s390_r13, G_STRUCT_OFFSET(MonoLMF, eip));
377 /*---------------------------------------------------------------*/
378 /* Save general and floating point registers */
379 /*---------------------------------------------------------------*/
380 s390_stm (buf, s390_r2, s390_r12, s390_r13,
381 G_STRUCT_OFFSET(MonoLMF, gregs[2]));
382 for (i = 0; i < 16; i++) {
383 s390_std (buf, i, 0, s390_r13,
384 G_STRUCT_OFFSET(MonoLMF, fregs[i]));
387 /*---------------------------------------------------------------*/
388 /* STEP 2: call 's390_magic_trampoline()', who will compile the */
389 /* code and fix the method vtable entry for us */
390 /*---------------------------------------------------------------*/
392 /* Set arguments */
394 /* Arg 1: MonoMethod *method. It was put in r11 by the
395 method-specific trampoline code, and then saved before the call
396 to mono_get_lmf_addr()'. Restore r13, by the way :-) */
397 s390_l (buf, s390_r2, 0, s390_r11, METHOD_SAVE_OFFSET);
399 /* Arg 2: code (next address to the instruction that called us) */
400 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
401 s390_lhi (buf, s390_r3, 0);
402 } else {
403 s390_l (buf, s390_r3, 0, s390_r11, S390_RET_ADDR_OFFSET);
406 /* Arg 3: stack pointer */
407 s390_lr (buf, s390_r4, STK_BASE);
409 /* Calculate call address and call
410 's390_magic_trampoline'. Return value will be in r2 */
411 s390_basr (buf, s390_r13, 0);
412 s390_j (buf, 4);
413 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
414 s390_word (buf, s390_class_init_trampoline);
415 } else {
416 s390_word (buf, s390_magic_trampoline);
418 s390_l (buf, s390_r1, 0, s390_r13, 4);
419 s390_basr (buf, s390_r14, s390_r1);
421 /* OK, code address is now on r2. Move it to r1, so that we
422 can restore r2 and use it from r1 later */
423 s390_lr (buf, s390_r1, s390_r2);
425 /*----------------------------------------------------------
426 STEP 3: Restore the LMF
427 ----------------------------------------------------------*/
428 restoreLMF(buf, STK_BASE, CREATE_STACK_SIZE);
430 /*----------------------------------------------------------
431 STEP 4: call the compiled method
432 ----------------------------------------------------------*/
434 /* Restore registers */
436 s390_lm (buf, s390_r2, s390_r5, STK_BASE, CREATE_GR_OFFSET);
438 /* Restore the FP registers */
439 offset = CREATE_FP_OFFSET;
440 for (i = s390_f0; i <= s390_f15; ++i) {
441 s390_ld (buf, i, 0, STK_BASE, offset);
442 offset += 8;
445 /* Restore stack pointer and jump to the code -
446 R14 contains the return address to our caller */
447 s390_lr (buf, STK_BASE, s390_r11);
448 s390_lm (buf, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
449 s390_br (buf, s390_r1);
451 /* Flush instruction cache, since we've generated code */
452 mono_arch_flush_icache (code, buf - code);
454 /* Sanity check */
455 g_assert ((buf - code) <= 512);
458 switch (tramp_type) {
459 case MONO_TRAMPOLINE_GENERIC:
460 mono_generic_trampoline_code = code;
461 break;
462 case MONO_TRAMPOLINE_JUMP:
463 generic_jump_trampoline = code;
464 break;
465 case MONO_TRAMPOLINE_CLASS_INIT:
466 generic_class_init_trampoline = code;
467 break;
470 return code;
473 /*========================= End of Function ========================*/
475 /*------------------------------------------------------------------*/
476 /* */
477 /* Name - mono_arch_create_jump_trampoline */
478 /* */
479 /* Function - Create the designated type of trampoline according*/
480 /* to the 'tramp_type' parameter. */
481 /* */
482 /*------------------------------------------------------------------*/
484 MonoJitInfo *
485 mono_arch_create_jump_trampoline (MonoMethod *method)
487 guint8 *code, *buf, *tramp = NULL;
488 MonoJitInfo *ji;
489 MonoDomain *domain = mono_domain_get();
490 gint32 displace;
492 tramp = create_trampoline_code (MONO_TRAMPOLINE_JUMP);
494 mono_domain_lock (domain);
495 code = buf = mono_code_manager_reserve (domain->code_mp, METHOD_TRAMPOLINE_SIZE);
496 mono_domain_unlock (domain);
498 s390_basr (buf, s390_r13, 0);
499 s390_j (buf, 4);
500 s390_word (buf, method);
501 s390_l (buf, s390_r13, 0, s390_r13, 4);
502 displace = (tramp - buf) / 2;
503 s390_jcl (buf, S390_CC_UN, displace);
505 mono_arch_flush_icache (code, buf-code);
507 g_assert ((buf - code) <= JUMP_TRAMPOLINE_SIZE);
509 ji = g_new0 (MonoJitInfo, 1);
510 ji->method = method;
511 ji->code_start = code;
512 ji->code_size = buf - code;
514 mono_jit_stats.method_trampolines++;
516 return ji;
519 /*========================= End of Function ========================*/
521 /*------------------------------------------------------------------*/
522 /* */
523 /* Name - mono_arch_create_jit_trampoline */
524 /* */
525 /* Function - Creates a trampoline function for virtual methods.*/
526 /* If the created code is called it first starts JIT */
527 /* compilation and then calls the newly created */
528 /* method. It also replaces the corresponding vtable */
529 /* entry (see s390_magic_trampoline). */
530 /* */
531 /* A trampoline consists of two parts: a main */
532 /* fragment, shared by all method trampolines, and */
533 /* and some code specific to each method, which */
534 /* hard-codes a reference to that method and then */
535 /* calls the main fragment. */
536 /* */
537 /* The main fragment contains a call to */
538 /* 's390_magic_trampoline', which performs a call */
539 /* to the JIT compiler and substitutes the method- */
540 /* specific fragment with some code that directly */
541 /* calls the JIT-compiled method. */
542 /* */
543 /* Parameter - method - Pointer to the method information */
544 /* */
545 /* Returns - A pointer to the newly created code */
546 /* */
547 /*------------------------------------------------------------------*/
549 gpointer
550 mono_arch_create_jit_trampoline (MonoMethod *method)
552 guint8 *code, *buf;
553 static guint8 *vc = NULL;
554 gint32 displace;
556 vc = create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
558 /* This is the method-specific part of the trampoline. Its purpose is
559 to provide the generic part with the MonoMethod *method pointer. We'll
560 use r13 to keep that value, for instance. However, the generic part of
561 the trampoline relies on r11 having the same value it had before coming
562 here, so we must save it before. */
563 code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
565 s390_basr (buf, s390_r13, 0);
566 s390_j (buf, 4);
567 s390_word (buf, method);
568 s390_l (buf, s390_r13, 0, s390_r13, 4);
569 displace = (vc - buf) / 2;
570 s390_jcl (buf, S390_CC_UN, displace);
572 /* Flush instruction cache, since we've generated code */
573 mono_arch_flush_icache (code, buf - code);
575 /* Sanity check */
576 g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
578 return code;
581 /*========================= End of Function ========================*/
583 /*------------------------------------------------------------------*/
584 /* */
585 /* Name - mono_arch_create_class_init_trampoline */
586 /* */
587 /* Function - Creates a trampoline function to run a type init- */
588 /* ializer. If the trampoline is called, it calls */
589 /* mono_runtime_class_init with the given vtable, */
590 /* then patches the caller code so it does not get */
591 /* called any more. */
592 /* */
593 /* Parameter - vtable - The type to initialize */
594 /* */
595 /* Returns - A pointer to the newly created code */
596 /* */
597 /*------------------------------------------------------------------*/
599 gpointer
600 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
602 guint8 *code, *buf, *tramp;
604 tramp = create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
606 /* This is the method-specific part of the trampoline. Its purpose is
607 to provide the generic part with the MonoMethod *method pointer. We'll
608 use r11 to keep that value, for instance. However, the generic part of
609 the trampoline relies on r11 having the same value it had before coming
610 here, so we must save it before. */
611 code = buf = g_malloc(METHOD_TRAMPOLINE_SIZE);
613 s390_st (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
614 s390_ahi (buf, STK_BASE, -S390_MINIMAL_STACK_SIZE);
616 s390_basr (buf, s390_r1, 0);
617 s390_j (buf, 6);
618 s390_word (buf, vtable);
619 s390_word (buf, s390_class_init_trampoline);
620 s390_lr (buf, s390_r3, s390_r14);
621 s390_l (buf, s390_r2, 0, s390_r1, 4);
622 s390_lhi (buf, s390_r4, 0);
623 s390_l (buf, s390_r1, 0, s390_r1, 8);
624 s390_basr (buf, s390_r14, s390_r1);
626 s390_ahi (buf, STK_BASE, S390_MINIMAL_STACK_SIZE);
627 s390_l (buf, s390_r14, 0, STK_BASE, S390_RET_ADDR_OFFSET);
628 s390_br (buf, s390_r14);
630 /* Flush instruction cache, since we've generated code */
631 mono_arch_flush_icache (code, buf - code);
633 /* Sanity check */
634 g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
636 mono_jit_stats.method_trampolines++;
638 return code;
641 /*========================= End of Function ========================*/
643 /*------------------------------------------------------------------*/
644 /* */
645 /* Name - mono_debuger_create_notification_function */
646 /* */
647 /* Function - This method is only called when running in the */
648 /* Mono debugger. It returns a pointer to the */
649 /* arch specific notification function. */
650 /* */
651 /*------------------------------------------------------------------*/
653 gpointer
654 mono_debugger_create_notification_function (gpointer *notification_address)
656 guint8 *ptr, *buf;
658 ptr = buf = g_malloc0 (16);
659 s390_break (buf);
660 if (notification_address)
661 *notification_address = buf;
662 s390_br (buf, s390_r14);
664 return ptr;
667 /*========================= End of Function ========================*/