Merge pull request #199 from slide/master
[mono.git] / mono / mini / tramp-alpha.c
1 /*------------------------------------------------------------------*/
2 /*                                                                  */
3 /* Name        - tramp-alpha.c                                      */
4 /*                                                                  */
5 /* Function    - JIT trampoline code for Alpha.                     */
6 /*                                                                  */
7 /* Name        - Sergey Tikhonov (tsv@solvo.ru)                     */
8 /*                                                                  */
9 /* Date        - January, 2006                                      */
10 /*                                                                  */
11 /* Derivation  - From exceptions-amd64 & exceptions-ia64            */
12 /*               Dietmar Maurer (dietmar@ximian.com)                */
13 /*               Zoltan Varga (vargaz@gmail.com)                    */
14 /*                                                                  */
15 /*                                                                  */
16 /*------------------------------------------------------------------*/
17
18 /*------------------------------------------------------------------*/
19 /*                 D e f i n e s                                    */
20 /*------------------------------------------------------------------*/
21 #define ALPHA_DEBUG(x) \
22         if (mini_alpha_verbose_level) \
23                 g_debug ("ALPHA_DEBUG: %s is called.", x);
24
25 #define ALPHA_PRINT if (mini_alpha_verbose_level)
26
27 /*========================= End of Defines =========================*/
28
29 /*------------------------------------------------------------------*/
30 /*                 I n c l u d e s                                  */
31 /*------------------------------------------------------------------*/
32
33 #include <config.h>
34 #include <glib.h>
35 #include <string.h>
36
37 #include <mono/metadata/appdomain.h>
38 #include <mono/metadata/marshal.h>
39 #include <mono/metadata/tabledefs.h>
40 #include <mono/arch/alpha/alpha-codegen.h>
41 #include <mono/metadata/mono-debug-debugger.h>
42
43 #include "mini.h"
44 #include "mini-alpha.h"
45
46 /*========================= End of Includes ========================*/
47
48 /*------------------------------------------------------------------*/
49 /*                 T y p e d e f s                                  */
50 /*------------------------------------------------------------------*/
51
52 /*========================= End of Typedefs ========================*/
53
54 /*------------------------------------------------------------------*/
55 /*                   P r o t o t y p e s                            */
56 /*------------------------------------------------------------------*/
57
58 /*========================= End of Prototypes ======================*/
59
60 /*------------------------------------------------------------------*/
61 /*                 G l o b a l   V a r i a b l e s                  */
62 /*------------------------------------------------------------------*/
63
64
65 /*====================== End of Global Variables ===================*/
66
67 extern int mini_alpha_verbose_level;
68
69 /*------------------------------------------------------------------*/
70 /*                                                                  */
71 /* Name         - mono_arch_create_trampoline_code                  */
72 /*                                                                  */
73 /* Function     - Create the designated type of trampoline according*/
74 /*                to the 'tramp_type' parameter.                    */
75 /*                                                                  */
76 /*
77   This code should expect to be called by tramp stub function
78   On Alpha:
79   - pv points to start of stub function
80   - at points to start of this trampoline
81   - allocate stack to save all regs and lmfs
82   - save regs
83   - save lmf
84   - fill params for trampoline methods (They expect 4 params)
85   - call trampoline method (by standard call convention (pv + ra))
86   - save return value (r0)
87   - restore saved regs + lmfs
88   - restore stack (don't forget space allocated by stub)
89   - use saved return values as new address to give control to
90   - return or jump to new address (don't expect to return here -
91     don't save return address. RA will be holding return address
92     of original caller of tramp stub function). New address function
93     expect standart calling convention (pv)
94
95 */
96 /*------------------------------------------------------------------*/
97
98 guchar *
99 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
100 {
101   unsigned int *buf, *code, *tramp;
102   int i, offset, framesize, off, lmf_offset, saved_regs_offset;
103   //int saved_fpregs_offset, saved_regs_offset, method_offset, tramp_offset;
104
105   gboolean has_caller;
106   
107   ALPHA_DEBUG("mono_arch_create_trampoline_code");
108   
109   if (tramp_type == MONO_TRAMPOLINE_JUMP)
110     has_caller = FALSE;
111   else
112     has_caller = TRUE;
113   
114   code = buf = mono_global_codeman_reserve (1024);
115   
116   framesize = 1024 + sizeof (MonoLMF);
117   framesize = (framesize +
118                (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
119   
120   offset = 16;
121   
122   // Expect that generated code is called with 2 parameters
123   // method and tramp (in a0 and a1)
124   
125   // Allocate stack
126   alpha_lda(code, alpha_sp, alpha_sp, -framesize);
127
128   /* store call convention parameters on stack.*/
129   alpha_stq( code, alpha_ra, alpha_sp, 0 ); // ra
130   alpha_stq( code, alpha_fp, alpha_sp, 8 ); // fp
131
132   saved_regs_offset = offset;
133
134   // Store all integer regs
135   for (i=0; i<30 /*alpha_pc*/; i++)
136     {
137       alpha_stq(code, i, alpha_sp, offset);
138       offset += 8;
139     }
140   
141   // Store all fp regs
142   for (i=0; i<alpha_fzero; i++)
143     {
144       alpha_stt(code, i, alpha_sp, offset);
145       offset += 8;
146     }
147
148   if (1)
149   {
150     // Save LMF (TSV_TODO don't forget callee saved regs)
151     lmf_offset = offset;
152     offset += sizeof (MonoLMF);
153
154     // Save PC
155     if (has_caller)
156       alpha_stq(code, alpha_ra, alpha_sp, (lmf_offset + G_STRUCT_OFFSET (MonoLMF, eip)));
157     else
158       alpha_stq(code, alpha_zero, alpha_sp, (lmf_offset + G_STRUCT_OFFSET (MonoLMF, eip)));
159     // Save FP
160     alpha_stq(code, alpha_fp, alpha_sp, (lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebp)));
161     
162     // Save method
163     alpha_ldq(code, alpha_r0, alpha_sp, framesize);
164     alpha_stq(code, alpha_r0, alpha_sp, (lmf_offset + G_STRUCT_OFFSET (MonoLMF, method)));
165
166     // Save SP
167     alpha_lda(code, alpha_r0, alpha_sp, (framesize+16));
168     alpha_stq(code, alpha_r0, alpha_sp, (lmf_offset + G_STRUCT_OFFSET (MonoLMF, rsp)));
169     
170     // Save GP
171     alpha_stq(code, alpha_gp, alpha_sp, (lmf_offset + G_STRUCT_OFFSET (MonoLMF, rgp)));
172     
173     // Get "lmf_addr"
174     off = (char *)code - (char *)buf;
175     off += 2*4;
176     
177     if (off % 8)
178       {
179         alpha_nop(code);
180         off += 4;
181       }
182     
183     // alpha_at points to start of this method !!!
184     alpha_ldq(code, alpha_pv, alpha_at, off);
185     alpha_br(code, alpha_zero, 2);
186     
187     *code = (unsigned int)(((unsigned long)mono_get_lmf_addr) & 0xFFFFFFFF);
188     code++;
189     *code = (unsigned int)((((unsigned long)mono_get_lmf_addr) >> 32) & 0xFFFFFFFF);
190     code++;
191     
192     /*
193      * The call might clobber argument registers, but they are already
194      * saved to the stack/global regs.
195      */
196     alpha_jsr(code, alpha_ra, alpha_pv, 0);
197     
198     // Save lmf_addr
199     alpha_stq(code, alpha_r0, alpha_sp,
200               (lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr)));
201     // Load "previous_lmf" member of MonoLMF struct
202     alpha_ldq(code, alpha_r1, alpha_r0, 0);
203     
204     // Save it to MonoLMF struct
205     alpha_stq(code, alpha_r1, alpha_sp,
206               (lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf)));
207     // Set new LMF
208     alpha_lda(code, alpha_r1, alpha_sp, lmf_offset);
209     alpha_stq(code, alpha_r1, alpha_r0, 0);
210   }
211
212
213   /* set the frame pointer */
214   alpha_mov1( code, alpha_sp, alpha_fp );
215   
216   /* Arg3 is the method/vtable ptr */
217   alpha_ldq(code, alpha_a2, alpha_sp, framesize);
218   //alpha_mov1(code, alpha_a0, alpha_a2);
219   
220   /* Arg4 is the trampoline address */
221   // Load PV from saved regs - later optimize it and load into a3 directly
222   alpha_ldq(code, alpha_pv, alpha_sp, (saved_regs_offset + (alpha_pv*8)));
223   alpha_mov1(code, alpha_pv, alpha_a3);
224   //alpha_mov1(code, alpha_a1, alpha_a3);
225   
226   /* Arg1 is the pointer to the saved registers */
227   alpha_lda(code, alpha_a0, alpha_sp, 16);
228
229   alpha_ldq(code, alpha_ra, alpha_sp, (saved_regs_offset + (alpha_ra*8)));  
230   /* Arg2 is the address of the calling code */
231   if (has_caller)
232     alpha_mov1(code, alpha_ra, alpha_a1);
233   else
234     alpha_mov1(code, alpha_zero, alpha_a1);
235   
236   /* Arg3 is the method/vtable ptr 
237      alpha_mov1(code, alpha_a0, alpha_a2);
238      
239      Arg4 is the trampoline address
240      alpha_mov1(code, alpha_a1, alpha_a3);
241   */
242   
243   if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
244     tramp = (unsigned int*)mono_class_init_trampoline;
245   else if (tramp_type == MONO_TRAMPOLINE_AOT)
246     tramp = (unsigned int*)mono_aot_trampoline;
247   else if (tramp_type == MONO_TRAMPOLINE_DELEGATE)
248     tramp = (unsigned int*)mono_delegate_trampoline;
249   else
250     tramp = (unsigned int*)mono_magic_trampoline;
251   
252   // Restore AT
253   alpha_ldq(code, alpha_at, alpha_sp, (saved_regs_offset + (alpha_at*8)));
254
255   off = (char *)code - (char *)buf;
256   off += 2*4;
257   
258   if (off % 8)
259     {
260       alpha_nop(code);
261       off += 4;
262     }
263   
264   // alpha_at points to start of this method !!!
265   alpha_ldq(code, alpha_pv, alpha_at, off);
266   alpha_br(code, alpha_zero, 2);
267   
268   *code = (unsigned int)(((unsigned long)tramp) & 0xFFFFFFFF);
269   code++;
270   *code = (unsigned int)((((unsigned long)tramp) >> 32) & 0xFFFFFFFF);
271   code++;
272   
273   alpha_jsr(code, alpha_ra, alpha_pv, 0);
274   
275   alpha_stq(code, alpha_r0, alpha_sp, framesize);
276   
277   /* Restore LMF */
278   if (1)
279   {
280     /* Restore previous lmf */
281     alpha_ldq(code, alpha_at, alpha_sp,
282               (lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf)));
283     alpha_ldq(code, alpha_ra, alpha_sp,
284               (lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr)));
285     alpha_stq(code, alpha_at, alpha_ra, 0);
286   }
287   
288   offset = 16;
289   
290   // Restore all integer regs
291   for (i=0; i<30 /*alpha_pc*/; i++)
292     {
293       alpha_ldq(code, i, alpha_sp, offset);
294       offset += 8;
295     }
296
297   // Restore all float regs
298   for (i=0; i<alpha_fzero; i++)
299     {
300       alpha_ldt(code, i, alpha_sp, offset);
301       offset += 8;
302     }
303   
304   alpha_ldq(code, alpha_r0, alpha_sp, framesize);
305   
306   // Restore stack
307   alpha_lda(code, alpha_sp, alpha_sp, (framesize+16));
308   
309   if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
310     alpha_ret (code, alpha_ra, 1);
311   else
312     {
313       /* call the compiled method */
314       // It will expect correct call frame 
315       
316       alpha_mov1(code, alpha_r0, alpha_pv);
317       alpha_jsr (code, alpha_zero, alpha_pv, 0);
318     }
319   
320   g_assert (((char *)code - (char *)buf) <= 1024);
321   
322   mono_arch_flush_icache ((guchar *)buf, (char *)code - (char *)buf);
323   
324   return (guchar *)buf;
325 }
326
327 /*========================= End of Function ========================*/
328
329 /*------------------------------------------------------------------*/
330 /*                                                                  */
331 /* Name         - mono_arch_create_jit_trampoline                   */
332 /*                                                                  */
333 /* Function     - Creates a trampoline function for virtual methods.*/
334 /*                If the created code is called it first starts JIT */
335 /*                compilation and then calls the newly created      */
336 /*                method. It also replaces the corresponding vtable */
337 /*                entry (see s390_magic_trampoline).                */
338 /*                                                                  */
339 /*                A trampoline consists of two parts: a main        */
340 /*                fragment, shared by all method trampolines, and   */
341 /*                and some code specific to each method, which      */
342 /*                hard-codes a reference to that method and then    */
343 /*                calls the main fragment.                          */
344 /*                                                                  */
345 /*                The main fragment contains a call to              */
346 /*                's390_magic_trampoline', which performs a call    */
347 /*                to the JIT compiler and substitutes the method-   */
348 /*                specific fragment with some code that directly    */
349 /*                calls the JIT-compiled method.                    */
350 /*                                                                  */
351 /* Parameter    - method - Pointer to the method information        */
352 /*                                                                  */
353 /* Returns      - A pointer to the newly created code               */
354 /*                                                                  */
355 /*------------------------------------------------------------------*/
356
357 gpointer
358 mono_arch_create_jit_trampoline (MonoMethod *method)
359 {
360         ALPHA_DEBUG("mono_arch_create_jit_trampoline: check MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE define");
361
362 //      NOT_IMPLEMENTED("mono_arch_create_jit_trampoline: check MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE define");
363
364         return 0;
365 }
366
367 /*========================= End of Function ========================*/
368
369 /*------------------------------------------------------------------*/
370 /*                                                                  */
371 /* Name         - mono_arch_create_jump_trampoline                  */
372 /*                                                                  */
373 /* Function     - Create the designated type of trampoline according*/
374 /*                to the 'tramp_type' parameter.                    */
375 /*                                                                  */
376 /*------------------------------------------------------------------*/
377
378 MonoJitInfo *
379 mono_arch_create_jump_trampoline (MonoMethod *method)
380 {
381         ALPHA_DEBUG("mono_arch_create_jump_trampoline");
382
383         NOT_IMPLEMENTED;
384         
385         return 0;
386 }
387
388 /*========================= End of Function ========================*/
389
390 /*------------------------------------------------------------------*/
391 /*                                                                  */
392 /* Name         - mono_arch_create_specific_trampoline              */
393 /*                                                                  */
394 /* Function     - ???Create the designated type of trampoline according*/
395 /*                to the 'tramp_type' parameter.                    */
396 /*                                                                  */
397 /* This method should create a stub code that will transfer
398    control to corresponding trampoline. We need to pass "arg1" and
399    start address of this stab method to trampoline code.
400    We should not modify any registers!!!
401    For Alpha:
402    - allocate 2 qword on stack
403    - save stab start address on 8(sp)
404    - save "arg1" on 0(sp)
405    - jump to trampoline code keeping original caller return address
406      in ra
407 */
408 /*------------------------------------------------------------------*/
409
410 #define TRAMPOLINE_SIZE 64
411
412 gpointer
413 mono_arch_create_specific_trampoline (gpointer arg1,
414         MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
415 {
416   unsigned int *code, *buf, *tramp, *real_code;
417   int offset, size; //, jump_offset;
418   
419   ALPHA_DEBUG("mono_arch_create_specific_trampoline");
420   
421   tramp = (unsigned int *)mono_get_trampoline_code (tramp_type);
422   
423   code = buf = g_alloca (TRAMPOLINE_SIZE);
424   
425   /* push trampoline address */
426   //amd64_lea_membase (code, AMD64_R11, AMD64_RIP, -7);
427   //amd64_push_reg (code, AMD64_R11);
428   
429   // Allocate two qwords on stack
430   alpha_lda(code, alpha_sp, alpha_sp, -16);
431
432   // Save my stub address at 8(sp)
433   alpha_stq(code, alpha_pv, alpha_sp, 8);
434   
435   // Load arg1 into alpha_at
436   offset = (char *)code - (char *)buf;
437   offset += 2*4;
438   if (offset % 8)
439     {
440       alpha_nop(code);
441       offset += 4;
442     }
443   
444   alpha_ldq(code, alpha_at, alpha_pv, offset);
445   alpha_br(code, alpha_zero, 2);
446   
447   *code = (unsigned int)(((unsigned long)arg1) & 0xFFFFFFFF);
448   code++;
449   *code = (unsigned int)((((unsigned long)arg1) >> 32) & 0xFFFFFFFF);
450   code++;
451   
452   // Store arg1 on stack
453   alpha_stq(code, alpha_at, alpha_sp, 0);
454   
455   offset = (char *)code - (char *)buf;
456   offset += 2*4;
457   if (offset % 8)
458     {
459       alpha_nop(code);
460       offset += 4;
461     }
462   
463   alpha_ldq(code, alpha_at, alpha_pv, offset);
464   alpha_br(code, alpha_zero, 2);
465   
466   *code = (unsigned int)(((unsigned long)tramp) & 0xFFFFFFFF);
467   code++;
468   *code = (unsigned int)((((unsigned long)tramp) >> 32) & 0xFFFFFFFF);
469   code++;
470   
471   // Jump to trampoline
472   alpha_jmp(code, alpha_zero, alpha_at, 0);
473   
474   g_assert (((char *)code - (char *)buf) <= TRAMPOLINE_SIZE);
475   /*
476    * FIXME: Changing the size to code - buf causes strange crashes during
477    * mcs bootstrap.
478    */
479   real_code = mono_domain_code_reserve (domain, TRAMPOLINE_SIZE);
480   size = (char *)code - (char *)buf;
481   
482   memcpy (real_code, buf, size);
483  
484   ALPHA_PRINT 
485         g_debug("mono_arch_create_specific_trampoline: Target: %p, Arg1: %p",
486          real_code, arg1);
487   
488   if (code_len)
489     *code_len = size;
490   
491   mono_arch_flush_icache ((guchar *)real_code, size);
492   
493   return real_code;
494 }
495 /*========================= End of Function ========================*/
496
497 void
498 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
499 {
500   unsigned int *pcode = (unsigned int *)code;
501
502   ALPHA_DEBUG("mono_arch_nullify_class_init_trampoline");
503
504   // pcode[-2] ldq     t12,n(gp)
505   // pcode[-1] jsr     ra,(t12),0x20003efcb40
506   if ((pcode[-2] & 0xFFFF0000) == 0xa77d0000 &&
507        pcode[-1] == 0x6b5b4000)
508   {
509       // Put "unop" into call inst
510       pcode--;
511       alpha_nop(pcode);
512       alpha_nop(pcode);
513       alpha_nop(pcode);
514
515       mono_arch_flush_icache ((code-4), 3*4);
516   }
517   else
518       g_assert_not_reached ();
519 }
520
521 void
522 mono_arch_patch_callsite (guint8 *method_start, guint8 *code, guint8 *addr)
523 {
524   unsigned long *p = (unsigned int *)(code-12);
525   
526   unsigned int *pcode = (unsigned int *)code;
527   unsigned long gp = (unsigned long)pcode;
528   unsigned int call_addr_inst;
529   short high_offset, low_offset;
530
531   ALPHA_DEBUG("mono_arch_patch_callsite");
532
533   // Code points to the next instruction after the "jsr"
534   // In current implementation where we put jump addresses
535   // inside the code - we need to put "new" address into
536   // "code-12"
537
538   // With new method of using GOT we need to find address
539   // where function address is stored
540   // code points to two insts:
541   // ldah gp, high_offset(ra)
542   // lda gp, low_offset(gp)
543   // 
544
545   high_offset = *((short *)pcode);
546   low_offset = *((short *)(pcode + 1));
547
548   gp += 65536 * high_offset + low_offset;
549
550   call_addr_inst = *(pcode - 2);
551
552   // Check for load address instruction
553   // It should be ldq t12, offset(gp)
554   if ((call_addr_inst & 0xFFFF0000) == 0xA77D0000)
555   {
556     gp += *((short *)(pcode - 2));
557
558     p = (unsigned long *)gp;
559
560     ALPHA_PRINT g_debug("Patch callsite at %p to %p\n", p, addr);
561
562     // TODO - need to to interlocked update here
563     *p = (unsigned long)addr;
564   }
565 }
566
567 /*
568  * mono_arch_get_unbox_trampoline:
569  * @gsctx: the generic sharing context
570  * @m: method pointer
571  * @addr: pointer to native code for @m
572  *
573  * when value type methods are called through the vtable we need to unbox the
574  * this argument. This method returns a pointer to a trampoline which does
575  * unboxing before calling the method
576  */
577 gpointer
578 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
579 {
580   unsigned int *code, *start_code;
581   int this_reg = 16; //R16
582   int off;
583   MonoDomain *domain = mono_domain_get ();
584
585   ALPHA_DEBUG("mono_arch_get_unbox_trampoline");
586
587   if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
588     this_reg = 17; //R17
589
590   start_code = code = (unsigned int *)mono_domain_code_reserve (domain, 32);
591
592   // Adjust this by size of MonoObject
593   alpha_addq_(code, this_reg, sizeof(MonoObject), this_reg);  // 0
594   alpha_bsr(code, alpha_pv, 2);  // 4
595
596   *code = (unsigned int)(((unsigned long)addr) & 0xFFFFFFFF);
597   code++;
598   *code = (unsigned int)((((unsigned long)addr) >> 32) & 0xFFFFFFFF);
599   code++;
600
601   // Load "addr" into PV (R12)
602   alpha_ldq(code, alpha_pv, alpha_pv, 0);
603
604   // Jump to addr
605   alpha_jsr(code, alpha_zero, alpha_pv, 0);
606
607   g_assert (((char *)code - (char *)start_code) < 32);
608
609   mono_arch_flush_icache (start_code, (char *)code - (char *)start_code);
610
611   return start_code;
612 }
613
614 void
615 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
616 {
617         g_assert_not_reached ();
618 }
619
620 void
621 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
622 {
623         g_assert_not_reached ();
624 }
625
626 gpointer
627 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
628 {
629         /* FIXME: implement! */
630         g_assert_not_reached ();
631         return NULL;
632 }