2009-06-12 Bill Holmes <billholmes54@gmail.com>
[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   mono_jit_stats.method_trampolines++;
489   
490   if (code_len)
491     *code_len = size;
492   
493   mono_arch_flush_icache ((guchar *)real_code, size);
494   
495   return real_code;
496 }
497 /*========================= End of Function ========================*/
498
499 void
500 mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs)
501 {
502   unsigned int *pcode = (unsigned int *)code;
503
504   ALPHA_DEBUG("mono_arch_nullify_class_init_trampoline");
505
506   // pcode[-2] ldq     t12,n(gp)
507   // pcode[-1] jsr     ra,(t12),0x20003efcb40
508   if ((pcode[-2] & 0xFFFF0000) == 0xa77d0000 &&
509        pcode[-1] == 0x6b5b4000)
510   {
511       // Put "unop" into call inst
512       pcode--;
513       alpha_nop(pcode);
514       alpha_nop(pcode);
515       alpha_nop(pcode);
516
517       mono_arch_flush_icache ((code-4), 3*4);
518   }
519   else
520       g_assert_not_reached ();
521 }
522
523 void
524 mono_arch_patch_callsite (guint8 *method_start, guint8 *code, guint8 *addr)
525 {
526   unsigned long *p = (unsigned int *)(code-12);
527   
528   unsigned int *pcode = (unsigned int *)code;
529   unsigned long gp = (unsigned long)pcode;
530   unsigned int call_addr_inst;
531   short high_offset, low_offset;
532
533   ALPHA_DEBUG("mono_arch_patch_callsite");
534
535   // Code points to the next instruction after the "jsr"
536   // In current implementation where we put jump addresses
537   // inside the code - we need to put "new" address into
538   // "code-12"
539
540   // With new method of using GOT we need to find address
541   // where function address is stored
542   // code points to two insts:
543   // ldah gp, high_offset(ra)
544   // lda gp, low_offset(gp)
545   // 
546
547   high_offset = *((short *)pcode);
548   low_offset = *((short *)(pcode + 1));
549
550   gp += 65536 * high_offset + low_offset;
551
552   call_addr_inst = *(pcode - 2);
553
554   // Check for load address instruction
555   // It should be ldq t12, offset(gp)
556   if ((call_addr_inst & 0xFFFF0000) == 0xA77D0000)
557   {
558     gp += *((short *)(pcode - 2));
559
560     p = (unsigned long *)gp;
561
562     ALPHA_PRINT g_debug("Patch callsite at %p to %p\n", p, addr);
563
564     // TODO - need to to interlocked update here
565     *p = (unsigned long)addr;
566   }
567 }
568
569 /*
570  * mono_arch_get_unbox_trampoline:
571  * @gsctx: the generic sharing context
572  * @m: method pointer
573  * @addr: pointer to native code for @m
574  *
575  * when value type methods are called through the vtable we need to unbox the
576  * this argument. This method returns a pointer to a trampoline which does
577  * unboxing before calling the method
578  */
579 gpointer
580 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
581 {
582   unsigned int *code, *start_code;
583   int this_reg = 16; //R16
584   int off;
585   MonoDomain *domain = mono_domain_get ();
586
587   ALPHA_DEBUG("mono_arch_get_unbox_trampoline");
588
589   if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
590     this_reg = 17; //R17
591
592   start_code = code = (unsigned int *)mono_domain_code_reserve (domain, 32);
593
594   // Adjust this by size of MonoObject
595   alpha_addq_(code, this_reg, sizeof(MonoObject), this_reg);  // 0
596   alpha_bsr(code, alpha_pv, 2);  // 4
597
598   *code = (unsigned int)(((unsigned long)addr) & 0xFFFFFFFF);
599   code++;
600   *code = (unsigned int)((((unsigned long)addr) >> 32) & 0xFFFFFFFF);
601   code++;
602
603   // Load "addr" into PV (R12)
604   alpha_ldq(code, alpha_pv, alpha_pv, 0);
605
606   // Jump to addr
607   alpha_jsr(code, alpha_zero, alpha_pv, 0);
608
609   g_assert (((char *)code - (char *)start_code) < 32);
610
611   mono_arch_flush_icache (start_code, (char *)code - (char *)start_code);
612
613   return start_code;
614 }
615
616 void
617 mono_arch_nullify_plt_entry (guint8 *code)
618 {
619         g_assert_not_reached ();
620 }
621
622 void
623 mono_arch_patch_plt_entry (guint8 *code, guint8 *addr)
624 {
625         g_assert_not_reached ();
626 }
627
628 gpointer
629 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
630 {
631         /* FIXME: implement! */
632         g_assert_not_reached ();
633         return NULL;
634 }