2006-07-19 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / arch / alpha / tramp.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Create trampolines to invoke arbitrary functions.
4  * 
5  * Copyright (C) Ximian Inc.
6  *
7  * Authors: Laramie Leavitt (lar@leavitt.us)
8  *
9  *
10  */
11
12 /* A typical Alpha stack frame looks like this */
13 /*
14 fun:                         // called from outside the module.
15         ldgp gp,0(pv)        // load the global pointer
16 fun..ng:                     // called from inside the module.
17         lda sp, -SIZE( sp )  // grow the stack downwards.
18
19         stq ra, 0(sp)        // save the return address.
20
21         stq s0, 8(sp)        // callee-saved registers.
22         stq s1, 16(sp)       // ...
23
24         // Move the arguments to the argument registers...
25         
26         mov addr, pv         // Load the callee address
27         jsr  ra, (pv)        // call the method.
28         ldgp gp, 0(ra)       // restore gp
29
30         // return value is in v0
31         
32         ldq ra, 0(sp)        // free stack frame
33         ldq s0, 8(sp)        // restore callee-saved registers.
34         ldq s1, 16(sp)       
35         ldq sp, 32(sp)       // restore stack pointer
36
37         ret zero, (ra), 1    // return.
38
39 // min SIZE = 48
40 // our call must look like this.
41
42 call_func:
43         ldgp gp, 0(pv)
44 call_func..ng:
45         .prologue
46         lda sp, -SIZE(sp)  // grow stack SIZE bytes.
47         stq ra, SIZE-48(sp)   // store ra
48         stq fp, SIZE-40(sp)   // store fp (frame pointer)        
49         stq a0, SIZE-32(sp)   // store args. a0 = func
50         stq a1, SIZE-24(sp)   // a1 = retval
51         stq a2, SIZE-16(sp)   // a2 = this
52         stq a3, SIZE-8(sp)    // a3 = args
53         mov sp, fp            // set frame pointer
54         mov pv, a0            // func
55         
56         .calling_arg_this       
57         mov a1, a2
58         
59         .calling_arg_6plus      
60         ldq t0, POS(a3)
61         stq t0, 0(sp)        
62         ldq t1, POS(a3)
63         stq t1, 8(sp)        
64         ... SIZE-56 ...
65
66         mov zero,a1
67         mov zero,a2
68         mov zero,a3
69         mov zero,a4
70         mov zero,a5
71         
72         .do_call
73         jsr ra, (pv)    // call func
74         ldgp gp, 0(ra)  // restore gp.
75         mov v0, t1      // move return value into t1
76         
77         .do_store_retval
78         ldq t0, SIZE-24(fp) // load retval into t2
79         stl t1, 0(t0)       // store value.
80
81         .finished       
82         mov fp,sp
83         ldq ra,SIZE-48(sp)
84         ldq fp,SIZE-40(sp)
85         lda sp,SIZE(sp)
86         ret zero,(ra),1
87                         
88         
89 */
90 /*****************************************************/
91
92 #include "config.h"
93 #include <stdlib.h>
94 #include <string.h>
95
96 #include "alpha-codegen.h"
97
98 #include "mono/metadata/class.h"
99 #include "mono/metadata/tabledefs.h"
100 #include "mono/interpreter/interp.h"
101 #include "mono/metadata/appdomain.h"
102 #include "mono/metadata/debug-helpers.h"
103
104 #define AXP_GENERAL_REGS     6
105 #define AXP_MIN_STACK_SIZE   24
106 #define ARG_SIZE   sizeof(stackval)
107 #define ARG_LOC(x) (x * sizeof( stackval ) )
108
109 /*****************************************************/
110
111 /*                                                                  */
112 /*                void func (void (*callme)(), void *retval,        */
113 /*                           void *this_obj, stackval *arguments);  */
114 static inline unsigned int *
115 emit_prolog (unsigned int *pi, const gint SIZE, int hasthis )
116 {
117         unsigned int *p = (unsigned int *)pi;
118         // 9 instructions.
119         alpha_ldah( p, alpha_gp, alpha_pv, 0 );  
120         alpha_lda( p, alpha_gp, alpha_gp, 0 );     // ldgp gp, 0(pv)
121         alpha_lda( p, alpha_sp, alpha_sp, -((SIZE & 8) ? (SIZE+8) : SIZE) ); // grow stack down SIZE (align to 16 bytes like gcc does)
122         
123         /* TODO: we really don't need to store everything.
124            alpha_a1: We have to store this in order to return the retval.
125            
126            alpha_a0: func pointer can be moved directly to alpha_pv
127            alpha_a3: don't need args after we are finished.
128            alpha_a2: will be moved into alpha_a0... if hasthis is true.
129         */
130         /* store parameters on stack.*/
131         alpha_stq( p, alpha_ra, alpha_sp, (SIZE-24) ); // ra
132         alpha_stq( p, alpha_fp, alpha_sp, (SIZE-16) ); // fp
133         alpha_stq( p, alpha_a1, alpha_sp, (SIZE-8) );  // retval
134         
135         /* set the frame pointer */
136         alpha_mov1( p, alpha_sp, alpha_fp );
137        
138         /* move the args into t0, pv */
139         alpha_mov1( p, alpha_a0, alpha_pv );
140         alpha_mov1( p, alpha_a3, alpha_t0 );
141
142         // Move the this pointer into a0.       
143         if( hasthis )
144             alpha_mov1( p, alpha_a2, alpha_a0 );
145         return p;
146 }
147
148 static inline unsigned int *
149 emit_call( unsigned int *pi , const gint SIZE )
150 {
151         unsigned int *p = (unsigned int *)pi;
152
153         // 3 instructions
154         /* call func */
155         alpha_jsr( p, alpha_ra, alpha_pv, 0 );     // jsr ra, 0(pv)
156
157         /* reload the gp */
158         alpha_ldah( p, alpha_gp, alpha_ra, 0 );  
159         alpha_lda( p, alpha_gp, alpha_gp, 0 );     // ldgp gp, 0(ra)
160         
161         return p;
162 }
163
164 static inline unsigned int *
165 emit_store_return_default(unsigned int *pi, const gint SIZE )
166 {
167         // 2 instructions.
168         unsigned int *p = (unsigned int *)pi;
169         
170         /* TODO: This probably do different stuff based on the value.  
171            you know, like stq/l/w. and s/f.
172         */
173         alpha_ldq( p, alpha_t0, alpha_fp, (SIZE-8) );  // load void * retval
174         alpha_stq( p, alpha_v0, alpha_t0, 0 );       // store the result to *retval.
175         return p;
176 }
177
178
179 static inline unsigned int *
180 emit_epilog (unsigned int *pi, const gint SIZE )
181 {       
182         unsigned int *p = (unsigned int *)pi;
183
184         // 5 instructions.
185         alpha_mov1( p, alpha_fp, alpha_sp );
186
187         /* restore fp, ra, sp */
188         alpha_ldq( p, alpha_ra, alpha_sp, (SIZE-24) ); 
189         alpha_ldq( p, alpha_fp, alpha_sp, (SIZE-16) ); 
190         alpha_lda( p, alpha_sp, alpha_sp, ((SIZE & 8) ? (SIZE+8) : SIZE) ); 
191         
192         /* return */
193         alpha_ret( p, alpha_ra, 1 );
194         return p;
195 }
196
197 static void calculate_size(MonoMethodSignature *sig, int * INSTRUCTIONS, int * STACK )
198 {
199         int alpharegs;
200         
201         alpharegs = AXP_GENERAL_REGS - (sig->hasthis?1:0);
202
203         *STACK        = AXP_MIN_STACK_SIZE;
204         *INSTRUCTIONS = 20;  // Base: 20 instructions.
205         
206         if( sig->param_count - alpharegs > 0 )
207         {
208                 *STACK += ARG_SIZE * (sig->param_count - alpharegs );
209                 // plus 3 (potential) for each stack parameter. 
210                 *INSTRUCTIONS += ( sig->param_count - alpharegs ) * 3;
211                 // plus 2 (potential) for each register parameter. 
212                 *INSTRUCTIONS += ( alpharegs * 2 );
213         }
214         else
215         {
216                 // plus 2 (potential) for each register parameter. 
217                 *INSTRUCTIONS += ( sig->param_count * 2 );
218         }
219 }
220
221 MonoPIFunc
222 mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
223 {
224         unsigned int *p;
225         unsigned int *buffer;
226         MonoType* param;
227
228         int i, pos;
229         int alpharegs;
230         int hasthis;
231         int STACK_SIZE;
232         int BUFFER_SIZE;
233         int simple_type;
234         int regbase;
235         
236         // Set up basic stuff.  like has this.  
237         hasthis = !!sig->hasthis;
238         alpharegs = AXP_GENERAL_REGS - hasthis;
239         regbase  = hasthis?alpha_a1:alpha_a0 ;
240         
241         // Make a ballpark estimate for now.
242         calculate_size( sig, &BUFFER_SIZE, &STACK_SIZE );
243         
244         // convert to the correct number of bytes.
245         BUFFER_SIZE = BUFFER_SIZE * 4;
246
247         
248         // allocate.    
249         buffer = p = (unsigned int *)malloc(BUFFER_SIZE);
250         memset( buffer, 0, BUFFER_SIZE );
251         pos = 8 * (sig->param_count - alpharegs - 1);
252         
253         // Ok, start creating this thing.
254         p = emit_prolog( p, STACK_SIZE, hasthis );
255
256         // copy everything into the correct register/stack space
257         for (i = sig->param_count; --i >= 0; ) 
258         {
259                 param = sig->params [i];
260                 
261                 if( param->byref )
262                 {
263                         if( i >= alpharegs )
264                         {
265                                 // load into temp register, then store on the stack 
266                                 alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC( i ));
267                                 alpha_stq( p, alpha_t1, alpha_sp, pos );
268                                 pos -= 8;
269                         }
270                         else
271                         {
272                                 // load into register
273                                 alpha_ldq( p, (regbase + i), alpha_t0, ARG_LOC( i ) );
274                         }
275                 }
276                 else
277                 {
278                         simple_type = param->type;
279                         if( simple_type == MONO_TYPE_VALUETYPE )
280                         {
281                                 if (param->data.klass->enumtype)
282                                         simple_type = param->data.klass->enum_basetype->type;
283                         }
284                         
285                         switch (simple_type) 
286                         {
287                         case MONO_TYPE_VOID:
288                                 break;
289                         case MONO_TYPE_BOOLEAN:
290                         case MONO_TYPE_CHAR:
291                         case MONO_TYPE_I1:
292                         case MONO_TYPE_U1:
293                         case MONO_TYPE_I2:
294                         case MONO_TYPE_U2:
295                         case MONO_TYPE_I4:
296                         case MONO_TYPE_U4:
297                                 // 4 bytes - need to sign-extend (stackvals are not extended)
298                                 if( i >= alpharegs )
299                                 {
300                                         // load into temp register, then store on the stack
301                                         alpha_ldl( p, alpha_t1, alpha_t0, ARG_LOC( i ) );
302                                         alpha_stq( p, alpha_t1, alpha_sp, pos );
303                                         pos -= 8;
304                                 }
305                                 else
306                                 {
307                                         // load into register
308                                         alpha_ldl( p, (regbase + i), alpha_t0, (ARG_LOC(i)) );
309                                 }
310                                 break;
311                         case MONO_TYPE_I:
312                         case MONO_TYPE_U:
313                         case MONO_TYPE_PTR:
314                         case MONO_TYPE_CLASS:
315                         case MONO_TYPE_OBJECT:
316                         case MONO_TYPE_SZARRAY:
317                         case MONO_TYPE_STRING:
318                         case MONO_TYPE_I8:
319                                 // 8 bytes
320                                 if( i >= alpharegs )
321                                 {
322                                         // load into temp register, then store on the stack
323                                         alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC( i ) );
324                                         alpha_stq( p, alpha_t1, alpha_sp, pos );
325                                         pos -= 8;
326                                 }
327                                 else
328                                 {
329                                         // load into register
330                                         alpha_ldq( p, (regbase + i), alpha_t0, ARG_LOC(i) );
331                                 }
332                                 break;
333                         case MONO_TYPE_R4:
334                         case MONO_TYPE_R8:
335                                 /*
336                                 // floating point... Maybe this does the correct thing.
337                                 if( i > alpharegs )
338                                 {
339                                         alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC( i ) );
340                                         alpha_cpys( p, alpha_ft1, alpha_ft1, alpha_ft2 );
341                                         alpha_stt( p, alpha_ft2, alpha_sp, pos );
342                                         pos -= 8;
343                                 }
344                                 else
345                                 {
346                                         alpha_ldq( p, alpha_t1, alpha_t0, ARG_LOC(i) );
347                                         alpha_cpys( p, alpha_ft1, alpha_ft1, alpha_fa0 + i + hasthis );
348                                 }
349                                 break;
350                                 */
351                         case MONO_TYPE_VALUETYPE:
352                                 g_error ("Not implemented: ValueType as parameter to delegate." );
353                                 break;
354                         default:
355                                 g_error( "Not implemented: 0x%x.", simple_type );
356                                 break;  
357                         }
358                 }
359         }
360         
361         // Now call the function and store the return parameter.
362         p = emit_call( p, STACK_SIZE );
363         p = emit_store_return_default( p, STACK_SIZE );
364         p = emit_epilog( p, STACK_SIZE );
365
366         if( p > buffer + BUFFER_SIZE )
367                 g_error( "Buffer overflow: got 0x%lx, expected <=0x%x.", (long)(p-buffer), BUFFER_SIZE );
368
369         /* flush instruction cache to see trampoline code */
370         asm volatile("imb":::"memory");
371         
372         return (MonoPIFunc)buffer;
373 }
374
375 void *
376 mono_arch_create_method_pointer (MonoMethod *method)
377 {
378         g_error ("Unsupported arch");
379         return NULL;
380 }