2010-03-30 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / arch / amd64 / tramp.c
blob5a4f9a9ed2b7ccf0662627509093d7a11eef8b55
1 /*
2 * Create trampolines to invoke arbitrary functions.
3 *
4 * Copyright (C) Ximian Inc.
5 *
6 * Author:
7 * Zalman Stern
8 * Based on code by:
9 * Paolo Molaro (lupus@ximian.com)
10 * Dietmar Maurer (dietmar@ximian.com)
12 * To understand this code, one will want to the calling convention section of the ABI sepc at:
13 * http://x86-64.org/abi.pdf
14 * and the AMD64 architecture docs found at amd.com .
17 #include "config.h"
18 #include <stdlib.h>
19 #include <string.h>
20 #include "amd64-codegen.h"
21 #include "mono/metadata/class.h"
22 #include "mono/metadata/tabledefs.h"
23 #include "mono/interpreter/interp.h"
24 #include "mono/metadata/appdomain.h"
25 #include "mono/metadata/marshal.h"
28 * The resulting function takes the form:
29 * void func (void (*callme)(), void *retval, void *this_obj, stackval *arguments);
31 #define FUNC_ADDR_POS 8
32 #define RETVAL_POS 12
33 #define THIS_POS 16
34 #define ARGP_POS 20
35 #define LOC_POS -4
37 #define ARG_SIZE sizeof (stackval)
39 #define MAX_INT_ARG_REGS 6
40 #define MAX_FLOAT_ARG_REGS 8
42 // TODO get these right. They are upper bounds anyway, so it doesn't much matter.
43 #define PUSH_INT_STACK_ARG_SIZE 16
44 #define MOVE_INT_REG_ARG_SIZE 16
45 #define PUSH_FLOAT_STACK_ARG_SIZE 16
46 #define MOVE_FLOAT_REG_ARG_SIZE 16
47 #define COPY_STRUCT_STACK_ARG_SIZE 16
49 /* Maps an argument number (starting at 0) to the register it is passed in (if it fits).
50 * E.g. int foo(int bar, int quux) has the foo arg in RDI and the quux arg in RSI
51 * There is no such map for floating point args as they go in XMM0-XMM7 in order and thus the
52 * index is the register number.
54 static int int_arg_regs[] = { AMD64_RDI, AMD64_RSI, AMD64_RDX, AMD64_RCX, AMD64_R8, AMD64_R9 };
56 /* This next block of code resolves the ABI rules for passing structures in the argument registers.
57 * These basically amount to "Use up to two registers if they are all integer or all floating point.
58 * If the structure is bigger than two registers or would be in one integer register and one floating point,
59 * it is passed in memory instead.
61 * It is possible this code needs to be recursive to be correct in the case when one of the structure members
62 * is itself a structure.
64 * The 80-bit floating point stuff is ignored.
66 typedef enum {
67 ARG_IN_MEMORY,
68 ARG_IN_INT_REGS,
69 ARG_IN_FLOAT_REGS
70 } struct_arg_type;
72 static struct_arg_type compute_arg_type(MonoType *type)
74 guint32 simpletype = type->type;
76 switch (simpletype) {
77 case MONO_TYPE_BOOLEAN:
78 case MONO_TYPE_CHAR:
79 case MONO_TYPE_I1:
80 case MONO_TYPE_U1:
81 case MONO_TYPE_I2:
82 case MONO_TYPE_U2:
83 case MONO_TYPE_I4:
84 case MONO_TYPE_U4:
85 case MONO_TYPE_I:
86 case MONO_TYPE_U:
87 case MONO_TYPE_PTR:
88 case MONO_TYPE_SZARRAY:
89 case MONO_TYPE_CLASS:
90 case MONO_TYPE_OBJECT:
91 case MONO_TYPE_STRING:
92 case MONO_TYPE_I8:
93 return ARG_IN_INT_REGS;
94 break;
95 case MONO_TYPE_VALUETYPE: {
96 if (type->data.klass->enumtype)
97 return ARG_IN_INT_REGS;
98 return ARG_IN_MEMORY;
99 break;
101 case MONO_TYPE_R4:
102 case MONO_TYPE_R8:
103 return ARG_IN_FLOAT_REGS;
104 break;
105 default:
106 g_error ("Can't trampoline 0x%x", type->type);
109 return ARG_IN_MEMORY;
112 static struct_arg_type value_type_info(MonoClass *klass, int *native_size, int *regs_used, int *offset1, int *size1, int *offset2, int *size2)
114 MonoMarshalType *info = mono_marshal_load_type_info (klass);
116 *native_size = info->native_size;
118 if (info->native_size > 8 || info->num_fields > 2)
120 *regs_used = 0;
121 *offset1 = -1;
122 *offset2 = -1;
123 return ARG_IN_MEMORY;
126 if (info->num_fields == 1)
128 struct_arg_type result = compute_arg_type(info->fields[0].field->type);
129 if (result != ARG_IN_MEMORY)
131 *regs_used = 1;
132 *offset1 = info->fields[0].offset;
133 *size1 = mono_marshal_type_size (info->fields[0].field->type, info->fields[0].mspec, NULL, 1, 1);
135 else
137 *regs_used = 0;
138 *offset1 = -1;
141 *offset2 = -1;
142 return result;
145 struct_arg_type result1 = compute_arg_type(info->fields[0].field->type);
146 struct_arg_type result2 = compute_arg_type(info->fields[0].field->type);
148 if (result1 == result2 && result1 != ARG_IN_MEMORY)
150 *regs_used = 2;
151 *offset1 = info->fields[0].offset;
152 *size1 = mono_marshal_type_size (info->fields[0].field->type, info->fields[0].mspec, NULL, 1, 1);
153 *offset2 = info->fields[1].offset;
154 *size2 = mono_marshal_type_size (info->fields[1].field->type, info->fields[1].mspec, NULL, 1, 1);
155 return result1;
158 return ARG_IN_MEMORY;
161 MonoPIFunc
162 mono_arch_create_trampoline (MonoMethodSignature *sig, gboolean string_ctor)
164 unsigned char *p, *code_buffer;
165 guint32 stack_size = 0, code_size = 50;
166 guint32 arg_pos, simpletype;
167 int i;
168 static GHashTable *cache = NULL;
169 MonoPIFunc res;
171 guint32 int_arg_regs_used = 0;
172 guint32 float_arg_regs_used = 0;
173 guint32 next_int_arg_reg = 0;
174 guint32 next_float_arg_reg = 0;
175 /* Indicates that the return value is filled in inside the called function. */
176 int retval_implicit = 0;
177 char *arg_in_reg_bitvector; /* A set index by argument number saying if it is in a register
178 (integer or floating point according to type) */
180 if (!cache)
181 cache = g_hash_table_new ((GHashFunc)mono_signature_hash,
182 (GCompareFunc)mono_metadata_signature_equal);
184 if ((res = (MonoPIFunc)g_hash_table_lookup (cache, sig)))
185 return res;
187 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype) {
188 int_arg_regs_used++;
189 code_size += MOVE_INT_REG_ARG_SIZE;
192 if (sig->hasthis) {
193 int_arg_regs_used++;
194 code_size += MOVE_INT_REG_ARG_SIZE;
197 /* Run through stuff to calculate code size and argument bytes that will be pushed on stack (stack_size). */
198 for (i = 0; i < sig->param_count; ++i) {
199 if (sig->params [i]->byref)
200 simpletype = MONO_TYPE_PTR;
201 else
202 simpletype = sig->params [i]->type;
203 enum_calc_size:
204 switch (simpletype) {
205 case MONO_TYPE_BOOLEAN:
206 case MONO_TYPE_CHAR:
207 case MONO_TYPE_I1:
208 case MONO_TYPE_U1:
209 case MONO_TYPE_I2:
210 case MONO_TYPE_U2:
211 case MONO_TYPE_I4:
212 case MONO_TYPE_U4:
213 case MONO_TYPE_I:
214 case MONO_TYPE_U:
215 case MONO_TYPE_PTR:
216 case MONO_TYPE_SZARRAY:
217 case MONO_TYPE_CLASS:
218 case MONO_TYPE_OBJECT:
219 case MONO_TYPE_STRING:
220 case MONO_TYPE_I8:
221 if (int_arg_regs_used++ > MAX_INT_ARG_REGS) {
222 stack_size += 8;
223 code_size += PUSH_INT_STACK_ARG_SIZE;
225 else
226 code_size += MOVE_INT_REG_ARG_SIZE;
227 break;
228 case MONO_TYPE_VALUETYPE: {
229 int size;
230 int arg_type;
231 int regs_used;
232 int offset1;
233 int size1;
234 int offset2;
235 int size2;
237 if (sig->params [i]->data.klass->enumtype) {
238 simpletype = sig->params [i]->data.klass->enum_basetype->type;
239 goto enum_calc_size;
242 arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
243 if (arg_type == ARG_IN_INT_REGS &&
244 (int_arg_regs_used + regs_used) <= MAX_INT_ARG_REGS)
246 code_size += MOVE_INT_REG_ARG_SIZE;
247 int_arg_regs_used += regs_used;
248 break;
251 if (arg_type == ARG_IN_FLOAT_REGS &&
252 (float_arg_regs_used + regs_used) <= MAX_FLOAT_ARG_REGS)
254 code_size += MOVE_FLOAT_REG_ARG_SIZE;
255 float_arg_regs_used += regs_used;
256 break;
259 /* Else item is in memory. */
261 stack_size += size + 7;
262 stack_size &= ~7;
263 code_size += COPY_STRUCT_STACK_ARG_SIZE;
265 break;
267 case MONO_TYPE_R4:
268 case MONO_TYPE_R8:
269 if (float_arg_regs_used++ > MAX_FLOAT_ARG_REGS) {
270 stack_size += 8;
271 code_size += PUSH_FLOAT_STACK_ARG_SIZE;
273 else
274 code_size += MOVE_FLOAT_REG_ARG_SIZE;
275 break;
276 default:
277 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
281 * FIXME: take into account large return values.
282 * (Comment carried over from IA32 code. Not sure what it means :-)
285 code_buffer = p = alloca (code_size);
288 * Standard function prolog.
290 amd64_push_reg (p, AMD64_RBP);
291 amd64_mov_reg_reg (p, AMD64_RBP, AMD64_RSP, 8);
293 * and align to 16 byte boundary...
296 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
297 MonoClass *klass = sig->ret->data.klass;
298 if (!klass->enumtype) {
299 retval_implicit = 1;
303 if (sig->ret->byref || string_ctor || !(retval_implicit || sig->ret->type == MONO_TYPE_VOID)) {
304 /* Push the retval register so it is saved across the call. It will be addressed via RBP later. */
305 amd64_push_reg (p, AMD64_RSI);
306 stack_size += 8;
309 /* Ensure stack is 16 byte aligned when entering called function as required by calling convention.
310 * Getting this wrong results in a general protection fault on an SSE load or store somewhere in the
311 * code called under the trampoline.
313 if ((stack_size & 15) != 0)
314 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, 16 - (stack_size & 15));
317 * On entry to generated function:
318 * RDI has target function address
319 * RSI has return value location address
320 * RDX has this pointer address
321 * RCX has the pointer to the args array.
323 * Inside the stub function:
324 * R10 holds the pointer to the args
325 * R11 holds the target function address.
326 * The return value address is pushed on the stack.
327 * The this pointer is moved into the first arg register at the start.
329 * Optimization note: we could keep the args pointer in RCX and then
330 * load over itself at the end. Ditto the callee addres could be left in RDI in some cases.
333 /* Move args pointer to temp register. */
334 amd64_mov_reg_reg (p, AMD64_R10, AMD64_RCX, 8);
335 amd64_mov_reg_reg (p, AMD64_R11, AMD64_RDI, 8);
337 /* First args register gets return value pointer, if need be.
338 * Note that "byref" equal true means the called function returns a pointer.
340 if (retval_implicit) {
341 amd64_mov_reg_reg (p, int_arg_regs[next_int_arg_reg], AMD64_RSI, 8);
342 next_int_arg_reg++;
345 /* this pointer goes in next args register. */
346 if (sig->hasthis) {
347 amd64_mov_reg_reg (p, int_arg_regs[next_int_arg_reg], AMD64_RDX, 8);
348 next_int_arg_reg++;
352 * Generate code to handle arguments in registers. Stack arguments will happen in a loop after this.
354 arg_in_reg_bitvector = (char *)alloca((sig->param_count + 7) / 8);
355 memset(arg_in_reg_bitvector, 0, (sig->param_count + 7) / 8);
357 /* First, load all the arguments that are passed in registers into the appropriate registers.
358 * Below there is another loop to handle arguments passed on the stack.
360 for (i = 0; i < sig->param_count; i++) {
361 arg_pos = ARG_SIZE * i;
363 if (sig->params [i]->byref)
364 simpletype = MONO_TYPE_PTR;
365 else
366 simpletype = sig->params [i]->type;
367 enum_marshal:
368 switch (simpletype) {
369 case MONO_TYPE_BOOLEAN:
370 case MONO_TYPE_I1:
371 case MONO_TYPE_U1:
372 case MONO_TYPE_I2:
373 case MONO_TYPE_U2:
374 case MONO_TYPE_CHAR:
375 case MONO_TYPE_I4:
376 case MONO_TYPE_U4:
377 case MONO_TYPE_I:
378 case MONO_TYPE_U:
379 case MONO_TYPE_PTR:
380 case MONO_TYPE_OBJECT:
381 case MONO_TYPE_STRING:
382 case MONO_TYPE_SZARRAY:
383 case MONO_TYPE_I8:
384 case MONO_TYPE_U8:
385 case MONO_TYPE_CLASS:
386 if (next_int_arg_reg < MAX_INT_ARG_REGS) {
387 amd64_mov_reg_membase (p, int_arg_regs[next_int_arg_reg], AMD64_R10, arg_pos, 8);
388 next_int_arg_reg++;
389 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
391 break;
392 case MONO_TYPE_R4:
393 if (next_float_arg_reg < MAX_FLOAT_ARG_REGS) {
394 amd64_movss_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos);
395 next_float_arg_reg++;
396 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
398 break;
399 case MONO_TYPE_R8:
400 if (next_float_arg_reg < MAX_FLOAT_ARG_REGS) {
401 amd64_movsd_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos);
402 next_float_arg_reg++;
403 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
405 break;
406 case MONO_TYPE_VALUETYPE: {
407 if (!sig->params [i]->data.klass->enumtype) {
408 int size;
409 int arg_type;
410 int regs_used;
411 int offset1;
412 int size1;
413 int offset2;
414 int size2;
416 arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
418 if (arg_type == ARG_IN_INT_REGS &&
419 (next_int_arg_reg + regs_used) <= MAX_INT_ARG_REGS)
421 amd64_mov_reg_membase (p, int_arg_regs[next_int_arg_reg], AMD64_R10, arg_pos + offset1, size1);
422 next_int_arg_reg++;
423 if (regs_used > 1)
425 amd64_mov_reg_membase (p, int_arg_regs[next_int_arg_reg], AMD64_R10, arg_pos + offset2, size2);
426 next_int_arg_reg++;
428 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
429 break;
432 if (arg_type == ARG_IN_FLOAT_REGS &&
433 (next_float_arg_reg + regs_used) <= MAX_FLOAT_ARG_REGS)
435 if (size1 == 4)
436 amd64_movss_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset1);
437 else
438 amd64_movsd_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset1);
439 next_float_arg_reg++;
441 if (regs_used > 1)
443 if (size2 == 4)
444 amd64_movss_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset2);
445 else
446 amd64_movsd_reg_membase (p, next_float_arg_reg, AMD64_R10, arg_pos + offset2);
447 next_float_arg_reg++;
449 arg_in_reg_bitvector[i >> 3] |= (1 << (i & 7));
450 break;
453 /* Structs in memory are handled in the next loop. */
454 } else {
455 /* it's an enum value */
456 simpletype = sig->params [i]->data.klass->enum_basetype->type;
457 goto enum_marshal;
459 break;
461 default:
462 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
466 /* Handle stack arguments, pushing the rightmost argument first. */
467 for (i = sig->param_count; i > 0; --i) {
468 arg_pos = ARG_SIZE * (i - 1);
469 if (sig->params [i - 1]->byref)
470 simpletype = MONO_TYPE_PTR;
471 else
472 simpletype = sig->params [i - 1]->type;
473 enum_marshal2:
474 switch (simpletype) {
475 case MONO_TYPE_BOOLEAN:
476 case MONO_TYPE_I1:
477 case MONO_TYPE_U1:
478 case MONO_TYPE_I2:
479 case MONO_TYPE_U2:
480 case MONO_TYPE_CHAR:
481 case MONO_TYPE_I4:
482 case MONO_TYPE_U4:
483 case MONO_TYPE_I:
484 case MONO_TYPE_U:
485 case MONO_TYPE_PTR:
486 case MONO_TYPE_OBJECT:
487 case MONO_TYPE_STRING:
488 case MONO_TYPE_SZARRAY:
489 case MONO_TYPE_I8:
490 case MONO_TYPE_U8:
491 case MONO_TYPE_CLASS:
492 if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0) {
493 amd64_push_membase (p, AMD64_R10, arg_pos);
495 break;
496 case MONO_TYPE_R4:
497 if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0) {
498 amd64_push_membase (p, AMD64_R10, arg_pos);
500 break;
501 case MONO_TYPE_R8:
502 if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0) {
503 amd64_push_membase (p, AMD64_R10, arg_pos);
505 break;
506 case MONO_TYPE_VALUETYPE:
507 if (!sig->params [i - 1]->data.klass->enumtype) {
508 if ((arg_in_reg_bitvector[(i - 1) >> 3] & (1 << ((i - 1) & 7))) == 0)
510 int ss = mono_class_native_size (sig->params [i - 1]->data.klass, NULL);
511 ss += 7;
512 ss &= ~7;
514 amd64_alu_reg_imm(p, X86_SUB, AMD64_RSP, ss);
515 /* Count register */
516 amd64_mov_reg_imm(p, AMD64_RCX, ss);
517 /* Source register */
518 amd64_lea_membase(p, AMD64_RSI, AMD64_R10, arg_pos);
519 /* Dest register */
520 amd64_mov_reg_reg(p, AMD64_RDI, AMD64_RSP, 8);
522 /* AMD64 calling convention guarantees direction flag is clear at call boundary. */
523 x86_prefix(p, AMD64_REX(AMD64_REX_W));
524 x86_prefix(p, X86_REP_PREFIX);
525 x86_movsb(p);
527 } else {
528 /* it's an enum value */
529 simpletype = sig->params [i - 1]->data.klass->enum_basetype->type;
530 goto enum_marshal2;
532 break;
533 default:
534 g_error ("Can't trampoline 0x%x", sig->params [i - 1]->type);
538 /* TODO: Set RAL to number of XMM registers used in case this is a varags function? */
541 * Insert call to function
543 amd64_call_reg (p, AMD64_R11);
545 if (sig->ret->byref || string_ctor || !(retval_implicit || sig->ret->type == MONO_TYPE_VOID)) {
546 amd64_mov_reg_membase(p, AMD64_RSI, AMD64_RBP, -8, 8);
549 * Handle retval.
550 * Small integer and pointer values are in EAX.
551 * Long integers are in EAX:EDX.
552 * FP values are on the FP stack.
555 if (sig->ret->byref || string_ctor) {
556 simpletype = MONO_TYPE_PTR;
557 } else {
558 simpletype = sig->ret->type;
560 enum_retvalue:
561 switch (simpletype) {
562 case MONO_TYPE_BOOLEAN:
563 case MONO_TYPE_I1:
564 case MONO_TYPE_U1:
565 amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 1);
566 break;
567 case MONO_TYPE_CHAR:
568 case MONO_TYPE_I2:
569 case MONO_TYPE_U2:
570 amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 2);
571 break;
572 case MONO_TYPE_I4:
573 case MONO_TYPE_U4:
574 case MONO_TYPE_I:
575 case MONO_TYPE_U:
576 case MONO_TYPE_CLASS:
577 case MONO_TYPE_OBJECT:
578 case MONO_TYPE_SZARRAY:
579 case MONO_TYPE_ARRAY:
580 case MONO_TYPE_STRING:
581 case MONO_TYPE_PTR:
582 amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 8);
583 break;
584 case MONO_TYPE_R4:
585 amd64_movss_regp_reg (p, AMD64_RSI, AMD64_XMM0);
586 break;
587 case MONO_TYPE_R8:
588 amd64_movsd_regp_reg (p, AMD64_RSI, AMD64_XMM0);
589 break;
590 case MONO_TYPE_I8:
591 amd64_mov_regp_reg (p, AMD64_RSI, X86_EAX, 8);
592 break;
593 case MONO_TYPE_VALUETYPE: {
594 int size;
595 int arg_type;
596 int regs_used;
597 int offset1;
598 int size1;
599 int offset2;
600 int size2;
602 if (sig->ret->data.klass->enumtype) {
603 simpletype = sig->ret->data.klass->enum_basetype->type;
604 goto enum_retvalue;
607 arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
609 if (arg_type == ARG_IN_INT_REGS)
611 amd64_mov_membase_reg (p, AMD64_RSI, offset1, AMD64_RAX, size1);
612 if (regs_used > 1)
613 amd64_mov_membase_reg (p, AMD64_RSI, offset2, AMD64_RDX, size2);
614 break;
617 if (arg_type == ARG_IN_FLOAT_REGS)
619 if (size1 == 4)
620 amd64_movss_membase_reg (p, AMD64_RSI, offset1, AMD64_XMM0);
621 else
622 amd64_movsd_membase_reg (p, AMD64_RSI, offset1, AMD64_XMM0);
624 if (regs_used > 1)
626 if (size2 == 4)
627 amd64_movss_membase_reg (p, AMD64_RSI, offset2, AMD64_XMM1);
628 else
629 amd64_movsd_membase_reg (p, AMD64_RSI, offset2, AMD64_XMM1);
631 break;
634 /* Else result should have been stored in place already. */
635 break;
637 case MONO_TYPE_VOID:
638 break;
639 default:
640 g_error ("Can't handle as return value 0x%x", sig->ret->type);
644 * Standard epilog.
646 amd64_leave (p);
647 amd64_ret (p);
649 g_assert (p - code_buffer < code_size);
650 res = (MonoPIFunc)g_memdup (code_buffer, p - code_buffer);
652 g_hash_table_insert (cache, sig, res);
654 return res;
658 * Returns a pointer to a native function that can be used to
659 * call the specified method.
660 * The function created will receive the arguments according
661 * to the call convention specified in the method.
662 * This function works by creating a MonoInvocation structure,
663 * filling the fields in and calling ves_exec_method on it.
664 * Still need to figure out how to handle the exception stuff
665 * across the managed/unmanaged boundary.
667 void *
668 mono_arch_create_method_pointer (MonoMethod *method)
670 MonoMethodSignature *sig;
671 MonoJitInfo *ji;
672 unsigned char *p, *code_buffer;
673 guint32 simpletype;
674 gint32 local_size;
675 gint32 stackval_pos;
676 gint32 mono_invocation_pos;
677 int i, cpos;
678 int *vtbuf;
679 int *rbpoffsets;
680 int int_arg_regs_used = 0;
681 int float_arg_regs_used = 0;
682 int stacked_args_size = 0; /* bytes of register passed arguments pushed on stack for safe keeping. Used to get alignment right. */
683 int next_stack_arg_rbp_offset = 16;
684 int retval_ptr_rbp_offset = 0;
685 int this_reg = -1; /* Remember register this ptr is in. */
688 * If it is a static P/Invoke method, we can just return the pointer
689 * to the method implementation.
691 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL && ((MonoMethodPInvoke*) method)->addr) {
692 ji = g_new0 (MonoJitInfo, 1);
693 ji->method = method;
694 ji->code_size = 1;
695 ji->code_start = ((MonoMethodPInvoke*) method)->addr;
697 mono_jit_info_table_add (mono_get_root_domain (), ji);
698 return ((MonoMethodPInvoke*) method)->addr;
701 sig = method->signature;
703 code_buffer = p = alloca (512); /* FIXME: check for overflows... */
704 vtbuf = alloca (sizeof(int)*sig->param_count);
705 rbpoffsets = alloca (sizeof(int)*sig->param_count);
709 * Standard function prolog.
711 amd64_push_reg (p, AMD64_RBP);
712 amd64_mov_reg_reg (p, AMD64_RBP, AMD64_RSP, 8);
714 /* If there is an implicit return value pointer in the first args reg, save it now so
715 * the result can be stored through the pointer at the end.
717 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref && !sig->ret->data.klass->enumtype)
719 amd64_push_reg (p, int_arg_regs[int_arg_regs_used]);
720 int_arg_regs_used++;
721 stacked_args_size += 8;
722 retval_ptr_rbp_offset = -stacked_args_size;
726 * If there is a this pointer, remember the number of the register it is in.
728 if (sig->hasthis) {
729 this_reg = int_arg_regs[int_arg_regs_used++];
732 /* Put all arguments passed in registers on the stack.
733 * Record offsets from RBP to each argument.
735 cpos = 0;
737 for (i = 0; i < sig->param_count; i++) {
738 if (sig->params [i]->byref)
739 simpletype = MONO_TYPE_PTR;
740 else
741 simpletype = sig->params [i]->type;
742 enum_calc_size:
743 switch (simpletype) {
744 case MONO_TYPE_BOOLEAN:
745 case MONO_TYPE_CHAR:
746 case MONO_TYPE_I1:
747 case MONO_TYPE_U1:
748 case MONO_TYPE_I2:
749 case MONO_TYPE_U2:
750 case MONO_TYPE_I4:
751 case MONO_TYPE_U4:
752 case MONO_TYPE_I:
753 case MONO_TYPE_U:
754 case MONO_TYPE_PTR:
755 case MONO_TYPE_SZARRAY:
756 case MONO_TYPE_CLASS:
757 case MONO_TYPE_OBJECT:
758 case MONO_TYPE_STRING:
759 case MONO_TYPE_I8:
760 if (int_arg_regs_used < MAX_INT_ARG_REGS) {
761 amd64_push_reg (p, int_arg_regs[int_arg_regs_used]);
762 int_arg_regs_used++;
763 stacked_args_size += 8;
764 rbpoffsets[i] = -stacked_args_size;
766 else
768 rbpoffsets[i] = next_stack_arg_rbp_offset;
769 next_stack_arg_rbp_offset += 8;
771 break;
772 case MONO_TYPE_VALUETYPE: {
773 if (sig->params [i]->data.klass->enumtype) {
774 simpletype = sig->params [i]->data.klass->enum_basetype->type;
775 goto enum_calc_size;
777 else
779 int size;
780 int arg_type;
781 int regs_used;
782 int offset1;
783 int size1;
784 int offset2;
785 int size2;
787 arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
789 if (arg_type == ARG_IN_INT_REGS &&
790 (int_arg_regs_used + regs_used) <= MAX_INT_ARG_REGS)
792 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, size);
793 stacked_args_size += size;
794 rbpoffsets[i] = stacked_args_size;
796 amd64_mov_reg_membase (p, int_arg_regs[int_arg_regs_used], AMD64_RSP, offset1, size1);
797 int_arg_regs_used++;
798 if (regs_used > 1)
800 amd64_mov_reg_membase (p, int_arg_regs[int_arg_regs_used], AMD64_RSP, offset2, size2);
801 int_arg_regs_used++;
803 break;
806 if (arg_type == ARG_IN_FLOAT_REGS &&
807 (float_arg_regs_used + regs_used) <= MAX_FLOAT_ARG_REGS)
809 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, size);
810 stacked_args_size += size;
811 rbpoffsets[i] = stacked_args_size;
813 if (size1 == 4)
814 amd64_movss_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset1);
815 else
816 amd64_movsd_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset1);
817 float_arg_regs_used++;
819 if (regs_used > 1)
821 if (size2 == 4)
822 amd64_movss_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset2);
823 else
824 amd64_movsd_reg_membase (p, float_arg_regs_used, AMD64_RSP, offset2);
825 float_arg_regs_used++;
827 break;
830 rbpoffsets[i] = next_stack_arg_rbp_offset;
831 next_stack_arg_rbp_offset += size;
833 break;
835 case MONO_TYPE_R4:
836 if (float_arg_regs_used < MAX_FLOAT_ARG_REGS) {
837 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, 8);
838 amd64_movss_regp_reg (p, AMD64_RSP, float_arg_regs_used);
839 float_arg_regs_used++;
840 stacked_args_size += 8;
841 rbpoffsets[i] = -stacked_args_size;
843 else
845 rbpoffsets[i] = next_stack_arg_rbp_offset;
846 next_stack_arg_rbp_offset += 8;
848 break;
849 case MONO_TYPE_R8:
850 stacked_args_size += 8;
851 if (float_arg_regs_used < MAX_FLOAT_ARG_REGS) {
852 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, 8);
853 amd64_movsd_regp_reg (p, AMD64_RSP, float_arg_regs_used);
854 float_arg_regs_used++;
855 stacked_args_size += 8;
856 rbpoffsets[i] = -stacked_args_size;
858 else
860 rbpoffsets[i] = next_stack_arg_rbp_offset;
861 next_stack_arg_rbp_offset += 8;
863 break;
864 default:
865 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
869 local_size = sizeof (MonoInvocation) + sizeof (stackval) * (sig->param_count + 1) + stacked_args_size;
871 local_size += 15;
872 local_size &= ~15;
874 stackval_pos = -local_size;
875 mono_invocation_pos = stackval_pos + sizeof (stackval) * (sig->param_count + 1);
877 /* stacked_args_size has already been pushed onto the stack. Make room for the rest of it. */
878 amd64_alu_reg_imm (p, X86_SUB, AMD64_RSP, local_size - stacked_args_size);
880 /* Be careful not to trash any arg regs before saving this_reg to MonoInvocation structure below. */
883 * Initialize MonoInvocation fields, first the ones known now.
885 amd64_alu_reg_reg (p, X86_XOR, AMD64_RAX, AMD64_RAX);
886 amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, ex)), AMD64_RAX, 8);
887 amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, ex_handler)), AMD64_RAX, 8);
888 amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, parent)), AMD64_RAX, 8);
890 * Set the method pointer.
892 amd64_mov_membase_imm (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, method)), (long)method, 8);
895 * Handle this.
897 if (sig->hasthis)
898 amd64_mov_membase_reg(p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, obj)), this_reg, 8);
901 * Handle the arguments. stackval_pos is the offset from RBP of the stackval in the MonoInvocation args array .
902 * arg_pos is the offset from RBP to the incoming arg on the stack.
903 * We just call stackval_from_data to handle all the (nasty) issues....
905 amd64_lea_membase (p, AMD64_RAX, AMD64_RBP, stackval_pos);
906 amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, stack_args)), AMD64_RAX, 8);
907 for (i = 0; i < sig->param_count; ++i) {
908 /* Need to call stackval_from_data (MonoType *type, stackval *result, char *data, gboolean pinvoke); */
909 amd64_mov_reg_imm (p, AMD64_R11, stackval_from_data);
910 amd64_mov_reg_imm (p, int_arg_regs[0], sig->params[i]);
911 amd64_lea_membase (p, int_arg_regs[1], AMD64_RBP, stackval_pos);
912 amd64_lea_membase (p, int_arg_regs[2], AMD64_RBP, rbpoffsets[i]);
913 amd64_mov_reg_imm (p, int_arg_regs[3], sig->pinvoke);
914 amd64_call_reg (p, AMD64_R11);
915 stackval_pos += sizeof (stackval);
916 #if 0
917 /* fixme: alignment */
918 if (sig->pinvoke)
919 arg_pos += mono_type_native_stack_size (sig->params [i], &align);
920 else
921 arg_pos += mono_type_stack_size (sig->params [i], &align);
922 #endif
926 * Handle the return value storage area.
928 amd64_lea_membase (p, AMD64_RAX, AMD64_RBP, stackval_pos);
929 amd64_mov_membase_reg (p, AMD64_RBP, (mono_invocation_pos + G_STRUCT_OFFSET (MonoInvocation, retval)), AMD64_RAX, 8);
930 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) {
931 MonoClass *klass = sig->ret->data.klass;
932 if (!klass->enumtype) {
933 amd64_mov_reg_membase (p, AMD64_RCX, AMD64_RBP, retval_ptr_rbp_offset, 8);
934 amd64_mov_membase_reg (p, AMD64_RBP, stackval_pos, AMD64_RCX, 8);
939 * Call the method.
941 amd64_lea_membase (p, int_arg_regs[0], AMD64_RBP, mono_invocation_pos);
942 amd64_mov_reg_imm (p, AMD64_R11, ves_exec_method);
943 amd64_call_reg (p, AMD64_R11);
946 * Move the return value to the proper place.
948 amd64_lea_membase (p, AMD64_RAX, AMD64_RBP, stackval_pos);
949 if (sig->ret->byref) {
950 amd64_mov_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 8);
951 } else {
952 int simpletype = sig->ret->type;
953 enum_retvalue:
954 switch (sig->ret->type) {
955 case MONO_TYPE_VOID:
956 break;
957 case MONO_TYPE_BOOLEAN:
958 case MONO_TYPE_I1:
959 case MONO_TYPE_U1:
960 amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 1);
961 break;
962 case MONO_TYPE_CHAR:
963 case MONO_TYPE_I2:
964 case MONO_TYPE_U2:
965 amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 2);
966 break;
967 case MONO_TYPE_I4:
968 case MONO_TYPE_U4:
969 case MONO_TYPE_I:
970 case MONO_TYPE_U:
971 case MONO_TYPE_OBJECT:
972 case MONO_TYPE_STRING:
973 case MONO_TYPE_CLASS:
974 amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 4);
975 break;
976 case MONO_TYPE_I8:
977 amd64_movzx_reg_membase (p, AMD64_RAX, AMD64_RAX, 0, 8);
978 break;
979 case MONO_TYPE_R4:
980 amd64_movss_regp_reg (p, AMD64_RAX, AMD64_XMM0);
981 break;
982 case MONO_TYPE_R8:
983 amd64_movsd_regp_reg (p, AMD64_RAX, AMD64_XMM0);
984 break;
985 case MONO_TYPE_VALUETYPE: {
986 int size;
987 int arg_type;
988 int regs_used;
989 int offset1;
990 int size1;
991 int offset2;
992 int size2;
994 if (sig->ret->data.klass->enumtype) {
995 simpletype = sig->ret->data.klass->enum_basetype->type;
996 goto enum_retvalue;
999 arg_type = value_type_info(sig->params [i]->data.klass, &size, &regs_used, &offset1, &size1, &offset2, &size2);
1001 if (arg_type == ARG_IN_INT_REGS)
1003 if (regs_used > 1)
1004 amd64_mov_membase_reg (p, AMD64_RAX, offset2, AMD64_RDX, size2);
1005 amd64_mov_membase_reg (p, AMD64_RAX, offset1, AMD64_RAX, size1);
1006 break;
1009 if (arg_type == ARG_IN_FLOAT_REGS)
1011 if (size1 == 4)
1012 amd64_movss_membase_reg (p, AMD64_RAX, offset1, AMD64_XMM0);
1013 else
1014 amd64_movsd_membase_reg (p, AMD64_RAX, offset1, AMD64_XMM0);
1016 if (regs_used > 1)
1018 if (size2 == 4)
1019 amd64_movss_membase_reg (p, AMD64_RAX, offset2, AMD64_XMM1);
1020 else
1021 amd64_movsd_membase_reg (p, AMD64_RAX, offset2, AMD64_XMM1);
1023 break;
1026 /* Else result should have been stored in place already. IA32 code has a stackval_to_data call here, which
1027 * looks wrong to me as the pointer in the stack val being converted is setup to point to the output area anyway.
1028 * It all looks a bit suspect anyway.
1030 break;
1032 default:
1033 g_error ("Type 0x%x not handled yet in thunk creation", sig->ret->type);
1034 break;
1039 * Standard epilog.
1041 amd64_leave (p);
1042 amd64_ret (p);
1044 g_assert (p - code_buffer < 512);
1046 ji = g_new0 (MonoJitInfo, 1);
1047 ji->method = method;
1048 ji->code_size = p - code_buffer;
1049 ji->code_start = g_memdup (code_buffer, p - code_buffer);
1051 mono_jit_info_table_add (mono_get_root_domain (), ji);
1053 return ji->code_start;